tls protocol for DoT added, minor refactoring

This commit is contained in:
Vladimir Stoilov 2022-07-21 11:07:44 +02:00
parent 598dc9d254
commit efd33c223f
5 changed files with 35 additions and 42 deletions

View file

@ -111,7 +111,7 @@ The format is: "protocol://ip:port?parameter=value&parameter=value"
ExpertiseLevel: config.ExpertiseLevelExpert, ExpertiseLevel: config.ExpertiseLevelExpert,
ReleaseLevel: config.ReleaseLevelStable, ReleaseLevel: config.ReleaseLevelStable,
DefaultValue: defaultNameServers, DefaultValue: defaultNameServers,
ValidationRegex: fmt.Sprintf("^(%s|%s|%s|%s|%s)://.*", ServerTypeDoT, ServerTypeDoH, ServerTypeDNS, ServerTypeTCP, HttpsProtocol), ValidationRegex: fmt.Sprintf("^(%s|%s|%s|%s|%s|%s)://.*", ServerTypeDoT, ServerTypeDoH, ServerTypeDNS, ServerTypeTCP, HttpsProtocol, TlsProtocol),
ValidationFunc: validateNameservers, ValidationFunc: validateNameservers,
Annotations: config.Annotations{ Annotations: config.Annotations{
config.DisplayHintAnnotation: config.DisplayHintOrdered, config.DisplayHintAnnotation: config.DisplayHintOrdered,

View file

@ -6,10 +6,8 @@ import (
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net"
"net/http" "net/http"
"net/url" "net/url"
"strconv"
"github.com/miekg/dns" "github.com/miekg/dns"
) )
@ -43,11 +41,11 @@ func (tq *HttpsQuery) MakeCacheRecord(reply *dns.Msg, resolverInfo *ResolverInfo
func NewHTTPSResolver(resolver *Resolver) *HttpsResolver { func NewHTTPSResolver(resolver *Resolver) *HttpsResolver {
tr := &http.Transport{} tr := &http.Transport{}
if resolver.ServerAddress != "" { if resolver.Info.IP != nil {
tr = &http.Transport{ tr = &http.Transport{
TLSClientConfig: &tls.Config{ TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS12, MinVersion: tls.VersionTLS12,
ServerName: resolver.VerifyDomain, ServerName: resolver.Info.Domain,
// TODO: use portbase rng // TODO: use portbase rng
}, },
} }
@ -70,7 +68,7 @@ func (hr *HttpsResolver) Query(ctx context.Context, q *Query) (*RRCache, error)
// Do not resolve domain names that are needed to initialize a resolver // Do not resolve domain names that are needed to initialize a resolver
if hr.resolver.Info.IP == nil { if hr.resolver.Info.IP == nil {
if _, ok := resolverInitDomains[q.FQDN[:len(q.FQDN)-1]]; ok { if _, ok := resolverInitDomains[q.FQDN]; ok {
return nil, ErrContinue return nil, ErrContinue
} }
} }
@ -85,16 +83,10 @@ func (hr *HttpsResolver) Query(ctx context.Context, q *Query) (*RRCache, error)
} }
b64dns := base64.RawStdEncoding.EncodeToString(buf) b64dns := base64.RawStdEncoding.EncodeToString(buf)
// Set the host, if we dont have IP address just use the domain
host := hr.resolver.ServerAddress
if host == "" {
host = net.JoinHostPort(hr.resolver.VerifyDomain, strconv.Itoa(int(hr.resolver.Info.Port)))
}
// Build and execute http reuqest // Build and execute http reuqest
url := &url.URL{ url := &url.URL{
Scheme: "https", Scheme: "https",
Host: host, Host: hr.resolver.ServerAddress,
Path: hr.resolver.Path, Path: hr.resolver.Path,
ForceQuery: true, ForceQuery: true,
RawQuery: fmt.Sprintf("dns=%s", b64dns), RawQuery: fmt.Sprintf("dns=%s", b64dns),

View file

@ -7,7 +7,6 @@ import (
"fmt" "fmt"
"io" "io"
"net" "net"
"strconv"
"time" "time"
"github.com/miekg/dns" "github.com/miekg/dns"
@ -100,7 +99,7 @@ func (tr *TCPResolver) UseTLS() *TCPResolver {
tr.dnsClient.Net = "tcp-tls" tr.dnsClient.Net = "tcp-tls"
tr.dnsClient.TLSConfig = &tls.Config{ tr.dnsClient.TLSConfig = &tls.Config{
MinVersion: tls.VersionTLS12, MinVersion: tls.VersionTLS12,
ServerName: tr.resolver.VerifyDomain, ServerName: tr.resolver.Info.Domain,
// TODO: use portbase rng // TODO: use portbase rng
} }
return tr return tr
@ -143,14 +142,8 @@ func (tr *TCPResolver) getOrCreateResolverConn(ctx context.Context) (*tcpResolve
KeepAlive: defaultClientTTL, KeepAlive: defaultClientTTL,
} }
// Set the host, if we dont have IP address just use the domain
host := tr.resolver.ServerAddress
if host == "" {
host = net.JoinHostPort(tr.resolver.VerifyDomain, strconv.Itoa(int(tr.resolver.Info.Port)))
}
// Connect to server. // Connect to server.
conn, err := tr.dnsClient.Dial(host) conn, err := tr.dnsClient.Dial(tr.resolver.ServerAddress)
if err != nil { if err != nil {
// Hint network environment at failed connection. // Hint network environment at failed connection.
netenv.ReportFailedConnection() netenv.ReportFailedConnection()
@ -194,7 +187,7 @@ func (tr *TCPResolver) getOrCreateResolverConn(ctx context.Context) (*tcpResolve
func (tr *TCPResolver) Query(ctx context.Context, q *Query) (*RRCache, error) { func (tr *TCPResolver) Query(ctx context.Context, q *Query) (*RRCache, error) {
// Do not resolve domain names that are needed to initialize a resolver // Do not resolve domain names that are needed to initialize a resolver
if tr.resolver.Info.IP == nil && tr.dnsClient.TLSConfig != nil { if tr.resolver.Info.IP == nil && tr.dnsClient.TLSConfig != nil {
if _, ok := resolverInitDomains[q.FQDN[:len(q.FQDN)-1]]; ok { if _, ok := resolverInitDomains[q.FQDN]; ok {
return nil, ErrContinue return nil, ErrContinue
} }
} }

View file

@ -32,6 +32,7 @@ const (
const ( const (
HttpsProtocol = "https" HttpsProtocol = "https"
TlsProtocol = "tls"
) )
// FailThreshold is amount of errors a resolvers must experience in order to be regarded as failed. // FailThreshold is amount of errors a resolvers must experience in order to be regarded as failed.
@ -65,7 +66,6 @@ type Resolver struct {
UpstreamBlockDetection string UpstreamBlockDetection string
// Special Options // Special Options
VerifyDomain string
Search []string Search []string
SearchOnly bool SearchOnly bool
Path string Path string
@ -157,6 +157,12 @@ func (info *ResolverInfo) DescriptiveName() string {
info.Name, info.Name,
info.ID(), info.ID(),
) )
case info.IP == nil:
return fmt.Sprintf(
"%s (%s)",
info.Domain,
info.ID(),
)
default: default:
return fmt.Sprintf( return fmt.Sprintf(
"%s (%s)", "%s (%s)",

View file

@ -11,6 +11,7 @@ import (
"golang.org/x/net/publicsuffix" "golang.org/x/net/publicsuffix"
"github.com/miekg/dns"
"github.com/safing/portbase/log" "github.com/safing/portbase/log"
"github.com/safing/portbase/utils" "github.com/safing/portbase/utils"
"github.com/safing/portmaster/netenv" "github.com/safing/portmaster/netenv"
@ -41,7 +42,7 @@ var (
systemResolvers []*Resolver // all resolvers that were assigned by the system systemResolvers []*Resolver // all resolvers that were assigned by the system
localScopes []*Scope // list of scopes with a list of local resolvers that can resolve the scope localScopes []*Scope // list of scopes with a list of local resolvers that can resolve the scope
activeResolvers map[string]*Resolver // lookup map of all resolvers activeResolvers map[string]*Resolver // lookup map of all resolvers
resolverInitDomains map[string]bool // a set with all domains of the dns resolvers resolverInitDomains map[string]struct{} // a set with all domains of the dns resolvers
resolversLock sync.RWMutex resolversLock sync.RWMutex
) )
@ -97,13 +98,15 @@ func createResolver(resolverURL, source string) (*Resolver, bool, error) {
} }
if resolverInitDomains == nil { if resolverInitDomains == nil {
resolverInitDomains = make(map[string]bool) resolverInitDomains = make(map[string]struct{})
} }
switch u.Scheme { switch u.Scheme {
case ServerTypeDNS, ServerTypeDoT, ServerTypeDoH, ServerTypeTCP: case ServerTypeDNS, ServerTypeDoT, ServerTypeDoH, ServerTypeTCP:
case HttpsProtocol: case HttpsProtocol:
u.Scheme = ServerTypeDoH u.Scheme = ServerTypeDoH
case TlsProtocol:
u.Scheme = ServerTypeDoT
default: default:
return nil, false, fmt.Errorf("DNS resolver scheme %q invalid", u.Scheme) return nil, false, fmt.Errorf("DNS resolver scheme %q invalid", u.Scheme)
} }
@ -123,7 +126,6 @@ func createResolver(resolverURL, source string) (*Resolver, bool, error) {
Port: 0, Port: 0,
}, },
ServerAddress: "", ServerAddress: "",
VerifyDomain: "",
Path: u.Path, // Used for DoH Path: u.Path, // Used for DoH
UpstreamBlockDetection: "", UpstreamBlockDetection: "",
} }
@ -185,7 +187,7 @@ func checkAndSetResolverParamters(u *url.URL, resolver *Resolver) error {
ip := net.ParseIP(u.Hostname()) ip := net.ParseIP(u.Hostname())
hostnameIsDomaion := (ip == nil) hostnameIsDomaion := (ip == nil)
if ip == nil && u.Scheme != ServerTypeDoH && u.Scheme != ServerTypeDoT { if ip == nil && u.Scheme != ServerTypeDoH && u.Scheme != ServerTypeDoT {
return fmt.Errorf("resolver IP %q invalid", u.Hostname()) return fmt.Errorf("resolver IP %q is invalid", u.Hostname())
} else { } else {
resolver.Info.IP = ip resolver.Info.IP = ip
} }
@ -211,20 +213,20 @@ func checkAndSetResolverParamters(u *url.URL, resolver *Resolver) error {
// Known key, continue. // Known key, continue.
default: default:
// Unknown key, abort. // Unknown key, abort.
return fmt.Errorf(`unknown parameter "%s"`, key) return fmt.Errorf(`unknown parameter "%q"`, key)
} }
} }
resolver.VerifyDomain = query.Get(parameterVerify) resolver.Info.Domain = query.Get(parameterVerify)
paramterServerIP := query.Get(parameterIP) paramterServerIP := query.Get(parameterIP)
if u.Scheme == ServerTypeDoT || u.Scheme == ServerTypeDoH { if u.Scheme == ServerTypeDoT || u.Scheme == ServerTypeDoH {
// Check if IP and Domain are set correctly // Check if IP and Domain are set correctly
switch { switch {
case hostnameIsDomaion && resolver.VerifyDomain != "": case hostnameIsDomaion && resolver.Info.Domain != "":
return fmt.Errorf("cannot set the domain name via both the hostname in the URL and the verify parameter") return fmt.Errorf("cannot set the domain name via both the hostname in the URL and the verify parameter")
case !hostnameIsDomaion && resolver.VerifyDomain == "": case !hostnameIsDomaion && resolver.Info.Domain == "":
return fmt.Errorf("verify parameter must be set when using ip as domain") return fmt.Errorf("verify parameter must be set when using ip as domain")
case !hostnameIsDomaion && paramterServerIP != "": case !hostnameIsDomaion && paramterServerIP != "":
return fmt.Errorf("cannot set the IP address via both the hostname in the URL and the ip parameter") return fmt.Errorf("cannot set the IP address via both the hostname in the URL and the ip parameter")
@ -235,17 +237,17 @@ func checkAndSetResolverParamters(u *url.URL, resolver *Resolver) error {
case hostnameIsDomaion && paramterServerIP != "": // domain and ip as parameter case hostnameIsDomaion && paramterServerIP != "": // domain and ip as parameter
resolver.Info.IP = net.ParseIP(paramterServerIP) resolver.Info.IP = net.ParseIP(paramterServerIP)
resolver.ServerAddress = net.JoinHostPort(paramterServerIP, strconv.Itoa(int(resolver.Info.Port))) resolver.ServerAddress = net.JoinHostPort(paramterServerIP, strconv.Itoa(int(resolver.Info.Port)))
resolver.VerifyDomain = u.Hostname() resolver.Info.Domain = u.Hostname()
case !hostnameIsDomaion && resolver.VerifyDomain != "": // ip and domain as parameter case !hostnameIsDomaion && resolver.Info.Domain != "": // ip and domain as parameter
resolver.ServerAddress = net.JoinHostPort(ip.String(), strconv.Itoa(int(resolver.Info.Port))) resolver.ServerAddress = net.JoinHostPort(ip.String(), strconv.Itoa(int(resolver.Info.Port)))
case hostnameIsDomaion && resolver.VerifyDomain == "" && paramterServerIP == "": // only domain case hostnameIsDomaion && resolver.Info.Domain == "" && paramterServerIP == "": // only domain
resolver.VerifyDomain = u.Hostname() resolver.Info.Domain = u.Hostname()
resolver.ServerAddress = net.JoinHostPort(resolver.Info.Domain, strconv.Itoa(int(port)))
} }
resolver.Info.Domain = resolver.VerifyDomain resolverInitDomains[dns.Fqdn(resolver.Info.Domain)] = struct{}{}
resolverInitDomains[resolver.Info.Domain] = true
} else { } else {
if resolver.VerifyDomain != "" { if resolver.Info.Domain != "" {
return fmt.Errorf("domain verification is only supported by DoT and DoH servers") return fmt.Errorf("domain verification is only supported by DoT and DoH servers")
} }
resolver.ServerAddress = net.JoinHostPort(ip.String(), strconv.Itoa(int(resolver.Info.Port))) resolver.ServerAddress = net.JoinHostPort(ip.String(), strconv.Itoa(int(resolver.Info.Port)))