resolver

package
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Jun 20, 2024 License: GPL-3.0 Imports: 15 Imported by: 0

Documentation

Overview

Package resolver implements a DNS stub resolver, or DNS client, which resolves domain names.

The resolver is Psiphon-specific and oriented towards blocking resistance. See ResolveIP for more details.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type NetworkConfig

type NetworkConfig struct {

	// GetDNSServers returns a list of system DNS server addresses (IP:port, or
	// IP only with port 53 assumed), as determined via OS APIs, in priority
	// order. GetDNSServers may be nil.
	GetDNSServers func() []string

	// BindToDevice should ensure the input file descriptor, a UDP socket, is
	// excluded from VPN routing. BindToDevice may be nil.
	BindToDevice func(fd int) (string, error)

	// AllowDefaultResolverWithBindToDevice indicates that it's safe to use
	// the default resolver when BindToDevice is configured, as the host OS
	// will automatically exclude DNS requests from the VPN.
	AllowDefaultResolverWithBindToDevice bool

	// IPv6Synthesize should apply NAT64 synthesis to the input IPv4 address,
	// returning a synthesized IPv6 address that will route to the same
	// endpoint. IPv6Synthesize may be nil.
	IPv6Synthesize func(IPv4 string) string

	// HasIPv6Route should return true when the host has an IPv6 route.
	// Resolver has an internal implementation, hasRoutableIPv6Interface, to
	// determine this, but it can fail on some platforms ("route ip+net:
	// netlinkrib: permission denied" on Android, for example; see Go issue
	// 40569). When HasIPv6Route is nil, the internal implementation is used.
	HasIPv6Route func() bool

	// LogWarning is an optional callback which is used to log warnings and
	// transient errors which would otherwise not be recorded or returned.
	LogWarning func(error)

	// LogHostnames indicates whether to log hostname in errors or not.
	LogHostnames bool

	// CacheExtensionInitialTTL specifies a minimum TTL to use when caching
	// domain resolution results. This minimum will override any TTL in the
	// DNS response. CacheExtensionInitialTTL is off when 0.
	CacheExtensionInitialTTL time.Duration

	// CacheExtensionVerifiedTTL specifies the minimum TTL to set for a cached
	// domain resolution result after the result has been verified.
	// CacheExtensionVerifiedTTL is off when 0.
	//
	// DNS cache extension is a workaround to partially mitigate issues with
	// obtaining underlying system DNS server IPs on platforms such as iOS
	// once a VPN is running and after network changes, such as changing from
	// Wi-Fi to mobile. While ResolveParameters.AlternateDNSServer can be
	// used to specify a known public DNS server, it may be the case that
	// public DNS servers are blocked or always falling back to a public DNS
	// server creates unusual traffic. And while it may be possible to use
	// the default system resolver, it lacks certain circumvention
	// capabilities.
	//
	// Extending the TTL for cached responses allows Psiphon to redial domains
	// using recently successful IPs.
	//
	// CacheExtensionInitialTTL allows for a greater initial minimum TTL, so
	// that the response entry remains in the cache long enough for a dial to
	// fully complete and verify the endpoint. Psiphon will call
	// Resolver.VerifyExtendCacheTTL once a dial has authenticated, for
	// example, the destination Psiphon server. VerifyCacheExtension will
	// further extend the corresponding TTL to CacheExtensionVerifiedTTL, a
	// longer TTL. CacheExtensionInitialTTL is intended to be on the order of
	// minutes and CacheExtensionVerifiedTTL may be on the order of hours.
	//
	// When CacheExtensionVerifiedTTL is on, the DNS cache is not flushed on
	// network changes, to allow for the previously cached entries to remain
	// available in the problematic scenario. Like adjusting TTLs, this is an
	// explicit trade-off which doesn't adhere to standard best practise, but
	// is expected to be more blocking resistent; this approach also assumes
	// that endpoints such as CDN IPs are typically available on any network.
	CacheExtensionVerifiedTTL time.Duration
}

NetworkConfig specifies network-level configuration for a Resolver.

type ResolveParameters

type ResolveParameters struct {

	// AttemptsPerServer specifies how many requests to send to each DNS
	// server before trying the next server. IPv4 and IPv6 requests are sent
	// concurrently and count as one attempt.
	AttemptsPerServer int

	// AttemptsPerPreferredServer is AttemptsPerServer for a preferred
	// alternate DNS server.
	AttemptsPerPreferredServer int

	// RequestTimeout specifies how long to wait for a valid response before
	// moving on to the next attempt.
	RequestTimeout time.Duration

	// AwaitTimeout specifies how long to await an additional response after
	// the first response is received. This additional wait time applies only
	// when there is either no IPv4 or IPv6 response.
	AwaitTimeout time.Duration

	// PreresolvedIPAddress specifies an IP address result to be used in place
	// of making a request.
	PreresolvedIPAddress string

	// PreresolvedDomain is the domain for which PreresolvedIPAddress is to be
	// used.
	PreresolvedDomain string

	// AlternateDNSServer specifies an alterate DNS server (IP:port, or IP
	// only with port 53 assumed) to be used when either no system DNS
	// servers are available or when PreferAlternateDNSServer is set.
	AlternateDNSServer string

	// PreferAlternateDNSServer indicates whether to prioritize using the
	// AlternateDNSServer. When set, the AlternateDNSServer is attempted
	// before any system DNS servers.
	PreferAlternateDNSServer bool

	// ProtocolTransformName specifies the name associated with
	// ProtocolTransformSpec and is used for metrics.
	ProtocolTransformName string

	// ProtocolTransformSpec specifies a transform to apply to the DNS request packet.
	// See: "github.com/nandoxscr/popon-core/psiphon/common/transforms".
	//
	// As transforms operate on strings and DNS requests are binary,
	// transforms should be expressed using hex characters.
	//
	// DNS transforms include strategies discovered by the Geneva team,
	// https://geneva.cs.umd.edu.
	ProtocolTransformSpec transforms.Spec

	// ProtocolTransformSeed specifies the seed to use for generating random
	// data in the ProtocolTransformSpec transform. To replay a transform,
	// specify the same seed.
	ProtocolTransformSeed *prng.Seed

	// IncludeEDNS0 indicates whether to include the EDNS(0) UDP maximum
	// response size extension in DNS requests. The resolver can handle
	// responses larger than 512 bytes (RFC 1035 maximum) regardless of
	// whether the extension is included; the extension may be included as
	// part of appearing similar to other DNS traffic.
	IncludeEDNS0 bool
	// contains filtered or unexported fields
}

