Skip to content

Commit

Permalink
Merge branch 'master' into AG-28327-upstream-config-parser
Browse files Browse the repository at this point in the history
  • Loading branch information
schzhn committed Jan 29, 2024
2 parents f9e7372 + a2506cd commit 11a2500
Show file tree
Hide file tree
Showing 31 changed files with 983 additions and 511 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ Application Options:
--ipv6-disabled If specified, all AAAA requests will be replied with NoError RCode and empty answer
--bogus-nxdomain= Transform the responses containing at least a single IP that matches specified addresses and CIDRs into NXDOMAIN. Can be specified multiple times.
--udp-buf-size= Set the size of the UDP buffer in bytes. A value <= 0 will use the system default.
--max-go-routines= Set the maximum number of go routines. A value <= 0 will not not set a maximum.
--max-go-routines= Set the maximum number of go routines. A zero value will not not set a maximum.
--pprof If present, exposes pprof information on localhost:6060.
--version Prints the program version
Expand Down
17 changes: 9 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/AdguardTeam/dnsproxy
go 1.20

require (
github.com/AdguardTeam/golibs v0.18.0
github.com/AdguardTeam/golibs v0.19.0
github.com/ameshkov/dnscrypt/v2 v2.2.7
github.com/ameshkov/dnsstamps v1.0.3
github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0
Expand All @@ -13,9 +13,9 @@ require (
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/quic-go/quic-go v0.39.1
github.com/stretchr/testify v1.8.4
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
golang.org/x/net v0.17.0
golang.org/x/sys v0.13.0
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848
golang.org/x/net v0.19.0
golang.org/x/sys v0.15.0
gopkg.in/yaml.v3 v3.0.1
)

Expand All @@ -32,9 +32,10 @@ require (
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.3.4 // indirect
go.uber.org/mock v0.3.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/tools v0.13.0 // indirect
golang.org/x/crypto v0.16.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.16.0 // indirect
gonum.org/v1/gonum v0.14.0
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
)
36 changes: 19 additions & 17 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
github.com/AdguardTeam/golibs v0.18.0 h1:ckS2YK7t2Ub6UkXl0fnreVaM15Zb07Hh1gmFqttjpWg=
github.com/AdguardTeam/golibs v0.18.0/go.mod h1:DKhCIXHcUYtBhU8ibTLKh1paUL96n5zhQBlx763sj+U=
github.com/AdguardTeam/golibs v0.19.0 h1:y/x+Xn3pDg1ZfQ+QEZapPJqaeVYUIMp/EODMtVhn7PM=
github.com/AdguardTeam/golibs v0.19.0/go.mod h1:3WunclLLfrVAq7fYQRhd6f168FHOEMssnipVXCxDL/w=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
Expand Down Expand Up @@ -52,22 +52,24 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 h1:+iq7lrkxmFNBM7xx+Rae2W6uyPfhPeDWD+n+JgppptE=
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0=
gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
Expand Down
75 changes: 46 additions & 29 deletions internal/bootstrap/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,42 @@ import (
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"golang.org/x/exp/slices"
)

// Network is a network type for use in [Resolver]'s methods.
type Network = string

const (
// NetworkIP is a network type for both address families.
NetworkIP Network = "ip"

// NetworkIP4 is a network type for IPv4 address family.
NetworkIP4 Network = "ip4"

// NetworkIP6 is a network type for IPv6 address family.
NetworkIP6 Network = "ip6"

// NetworkTCP is a network type for TCP connections.
NetworkTCP Network = "tcp"

// NetworkUDP is a network type for UDP connections.
NetworkUDP Network = "udp"
)

// DialHandler is a dial function for creating unencrypted network connections
// to the upstream server. It establishes the connection to the server
// specified at initialization and ignores the addr.
type DialHandler func(ctx context.Context, network, addr string) (conn net.Conn, err error)
// specified at initialization and ignores the addr. network must be one of
// [NetworkTCP] or [NetworkUDP].
type DialHandler func(ctx context.Context, network Network, addr string) (conn net.Conn, err error)

// ResolveDialContext returns a DialHandler that uses addresses resolved from u
// using resolver. u must not be nil.
func ResolveDialContext(
u *url.URL,
timeout time.Duration,
resolver Resolver,
preferIPv6 bool,
r Resolver,
preferV6 bool,
) (h DialHandler, err error) {
defer func() { err = errors.Annotate(err, "dialing %q: %w", u.Host) }()

Expand All @@ -38,7 +60,7 @@ func ResolveDialContext(
return nil, err
}

if resolver == nil {
if r == nil {
return nil, fmt.Errorf("resolver is nil: %w", ErrNoResolvers)
}

Expand All @@ -49,36 +71,28 @@ func ResolveDialContext(
defer cancel()
}

ips, err := resolver.LookupNetIP(ctx, "ip", host)
ips, err := r.LookupNetIP(ctx, NetworkIP, host)
if err != nil {
return nil, fmt.Errorf("resolving hostname: %w", err)
}

proxynetutil.SortNetIPAddrs(ips, preferIPv6)
if preferV6 {
slices.SortStableFunc(ips, proxynetutil.PreferIPv6)
} else {
slices.SortStableFunc(ips, proxynetutil.PreferIPv4)
}

addrs := make([]string, 0, len(ips))
for _, ip := range ips {
if !ip.IsValid() {
// All invalid addresses should be in the tail after sorting.
break
}

addrs = append(addrs, netip.AddrPortFrom(ip, uint16(port)).String())
addrs = append(addrs, netip.AddrPortFrom(ip, port).String())
}

return NewDialContext(timeout, addrs...), nil
}

// NewDialContext returns a DialHandler that dials addrs and returns the first
// successful connection. At least a single addr should be specified.
//
// TODO(e.burkov): Consider using [Resolver] instead of
// [upstream.Options.Bootstrap] and [upstream.Options.ServerIPAddrs].
func NewDialContext(timeout time.Duration, addrs ...string) (h DialHandler) {
dialer := &net.Dialer{
Timeout: timeout,
}

l := len(addrs)
if l == 0 {
log.Debug("bootstrap: no addresses to dial")
Expand All @@ -88,9 +102,11 @@ func NewDialContext(timeout time.Duration, addrs ...string) (h DialHandler) {
}
}

// TODO(e.burkov): Check IPv6 preference here.
dialer := &net.Dialer{
Timeout: timeout,
}

return func(ctx context.Context, network, _ string) (conn net.Conn, err error) {
return func(ctx context.Context, network Network, _ string) (conn net.Conn, err error) {
var errs []error

// Return first succeeded connection. Note that we're using addrs
Expand All @@ -101,17 +117,18 @@ func NewDialContext(timeout time.Duration, addrs ...string) (h DialHandler) {
start := time.Now()
conn, err = dialer.DialContext(ctx, network, addr)
elapsed := time.Since(start)
if err == nil {
log.Debug("bootstrap: connection to %s succeeded in %s", addr, elapsed)
if err != nil {
log.Debug("bootstrap: connection to %s failed in %s: %s", addr, elapsed, err)
errs = append(errs, err)

return conn, nil
continue
}

log.Debug("bootstrap: connection to %s failed in %s: %s", addr, elapsed, err)
errs = append(errs, err)
log.Debug("bootstrap: connection to %s succeeded in %s", addr, elapsed)

return conn, nil
}

// TODO(e.burkov): Use errors.Join in Go 1.20.
return nil, errors.List("all dialers failed", errs...)
return nil, errors.Join(errs...)
}
}
8 changes: 4 additions & 4 deletions internal/bootstrap/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func TestResolveDialContext(t *testing.T) {
network string,
host string,
) (addrs []netip.Addr, err error) {
require.Equal(pt, "ip", network)
require.Equal(pt, bootstrap.NetworkIP, network)
require.Equal(pt, hostname, host)

return tc.addresses, nil
Expand All @@ -103,7 +103,7 @@ func TestResolveDialContext(t *testing.T) {
)
require.NoError(t, err)

conn, err := dialContext(context.Background(), "tcp", "")
conn, err := dialContext(context.Background(), bootstrap.NetworkTCP, "")
require.NoError(t, err)

expected, ok := testutil.RequireReceive(t, sig, testTimeout)
Expand All @@ -120,7 +120,7 @@ func TestResolveDialContext(t *testing.T) {
network string,
host string,
) (addrs []netip.Addr, err error) {
require.Equal(pt, "ip", network)
require.Equal(pt, bootstrap.NetworkIP, network)
require.Equal(pt, hostname, host)

return nil, nil
Expand All @@ -135,7 +135,7 @@ func TestResolveDialContext(t *testing.T) {
)
require.NoError(t, err)

_, err = dialContext(context.Background(), "tcp", "")
_, err = dialContext(context.Background(), bootstrap.NetworkTCP, "")
testutil.AssertErrorMsg(t, "no addresses", err)
})

Expand Down
6 changes: 6 additions & 0 deletions internal/bootstrap/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package bootstrap

import "github.com/AdguardTeam/golibs/errors"

// ErrNoResolvers is returned when zero resolvers specified.
const ErrNoResolvers errors.Error = "no resolvers specified"
66 changes: 56 additions & 10 deletions internal/bootstrap/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,21 @@ import (

"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"golang.org/x/exp/slices"
)

// Resolver resolves the hostnames to IP addresses.
// Resolver resolves the hostnames to IP addresses. Note, that [net.Resolver]
// from standard library also implements this interface.
type Resolver interface {
// LookupNetIP looks up the IP addresses for the given host. network must
// be one of "ip", "ip4" or "ip6". The response may be empty even if err is
// nil.
LookupNetIP(ctx context.Context, network, host string) (addrs []netip.Addr, err error)
// LookupNetIP looks up the IP addresses for the given host. network should
// be one of [NetworkIP], [NetworkIP4] or [NetworkIP6]. The response may be
// empty even if err is nil. All the addrs must be valid.
LookupNetIP(ctx context.Context, network Network, host string) (addrs []netip.Addr, err error)
}

// type check
var _ Resolver = &net.Resolver{}

// ErrNoResolvers is returned when zero resolvers specified.
const ErrNoResolvers errors.Error = "no resolvers specified"

// ParallelResolver is a slice of resolvers that are queried concurrently. The
// first successful response is returned.
type ParallelResolver []Resolver
Expand All @@ -34,7 +33,7 @@ var _ Resolver = ParallelResolver(nil)
// LookupNetIP implements the [Resolver] interface for ParallelResolver.
func (r ParallelResolver) LookupNetIP(
ctx context.Context,
network string,
network Network,
host string,
) (addrs []netip.Addr, err error) {
resolversNum := len(r)
Expand All @@ -48,7 +47,7 @@ func (r ParallelResolver) LookupNetIP(
}

// Size of channel must accommodate results of lookups from all resolvers,
// sending into channel will be block otherwise.
// sending into channel will block otherwise.
ch := make(chan any, resolversNum)
for _, rslv := range r {
go lookupAsync(ctx, rslv, network, host, ch)
Expand Down Expand Up @@ -97,3 +96,50 @@ func lookup(ctx context.Context, r Resolver, network, host string) (addrs []neti

return addrs, err
}

// ConsequentResolver is a slice of resolvers that are queried in order until
// the first successful non-empty response, as opposed to just successful
// response requirement in [ParallelResolver].
type ConsequentResolver []Resolver

// type check
var _ Resolver = ConsequentResolver(nil)

// LookupNetIP implements the [Resolver] interface for ConsequentResolver.
func (resolvers ConsequentResolver) LookupNetIP(
ctx context.Context,
network Network,
host string,
) (addrs []netip.Addr, err error) {
if len(resolvers) == 0 {
return nil, ErrNoResolvers
}

var errs []error
for _, r := range resolvers {
addrs, err = r.LookupNetIP(ctx, network, host)
if err == nil && len(addrs) > 0 {
return addrs, nil
}

errs = append(errs, err)
}

return nil, errors.Join(errs...)
}

// StaticResolver is a resolver which always responds with an underlying slice
// of IP addresses regardless of host and network.
type StaticResolver []netip.Addr

// type check
var _ Resolver = StaticResolver(nil)

// LookupNetIP implements the [Resolver] interface for StaticResolver.
func (r StaticResolver) LookupNetIP(
_ context.Context,
_ Network,
_ string,
) (addrs []netip.Addr, err error) {
return slices.Clone(r), nil
}
Loading

0 comments on commit 11a2500

Please sign in to comment.