Documentation
¶
Overview ¶
Package upstream implements DNS clients for all known DNS encryption protocols.
Index ¶
- Constants
- Variables
- type CachingResolver
- type ConsequentResolver
- type DialHandler
- type DialerInitializer
- type ExchangeAllResult
- type HTTPVersion
- type HostsResolver
- type Network
- type NotBootstrapError
- type Options
- func (o *Options) Clone() (clone UpstreamOptions)
- func (o *Options) DialTCP(ctx context.Context, addr string) (net.Conn, error)
- func (o *Options) DialUDP(ctx context.Context, addr string) (*net.UDPConn, error)
- func (o *Options) GetBootstrap() Resolver
- func (o *Options) GetCipherSuites() []uint16
- func (o *Options) GetHTTPVersions() []HTTPVersion
- func (o *Options) GetInsecureSkipVerify() bool
- func (o *Options) GetLogger() *slog.Logger
- func (o *Options) GetPreferIPv6() bool
- func (o *Options) GetQUICTracer() QUICTraceFunc
- func (o *Options) GetRootCAs() *x509.CertPool
- func (o *Options) GetTimeout() time.Duration
- func (o *Options) GetVerifyConnection() func(state tls.ConnectionState) error
- func (o *Options) GetVerifyDNSCryptCertificate() func(cert *dnscrypt.Cert) error
- func (o *Options) GetVerifyServerCertificate() func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
- func (o *Options) LookupIP(host string) ([]net.IP, error)
- func (o *Options) SetBootstrap(bootstrap Resolver)
- func (o *Options) SetLogger(logger *slog.Logger)
- type ParallelResolver
- type QUICTraceFunc
- type Resolver
- type StaticResolver
- type Upstream
- type UpstreamOptions
- type UpstreamResolver
Constants ¶
const ( // QUICCodeNoError is used when the connection or stream needs to be closed, // but there is no error to signal. QUICCodeNoError = quic.ApplicationErrorCode(0) // QUICCodeInternalError signals that the DoQ implementation encountered // an internal error and is incapable of pursuing the transaction or the // connection. QUICCodeInternalError = quic.ApplicationErrorCode(1) // QUICKeepAlivePeriod is the value that we pass to *quic.Config and that // controls the period with with keep-alive frames are being sent to the // connection. We set it to 20s as it would be in the quic-go@v0.27.1 with // KeepAlive field set to true This value is specified in // https://pkg.go.dev/github.com/quic-go/quic-go/internal/protocol#MaxKeepAliveInterval. // // TODO(ameshkov): Consider making it configurable. QUICKeepAlivePeriod = time.Second * 20 // NextProtoDQ is the ALPN token for DoQ. During the connection establishment, // DNS/QUIC support is indicated by selecting the ALPN token "doq" in the // crypto handshake. // // See https://datatracker.ietf.org/doc/rfc9250. NextProtoDQ = "doq" )
const ( // ErrNoUpstreams is returned from the methods that expect at least a single // upstream to work with when no upstreams specified. ErrNoUpstreams errors.Error = "no upstream specified" // ErrNoReply is returned from [ExchangeAll] when no upstreams replied. ErrNoReply errors.Error = "no reply" )
TODO(e.burkov): Consider using wrapped errors.ErrNoValue and errors.ErrEmptyValue instead.
const ( // NetworkIP is a network type for both address families. NetworkIP = types.NetworkIP // NetworkIP4 is a network type for IPv4 address family. NetworkIP4 = types.NetworkIP4 // NetworkIP6 is a network type for IPv6 address family. NetworkIP6 = types.NetworkIP6 // NetworkTCP is a network type for TCP connections. NetworkTCP = types.NetworkTCP // NetworkUDP is a network type for UDP connections. NetworkUDP = types.NetworkUDP )
Variables ¶
var DefaultHTTPVersions = []HTTPVersion{HTTPVersion11, HTTPVersion2}
DefaultHTTPVersions is the list of HTTPVersion that we use by default in the DNS-over-HTTPS client.
Functions ¶
This section is empty.
Types ¶
type CachingResolver ¶
type CachingResolver struct {
// contains filtered or unexported fields
}
CachingResolver is a Resolver that caches the results of lookups. It's required to be created with NewCachingResolver.
func NewCachingResolver ¶
func NewCachingResolver(r *UpstreamResolver) (cr *CachingResolver)
NewCachingResolver creates a new caching resolver that uses r for lookups.
func (*CachingResolver) LookupNetIP ¶
func (r *CachingResolver) LookupNetIP( ctx context.Context, network Network, host string, ) (addrs []netip.Addr, err error)
LookupNetIP implements the Resolver interface for *CachingResolver.
TODO(e.burkov): It may appear that several concurrent lookup results rewrite each other in the cache.
type ConsequentResolver ¶
type ConsequentResolver = types.ConsequentResolver
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.
func NewConsequentResolver ¶
func NewConsequentResolver(resolvers ...Resolver) *ConsequentResolver
NewConsequentResolver creates a new ConsequentResolver with the specified resolvers.
type DialHandler ¶
DialHandler is a function type for dialing connections
type DialerInitializer ¶
type DialerInitializer func() (handler DialHandler, err error)
DialerInitializer returns the handler that it creates.
type ExchangeAllResult ¶
type ExchangeAllResult struct {
// Resp is the response DNS request resolved into.
Resp *dns.Msg
// Upstream is the upstream that successfully resolved the request.
Upstream Upstream
}
ExchangeAllResult is the successful result of ExchangeAll for a single upstream.
func ExchangeAll ¶
func ExchangeAll(ups []Upstream, req *dns.Msg) (res []ExchangeAllResult, err error)
ExchangeAll returns the responses from all of u. It returns an error only if all upstreams failed to exchange the request.
type HTTPVersion ¶
type HTTPVersion string
HTTPVersion is an enumeration of the HTTP versions that we support. Values that we use in this enumeration are also used as ALPN values.
const ( // HTTPVersion11 is HTTP/1.1. HTTPVersion11 HTTPVersion = "http/1.1" // HTTPVersion2 is HTTP/2. HTTPVersion2 HTTPVersion = "h2" // HTTPVersion3 is HTTP/3. HTTPVersion3 HTTPVersion = "h3" )
type HostsResolver ¶
type HostsResolver struct {
// contains filtered or unexported fields
}
HostsResolver is a Resolver that looks into system hosts files, see hostsfile.
func NewDefaultHostsResolver ¶
func NewDefaultHostsResolver( ctx context.Context, rootFSys fs.FS, l *slog.Logger, ) (hr *HostsResolver, err error)
NewDefaultHostsResolver returns a resolver based on system hosts files provided by the hostsfile.DefaultHostsPaths and read from rootFSys. In case the file by any default path doesn't exist it adds a log debug record. If l is nil, slog.Default is used.
func NewHostsResolver ¶
func NewHostsResolver(hosts hostsfile.Storage) (hr *HostsResolver)
NewHostsResolver is the resolver based on system hosts files.
type NotBootstrapError ¶
type NotBootstrapError struct {
// contains filtered or unexported fields
}
NotBootstrapError is returned by AddressToUpstream when the parsed upstream can't be used as a bootstrap and wraps the actual reason.
func (NotBootstrapError) Error ¶
func (e NotBootstrapError) Error() (msg string)
Error implements the [error] interface for NotBootstrapError.
func (NotBootstrapError) Unwrap ¶
func (e NotBootstrapError) Unwrap() (reason error)
Unwrap implements the errors.Wrapper interface.
type Options ¶
type Options struct {
// Logger is used for logging during parsing and upstream exchange. If nil,
// [slog.Default] is used.
Logger *slog.Logger
// VerifyServerCertificate is used to set the VerifyPeerCertificate property
// of the *tls.Config for DNS-over-HTTPS, DNS-over-QUIC, and DNS-over-TLS.
VerifyServerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
// VerifyConnection is used to set the VerifyConnection property
// of the *tls.Config for DNS-over-HTTPS, DNS-over-QUIC, and DNS-over-TLS.
VerifyConnection func(state tls.ConnectionState) error
// VerifyDNSCryptCertificate is the callback the DNSCrypt server certificate
// will be passed to. It's called in dnsCrypt.exchangeDNSCrypt.
// Upstream.Exchange method returns any error caused by it.
VerifyDNSCryptCertificate func(cert *dnscrypt.Cert) error
// QUICTracer is an optional callback that allows tracing every QUIC
// connection and logging every packet that goes through.
QUICTracer QUICTraceFunc
// RootCAs is the CertPool that must be used by all upstreams. Redefining
// RootCAs makes sense on iOS to overcome the 15MB memory limit of the
// NEPacketTunnelProvider.
RootCAs *x509.CertPool
// CipherSuites is a custom list of TLSv1.2 ciphers.
CipherSuites []uint16
// Bootstrap is used to resolve upstreams' hostnames. If nil, the
// [net.DefaultResolver] will be used.
Bootstrap Resolver
// HTTPVersions is a list of HTTP versions that should be supported by the
// DNS-over-HTTPS client. If not set, HTTP/1.1 and HTTP/2 will be used.
HTTPVersions []HTTPVersion
// Timeout is the default upstream timeout. It's also used as a timeout for
// bootstrap DNS requests. Zero value disables the timeout.
Timeout time.Duration
// InsecureSkipVerify disables verifying the server's certificate.
InsecureSkipVerify bool
// PreferIPv6 tells the bootstrapper to prefer IPv6 addresses for an
// upstream.
PreferIPv6 bool
}
Options for AddressToUpstream func. With these options we can configure the upstream properties.
func NewOptions ¶
func NewOptions( logger *slog.Logger, verifyServerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error, verifyConnection func(state tls.ConnectionState) error, verifyDNSCryptCertificate func(cert *dnscrypt.Cert) error, quicTracer QUICTraceFunc, rootCAs *x509.CertPool, cipherSuites []uint16, bootstrap Resolver, httpVersions []HTTPVersion, timeout time.Duration, insecureSkipVerify bool, preferIPv6 bool, ) *Options
NewOptions creates a new Options struct with the specified values.
func (*Options) Clone ¶
func (o *Options) Clone() (clone UpstreamOptions)
Clone copies o to a new struct. Note, that this is not a deep clone.
func (*Options) GetBootstrap ¶
GetBootstrap implements UpstreamOptions.
func (*Options) GetCipherSuites ¶
GetCipherSuites implements UpstreamOptions.
func (*Options) GetHTTPVersions ¶
func (o *Options) GetHTTPVersions() []HTTPVersion
GetHTTPVersions implements UpstreamOptions.
func (*Options) GetInsecureSkipVerify ¶
GetInsecureSkipVerify implements UpstreamOptions.
func (*Options) GetPreferIPv6 ¶
GetPreferIPv6 implements UpstreamOptions.
func (*Options) GetQUICTracer ¶
func (o *Options) GetQUICTracer() QUICTraceFunc
GetQUICTracer implements UpstreamOptions.
func (*Options) GetRootCAs ¶
GetRootCAs implements UpstreamOptions.
func (*Options) GetTimeout ¶
GetTimeout implements UpstreamOptions.
func (*Options) GetVerifyConnection ¶
func (o *Options) GetVerifyConnection() func(state tls.ConnectionState) error
GetVerifyConnection implements UpstreamOptions.
func (*Options) GetVerifyDNSCryptCertificate ¶
GetVerifyDNSCryptCertificate implements UpstreamOptions.
func (*Options) GetVerifyServerCertificate ¶
func (o *Options) GetVerifyServerCertificate() func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
GetVerifyServerCertificate implements UpstreamOptions.
func (*Options) SetBootstrap ¶ added in v1.0.2
SetBootstrap implements UpstreamOptions.
type ParallelResolver ¶
type ParallelResolver = types.ParallelResolver
ParallelResolver is a slice of resolvers that are queried concurrently until the first successful response is returned, as opposed to all resolvers being queried in order in ConsequentResolver.
func NewParallelResolver ¶
func NewParallelResolver(resolvers ...Resolver) *ParallelResolver
NewParallelResolver creates a new ParallelResolver with the specified resolvers.
type QUICTraceFunc ¶
type QUICTraceFunc func( ctx context.Context, role logging.Perspective, connID quic.ConnectionID, ) (tracer *logging.ConnectionTracer)
QUICTraceFunc is a function that returns a logging.ConnectionTracer specific for a given role and connection ID.
type Resolver ¶
Resolver resolves the hostnames to IP addresses. Note, that net.Resolver from standard library also implements this interface.
type StaticResolver ¶
type StaticResolver = types.StaticResolver
StaticResolver is a resolver which always responds with an underlying slice of IP addresses.
type Upstream ¶
type Upstream interface {
// Exchange sends req to this upstream and returns the response that has
// been received or an error if something went wrong. The implementations
// must not modify req as well as the caller must not modify it until the
// method returns. It shouldn't be called after closing.
Exchange(req *dns.Msg) (resp *dns.Msg, err error)
// Address returns the human-readable address of the upstream DNS resolver.
// It may differ from what was passed to [AddressToUpstream].
Address() (addr string)
// Closer used to close the upstreams properly.
io.Closer
}
Upstream is an interface for a DNS resolver. All the methods must be safe for concurrent use.
func AddressToUpstream ¶
func AddressToUpstream(addr string, opts UpstreamOptions) (u Upstream, err error)
AddressToUpstream converts addr to an Upstream using the specified options. addr can be either a URL, or a plain address, either a domain name or an IP.
- 1.2.3.4 or 1.2.3.4:4321 for plain DNS using IP address;
- udp://5.3.5.3:53 or 5.3.5.3:53 for plain DNS using IP address;
- udp://name.server:53 or name.server:53 for plain DNS using domain name;
- tcp://5.3.5.3:53 for plain DNS-over-TCP using IP address;
- tcp://name.server:53 for plain DNS-over-TCP using domain name;
- tls://5.3.5.3:853 for DNS-over-TLS using IP address;
- tls://name.server:853 for DNS-over-TLS using domain name;
- https://5.3.5.3:443/dns-query for DNS-over-HTTPS using IP address;
- https://name.server:443/dns-query for DNS-over-HTTPS using domain name;
- quic://5.3.5.3:853 for DNS-over-QUIC using IP address;
- quic://name.server:853 for DNS-over-QUIC using domain name;
- h3://dns.google for DNS-over-HTTPS that only works with HTTP/3;
- sdns://... for DNS stamp, see https://dnscrypt.info/stamps-specifications.
If addr doesn't have port specified, the default port of the appropriate protocol will be used.
opts are applied to the u and shouldn't be modified afterwards, nil value is valid.
TODO(e.burkov): Clone opts?
type UpstreamOptions ¶
type UpstreamOptions interface {
// Getter methods for Options fields
GetLogger() *slog.Logger
GetVerifyServerCertificate() func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
GetVerifyConnection() func(state tls.ConnectionState) error
GetVerifyDNSCryptCertificate() func(cert *dnscrypt.Cert) error
GetQUICTracer() QUICTraceFunc
GetRootCAs() *x509.CertPool
GetCipherSuites() []uint16
GetBootstrap() Resolver
GetHTTPVersions() []HTTPVersion
GetTimeout() time.Duration
GetInsecureSkipVerify() bool
GetPreferIPv6() bool
// DialTCP creates a TCP connection to the specified address using the
// configuration from this UpstreamOptions.
DialTCP(ctx context.Context, addr string) (net.Conn, error)
// DialUDP creates a UDP connection to the specified address using the
// configuration from this UpstreamOptions.
DialUDP(ctx context.Context, addr string) (*net.UDPConn, error)
// LookupIP resolves a hostname to IP addresses using the bootstrap resolver.
// This method has the same interface as net.LookupIP.
LookupIP(host string) ([]net.IP, error)
// Setter methods for mutable fields
SetLogger(logger *slog.Logger)
SetBootstrap(bootstrap Resolver)
Clone() (clone UpstreamOptions)
}
UpstreamOptions defines the interface for upstream configuration and connection management. This abstracts the Options struct and provides unified dialing methods.
type UpstreamResolver ¶
type UpstreamResolver struct {
// Upstream is used for lookups. It must not be nil.
Upstream
}
UpstreamResolver is a wrapper around Upstream that implements the Resolver interface.
func NewUpstreamResolver ¶
func NewUpstreamResolver(resolverAddress string, opts *Options) (r *UpstreamResolver, err error)
NewUpstreamResolver creates an upstream that can be used as bootstrap Resolver. resolverAddress format is the same as in the AddressToUpstream. If the upstream can't be used as a bootstrap, the returned error will have the underlying type of NotBootstrapError, and r itself will be fully usable. Closing r.Upstream is caller's responsibility.
func (*UpstreamResolver) LookupNetIP ¶
func (r *UpstreamResolver) LookupNetIP( ctx context.Context, network Network, host string, ) (ips []netip.Addr, err error)
LookupNetIP implements the Resolver interface for *UpstreamResolver. It doesn't consider the TTL of the DNS records.
TODO(e.burkov): Investigate why the empty slice is returned instead of nil.