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 ¶
- type NetworkConfig
- type ResolveParameters
- type Resolver
- func (r *Resolver) GetMetrics() string
- func (r *Resolver) MakeResolveParameters(p parameters.ParametersAccessor, frontingProviderID string) (*ResolveParameters, error)
- func (r *Resolver) ResolveAddress(ctx context.Context, networkID string, params *ResolveParameters, ...) (string, error)
- func (r *Resolver) ResolveIP(ctx context.Context, networkID string, params *ResolveParameters, ...) ([]net.IP, error)
- func (r *Resolver) Stop()
- func (r *Resolver) VerifyCacheExtension(hostname string)
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 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 // 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/Psiphon-Labs/psiphon-tunnel-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 ¶
GetMetrics returns a summary of DNS metrics.
func (*Resolver) MakeResolveParameters ¶
func (r *Resolver) MakeResolveParameters( p parameters.ParametersAccessor, frontingProviderID 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, 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.
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.
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 ¶
VerifyCacheExtension extends the TTL for any cached result for the specified hostname to at least NetworkConfig.CacheExtensionVerifiedTTL.