ResolveParameters specifies the configuration and behavior of a single ResolveIP call, a single domain name resolution.

New ResolveParameters may be generated by calling MakeResolveParameters, which takes tactics parameters as an input.

ResolveParameters may be persisted for replay.

func (*ResolveParameters) GetFirstAttemptWithAnswer

func (r *ResolveParameters) GetFirstAttemptWithAnswer() int

GetFirstAttemptWithAnswer returns the index of the first request attempt that received a valid response, for the most recent ResolveIP call using this ResolveParameters. This information is used for logging metrics. The first attempt has index 1. GetFirstAttemptWithAnswer return 0 when no request attempt has reported a valid response.

The caller is responsible for synchronizing use of a ResolveParameters instance (e.g, use a distinct ResolveParameters per ResolveIP to ensure GetFirstAttemptWithAnswer refers to a specific ResolveIP).

type Resolver

type Resolver struct {
	// contains filtered or unexported fields
}

Resolver is a DNS stub resolver, or DNS client, which resolves domain names. A Resolver instance maintains a cache, a network state snapshot, and metrics. All ResolveIP calls will share the same cache and state. Multiple concurrent ResolveIP calls are supported.

func NewResolver

func NewResolver(networkConfig *NetworkConfig, networkID string) *Resolver

NewResolver creates a new Resolver instance.

func (*Resolver) GetMetrics

func (r *Resolver) GetMetrics() string

GetMetrics returns a summary of DNS metrics.

func (*Resolver) MakeResolveParameters

func (r *Resolver) MakeResolveParameters(
	p parameters.ParametersAccessor,
	frontingProviderID string,
	frontingDialDomain string) (*ResolveParameters, error)

MakeResolveParameters generates ResolveParameters using the input tactics parameters and optional frontingProviderID context.

func (*Resolver) ResolveAddress

func (r *Resolver) ResolveAddress(
	ctx context.Context,
	networkID string,
	params *ResolveParameters,
	network, address string) (string, error)

ResolveAddress splits the input host:port address, calls ResolveIP to resolve the IP address of the host, selects an IP if there are multiple, and returns a rejoined IP:port.

IP address selection is random. When network input is set to "ip4"/"tcp4"/"udp4" or "ip6"/"tcp6"/"udp6", selection is limited to IPv4 or IPv6, respectively.

func (*Resolver) ResolveIP

func (r *Resolver) ResolveIP(
	ctx context.Context,
	networkID string,
	params *ResolveParameters,
	hostname string) ([]net.IP, error)

ResolveIP resolves a domain name.

The input params may be nil, in which case default timeouts are used.

ResolveIP performs concurrent A and AAAA lookups, returns any valid response IPs, and caches results. An error is returned when there are no valid response IPs.

ResolveIP is not a general purpose resolver and is Psiphon-specific. For example, resolved domains are expected to exist; ResolveIP does not fallback to TCP; does not consult any "hosts" file; does not perform RFC 3484 sorting logic (see Go issue 18518); only implements a subset of Go/glibc/resolv.conf(5) resolver parameters (attempts and timeouts, but not rotate, single-request etc.) ResolveIP does not implement singleflight logic, as the Go resolver does, and allows multiple concurrent request for the same domain -- Psiphon won't often resolve the exact same domain multiple times concurrently, and, when it does, there's a circumvention benefit to attempting different DNS servers and protocol transforms.

ResolveIP does not currently support DoT, DoH, or TCP; those protocols are often blocked or less common. Instead, ResolveIP makes a best effort to evade plaintext UDP DNS interference by ignoring invalid responses and by optionally applying protocol transforms that may evade blocking.

Due to internal caching, the caller must not mutate returned net.IP slice or entries.

func (*Resolver) Stop

func (r *Resolver) Stop()

Stop clears the Resolver cache and resets metrics. Stop must be called only after ceasing all in-flight ResolveIP goroutines, or else the cache or metrics may repopulate. A Resolver may be resumed after calling Stop, but Update must be called first.

func (*Resolver) VerifyCacheExtension

func (r *Resolver) VerifyCacheExtension(hostname string)

VerifyCacheExtension extends the TTL for any cached result for the specified hostname to at least NetworkConfig.CacheExtensionVerifiedTTL.

Jump to

Keyboard shortcuts

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