From 508a8dd7c45f8e650961abe37b5d25c3e3f94b9d Mon Sep 17 00:00:00 2001 From: Karan Sharma Date: Fri, 26 Feb 2021 20:06:46 +0530 Subject: [PATCH] feat: refactor resolver in separate package --- Makefile | 32 ++++++++---- TODO.md | 2 +- cmd/doggo/api/api.go | 82 ++++++++++++++++++++++++++++++ cmd/doggo/{ => cli}/cli.go | 20 +++++--- cmd/doggo/{ => cli}/help.go | 0 cmd/doggo/cli/hub.go | 36 +++++++++++++ cmd/doggo/{ => cli}/nameservers.go | 46 +++++++---------- cmd/doggo/{ => cli}/output.go | 0 cmd/doggo/{ => cli}/parse.go | 0 cmd/doggo/cli/resolver.go | 1 + cmd/doggo/hub.go | 71 -------------------------- cmd/doggo/resolver.go | 79 ---------------------------- go.mod | 2 + go.sum | 10 ++-- pkg/models/models.go | 40 +++++++++++++++ pkg/resolvers/resolver.go | 79 ++++++++++++++++++++++++++-- pkg/utils/logger.go | 13 +++++ 17 files changed, 306 insertions(+), 207 deletions(-) create mode 100644 cmd/doggo/api/api.go rename cmd/doggo/{ => cli}/cli.go (88%) rename cmd/doggo/{ => cli}/help.go (100%) create mode 100644 cmd/doggo/cli/hub.go rename cmd/doggo/{ => cli}/nameservers.go (66%) rename cmd/doggo/{ => cli}/output.go (100%) rename cmd/doggo/{ => cli}/parse.go (100%) create mode 100644 cmd/doggo/cli/resolver.go delete mode 100644 cmd/doggo/hub.go delete mode 100644 cmd/doggo/resolver.go create mode 100644 pkg/models/models.go create mode 100644 pkg/utils/logger.go diff --git a/Makefile b/Makefile index 774cd76..49184a9 100644 --- a/Makefile +++ b/Makefile @@ -1,25 +1,35 @@ -BIN := ./bin/doggo +CLI_BIN := ./bin/doggo-cli.bin +API_BIN := ./bin/doggo-api.bin HASH := $(shell git rev-parse --short HEAD) BUILD_DATE := $(shell date '+%Y-%m-%d %H:%M:%S') VERSION := ${HASH} -.PHONY: build -build: - go build -o ${BIN} -ldflags="-X 'main.buildVersion=${VERSION}' -X 'main.buildDate=${BUILD_DATE}'" ./cmd/doggo/ +.PHONY: build-cli +build-cli: + go build -o ${CLI_BIN} -ldflags="-X 'main.buildVersion=${VERSION}' -X 'main.buildDate=${BUILD_DATE}'" ./cmd/doggo/cli/ -.PHONY: run -run: build ## Build and Execute the binary after the build step - ${BIN} +.PHONY: build-api +build-api: + go build -o ${API_BIN} -ldflags="-X 'main.buildVersion=${VERSION}' -X 'main.buildDate=${BUILD_DATE}'" ./cmd/doggo/api/ + + +.PHONY: build +build: build-api build-cli + +.PHONY: run-cli +run-cli: build-cli ## Build and Execute the CLI binary after the build step. + ${CLI_BIN} + +.PHONY: run-api +run-api: build-api ## Build and Execute the API binary after the build step. + ${API_BIN} .PHONY: clean clean: go clean - - rm -f ${BIN} + - rm -rf ./bin/ .PHONY: lint lint: golangci-lint run - -.PHONY: fresh -fresh: clean build diff --git a/TODO.md b/TODO.md index b76478c..30effea 100644 --- a/TODO.md +++ b/TODO.md @@ -61,7 +61,7 @@ - [ ] zsh - [ ] fish - [ ] Add tests for Resolvers. -- [ ] Add tests for CLI Output. +- [ ] Add tests for CLI Output. - [ ] Homebrew - Goreleaser - [x] Separate Authority/Answer in JSON output. - [x] Error on NXDomain (Related upstream [bug](https://github.com/miekg/dns/issues/1198)) diff --git a/cmd/doggo/api/api.go b/cmd/doggo/api/api.go new file mode 100644 index 0000000..9f43c8e --- /dev/null +++ b/cmd/doggo/api/api.go @@ -0,0 +1,82 @@ +package main + +import ( + "encoding/json" + "net/http" + + "github.com/go-chi/chi" + "github.com/go-chi/chi/middleware" + "github.com/knadh/koanf" + "github.com/mr-karan/doggo/pkg/utils" +) + +var ( + logger = utils.InitLogger() + k = koanf.New(".") +) + +type resp struct { + Status string `json:"status"` + Message string `json:"message,omitempty"` + Data interface{} `json:"data,omitempty"` +} + +func main() { + + r := chi.NewRouter() + + // Setup middlewares. + r.Use(middleware.RequestID) + r.Use(middleware.RealIP) + r.Use(middleware.Logger) + r.Use(middleware.Recoverer) + + r.Get("/", func(w http.ResponseWriter, r *http.Request) { + sendSuccessResponse("Welcome to Doggo DNS!", w) + return + }) + + r.Get("/ping/", func(w http.ResponseWriter, r *http.Request) { + sendSuccessResponse("PONG", w) + return + }) + + r.Post("/lookup/", func(w http.ResponseWriter, r *http.Request) { + return + }) + + http.ListenAndServe(":3000", r) +} + +// sendResponse sends an HTTP success response. +func sendResponse(data interface{}, statusText string, status int, w http.ResponseWriter) { + w.WriteHeader(status) + w.Header().Set("Content-Type", "application/json; charset=utf-8") + + out, err := json.Marshal(resp{Status: statusText, Data: data}) + if err != nil { + sendErrorResponse("Internal Server Error", http.StatusInternalServerError, nil, w) + return + } + + _, _ = w.Write(out) +} + +// sendSuccessResponse sends an HTTP success (200 OK) response. +func sendSuccessResponse(data interface{}, w http.ResponseWriter) { + sendResponse(data, "success", http.StatusOK, w) +} + +// sendErrorResponse sends an HTTP error response. +func sendErrorResponse(message string, status int, data interface{}, w http.ResponseWriter) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(status) + + resp := resp{Status: "error", + Message: message, + Data: data} + + out, _ := json.Marshal(resp) + + _, _ = w.Write(out) +} diff --git a/cmd/doggo/cli.go b/cmd/doggo/cli/cli.go similarity index 88% rename from cmd/doggo/cli.go rename to cmd/doggo/cli/cli.go index dd47d02..e3a7168 100644 --- a/cmd/doggo/cli.go +++ b/cmd/doggo/cli/cli.go @@ -3,11 +3,13 @@ package main import ( "os" "strings" + "time" "github.com/knadh/koanf" "github.com/knadh/koanf/providers/posflag" "github.com/miekg/dns" "github.com/mr-karan/doggo/pkg/resolvers" + "github.com/mr-karan/doggo/pkg/utils" "github.com/sirupsen/logrus" flag "github.com/spf13/pflag" ) @@ -20,7 +22,7 @@ var ( func main() { var ( - logger = initLogger() + logger = utils.InitLogger() k = koanf.New(".") ) @@ -110,15 +112,21 @@ func main() { hub.Logger.Exit(2) } - // Load Resolver Options. - hub.loadResolverOptions() - // Load Resolvers. - err = hub.loadResolvers() + rslvrs, err := resolvers.LoadResolvers(resolvers.Options{ + Nameservers: hub.Nameservers, + UseIPv4: hub.QueryFlags.UseIPv4, + UseIPv6: hub.QueryFlags.UseIPv6, + SearchList: hub.ResolverOpts.SearchList, + Ndots: hub.ResolverOpts.Ndots, + Timeout: hub.QueryFlags.Timeout * time.Second, + Logger: hub.Logger, + }) if err != nil { hub.Logger.WithError(err).Error("error loading resolver") hub.Logger.Exit(2) } + hub.Resolvers = rslvrs // Run the app. hub.Logger.Debug("Starting doggo 🐶") @@ -130,7 +138,7 @@ func main() { // Resolve Queries. var responses []resolvers.Response for _, q := range hub.Questions { - for _, rslv := range hub.Resolver { + for _, rslv := range hub.Resolvers { resp, err := rslv.Lookup(q) if err != nil { hub.Logger.WithError(err).Error("error looking up DNS records") diff --git a/cmd/doggo/help.go b/cmd/doggo/cli/help.go similarity index 100% rename from cmd/doggo/help.go rename to cmd/doggo/cli/help.go diff --git a/cmd/doggo/cli/hub.go b/cmd/doggo/cli/hub.go new file mode 100644 index 0000000..fec8a29 --- /dev/null +++ b/cmd/doggo/cli/hub.go @@ -0,0 +1,36 @@ +package main + +import ( + "github.com/miekg/dns" + "github.com/mr-karan/doggo/pkg/models" + "github.com/mr-karan/doggo/pkg/resolvers" + "github.com/sirupsen/logrus" +) + +// Hub represents the structure for all app wide configuration. +type Hub struct { + Logger *logrus.Logger + Version string + QueryFlags models.QueryFlags + UnparsedArgs []string + Questions []dns.Question + Resolvers []resolvers.Resolver + ResolverOpts resolvers.Options + Nameservers []models.Nameserver +} + +// NewHub initializes an instance of Hub which holds app wide configuration. +func NewHub(logger *logrus.Logger, buildVersion string) *Hub { + hub := &Hub{ + Logger: logger, + Version: buildVersion, + QueryFlags: models.QueryFlags{ + QNames: []string{}, + QTypes: []string{}, + QClasses: []string{}, + Nameservers: []string{}, + }, + Nameservers: []models.Nameserver{}, + } + return hub +} diff --git a/cmd/doggo/nameservers.go b/cmd/doggo/cli/nameservers.go similarity index 66% rename from cmd/doggo/nameservers.go rename to cmd/doggo/cli/nameservers.go index e5a8437..680b9f0 100644 --- a/cmd/doggo/nameservers.go +++ b/cmd/doggo/cli/nameservers.go @@ -6,19 +6,7 @@ import ( "net/url" "github.com/mr-karan/doggo/pkg/config" -) - -const ( - // DefaultTLSPort specifies the default port for a DNS server connecting over TCP over TLS - DefaultTLSPort = "853" - // DefaultUDPPort specifies the default port for a DNS server connecting over UDP - DefaultUDPPort = "53" - // DefaultTCPPort specifies the default port for a DNS server connecting over TCP - DefaultTCPPort = "53" - UDPResolver = "udp" - DOHResolver = "doh" - TCPResolver = "tcp" - DOTResolver = "dot" + "github.com/mr-karan/doggo/pkg/models" ) // loadNameservers reads all the user given @@ -62,56 +50,56 @@ func (hub *Hub) loadNameservers() error { return nil } -func getDefaultServers() ([]Nameserver, int, []string, error) { +func getDefaultServers() ([]models.Nameserver, int, []string, error) { dnsServers, ndots, search, err := config.GetDefaultServers() if err != nil { return nil, 0, nil, err } - servers := make([]Nameserver, 0, len(dnsServers)) + servers := make([]models.Nameserver, 0, len(dnsServers)) for _, s := range dnsServers { - ns := Nameserver{ - Type: UDPResolver, - Address: net.JoinHostPort(s, DefaultUDPPort), + ns := models.Nameserver{ + Type: models.UDPResolver, + Address: net.JoinHostPort(s, models.DefaultUDPPort), } servers = append(servers, ns) } return servers, ndots, search, nil } -func initNameserver(n string) (Nameserver, error) { +func initNameserver(n string) (models.Nameserver, error) { // Instantiate a UDP resolver with default port as a fallback. - ns := Nameserver{ - Type: UDPResolver, - Address: net.JoinHostPort(n, DefaultUDPPort), + ns := models.Nameserver{ + Type: models.UDPResolver, + Address: net.JoinHostPort(n, models.DefaultUDPPort), } u, err := url.Parse(n) if err != nil { return ns, err } if u.Scheme == "https" { - ns.Type = DOHResolver + ns.Type = models.DOHResolver ns.Address = u.String() } if u.Scheme == "tls" { - ns.Type = DOTResolver + ns.Type = models.DOTResolver if u.Port() == "" { - ns.Address = net.JoinHostPort(u.Hostname(), DefaultTLSPort) + ns.Address = net.JoinHostPort(u.Hostname(), models.DefaultTLSPort) } else { ns.Address = net.JoinHostPort(u.Hostname(), u.Port()) } } if u.Scheme == "tcp" { - ns.Type = TCPResolver + ns.Type = models.TCPResolver if u.Port() == "" { - ns.Address = net.JoinHostPort(u.Hostname(), DefaultTCPPort) + ns.Address = net.JoinHostPort(u.Hostname(), models.DefaultTCPPort) } else { ns.Address = net.JoinHostPort(u.Hostname(), u.Port()) } } if u.Scheme == "udp" { - ns.Type = UDPResolver + ns.Type = models.UDPResolver if u.Port() == "" { - ns.Address = net.JoinHostPort(u.Hostname(), DefaultUDPPort) + ns.Address = net.JoinHostPort(u.Hostname(), models.DefaultUDPPort) } else { ns.Address = net.JoinHostPort(u.Hostname(), u.Port()) } diff --git a/cmd/doggo/output.go b/cmd/doggo/cli/output.go similarity index 100% rename from cmd/doggo/output.go rename to cmd/doggo/cli/output.go diff --git a/cmd/doggo/parse.go b/cmd/doggo/cli/parse.go similarity index 100% rename from cmd/doggo/parse.go rename to cmd/doggo/cli/parse.go diff --git a/cmd/doggo/cli/resolver.go b/cmd/doggo/cli/resolver.go new file mode 100644 index 0000000..06ab7d0 --- /dev/null +++ b/cmd/doggo/cli/resolver.go @@ -0,0 +1 @@ +package main diff --git a/cmd/doggo/hub.go b/cmd/doggo/hub.go deleted file mode 100644 index ee0bca0..0000000 --- a/cmd/doggo/hub.go +++ /dev/null @@ -1,71 +0,0 @@ -package main - -import ( - "time" - - "github.com/miekg/dns" - "github.com/mr-karan/doggo/pkg/resolvers" - "github.com/sirupsen/logrus" -) - -// Hub represents the structure for all app wide configuration. -type Hub struct { - Logger *logrus.Logger - Version string - QueryFlags QueryFlags - UnparsedArgs []string - Questions []dns.Question - Resolver []resolvers.Resolver - ResolverOpts resolvers.Options - Nameservers []Nameserver -} - -// QueryFlags is used store the query params -// supplied by the user. -type QueryFlags struct { - QNames []string `koanf:"query"` - QTypes []string `koanf:"type"` - QClasses []string `koanf:"class"` - Nameservers []string `koanf:"nameserver"` - UseIPv4 bool `koanf:"ipv4"` - UseIPv6 bool `koanf:"ipv6"` - DisplayTimeTaken bool `koanf:"time"` - ShowJSON bool `koanf:"json"` - UseSearchList bool `koanf:"search"` - Ndots int `koanf:"ndots"` - Color bool `koanf:"color"` - Timeout time.Duration `koanf:"timeout"` -} - -// Nameserver represents the type of Nameserver -// along with the server address. -type Nameserver struct { - Address string - Type string -} - -// NewHub initializes an instance of Hub which holds app wide configuration. -func NewHub(logger *logrus.Logger, buildVersion string) *Hub { - hub := &Hub{ - Logger: logger, - Version: buildVersion, - QueryFlags: QueryFlags{ - QNames: []string{}, - QTypes: []string{}, - QClasses: []string{}, - Nameservers: []string{}, - }, - Nameservers: []Nameserver{}, - } - return hub -} - -// initLogger initializes logger -func initLogger() *logrus.Logger { - logger := logrus.New() - logger.SetFormatter(&logrus.TextFormatter{ - FullTimestamp: true, - DisableLevelTruncation: true, - }) - return logger -} diff --git a/cmd/doggo/resolver.go b/cmd/doggo/resolver.go deleted file mode 100644 index 7e65cf6..0000000 --- a/cmd/doggo/resolver.go +++ /dev/null @@ -1,79 +0,0 @@ -package main - -import ( - "time" - - "github.com/mr-karan/doggo/pkg/resolvers" -) - -// loadResolverOptions loads the common options -// to configure a resolver from the query args. -func (hub *Hub) loadResolverOptions() { - hub.ResolverOpts.Timeout = hub.QueryFlags.Timeout -} - -// loadResolvers loads differently configured -// resolvers based on a list of nameserver. -func (hub *Hub) loadResolvers() error { - var resolverOpts = resolvers.Options{ - Timeout: hub.QueryFlags.Timeout * time.Second, - Ndots: hub.ResolverOpts.Ndots, - SearchList: hub.ResolverOpts.SearchList, - Logger: hub.Logger, - } - // for each nameserver, initialise the correct resolver - for _, ns := range hub.Nameservers { - if ns.Type == DOHResolver { - hub.Logger.Debug("initiating DOH resolver") - rslvr, err := resolvers.NewDOHResolver(ns.Address, resolverOpts) - if err != nil { - return err - } - hub.Resolver = append(hub.Resolver, rslvr) - } - if ns.Type == DOTResolver { - hub.Logger.Debug("initiating DOT resolver") - rslvr, err := resolvers.NewClassicResolver(ns.Address, - resolvers.ClassicResolverOpts{ - IPv4Only: hub.QueryFlags.UseIPv4, - IPv6Only: hub.QueryFlags.UseIPv6, - UseTLS: true, - UseTCP: true, - }, resolverOpts) - - if err != nil { - return err - } - hub.Resolver = append(hub.Resolver, rslvr) - } - if ns.Type == TCPResolver { - hub.Logger.Debug("initiating TCP resolver") - rslvr, err := resolvers.NewClassicResolver(ns.Address, - resolvers.ClassicResolverOpts{ - IPv4Only: hub.QueryFlags.UseIPv4, - IPv6Only: hub.QueryFlags.UseIPv6, - UseTLS: false, - UseTCP: true, - }, resolverOpts) - if err != nil { - return err - } - hub.Resolver = append(hub.Resolver, rslvr) - } - if ns.Type == UDPResolver { - hub.Logger.Debug("initiating UDP resolver") - rslvr, err := resolvers.NewClassicResolver(ns.Address, - resolvers.ClassicResolverOpts{ - IPv4Only: hub.QueryFlags.UseIPv4, - IPv6Only: hub.QueryFlags.UseIPv6, - UseTLS: false, - UseTCP: false, - }, resolverOpts) - if err != nil { - return err - } - hub.Resolver = append(hub.Resolver, rslvr) - } - } - return nil -} diff --git a/go.mod b/go.mod index 35e7edf..103944d 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,8 @@ go 1.16 require ( github.com/fatih/color v1.10.0 + github.com/go-chi/chi v1.5.3 + github.com/go-chi/render v1.0.1 github.com/knadh/koanf v0.14.0 github.com/mattn/go-runewidth v0.0.9 // indirect github.com/miekg/dns v1.1.35 diff --git a/go.sum b/go.sum index 176b344..8f99064 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,3 @@ -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -8,6 +7,10 @@ github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGE github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/go-chi/chi v1.5.3 h1:+DVDS9/D3MTbEu3WrrH3oz9oP6PlSPSNj8LLw3X17yU= +github.com/go-chi/chi v1.5.3/go.mod h1:Q8xfe6s3fjZyMr8ZTv5jL+vxhVaFyCq2s+RvSfzTD0E= +github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8= +github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= @@ -37,7 +40,6 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= @@ -54,20 +56,16 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEha golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d h1:nc5K6ox/4lTFbMVSL9WRR81ixkcwXThoiF6yf+R9scA= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/pkg/models/models.go b/pkg/models/models.go new file mode 100644 index 0000000..edbd0d4 --- /dev/null +++ b/pkg/models/models.go @@ -0,0 +1,40 @@ +package models + +import "time" + +const ( + // DefaultTLSPort specifies the default port for a DNS server connecting over TCP over TLS + DefaultTLSPort = "853" + // DefaultUDPPort specifies the default port for a DNS server connecting over UDP + DefaultUDPPort = "53" + // DefaultTCPPort specifies the default port for a DNS server connecting over TCP + DefaultTCPPort = "53" + UDPResolver = "udp" + DOHResolver = "doh" + TCPResolver = "tcp" + DOTResolver = "dot" +) + +// QueryFlags is used store the query params +// supplied by the user. +type QueryFlags struct { + QNames []string `koanf:"query"` + QTypes []string `koanf:"type"` + QClasses []string `koanf:"class"` + Nameservers []string `koanf:"nameserver"` + UseIPv4 bool `koanf:"ipv4"` + UseIPv6 bool `koanf:"ipv6"` + DisplayTimeTaken bool `koanf:"time"` + ShowJSON bool `koanf:"json"` + UseSearchList bool `koanf:"search"` + Ndots int `koanf:"ndots"` + Color bool `koanf:"color"` + Timeout time.Duration `koanf:"timeout"` +} + +// Nameserver represents the type of Nameserver +// along with the server address. +type Nameserver struct { + Address string + Type string +} diff --git a/pkg/resolvers/resolver.go b/pkg/resolvers/resolver.go index 0548386..3af8db8 100644 --- a/pkg/resolvers/resolver.go +++ b/pkg/resolvers/resolver.go @@ -4,16 +4,20 @@ import ( "time" "github.com/miekg/dns" + "github.com/mr-karan/doggo/pkg/models" "github.com/sirupsen/logrus" ) // Options represent a set of common options // to configure a Resolver. type Options struct { - SearchList []string - Ndots int - Timeout time.Duration - Logger *logrus.Logger + Nameservers []models.Nameserver + UseIPv4 bool + UseIPv6 bool + SearchList []string + Ndots int + Timeout time.Duration + Logger *logrus.Logger } // Resolver implements the configuration for a DNS @@ -59,3 +63,70 @@ type Authority struct { RTT string `json:"rtt"` Nameserver string `json:"nameserver"` } + +// 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 + 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) + if err != nil { + return rslvrs, err + } + rslvrs = append(rslvrs, rslvr) + } + if ns.Type == models.DOTResolver { + opts.Logger.Debug("initiating DOT resolver") + rslvr, err := NewClassicResolver(ns.Address, + ClassicResolverOpts{ + IPv4Only: opts.UseIPv4, + IPv6Only: opts.UseIPv6, + UseTLS: true, + UseTCP: true, + }, resolverOpts) + + if err != nil { + return rslvrs, err + } + rslvrs = append(rslvrs, rslvr) + } + if ns.Type == models.TCPResolver { + opts.Logger.Debug("initiating TCP resolver") + rslvr, err := NewClassicResolver(ns.Address, + ClassicResolverOpts{ + IPv4Only: opts.UseIPv4, + IPv6Only: opts.UseIPv6, + UseTLS: false, + UseTCP: true, + }, resolverOpts) + if err != nil { + return rslvrs, err + } + rslvrs = append(rslvrs, rslvr) + } + if ns.Type == models.UDPResolver { + opts.Logger.Debug("initiating UDP resolver") + rslvr, err := NewClassicResolver(ns.Address, + ClassicResolverOpts{ + IPv4Only: opts.UseIPv4, + IPv6Only: opts.UseIPv6, + UseTLS: false, + UseTCP: false, + }, resolverOpts) + if err != nil { + return rslvrs, err + } + rslvrs = append(rslvrs, rslvr) + } + } + return rslvrs, nil +} diff --git a/pkg/utils/logger.go b/pkg/utils/logger.go new file mode 100644 index 0000000..3592aa5 --- /dev/null +++ b/pkg/utils/logger.go @@ -0,0 +1,13 @@ +package utils + +import "github.com/sirupsen/logrus" + +// InitLogger initializes logger. +func InitLogger() *logrus.Logger { + logger := logrus.New() + logger.SetFormatter(&logrus.TextFormatter{ + FullTimestamp: true, + DisableLevelTruncation: true, + }) + return logger +}