upstream

package
v1.0.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 24, 2025 License: Apache-2.0 Imports: 35 Imported by: 0

Documentation

Overview

Package upstream implements DNS clients for all known DNS encryption protocols.

Index

Constants

View Source
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"
)
View Source
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.

View Source
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

View Source
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

type DialHandler func(ctx context.Context, network string, addr string) (conn net.Conn, err error)

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.

func (*HostsResolver) LookupNetIP

func (hr *HostsResolver) LookupNetIP(
	context context.Context,
	network string,
	host string,
) (addrs []netip.Addr, err error)

LookupNetIP implements the Resolver interface for *hostsResolver.

type Network

type Network = types.Network

Network is a network type for use in Resolver's methods.

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) DialTCP

func (o *Options) DialTCP(ctx context.Context, addr string) (net.Conn, error)

DialTCP creates a TCP connection using the configuration from Options.

func (*Options) DialUDP

func (o *Options) DialUDP(ctx context.Context, addr string) (*net.UDPConn, error)

DialUDP creates a UDP connection using the configuration from Options.

func (*Options) GetBootstrap

func (o *Options) GetBootstrap() Resolver

GetBootstrap implements UpstreamOptions.

func (*Options) GetCipherSuites

func (o *Options) GetCipherSuites() []uint16

GetCipherSuites implements UpstreamOptions.

func (*Options) GetHTTPVersions

func (o *Options) GetHTTPVersions() []HTTPVersion

GetHTTPVersions implements UpstreamOptions.

func (*Options) GetInsecureSkipVerify

func (o *Options) GetInsecureSkipVerify() bool

GetInsecureSkipVerify implements UpstreamOptions.

func (*Options) GetLogger

func (o *Options) GetLogger() *slog.Logger

GetLogger implements UpstreamOptions.

func (*Options) GetPreferIPv6

func (o *Options) GetPreferIPv6() bool

GetPreferIPv6 implements UpstreamOptions.

func (*Options) GetQUICTracer

func (o *Options) GetQUICTracer() QUICTraceFunc

GetQUICTracer implements UpstreamOptions.

func (*Options) GetRootCAs

func (o *Options) GetRootCAs() *x509.CertPool

GetRootCAs implements UpstreamOptions.

func (*Options) GetTimeout

func (o *Options) GetTimeout() time.Duration

GetTimeout implements UpstreamOptions.

func (*Options) GetVerifyConnection

func (o *Options) GetVerifyConnection() func(state tls.ConnectionState) error

GetVerifyConnection implements UpstreamOptions.

func (*Options) GetVerifyDNSCryptCertificate

func (o *Options) GetVerifyDNSCryptCertificate() func(cert *dnscrypt.Cert) error

GetVerifyDNSCryptCertificate implements UpstreamOptions.

func (*Options) GetVerifyServerCertificate

func (o *Options) GetVerifyServerCertificate() func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error

GetVerifyServerCertificate implements UpstreamOptions.

func (*Options) LookupIP added in v1.0.4

func (o *Options) LookupIP(host string) ([]net.IP, error)

LookupIP implements UpstreamOptions.

func (*Options) SetBootstrap added in v1.0.2

func (o *Options) SetBootstrap(bootstrap Resolver)

SetBootstrap implements UpstreamOptions.

func (*Options) SetLogger added in v1.0.2

func (o *Options) SetLogger(logger *slog.Logger)

SetLogger 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

type Resolver = types.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?

func ExchangeParallel

func ExchangeParallel(ups []Upstream, req *dns.Msg) (reply *dns.Msg, resolved Upstream, err error)

ExchangeParallel returns the first successful response from one of u. It returns an error if all upstreams failed to exchange the request.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL