Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
EugeneOne1 committed Apr 10, 2024
1 parent afa9d61 commit 4de1eb2
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 194 deletions.
24 changes: 22 additions & 2 deletions internal/dnsforward/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,26 @@ func (s *Server) prepareIpsetListSettings() (err error) {
return s.ipset.init(ipsets)
}

// loadUpstreams parses upstream DNS servers from the configured file or from
// the configuration itself.
func (conf *ServerConfig) loadUpstreams() (upstreams []string, err error) {
if conf.UpstreamDNSFileName == "" {
return stringutil.FilterOut(conf.UpstreamDNS, IsCommentOrEmpty), nil
}

var data []byte
data, err = os.ReadFile(conf.UpstreamDNSFileName)
if err != nil {
return nil, fmt.Errorf("reading upstream from file: %w", err)
}

upstreams = stringutil.SplitTrimmed(string(data), "\n")

log.Debug("dnsforward: got %d upstreams in %q", len(upstreams), conf.UpstreamDNSFileName)

return stringutil.FilterOut(upstreams, IsCommentOrEmpty), nil
}

// collectListenAddr adds addrPort to addrs. It also adds its port to
// unspecPorts if its address is unspecified.
func collectListenAddr(
Expand Down Expand Up @@ -543,8 +563,8 @@ func (m *combinedAddrPortSet) Has(addrPort netip.AddrPort) (ok bool) {
return m.ports.Has(addrPort.Port()) && m.addrs.Has(addrPort.Addr())
}

// filterOut filters out all the upstreams that match um. It returns all the
// closing errors joined.
// filterOutAddrs filters out all the upstreams that match um. It returns all
// the closing errors joined.
func filterOutAddrs(upsConf *proxy.UpstreamConfig, set addrPortSet) (err error) {
var errs []error
delFunc := func(u upstream.Upstream) (ok bool) {
Expand Down
167 changes: 97 additions & 70 deletions internal/dnsforward/dnsforward.go
Original file line number Diff line number Diff line change
Expand Up @@ -451,69 +451,6 @@ func (s *Server) startLocked() error {
return err
}

// ErrBadPrivateRDNSUpstreams is returned when the private rDNS upstreams are
// invalid but enabled.
//
// TODO(e.burkov): Consider allowing to use incomplete private rDNS upstreams
// configuration in proxy when the private rDNS function is enabled. In theory,
// proxy supports the case when no upstreams provided to resolve the private
// request, since it already supports this for DNS64-prefixed PTR requests.
const ErrBadPrivateRDNSUpstreams errors.Error = "bad private rDNS upstreams"

// prepareLocalResolvers initializes the local upstreams configuration using
// boot as bootstrap. It assumes that s.serverLock is locked or s not running.
func (s *Server) prepareLocalResolvers(
boot upstream.Resolver,
) (uc *proxy.UpstreamConfig, err error) {
set, err := s.conf.ourAddrsSet()
if err != nil {
// Don't wrap the error because it's informative enough as is.
return nil, err
}

resolvers := s.conf.LocalPTRResolvers
confNeedsFiltering := len(resolvers) > 0
if confNeedsFiltering {
resolvers = stringutil.FilterOut(resolvers, IsCommentOrEmpty)
} else {
sysResolvers := slices.DeleteFunc(slices.Clone(s.sysResolvers.Addrs()), set.Has)
resolvers = make([]string, 0, len(sysResolvers))
for _, r := range sysResolvers {
resolvers = append(resolvers, r.String())
}
}

log.Debug("dnsforward: upstreams to resolve ptr for local addresses: %v", resolvers)

uc, err = s.prepareUpstreamConfig(resolvers, nil, &upstream.Options{
Bootstrap: boot,
Timeout: defaultLocalTimeout,
// TODO(e.burkov): Should we verify server's certificates?
PreferIPv6: s.conf.BootstrapPreferIPv6,
})
if err != nil {
return nil, fmt.Errorf("preparing private upstreams: %w", err)
}

if confNeedsFiltering {
err = filterOutAddrs(uc, set)
if err != nil {
return nil, fmt.Errorf("filtering private upstreams: %w", err)
}
}

// Prevalidate the config to catch the exact error before creating proxy.
// See TODO on [ErrBadPrivateRDNSUpstreams].
err = proxy.ValidatePrivateConfig(uc, s.privateNets)
if err != nil {
log.Debug("dnsforward: validating private rdns upstreams: %s", err)

return nil, ErrBadPrivateRDNSUpstreams
}

return uc, nil
}

// Prepare initializes parameters of s using data from conf. conf must not be
// nil.
func (s *Server) Prepare(conf *ServerConfig) (err error) {
Expand Down Expand Up @@ -571,6 +508,95 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
return nil
}

// prepareUpstreamSettings sets upstream DNS server settings.
func (s *Server) prepareUpstreamSettings(boot upstream.Resolver) (err error) {
// Load upstreams either from the file, or from the settings
var upstreams []string
upstreams, err = s.conf.loadUpstreams()
if err != nil {
return fmt.Errorf("loading upstreams: %w", err)
}

s.conf.UpstreamConfig, err = newUpstreamConfig(upstreams, defaultDNS, &upstream.Options{
Bootstrap: boot,
Timeout: s.conf.UpstreamTimeout,
HTTPVersions: UpstreamHTTPVersions(s.conf.UseHTTP3Upstreams),
PreferIPv6: s.conf.BootstrapPreferIPv6,
// Use a customized set of RootCAs, because Go's default mechanism of
// loading TLS roots does not always work properly on some routers so we're
// loading roots manually and pass it here.
//
// See [aghtls.SystemRootCAs].
//
// TODO(a.garipov): Investigate if that's true.
RootCAs: s.conf.TLSv12Roots,
CipherSuites: s.conf.TLSCiphers,
})
if err != nil {
return fmt.Errorf("preparing upstream config: %w", err)
}

return nil
}

// PrivateRDNSError is returned when the private rDNS upstreams are
// invalid but enabled.
//
// TODO(e.burkov): Consider allowing to use incomplete private rDNS upstreams
// configuration in proxy when the private rDNS function is enabled. In theory,
// proxy supports the case when no upstreams provided to resolve the private
// request, since it already supports this for DNS64-prefixed PTR requests.
type PrivateRDNSError struct {
err error
}

// Error implements the [errors.Error] interface.
func (e *PrivateRDNSError) Error() (s string) {
return e.err.Error()
}

func (e *PrivateRDNSError) Unwrap() (err error) {
return e.err
}

// prepareLocalResolvers initializes the private RDNS upstream configuration
// according to the server's settings. It assumes s.serverLock is locked or the
// Server not running.
func (s *Server) prepareLocalResolvers() (uc *proxy.UpstreamConfig, err error) {
if !s.conf.UsePrivateRDNS {
return nil, nil
}

var ownAddrs addrPortSet
ownAddrs, err = s.conf.ourAddrsSet()
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return nil, err
}

opts := &upstream.Options{
Bootstrap: s.bootstrap,
Timeout: defaultLocalTimeout,
// TODO(e.burkov): Should we verify server's certificates?
PreferIPv6: s.conf.BootstrapPreferIPv6,
}

addrs := s.conf.LocalPTRResolvers
uc, err = newLocalResolvers(addrs, ownAddrs, s.sysResolvers, s.privateNets, opts)
if err != nil {
return nil, fmt.Errorf("preparing resolvers: %w", err)
}

// Prevalidate the config to catch the exact error before creating proxy.
// See TODO on [ErrBadPrivateRDNSUpstreams].
err = proxy.ValidatePrivateConfig(uc, s.privateNets)
if err != nil {
return nil, err
}

return uc, nil
}

// prepareInternalDNS initializes the internal state of s before initializing
// the primary DNS proxy instance. It assumes s.serverLock is locked or the
// Server not running.
Expand All @@ -580,10 +606,12 @@ func (s *Server) prepareInternalDNS() (err error) {
return fmt.Errorf("preparing ipset settings: %w", err)
}

s.bootstrap, s.bootResolvers, err = s.createBootstrap(s.conf.BootstrapDNS, &upstream.Options{
bootOpts := &upstream.Options{
Timeout: DefaultTimeout,
HTTPVersions: UpstreamHTTPVersions(s.conf.UseHTTP3Upstreams),
})
}

s.bootstrap, s.bootResolvers, err = newBootstrap(s.conf.BootstrapDNS, s.etcHosts, bootOpts)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
Expand All @@ -595,11 +623,9 @@ func (s *Server) prepareInternalDNS() (err error) {
return err
}

if s.conf.UsePrivateRDNS {
s.conf.PrivateRDNSUpstreamConfig, err = s.prepareLocalResolvers(s.bootstrap)
if err != nil {
return fmt.Errorf("setting up resolvers: %w", err)
}
s.conf.PrivateRDNSUpstreamConfig, err = s.prepareLocalResolvers()
if err != nil {
return err
}

err = s.prepareInternalProxy()
Expand Down Expand Up @@ -694,6 +720,7 @@ func (s *Server) prepareInternalProxy() (err error) {
UseDNS64: srvConf.UseDNS64,
DNS64Prefs: srvConf.DNS64Prefixes,
UsePrivateRDNS: srvConf.UsePrivateRDNS,
PrivateSubnets: s.privateNets,
MessageConstructor: s,
}

Expand Down
68 changes: 34 additions & 34 deletions internal/dnsforward/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,10 +264,14 @@ func (req *jsonDNSConfig) checkUpstreamMode() (err error) {
// validate returns an error if any field of req is invalid.
//
// TODO(s.chzhen): Parse, don't validate.
func (req *jsonDNSConfig) validate(privateNets netutil.SubnetSet) (err error) {
func (req *jsonDNSConfig) validate(
ownAddrs addrPortSet,
sysResolvers SystemResolvers,
privateNets netutil.SubnetSet,
) (err error) {
defer func() { err = errors.Annotate(err, "validating dns config: %w") }()

err = req.validateUpstreamDNSServers(privateNets)
err = req.validateUpstreamDNSServers(ownAddrs, sysResolvers, privateNets)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
Expand Down Expand Up @@ -300,25 +304,6 @@ func (req *jsonDNSConfig) validate(privateNets netutil.SubnetSet) (err error) {
return nil
}

// checkUpstreams returns an error if lines can't be parsed as an upstream
// configuration. If privateNets is not nil, it also checks that the domain
// specifications are strictly ARPA domains containing the prefixes within the
// set.
func checkUpstreams(lines []string, section string, privateNets netutil.SubnetSet) (err error) {
defer func() { err = errors.Annotate(err, "%s servers: %w", section) }()

uc, err := proxy.ParseUpstreamsConfig(lines, &upstream.Options{})
if err == nil {
defer func() { err = errors.WithDeferred(err, uc.Close()) }()

if privateNets != nil {
err = proxy.ValidatePrivateConfig(uc, privateNets)
}
}

return err
}

// checkBootstrap returns an error if any bootstrap address is invalid.
func (req *jsonDNSConfig) checkBootstrap() (err error) {
if req.Bootstraps == nil {
Expand Down Expand Up @@ -348,20 +333,27 @@ func (req *jsonDNSConfig) checkBootstrap() (err error) {
}

// validateUpstreamDNSServers returns an error if any field of req is invalid.
func (req *jsonDNSConfig) validateUpstreamDNSServers(privateNets netutil.SubnetSet) (err error) {
func (req *jsonDNSConfig) validateUpstreamDNSServers(
ownAddrs addrPortSet,
sysResolvers SystemResolvers,
privateNets netutil.SubnetSet,
) (err error) {
var uc *proxy.UpstreamConfig
opts := &upstream.Options{}

if req.Upstreams != nil {
err = checkUpstreams(*req.Upstreams, "upstream", nil)
uc, err = newUpstreamConfig(*req.Upstreams, nil, opts)
err = errors.WithDeferred(err, uc.Close())
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
return fmt.Errorf("upstream servers: %w", err)
}
}

if req.LocalPTRUpstreams != nil {
err = checkUpstreams(*req.LocalPTRUpstreams, "private upstream", privateNets)
if addrs := req.LocalPTRUpstreams; addrs != nil {
uc, err = newLocalResolvers(*addrs, ownAddrs, sysResolvers, privateNets, opts)
err = errors.WithDeferred(err, uc.Close())
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
return fmt.Errorf("private upstream servers: %w", err)
}
}

Expand All @@ -372,10 +364,10 @@ func (req *jsonDNSConfig) validateUpstreamDNSServers(privateNets netutil.SubnetS
}

if req.Fallbacks != nil {
err = checkUpstreams(*req.Fallbacks, "fallback", nil)
uc, err = newUpstreamConfig(*req.Fallbacks, nil, opts)
err = errors.WithDeferred(err, uc.Close())
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
return fmt.Errorf("fallback servers: %w", err)
}
}

Expand Down Expand Up @@ -445,7 +437,15 @@ func (s *Server) handleSetConfig(w http.ResponseWriter, r *http.Request) {
return
}

err = req.validate(s.privateNets)
// TODO(e.burkov): Consider prebuilding this set on startup.
ourAddrs, err := s.conf.ourAddrsSet()
if err != nil {
aghhttp.Error(r, w, http.StatusInternalServerError, "getting our addresses: %s", err)

return
}

err = req.validate(ourAddrs, s.sysResolvers, s.privateNets)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)

Expand Down Expand Up @@ -596,7 +596,7 @@ func (s *Server) handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
}

var boots []*upstream.UpstreamResolver
opts.Bootstrap, boots, err = s.createBootstrap(req.BootstrapDNS, opts)
opts.Bootstrap, boots, err = newBootstrap(req.BootstrapDNS, s.etcHosts, opts)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "Failed to parse bootstrap servers: %s", err)

Expand Down
Loading

0 comments on commit 4de1eb2

Please sign in to comment.