wip: migrate to mono-repo. SPN has already been moved to spn/
This commit is contained in:
233
service/resolver/resolver.go
Normal file
233
service/resolver/resolver.go
Normal file
@@ -0,0 +1,233 @@
|
||||
package resolver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/portbase/utils"
|
||||
"github.com/safing/portmaster/service/netenv"
|
||||
"github.com/safing/portmaster/service/network/netutils"
|
||||
)
|
||||
|
||||
// DNS Resolver Attributes.
|
||||
const (
|
||||
ServerTypeDNS = "dns"
|
||||
ServerTypeTCP = "tcp"
|
||||
ServerTypeDoT = "dot"
|
||||
ServerTypeDoH = "doh"
|
||||
ServerTypeMDNS = "mdns"
|
||||
ServerTypeEnv = "env"
|
||||
|
||||
ServerSourceConfigured = "config"
|
||||
ServerSourceOperatingSystem = "system"
|
||||
ServerSourceMDNS = "mdns"
|
||||
ServerSourceEnv = "env"
|
||||
)
|
||||
|
||||
// DNS resolver scheme aliases.
|
||||
const (
|
||||
HTTPSProtocol = "https"
|
||||
TLSProtocol = "tls"
|
||||
)
|
||||
|
||||
// Resolver holds information about an active resolver.
|
||||
type Resolver struct {
|
||||
// Server config url (and ID)
|
||||
// Supported parameters:
|
||||
// - `verify=domain`: verify domain (dot only)
|
||||
// - `name=name`: human readable name for resolver
|
||||
// - `blockedif=empty`: how to detect if the dns service blocked something
|
||||
// - `empty`: NXDomain result, but without any other record in any section
|
||||
// - `refused`: Request was refused
|
||||
// - `zeroip`: Answer only contains zeroip
|
||||
ConfigURL string
|
||||
|
||||
// Info holds the parsed configuration.
|
||||
Info *ResolverInfo
|
||||
|
||||
// ServerAddress holds the resolver address for easier use.
|
||||
ServerAddress string
|
||||
|
||||
// UpstreamBlockDetection defines the detection type
|
||||
// to identifier upstream DNS query blocking.
|
||||
// Valid values are:
|
||||
// - zeroip
|
||||
// - empty
|
||||
// - refused (default)
|
||||
// - disabled
|
||||
UpstreamBlockDetection string
|
||||
|
||||
// Special Options
|
||||
Search []string
|
||||
SearchOnly bool
|
||||
Path string
|
||||
|
||||
// logic interface
|
||||
Conn ResolverConn `json:"-"`
|
||||
}
|
||||
|
||||
// ResolverInfo is a subset of resolver attributes that is attached to answers
|
||||
// from that server in order to use it later for decision making. It must not
|
||||
// be changed by anyone after creation and initialization is complete.
|
||||
type ResolverInfo struct { //nolint:golint,maligned // TODO
|
||||
// Name describes the name given to the resolver. The name is configured in the config URL using the name parameter.
|
||||
Name string
|
||||
|
||||
// Type describes the type of the resolver.
|
||||
// Possible values include dns, tcp, dot, doh, mdns, env.
|
||||
Type string
|
||||
|
||||
// Source describes where the resolver configuration came from.
|
||||
// Possible values include config, system, mdns, env.
|
||||
Source string
|
||||
|
||||
// IP is the IP address of the resolver
|
||||
IP net.IP
|
||||
|
||||
// Domain of the dns server if it has one
|
||||
Domain string
|
||||
|
||||
// IPScope is the network scope of the IP address.
|
||||
IPScope netutils.IPScope
|
||||
|
||||
// Port is the udp/tcp port of the resolver.
|
||||
Port uint16
|
||||
|
||||
// id holds a unique ID for this resolver.
|
||||
id string
|
||||
idGen sync.Once
|
||||
}
|
||||
|
||||
// ID returns the unique ID of the resolver.
|
||||
func (info *ResolverInfo) ID() string {
|
||||
// Generate the ID the first time.
|
||||
info.idGen.Do(func() {
|
||||
switch info.Type {
|
||||
case ServerTypeMDNS:
|
||||
info.id = ServerTypeMDNS
|
||||
case ServerTypeEnv:
|
||||
info.id = ServerTypeEnv
|
||||
case ServerTypeDoH:
|
||||
info.id = fmt.Sprintf( //nolint:nosprintfhostport // Not used as URL.
|
||||
"https://%s:%d#%s",
|
||||
info.Domain,
|
||||
info.Port,
|
||||
info.Source,
|
||||
)
|
||||
case ServerTypeDoT:
|
||||
info.id = fmt.Sprintf( //nolint:nosprintfhostport // Not used as URL.
|
||||
"dot://%s:%d#%s",
|
||||
info.Domain,
|
||||
info.Port,
|
||||
info.Source,
|
||||
)
|
||||
default:
|
||||
info.id = fmt.Sprintf(
|
||||
"%s://%s:%d#%s",
|
||||
info.Type,
|
||||
info.IP,
|
||||
info.Port,
|
||||
info.Source,
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
return info.id
|
||||
}
|
||||
|
||||
// DescriptiveName returns a human readable, but also detailed representation
|
||||
// of the resolver.
|
||||
func (info *ResolverInfo) DescriptiveName() string {
|
||||
switch {
|
||||
case info.Type == ServerTypeMDNS:
|
||||
return "MDNS"
|
||||
case info.Type == ServerTypeEnv:
|
||||
return "Portmaster Environment"
|
||||
case info.Name != "":
|
||||
return fmt.Sprintf(
|
||||
"%s (%s)",
|
||||
info.Name,
|
||||
info.ID(),
|
||||
)
|
||||
case info.Domain != "":
|
||||
return fmt.Sprintf(
|
||||
"%s (%s)",
|
||||
info.Domain,
|
||||
info.ID(),
|
||||
)
|
||||
default:
|
||||
return fmt.Sprintf(
|
||||
"%s (%s)",
|
||||
info.IP.String(),
|
||||
info.ID(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Copy returns a full copy of the ResolverInfo.
|
||||
func (info *ResolverInfo) Copy() *ResolverInfo {
|
||||
// Force idGen to run before we copy.
|
||||
_ = info.ID()
|
||||
|
||||
// Copy manually in order to not copy the mutex.
|
||||
cp := &ResolverInfo{
|
||||
Name: info.Name,
|
||||
Type: info.Type,
|
||||
Source: info.Source,
|
||||
IP: info.IP,
|
||||
Domain: info.Domain,
|
||||
IPScope: info.IPScope,
|
||||
Port: info.Port,
|
||||
id: info.id,
|
||||
}
|
||||
// Trigger idGen.Do(), as the ID is already generated.
|
||||
cp.idGen.Do(func() {})
|
||||
|
||||
return cp
|
||||
}
|
||||
|
||||
// IsBlockedUpstream returns true if the request has been blocked
|
||||
// upstream.
|
||||
func (resolver *Resolver) IsBlockedUpstream(answer *dns.Msg) bool {
|
||||
return isBlockedUpstream(resolver, answer)
|
||||
}
|
||||
|
||||
// String returns the URL representation of the resolver.
|
||||
func (resolver *Resolver) String() string {
|
||||
return resolver.Info.DescriptiveName()
|
||||
}
|
||||
|
||||
// ResolverConn is an interface to implement different types of query backends.
|
||||
type ResolverConn interface { //nolint:golint // TODO
|
||||
Query(ctx context.Context, q *Query) (*RRCache, error)
|
||||
ReportFailure()
|
||||
IsFailing() bool
|
||||
ResetFailure()
|
||||
ForceReconnect(ctx context.Context)
|
||||
}
|
||||
|
||||
// BasicResolverConn implements ResolverConn for standard dns clients.
|
||||
type BasicResolverConn struct {
|
||||
sync.Mutex // Also used by inheriting structs.
|
||||
|
||||
resolver *Resolver
|
||||
|
||||
failing *abool.AtomicBool
|
||||
failingStarted time.Time
|
||||
fails int
|
||||
failLock sync.Mutex
|
||||
|
||||
networkChangedFlag *utils.Flag
|
||||
}
|
||||
|
||||
// init initializes the basic resolver connection.
|
||||
func (brc *BasicResolverConn) init() {
|
||||
brc.failing = abool.New()
|
||||
brc.networkChangedFlag = netenv.GetNetworkChangedFlag()
|
||||
}
|
||||
Reference in New Issue
Block a user