From 53f7b70af45844a27db2e9fe99626cad1980f295 Mon Sep 17 00:00:00 2001 From: Karan Sharma Date: Wed, 18 May 2022 09:56:07 +0530 Subject: [PATCH] feat: add tls config for dot lookups Ref https://github.com/mr-karan/doggo/issues/29 --- README.md | 14 ++++++---- cmd/doggo/cli.go | 20 ++++++++------ cmd/doggo/help.go | 15 ++++++---- pkg/models/models.go | 32 +++++++++++---------- pkg/resolvers/classic.go | 16 +++++++---- pkg/resolvers/dnscrypt.go | 6 ++-- pkg/resolvers/resolver.go | 58 +++++++++++++++++---------------------- 7 files changed, 83 insertions(+), 78 deletions(-) diff --git a/README.md b/README.md index fd9e18c..c2a8b64 100644 --- a/README.md +++ b/README.md @@ -210,12 +210,14 @@ URL scheme of the server is used to identify which resolver to use for lookups. ### Resolver Options ``` - --strategy=STRATEGY Specify strategy to query nameserver listed in etc/resolv.conf. Defaults to `all` (`random`, `first`, `all`). - --ndots=INT Specify ndots parameter. Takes value from /etc/resolv.conf if using the system nameserver or 1 otherwise. - --search Use the search list defined in resolv.conf. Defaults to true. Set --search=false to disable search list. - --timeout Specify timeout (in seconds) for the resolver to return a response. - -4 --ipv4 Use IPv4 only. - -6 --ipv6 Use IPv6 only. + --strategy=STRATEGY Specify strategy to query nameserver listed in etc/resolv.conf. Defaults to `all` (`random`, `first`, `all`). + --ndots=INT Specify ndots parameter. Takes value from /etc/resolv.conf if using the system nameserver or 1 otherwise. + --search Use the search list defined in resolv.conf. Defaults to true. Set --search=false to disable search list. + --timeout Specify timeout (in seconds) for the resolver to return a response. + -4 --ipv4 Use IPv4 only. + -6 --ipv6 Use IPv6 only. + --tls-hostname=HOSTNAME Provide a hostname for doing verification of the certificate if the provided DoT nameserver is an IP. + --skip-hostname-verification Skip TLS Hostname Verification in case of DOT Lookups. ``` diff --git a/cmd/doggo/cli.go b/cmd/doggo/cli.go index b6152a7..fe5d550 100644 --- a/cmd/doggo/cli.go +++ b/cmd/doggo/cli.go @@ -46,6 +46,8 @@ func main() { f.BoolP("ipv4", "4", false, "Use IPv4 only") f.BoolP("ipv6", "6", false, "Use IPv6 only") f.String("strategy", "all", "Strategy to query nameservers in resolv.conf file (`all`, `random`, `first`)") + f.String("tls-hostname", "", "Provide a hostname for doing verification of the certificate if the provided DoT nameserver is an IP") + f.Bool("skip-hostname-verification", false, "Skip TLS Hostname Verification") // Output Options f.BoolP("json", "J", false, "Set the output format as JSON") @@ -121,14 +123,16 @@ func main() { // Load Resolvers. rslvrs, err := resolvers.LoadResolvers(resolvers.Options{ - Nameservers: app.Nameservers, - UseIPv4: app.QueryFlags.UseIPv4, - UseIPv6: app.QueryFlags.UseIPv6, - SearchList: app.ResolverOpts.SearchList, - Ndots: app.ResolverOpts.Ndots, - Timeout: app.QueryFlags.Timeout * time.Second, - Logger: app.Logger, - Strategy: app.QueryFlags.Strategy, + Nameservers: app.Nameservers, + UseIPv4: app.QueryFlags.UseIPv4, + UseIPv6: app.QueryFlags.UseIPv6, + SearchList: app.ResolverOpts.SearchList, + Ndots: app.ResolverOpts.Ndots, + Timeout: app.QueryFlags.Timeout * time.Second, + Logger: app.Logger, + Strategy: app.QueryFlags.Strategy, + InsecureSkipVerify: app.QueryFlags.InsecureSkipVerify, + TLSHostname: app.QueryFlags.TLSHostname, }) if err != nil { app.Logger.WithError(err).Error("error loading resolver") diff --git a/cmd/doggo/help.go b/cmd/doggo/help.go index 50cdd01..c714b7a 100644 --- a/cmd/doggo/help.go +++ b/cmd/doggo/help.go @@ -47,12 +47,15 @@ var appHelpTextTemplate = `{{ "NAME" | color "" "heading" }}: {{"-x, --reverse" | color "yellow" ""}} Performs a DNS Lookup for an IPv4 or IPv6 address. Sets the query type and class to PTR and IN respectively. {{ "Resolver Options" | color "" "heading" }}: - {{"--strategy=STRATEGY" | color "yellow" ""}} Specify strategy to query nameserver listed in etc/resolv.conf. ({{"all, random, first" | color "cyan" ""}}). - {{"--ndots=INT" | color "yellow" ""}} Specify ndots parameter. Takes value from /etc/resolv.conf if using the system namesever or 1 otherwise. - {{"--search" | color "yellow" ""}} Use the search list defined in resolv.conf. Defaults to true. Set --search=false to disable search list. - {{"--timeout" | color "yellow" ""}} Specify timeout (in seconds) for the resolver to return a response. - {{"-4 --ipv4" | color "yellow" ""}} Use IPv4 only. - {{"-6 --ipv6" | color "yellow" ""}} Use IPv6 only. + {{"--strategy=STRATEGY" | color "yellow" ""}} Specify strategy to query nameserver listed in etc/resolv.conf. ({{"all, random, first" | color "cyan" ""}}). + {{"--ndots=INT" | color "yellow" ""}} Specify ndots parameter. Takes value from /etc/resolv.conf if using the system namesever or 1 otherwise. + {{"--search" | color "yellow" ""}} Use the search list defined in resolv.conf. Defaults to true. Set --search=false to disable search list. + {{"--timeout" | color "yellow" ""}} Specify timeout (in seconds) for the resolver to return a response. + {{"-4 --ipv4" | color "yellow" ""}} Use IPv4 only. + {{"-6 --ipv6" | color "yellow" ""}} Use IPv6 only. + {{"--ndots=INT" | color "yellow" ""}} Specify ndots parameter. Takes value from /etc/resolv.conf if using the system namesever or 1 otherwise. + {{"--tls-hostname=HOSTNAME" | color "yellow" ""}} Provide a hostname for doing verification of the certificate if the provided DoT nameserver is an IP. + {{"--skip-hostname-verification" | color "yellow" ""}} Skip TLS Hostname Verification in case of DOT Lookups. {{ "Output Options" | color "" "heading" }}: {{"-J, --json " | color "yellow" ""}} Format the output as JSON. diff --git a/pkg/models/models.go b/pkg/models/models.go index 758d72b..c36495b 100644 --- a/pkg/models/models.go +++ b/pkg/models/models.go @@ -22,21 +22,23 @@ const ( // QueryFlags is used store the query params // supplied by the user. type QueryFlags struct { - QNames []string `koanf:"query" json:"query"` - QTypes []string `koanf:"type" json:"type"` - QClasses []string `koanf:"class" json:"class"` - Nameservers []string `koanf:"nameservers" json:"nameservers"` - UseIPv4 bool `koanf:"ipv4" json:"ipv4"` - UseIPv6 bool `koanf:"ipv6" json:"ipv6"` - Ndots int `koanf:"ndots" json:"ndots"` - Timeout time.Duration `koanf:"timeout" json:"timeout"` - Color bool `koanf:"color" json:"-"` - DisplayTimeTaken bool `koanf:"time" json:"-"` - ShowJSON bool `koanf:"json" json:"-"` - ShortOutput bool `koanf:"short" short:"-"` - UseSearchList bool `koanf:"search" json:"-"` - ReverseLookup bool `koanf:"reverse" reverse:"-"` - Strategy string `koanf:"strategy" strategy:"-"` + QNames []string `koanf:"query" json:"query"` + QTypes []string `koanf:"type" json:"type"` + QClasses []string `koanf:"class" json:"class"` + Nameservers []string `koanf:"nameservers" json:"nameservers"` + UseIPv4 bool `koanf:"ipv4" json:"ipv4"` + UseIPv6 bool `koanf:"ipv6" json:"ipv6"` + Ndots int `koanf:"ndots" json:"ndots"` + Timeout time.Duration `koanf:"timeout" json:"timeout"` + Color bool `koanf:"color" json:"-"` + DisplayTimeTaken bool `koanf:"time" json:"-"` + ShowJSON bool `koanf:"json" json:"-"` + ShortOutput bool `koanf:"short" short:"-"` + UseSearchList bool `koanf:"search" json:"-"` + ReverseLookup bool `koanf:"reverse" reverse:"-"` + Strategy string `koanf:"strategy" strategy:"-"` + InsecureSkipVerify bool `koanf:"skip-hostname-verification" skip-hostname-verification:"-"` + TLSHostname string `koanf:"tls-hostname" tls-hostname:"-"` } // Nameserver represents the type of Nameserver diff --git a/pkg/resolvers/classic.go b/pkg/resolvers/classic.go index f72c010..b922400 100644 --- a/pkg/resolvers/classic.go +++ b/pkg/resolvers/classic.go @@ -1,6 +1,7 @@ package resolvers import ( + "crypto/tls" "time" "github.com/miekg/dns" @@ -16,10 +17,8 @@ type ClassicResolver struct { // ClassicResolverOpts holds options for setting up a Classic resolver. type ClassicResolverOpts struct { - IPv4Only bool - IPv6Only bool - UseTLS bool - UseTCP bool + UseTLS bool + UseTCP bool } // NewClassicResolver accepts a list of nameservers and configures a DNS resolver. @@ -34,15 +33,20 @@ func NewClassicResolver(server string, classicOpts ClassicResolverOpts, resolver net = "tcp" } - if classicOpts.IPv4Only { + if resolverOpts.UseIPv4 { net = net + "4" } - if classicOpts.IPv6Only { + if resolverOpts.UseIPv6 { net = net + "6" } if classicOpts.UseTLS { net = net + "-tls" + // Provide extra TLS config for doing/skipping hostname verification. + client.TLSConfig = &tls.Config{ + ServerName: resolverOpts.TLSHostname, + InsecureSkipVerify: resolverOpts.InsecureSkipVerify, + } } client.Net = net diff --git a/pkg/resolvers/dnscrypt.go b/pkg/resolvers/dnscrypt.go index b908482..2239379 100644 --- a/pkg/resolvers/dnscrypt.go +++ b/pkg/resolvers/dnscrypt.go @@ -18,10 +18,7 @@ type DNSCryptResolver struct { // DNSCryptResolverOpts holds options for setting up a DNSCrypt resolver. type DNSCryptResolverOpts struct { - IPv4Only bool - IPv6Only bool - UseTLS bool - UseTCP bool + UseTCP bool } // NewDNSCryptResolver accepts a list of nameservers and configures a DNS resolver. @@ -30,6 +27,7 @@ func NewDNSCryptResolver(server string, dnscryptOpts DNSCryptResolverOpts, resol if dnscryptOpts.UseTCP { net = "tcp" } + client := &dnscrypt.Client{Net: net, Timeout: resolverOpts.Timeout, UDPSize: 4096} resolverInfo, err := client.Dial(server) if err != nil { diff --git a/pkg/resolvers/resolver.go b/pkg/resolvers/resolver.go index 7b33245..cccbf42 100644 --- a/pkg/resolvers/resolver.go +++ b/pkg/resolvers/resolver.go @@ -11,14 +11,17 @@ import ( // Options represent a set of common options // to configure a Resolver. type Options struct { - Nameservers []models.Nameserver - UseIPv4 bool - UseIPv6 bool - SearchList []string - Ndots int - Timeout time.Duration - Logger *logrus.Logger - Strategy string + Logger *logrus.Logger + + Nameservers []models.Nameserver + UseIPv4 bool + UseIPv6 bool + SearchList []string + Ndots int + Timeout time.Duration + Strategy string + InsecureSkipVerify bool + TLSHostname string } // Resolver implements the configuration for a DNS @@ -68,18 +71,13 @@ type Authority struct { // LoadResolvers loads differently configured // resolvers based on a list of nameserver. func LoadResolvers(opts Options) ([]Resolver, error) { - var resolverOpts = Options{ - Timeout: opts.Timeout, - Ndots: opts.Ndots, - SearchList: opts.SearchList, - Logger: opts.Logger, - } - // for each nameserver, initialise the correct resolver + // For each nameserver, initialise the correct resolver. rslvrs := make([]Resolver, 0, len(opts.Nameservers)) + for _, ns := range opts.Nameservers { if ns.Type == models.DOHResolver { opts.Logger.Debug("initiating DOH resolver") - rslvr, err := NewDOHResolver(ns.Address, resolverOpts) + rslvr, err := NewDOHResolver(ns.Address, opts) if err != nil { return rslvrs, err } @@ -89,11 +87,9 @@ func LoadResolvers(opts Options) ([]Resolver, error) { opts.Logger.Debug("initiating DOT resolver") rslvr, err := NewClassicResolver(ns.Address, ClassicResolverOpts{ - IPv4Only: opts.UseIPv4, - IPv6Only: opts.UseIPv6, - UseTLS: true, - UseTCP: true, - }, resolverOpts) + UseTLS: true, + UseTCP: true, + }, opts) if err != nil { return rslvrs, err @@ -104,11 +100,9 @@ func LoadResolvers(opts Options) ([]Resolver, error) { opts.Logger.Debug("initiating TCP resolver") rslvr, err := NewClassicResolver(ns.Address, ClassicResolverOpts{ - IPv4Only: opts.UseIPv4, - IPv6Only: opts.UseIPv6, - UseTLS: false, - UseTCP: true, - }, resolverOpts) + UseTLS: false, + UseTCP: true, + }, opts) if err != nil { return rslvrs, err } @@ -118,11 +112,9 @@ func LoadResolvers(opts Options) ([]Resolver, error) { opts.Logger.Debug("initiating UDP resolver") rslvr, err := NewClassicResolver(ns.Address, ClassicResolverOpts{ - IPv4Only: opts.UseIPv4, - IPv6Only: opts.UseIPv6, - UseTLS: false, - UseTCP: false, - }, resolverOpts) + UseTLS: false, + UseTCP: false, + }, opts) if err != nil { return rslvrs, err } @@ -133,7 +125,7 @@ func LoadResolvers(opts Options) ([]Resolver, error) { rslvr, err := NewDNSCryptResolver(ns.Address, DNSCryptResolverOpts{ UseTCP: false, - }, resolverOpts) + }, opts) if err != nil { return rslvrs, err } @@ -141,7 +133,7 @@ func LoadResolvers(opts Options) ([]Resolver, error) { } if ns.Type == models.DOQResolver { opts.Logger.Debug("initiating DOQ resolver") - rslvr, err := NewDOQResolver(ns.Address, resolverOpts) + rslvr, err := NewDOQResolver(ns.Address, opts) if err != nil { return rslvrs, err }