diff --git a/README.md b/README.md index bcfe25879..8ab08e15a 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,8 @@ Application Options: be specified multiple times --all-servers If specified, parallel queries to all configured upstream servers are enabled --fastest-addr Respond to A or AAAA requests only with the fastest IP address + --timeout= Timeout for outbound DNS queries to remote upstream servers in a + human-readable form (default: 10s) --cache If specified, DNS cache is enabled --cache-size= Cache size (in bytes). Default: 64k --cache-min-ttl= Minimum TTL value for DNS entries, in seconds. Capped at 3600. diff --git a/config.yaml.dist b/config.yaml.dist index 78bbaf742..584f47a8c 100644 --- a/config.yaml.dist +++ b/config.yaml.dist @@ -15,3 +15,4 @@ ratelimit: 0 udp-buf-size: 0 upstream: - "1.1.1.1:53" +timeout: '10s' diff --git a/main.go b/main.go index fd9289fa1..ac46d2e2e 100644 --- a/main.go +++ b/main.go @@ -17,7 +17,9 @@ import ( "github.com/AdguardTeam/dnsproxy/proxy" "github.com/AdguardTeam/dnsproxy/upstream" "github.com/AdguardTeam/golibs/log" + "github.com/AdguardTeam/golibs/mathutil" "github.com/AdguardTeam/golibs/netutil" + "github.com/AdguardTeam/golibs/timeutil" "github.com/ameshkov/dnscrypt/v2" goFlags "github.com/jessevdk/go-flags" "gopkg.in/yaml.v3" @@ -110,6 +112,10 @@ type Options struct { // detected by ICMP response time or TCP connection time FastestAddress bool `yaml:"fastest-addr" long:"fastest-addr" description:"Respond to A or AAAA requests only with the fastest IP address" optional:"yes" optional-value:"true"` + // Timeout for outbound DNS queries to remote upstream servers in a + // human-readable form. Default is 10s. + Timeout timeutil.Duration `yaml:"timeout" long:"timeout" description:"Timeout for outbound DNS queries to remote upstream servers in a human-readable form" default:"10s"` + // Cache settings // -- @@ -184,7 +190,6 @@ type Options struct { var VersionString = "dev" // nolint:gochecknoglobals const ( - defaultTimeout = 10 * time.Second defaultLocalTimeout = 1 * time.Second ) @@ -351,12 +356,13 @@ func initUpstreams(config *proxy.Config, options *Options) { var err error + timeout := options.Timeout.Duration upstreams := loadServersList(options.Upstreams) upsOpts := &upstream.Options{ HTTPVersions: httpVersions, InsecureSkipVerify: options.Insecure, Bootstrap: options.BootstrapDNS, - Timeout: defaultTimeout, + Timeout: timeout, } config.UpstreamConfig, err = proxy.ParseUpstreamsConfig(upstreams, upsOpts) if err != nil { @@ -367,7 +373,7 @@ func initUpstreams(config *proxy.Config, options *Options) { privUpsOpts := &upstream.Options{ HTTPVersions: httpVersions, Bootstrap: options.BootstrapDNS, - Timeout: defaultLocalTimeout, + Timeout: mathutil.Min(defaultLocalTimeout, timeout), } config.PrivateRDNSUpstreamConfig, err = proxy.ParseUpstreamsConfig(privUpstreams, privUpsOpts) if err != nil { diff --git a/vendor/github.com/AdguardTeam/golibs/timeutil/duration.go b/vendor/github.com/AdguardTeam/golibs/timeutil/duration.go new file mode 100644 index 000000000..3ce2fca59 --- /dev/null +++ b/vendor/github.com/AdguardTeam/golibs/timeutil/duration.go @@ -0,0 +1,71 @@ +// Package timeutil contains types and utilities for dealing with time and +// duration values. +package timeutil + +import ( + "time" + + "github.com/AdguardTeam/golibs/errors" +) + +// Day is the duration of one day. +const Day time.Duration = 24 * time.Hour + +// Duration is a wrapper for time.Duration providing functionality for encoding. +type Duration struct { + // time.Duration is embedded here to avoid implementing all the methods. + time.Duration +} + +// String implements the fmt.Stringer interface for Duration. It wraps +// time.Duration.String method and additionally cuts off non-leading zero values +// of minutes and seconds. Some values which are differ between the +// implementations: +// +// Duration: "1m", time.Duration: "1m0s" +// Duration: "1h", time.Duration: "1h0m0s" +// Duration: "1h1m", time.Duration: "1h1m0s" +func (d Duration) String() (str string) { + str = d.Duration.String() + + const ( + tailMin = len(`0s`) + tailMinSec = len(`0m0s`) + ) + + const ( + secsInHour = time.Hour / time.Second + minsInHour = time.Hour / time.Minute + ) + + switch rounded := d.Duration / time.Second; { + case + rounded == 0, + rounded*time.Second != d.Duration, + rounded%60 != 0: + // Return the uncut value if it's either equal to zero or has + // fractions of a second or even whole seconds in it. + return str + case (rounded%secsInHour)/minsInHour != 0: + return str[:len(str)-tailMin] + default: + return str[:len(str)-tailMinSec] + } +} + +// MarshalText implements the encoding.TextMarshaler interface for Duration. +func (d Duration) MarshalText() (text []byte, err error) { + return []byte(d.String()), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface for +// *Duration. +// +// TODO(e.burkov): Make it able to parse larger units like days. +func (d *Duration) UnmarshalText(b []byte) (err error) { + defer func() { err = errors.Annotate(err, "unmarshaling duration: %w") }() + + d.Duration, err = time.ParseDuration(string(b)) + + return err +} diff --git a/vendor/modules.txt b/vendor/modules.txt index e82f75302..fa9504dd3 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -7,6 +7,7 @@ github.com/AdguardTeam/golibs/mathutil github.com/AdguardTeam/golibs/netutil github.com/AdguardTeam/golibs/stringutil github.com/AdguardTeam/golibs/testutil +github.com/AdguardTeam/golibs/timeutil # github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da ## explicit github.com/aead/chacha20/chacha