From 508a8dd7c45f8e650961abe37b5d25c3e3f94b9d Mon Sep 17 00:00:00 2001 From: Karan Sharma Date: Fri, 26 Feb 2021 20:06:46 +0530 Subject: [PATCH 01/75] 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 +} From b7536310127412a83fcd464e3a4e93282937327e Mon Sep 17 00:00:00 2001 From: Karan Sharma Date: Sat, 27 Feb 2021 10:56:33 +0530 Subject: [PATCH 02/75] feat: refactor app in a separate package --- cmd/doggo/cli/cli.go | 99 +++++++++---------- cmd/doggo/cli/parse.go | 40 ++------ cmd/doggo/cli/resolver.go | 1 - cmd/doggo/cli/hub.go => internal/app/app.go | 15 ++- .../doggo/cli => internal/app}/nameservers.go | 62 ++++++------ {cmd/doggo/cli => internal/app}/output.go | 26 ++--- internal/app/questions.go | 35 +++++++ pkg/models/models.go | 24 ++--- 8 files changed, 151 insertions(+), 151 deletions(-) delete mode 100644 cmd/doggo/cli/resolver.go rename cmd/doggo/cli/hub.go => internal/app/app.go (69%) rename {cmd/doggo/cli => internal/app}/nameservers.go (80%) rename {cmd/doggo/cli => internal/app}/output.go (86%) create mode 100644 internal/app/questions.go diff --git a/cmd/doggo/cli/cli.go b/cmd/doggo/cli/cli.go index e3a7168..7b76f8e 100644 --- a/cmd/doggo/cli/cli.go +++ b/cmd/doggo/cli/cli.go @@ -2,12 +2,11 @@ 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/internal/app" "github.com/mr-karan/doggo/pkg/resolvers" "github.com/mr-karan/doggo/pkg/utils" "github.com/sirupsen/logrus" @@ -26,8 +25,8 @@ func main() { k = koanf.New(".") ) - // Initialize hub. - hub := NewHub(logger, buildVersion) + // Initialize app. + app := app.New(logger, buildVersion) // Configure Flags. f := flag.NewFlagSet("config", flag.ContinueOnError) @@ -39,7 +38,7 @@ func main() { f.StringSliceP("query", "q", []string{}, "Domain name to query") f.StringSliceP("type", "t", []string{}, "Type of DNS record to be queried (A, AAAA, MX etc)") f.StringSliceP("class", "c", []string{}, "Network class of the DNS record to be queried (IN, CH, HS etc)") - f.StringSliceP("nameserver", "n", []string{}, "Address of the nameserver to send packets to") + f.StringSliceP("nameservers", "n", []string{}, "Address of the nameserver to send packets to") // Resolver Options f.Int("timeout", 5, "Sets the timeout for a query to T seconds. The default timeout is 5 seconds.") @@ -57,98 +56,88 @@ func main() { // Parse and Load Flags. err := f.Parse(os.Args[1:]) if err != nil { - hub.Logger.WithError(err).Error("error parsing flags") - hub.Logger.Exit(2) + app.Logger.WithError(err).Error("error parsing flags") + app.Logger.Exit(2) } if err = k.Load(posflag.Provider(f, ".", k), nil); err != nil { - hub.Logger.WithError(err).Error("error loading flags") + app.Logger.WithError(err).Error("error loading flags") f.Usage() - hub.Logger.Exit(2) + app.Logger.Exit(2) } // Set log level. if k.Bool("debug") { // Set logger level - hub.Logger.SetLevel(logrus.DebugLevel) + app.Logger.SetLevel(logrus.DebugLevel) } else { - hub.Logger.SetLevel(logrus.InfoLevel) + app.Logger.SetLevel(logrus.InfoLevel) } - // Unmarshall flags to the hub. - err = k.Unmarshal("", &hub.QueryFlags) + // Unmarshall flags to the app. + err = k.Unmarshal("", &app.QueryFlags) if err != nil { - hub.Logger.WithError(err).Error("error loading args") - hub.Logger.Exit(2) + app.Logger.WithError(err).Error("error loading args") + app.Logger.Exit(2) } // Load all `non-flag` arguments // which will be parsed separately. - hub.UnparsedArgs = f.Args() + nsvrs, qt, qc, qn := loadUnparsedArgs(f.Args()) + app.QueryFlags.Nameservers = append(app.QueryFlags.Nameservers, nsvrs...) + app.QueryFlags.QTypes = append(app.QueryFlags.QTypes, qt...) + app.QueryFlags.QClasses = append(app.QueryFlags.QClasses, qc...) + app.QueryFlags.QNames = append(app.QueryFlags.QNames, qn...) - // Parse Query Args. - err = hub.loadQueryArgs() - if err != nil { - hub.Logger.WithError(err).Error("error parsing flags/arguments") - hub.Logger.Exit(2) - } + // Load fallbacks. + app.LoadFallbacks() // Load Questions. - for _, n := range hub.QueryFlags.QNames { - for _, t := range hub.QueryFlags.QTypes { - for _, c := range hub.QueryFlags.QClasses { - hub.Questions = append(hub.Questions, dns.Question{ - Name: n, - Qtype: dns.StringToType[strings.ToUpper(t)], - Qclass: dns.StringToClass[strings.ToUpper(c)], - }) - } - } - } + app.PrepareQuestions() // Load Nameservers. - err = hub.loadNameservers() + err = app.LoadNameservers() if err != nil { - hub.Logger.WithError(err).Error("error loading nameservers") - hub.Logger.Exit(2) + app.Logger.WithError(err).Error("error loading nameservers") + app.Logger.Exit(2) } // Load Resolvers. 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, + 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, }) if err != nil { - hub.Logger.WithError(err).Error("error loading resolver") - hub.Logger.Exit(2) + app.Logger.WithError(err).Error("error loading resolver") + app.Logger.Exit(2) } - hub.Resolvers = rslvrs + app.Resolvers = rslvrs // Run the app. - hub.Logger.Debug("Starting doggo 🐶") - if len(hub.QueryFlags.QNames) == 0 { + app.Logger.Debug("Starting doggo 🐶") + if len(app.QueryFlags.QNames) == 0 { f.Usage() - hub.Logger.Exit(0) + app.Logger.Exit(0) } // Resolve Queries. var responses []resolvers.Response - for _, q := range hub.Questions { - for _, rslv := range hub.Resolvers { + for _, q := range app.Questions { + for _, rslv := range app.Resolvers { resp, err := rslv.Lookup(q) if err != nil { - hub.Logger.WithError(err).Error("error looking up DNS records") - hub.Logger.Exit(2) + app.Logger.WithError(err).Error("error looking up DNS records") + app.Logger.Exit(2) } responses = append(responses, resp) } } - hub.Output(responses) + app.Output(responses) // Quitting. - hub.Logger.Exit(0) + app.Logger.Exit(0) } diff --git a/cmd/doggo/cli/parse.go b/cmd/doggo/cli/parse.go index a81fa4a..52ac09a 100644 --- a/cmd/doggo/cli/parse.go +++ b/cmd/doggo/cli/parse.go @@ -6,18 +6,6 @@ import ( "github.com/miekg/dns" ) -func (hub *Hub) loadQueryArgs() error { - // Appends a list of unparsed args to - // internal query flags. - err := hub.loadUnparsedArgs() - if err != nil { - return err - } - // Load all fallbacks in internal query flags. - hub.loadFallbacks() - return nil -} - // loadUnparsedArgs tries to parse all the arguments // which are unparsed by `flag` library. These arguments don't have any specific // order so we have to deduce based on the pattern of argument. @@ -28,30 +16,20 @@ func (hub *Hub) loadQueryArgs() error { // pattern it is considered to be a "hostname". // Eg of unparsed argument: `dig mrkaran.dev @1.1.1.1 AAAA` // where `@1.1.1.1` and `AAAA` are "unparsed" args. -func (hub *Hub) loadUnparsedArgs() error { - for _, arg := range hub.UnparsedArgs { +// Returns a list of nameserver, queryTypes, queryClasses, queryNames. +func loadUnparsedArgs(args []string) ([]string, []string, []string, []string) { + var ns, qt, qc, qn []string + for _, arg := range args { if strings.HasPrefix(arg, "@") { - hub.QueryFlags.Nameservers = append(hub.QueryFlags.Nameservers, strings.Trim(arg, "@")) + ns = append(ns, strings.Trim(arg, "@")) } else if _, ok := dns.StringToType[strings.ToUpper(arg)]; ok { - hub.QueryFlags.QTypes = append(hub.QueryFlags.QTypes, arg) + qt = append(qt, arg) } else if _, ok := dns.StringToClass[strings.ToUpper(arg)]; ok { - hub.QueryFlags.QClasses = append(hub.QueryFlags.QClasses, arg) + qc = append(qc, arg) } else { // if nothing matches, consider it's a query name. - hub.QueryFlags.QNames = append(hub.QueryFlags.QNames, arg) + qn = append(qn, arg) } } - return nil -} - -// loadFallbacks sets fallbacks for options -// that are not specified by the user but necessary -// for the resolver. -func (hub *Hub) loadFallbacks() { - if len(hub.QueryFlags.QTypes) == 0 { - hub.QueryFlags.QTypes = append(hub.QueryFlags.QTypes, "A") - } - if len(hub.QueryFlags.QClasses) == 0 { - hub.QueryFlags.QClasses = append(hub.QueryFlags.QClasses, "IN") - } + return ns, qt, qc, qn } diff --git a/cmd/doggo/cli/resolver.go b/cmd/doggo/cli/resolver.go deleted file mode 100644 index 06ab7d0..0000000 --- a/cmd/doggo/cli/resolver.go +++ /dev/null @@ -1 +0,0 @@ -package main diff --git a/cmd/doggo/cli/hub.go b/internal/app/app.go similarity index 69% rename from cmd/doggo/cli/hub.go rename to internal/app/app.go index fec8a29..c9378d7 100644 --- a/cmd/doggo/cli/hub.go +++ b/internal/app/app.go @@ -1,4 +1,4 @@ -package main +package app import ( "github.com/miekg/dns" @@ -7,21 +7,20 @@ import ( "github.com/sirupsen/logrus" ) -// Hub represents the structure for all app wide configuration. -type Hub struct { +// App represents the structure for all app wide configuration. +type App 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{ +// NewApp initializes an instance of App which holds app wide configuration. +func New(logger *logrus.Logger, buildVersion string) App { + app := App{ Logger: logger, Version: buildVersion, QueryFlags: models.QueryFlags{ @@ -32,5 +31,5 @@ func NewHub(logger *logrus.Logger, buildVersion string) *Hub { }, Nameservers: []models.Nameserver{}, } - return hub + return app } diff --git a/cmd/doggo/cli/nameservers.go b/internal/app/nameservers.go similarity index 80% rename from cmd/doggo/cli/nameservers.go rename to internal/app/nameservers.go index 680b9f0..eaa106c 100644 --- a/cmd/doggo/cli/nameservers.go +++ b/internal/app/nameservers.go @@ -1,4 +1,4 @@ -package main +package app import ( "fmt" @@ -9,63 +9,47 @@ import ( "github.com/mr-karan/doggo/pkg/models" ) -// loadNameservers reads all the user given -// nameservers and loads to Hub. -func (hub *Hub) loadNameservers() error { - for _, srv := range hub.QueryFlags.Nameservers { +// LoadNameservers reads all the user given +// nameservers and loads to App. +func (app *App) LoadNameservers() error { + for _, srv := range app.QueryFlags.Nameservers { ns, err := initNameserver(srv) if err != nil { return fmt.Errorf("error parsing nameserver: %s", srv) } // check if properly initialised. if ns.Address != "" && ns.Type != "" { - hub.Nameservers = append(hub.Nameservers, ns) + app.Nameservers = append(app.Nameservers, ns) } } // Set `ndots` to the user specified value. - hub.ResolverOpts.Ndots = hub.QueryFlags.Ndots + app.ResolverOpts.Ndots = app.QueryFlags.Ndots // fallback to system nameserver // in case no nameserver is specified by user. - if len(hub.Nameservers) == 0 { + if len(app.Nameservers) == 0 { ns, ndots, search, err := getDefaultServers() if err != nil { return fmt.Errorf("error fetching system default nameserver") } // `-1` indicates the flag is not set. // use from config if user hasn't specified any value. - if hub.ResolverOpts.Ndots == -1 { - hub.ResolverOpts.Ndots = ndots + if app.ResolverOpts.Ndots == -1 { + app.ResolverOpts.Ndots = ndots } - if len(search) > 0 && hub.QueryFlags.UseSearchList { - hub.ResolverOpts.SearchList = search + if len(search) > 0 && app.QueryFlags.UseSearchList { + app.ResolverOpts.SearchList = search } - hub.Nameservers = append(hub.Nameservers, ns...) + app.Nameservers = append(app.Nameservers, ns...) } // if the user hasn't given any override of `ndots` AND has // given a custom nameserver. Set `ndots` to 1 as the fallback value - if hub.ResolverOpts.Ndots == -1 { - hub.ResolverOpts.Ndots = 0 + if app.ResolverOpts.Ndots == -1 { + app.ResolverOpts.Ndots = 0 } return nil } -func getDefaultServers() ([]models.Nameserver, int, []string, error) { - dnsServers, ndots, search, err := config.GetDefaultServers() - if err != nil { - return nil, 0, nil, err - } - servers := make([]models.Nameserver, 0, len(dnsServers)) - for _, s := range dnsServers { - 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) (models.Nameserver, error) { // Instantiate a UDP resolver with default port as a fallback. ns := models.Nameserver{ @@ -106,3 +90,19 @@ func initNameserver(n string) (models.Nameserver, error) { } return ns, nil } + +func getDefaultServers() ([]models.Nameserver, int, []string, error) { + dnsServers, ndots, search, err := config.GetDefaultServers() + if err != nil { + return nil, 0, nil, err + } + servers := make([]models.Nameserver, 0, len(dnsServers)) + for _, s := range dnsServers { + ns := models.Nameserver{ + Type: models.UDPResolver, + Address: net.JoinHostPort(s, models.DefaultUDPPort), + } + servers = append(servers, ns) + } + return servers, ndots, search, nil +} diff --git a/cmd/doggo/cli/output.go b/internal/app/output.go similarity index 86% rename from cmd/doggo/cli/output.go rename to internal/app/output.go index 050cc39..a4332b3 100644 --- a/cmd/doggo/cli/output.go +++ b/internal/app/output.go @@ -1,4 +1,4 @@ -package main +package app import ( "encoding/json" @@ -11,17 +11,17 @@ import ( "github.com/olekukonko/tablewriter" ) -func (hub *Hub) outputJSON(rsp []resolvers.Response) { +func (app *App) outputJSON(rsp []resolvers.Response) { // Pretty print with 4 spaces. res, err := json.MarshalIndent(rsp, "", " ") if err != nil { - hub.Logger.WithError(err).Error("unable to output data in JSON") - hub.Logger.Exit(-1) + app.Logger.WithError(err).Error("unable to output data in JSON") + app.Logger.Exit(-1) } fmt.Printf("%s", res) } -func (hub *Hub) outputTerminal(rsp []resolvers.Response) { +func (app *App) outputTerminal(rsp []resolvers.Response) { var ( green = color.New(color.FgGreen, color.Bold).SprintFunc() blue = color.New(color.FgBlue, color.Bold).SprintFunc() @@ -32,14 +32,14 @@ func (hub *Hub) outputTerminal(rsp []resolvers.Response) { ) // Disables colorized output if user specified. - if !hub.QueryFlags.Color { + if !app.QueryFlags.Color { color.NoColor = true } // Conditional Time column. table := tablewriter.NewWriter(os.Stdout) header := []string{"Name", "Type", "Class", "TTL", "Address", "Nameserver"} - if hub.QueryFlags.DisplayTimeTaken { + if app.QueryFlags.DisplayTimeTaken { header = append(header, "Time Taken") } @@ -99,7 +99,7 @@ func (hub *Hub) outputTerminal(rsp []resolvers.Response) { } output := []string{green(ans.Name), typOut, ans.Class, ans.TTL, ans.Address, ans.Nameserver} // Print how long it took - if hub.QueryFlags.DisplayTimeTaken { + if app.QueryFlags.DisplayTimeTaken { output = append(output, ans.RTT) } if outputStatus { @@ -117,7 +117,7 @@ func (hub *Hub) outputTerminal(rsp []resolvers.Response) { } output := []string{green(auth.Name), typOut, auth.Class, auth.TTL, auth.MName, auth.Nameserver} // Print how long it took - if hub.QueryFlags.DisplayTimeTaken { + if app.QueryFlags.DisplayTimeTaken { output = append(output, auth.RTT) } if outputStatus { @@ -131,10 +131,10 @@ func (hub *Hub) outputTerminal(rsp []resolvers.Response) { // Output takes a list of `dns.Answers` and based // on the output format specified displays the information. -func (hub *Hub) Output(responses []resolvers.Response) { - if hub.QueryFlags.ShowJSON { - hub.outputJSON(responses) +func (app *App) Output(responses []resolvers.Response) { + if app.QueryFlags.ShowJSON { + app.outputJSON(responses) } else { - hub.outputTerminal(responses) + app.outputTerminal(responses) } } diff --git a/internal/app/questions.go b/internal/app/questions.go new file mode 100644 index 0000000..d95de6d --- /dev/null +++ b/internal/app/questions.go @@ -0,0 +1,35 @@ +package app + +import ( + "strings" + + "github.com/miekg/dns" +) + +// LoadFallbacks sets fallbacks for options +// that are not specified by the user but necessary +// for the resolver. +func (app *App) LoadFallbacks() { + if len(app.QueryFlags.QTypes) == 0 { + app.QueryFlags.QTypes = append(app.QueryFlags.QTypes, "A") + } + if len(app.QueryFlags.QClasses) == 0 { + app.QueryFlags.QClasses = append(app.QueryFlags.QClasses, "IN") + } +} + +// PrepareQuestions takes a list of query names, query types and query classes +// and prepare a question for each combination of the above. +func (app *App) PrepareQuestions() { + for _, n := range app.QueryFlags.QNames { + for _, t := range app.QueryFlags.QTypes { + for _, c := range app.QueryFlags.QClasses { + app.Questions = append(app.Questions, dns.Question{ + Name: n, + Qtype: dns.StringToType[strings.ToUpper(t)], + Qclass: dns.StringToClass[strings.ToUpper(c)], + }) + } + } + } +} diff --git a/pkg/models/models.go b/pkg/models/models.go index edbd0d4..c09f6db 100644 --- a/pkg/models/models.go +++ b/pkg/models/models.go @@ -18,18 +18,18 @@ const ( // 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"` + 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"` + DisplayTimeTaken bool `koanf:"time" json:"-"` + ShowJSON bool `koanf:"json" json:"-"` + UseSearchList bool `koanf:"search" json:"-"` + Ndots int `koanf:"ndots" json:"ndots"` + Color bool `koanf:"color" json:"color"` + Timeout time.Duration `koanf:"timeout" json:"timeout"` } // Nameserver represents the type of Nameserver From f389c9c87665a7390d0fbdfa0a33b0a513cdd0d6 Mon Sep 17 00:00:00 2001 From: Karan Sharma Date: Sat, 27 Feb 2021 14:01:59 +0530 Subject: [PATCH 03/75] feat: API Handlers --- Makefile | 2 +- cmd/doggo/api/api.go | 93 +++++++++------------------ cmd/doggo/api/config.go | 60 ++++++++++++++++++ cmd/doggo/api/handlers.go | 130 ++++++++++++++++++++++++++++++++++++++ cmd/doggo/cli/cli.go | 7 +- config-api-sample.toml | 9 +++ pkg/models/models.go | 6 +- www/api/api.md | 13 ++++ 8 files changed, 249 insertions(+), 71 deletions(-) create mode 100644 cmd/doggo/api/config.go create mode 100644 cmd/doggo/api/handlers.go create mode 100644 config-api-sample.toml create mode 100644 www/api/api.md diff --git a/Makefile b/Makefile index 49184a9..0b039c9 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ run-cli: build-cli ## Build and Execute the CLI binary after the build step. .PHONY: run-api run-api: build-api ## Build and Execute the API binary after the build step. - ${API_BIN} + ${API_BIN} --config config-api-sample.toml .PHONY: clean clean: diff --git a/cmd/doggo/api/api.go b/cmd/doggo/api/api.go index 9f43c8e..e8f184f 100644 --- a/cmd/doggo/api/api.go +++ b/cmd/doggo/api/api.go @@ -1,82 +1,51 @@ package main import ( - "encoding/json" "net/http" + "time" + + "github.com/mr-karan/doggo/internal/app" + "github.com/mr-karan/doggo/pkg/utils" + "github.com/sirupsen/logrus" "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(".") + ko = koanf.New(".") + // Version and date of the build. This is injected at build-time. + buildVersion = "unknown" + buildDate = "unknown" ) -type resp struct { - Status string `json:"status"` - Message string `json:"message,omitempty"` - Data interface{} `json:"data,omitempty"` -} - func main() { + initConfig() + // Initialize app. + app := app.New(logger, buildVersion) + + // Register handles. r := chi.NewRouter() + r.Get("/", wrap(app, handleIndex)) + r.Get("/ping/", wrap(app, handleHealthCheck)) + r.Post("/lookup/", wrap(app, handleLookup)) - // 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 + // HTTP Server. + srv := &http.Server{ + Addr: ko.String("server.address"), + Handler: r, + ReadTimeout: ko.Duration("server.read_timeout") * time.Millisecond, + WriteTimeout: ko.Duration("server.write_timeout") * time.Millisecond, + IdleTimeout: ko.Duration("server.keepalive_timeout") * time.Millisecond, } - _, _ = 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) + logger.WithFields(logrus.Fields{ + "address": srv.Addr, + }).Info("starting server") + + if err := srv.ListenAndServe(); err != nil { + logger.Fatalf("couldn't start server: %v", err) + } } diff --git a/cmd/doggo/api/config.go b/cmd/doggo/api/config.go new file mode 100644 index 0000000..c495293 --- /dev/null +++ b/cmd/doggo/api/config.go @@ -0,0 +1,60 @@ +package main + +import ( + "fmt" + "os" + "strings" + + "github.com/knadh/koanf/parsers/toml" + "github.com/knadh/koanf/providers/env" + "github.com/knadh/koanf/providers/file" + "github.com/knadh/koanf/providers/posflag" + "github.com/sirupsen/logrus" + flag "github.com/spf13/pflag" +) + +// Config is the config given by the user +type Config struct { + HTTPAddr string `koanf:"listen_addr"` +} + +func initConfig() { + f := flag.NewFlagSet("api", flag.ContinueOnError) + f.Usage = func() { + fmt.Println(f.FlagUsages()) + os.Exit(0) + } + + // Register --config flag. + f.StringSlice("config", []string{"config.toml"}, + "Path to one or more TOML config files to load in order") + + // Register --version flag. + f.Bool("version", false, "Show build version") + f.Parse(os.Args[1:]) + // Display version. + if ok, _ := f.GetBool("version"); ok { + fmt.Println(buildVersion, buildDate) + os.Exit(0) + } + + // Read the config files. + cFiles, _ := f.GetStringSlice("config") + for _, f := range cFiles { + logger.WithFields(logrus.Fields{ + "file": f, + }).Info("reading config") + if err := ko.Load(file.Provider(f), toml.Parser()); err != nil { + logger.Fatalf("error reading config: %v", err) + } + } + // Load environment variables and merge into the loaded config. + if err := ko.Load(env.Provider("DOGGO_API_", ".", func(s string) string { + return strings.Replace(strings.ToLower( + strings.TrimPrefix(s, "DOGGO_API_")), "__", ".", -1) + }), nil); err != nil { + logger.Fatalf("error loading env config: %v", err) + } + + ko.Load(posflag.Provider(f, ".", ko), nil) +} diff --git a/cmd/doggo/api/handlers.go b/cmd/doggo/api/handlers.go new file mode 100644 index 0000000..d7ba361 --- /dev/null +++ b/cmd/doggo/api/handlers.go @@ -0,0 +1,130 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "time" + + "github.com/mr-karan/doggo/internal/app" + "github.com/mr-karan/doggo/pkg/models" + "github.com/mr-karan/doggo/pkg/resolvers" +) + +type httpResp struct { + Status string `json:"status"` + Message string `json:"message,omitempty"` + Data interface{} `json:"data,omitempty"` +} + +func handleIndex(w http.ResponseWriter, r *http.Request) { + sendResponse(w, http.StatusOK, "Welcome to Doggo API.") + return +} + +func handleHealthCheck(w http.ResponseWriter, r *http.Request) { + sendResponse(w, http.StatusOK, "PONG") + return +} + +func handleLookup(w http.ResponseWriter, r *http.Request) { + var ( + app = r.Context().Value("app").(app.App) + ) + + // Read body. + b, err := ioutil.ReadAll(r.Body) + defer r.Body.Close() + if err != nil { + app.Logger.WithError(err).Error("error reading request body") + sendErrorResponse(w, fmt.Sprintf("Invalid JSON payload"), http.StatusBadRequest, nil) + return + } + // Prepare query flags. + var qFlags models.QueryFlags + if err := json.Unmarshal(b, &qFlags); err != nil { + app.Logger.WithError(err).Error("error unmarshalling payload") + sendErrorResponse(w, fmt.Sprintf("Invalid JSON payload"), http.StatusBadRequest, nil) + return + } + app.QueryFlags = qFlags + // Load fallbacks. + app.LoadFallbacks() + + // Load Questions. + app.PrepareQuestions() + + // Load Nameservers. + err = app.LoadNameservers() + if err != nil { + app.Logger.WithError(err).Error("error loading nameservers") + sendErrorResponse(w, fmt.Sprintf("Error lookup up for records"), http.StatusInternalServerError, nil) + } + + // 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, + }) + if err != nil { + app.Logger.WithError(err).Error("error loading resolver") + sendErrorResponse(w, fmt.Sprintf("Error lookup up for records"), http.StatusInternalServerError, nil) + + } + app.Resolvers = rslvrs + + var responses []resolvers.Response + for _, q := range app.Questions { + for _, rslv := range app.Resolvers { + resp, err := rslv.Lookup(q) + if err != nil { + app.Logger.WithError(err).Error("error looking up DNS records") + app.Logger.Exit(2) + } + responses = append(responses, resp) + } + } + sendResponse(w, http.StatusOK, responses) + return +} + +// wrap is a middleware that wraps HTTP handlers and injects the "app" context. +func wrap(app app.App, next http.HandlerFunc) http.HandlerFunc { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := context.WithValue(r.Context(), "app", app) + next.ServeHTTP(w, r.WithContext(ctx)) + }) +} + +// sendResponse sends a JSON envelope to the HTTP response. +func sendResponse(w http.ResponseWriter, code int, data interface{}) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(code) + + out, err := json.Marshal(httpResp{Status: "success", Data: data}) + if err != nil { + sendErrorResponse(w, "Internal Server Error", http.StatusInternalServerError, nil) + return + } + + w.Write(out) +} + +// sendErrorResponse sends a JSON error envelope to the HTTP response. +func sendErrorResponse(w http.ResponseWriter, message string, code int, data interface{}) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(code) + + resp := httpResp{Status: "error", + Message: message, + Data: data} + out, _ := json.Marshal(resp) + w.Write(out) +} diff --git a/cmd/doggo/cli/cli.go b/cmd/doggo/cli/cli.go index 7b76f8e..958fbec 100644 --- a/cmd/doggo/cli/cli.go +++ b/cmd/doggo/cli/cli.go @@ -17,14 +17,11 @@ var ( // Version and date of the build. This is injected at build-time. buildVersion = "unknown" buildDate = "unknown" + logger = utils.InitLogger() + k = koanf.New(".") ) func main() { - var ( - logger = utils.InitLogger() - k = koanf.New(".") - ) - // Initialize app. app := app.New(logger, buildVersion) diff --git a/config-api-sample.toml b/config-api-sample.toml new file mode 100644 index 0000000..f69d335 --- /dev/null +++ b/config-api-sample.toml @@ -0,0 +1,9 @@ +[server] +address = ":8080" +name = "doggo-api" +# WARNING If these timeouts are less than 1s, +# the server connection breaks. +read_timeout=7000 +write_timeout=7000 +keepalive_timeout=5000 +max_body_size=10000 \ No newline at end of file diff --git a/pkg/models/models.go b/pkg/models/models.go index c09f6db..aa4dc1e 100644 --- a/pkg/models/models.go +++ b/pkg/models/models.go @@ -24,12 +24,12 @@ type QueryFlags struct { Nameservers []string `koanf:"nameservers" json:"nameservers"` UseIPv4 bool `koanf:"ipv4" json:"ipv4"` UseIPv6 bool `koanf:"ipv6" json:"ipv6"` - DisplayTimeTaken bool `koanf:"time" json:"-"` - ShowJSON bool `koanf:"json" json:"-"` - UseSearchList bool `koanf:"search" json:"-"` Ndots int `koanf:"ndots" json:"ndots"` Color bool `koanf:"color" json:"color"` Timeout time.Duration `koanf:"timeout" json:"timeout"` + DisplayTimeTaken bool `koanf:"time" json:"-"` + ShowJSON bool `koanf:"json" json:"-"` + UseSearchList bool `koanf:"search" json:"-"` } // Nameserver represents the type of Nameserver diff --git a/www/api/api.md b/www/api/api.md new file mode 100644 index 0000000..e7609cf --- /dev/null +++ b/www/api/api.md @@ -0,0 +1,13 @@ +# Usage + +``` +curl --request POST \ + --url http://localhost:8080/lookup/ \ + --header 'Content-Type: application/json' \ + --data '{ + "query": ["mrkaran.dev"], + "type": ["A"], + "class": ["IN"], + "nameservers": ["9.9.9.9"] +}' +``` From 6e0ce47f91e872ad60fe398fb99276a20b160c72 Mon Sep 17 00:00:00 2001 From: Karan Sharma Date: Tue, 2 Mar 2021 18:21:59 +0530 Subject: [PATCH 04/75] feat: UI design --- cmd/doggo/api/api.go | 34 +++++- cmd/doggo/api/assets/main.js | 92 ++++++++++++++++ cmd/doggo/api/assets/style.css | 0 cmd/doggo/api/handlers.go | 18 ++- cmd/doggo/api/index.html | 195 +++++++++++++++++++++++++++++++++ pkg/models/models.go | 2 +- 6 files changed, 331 insertions(+), 10 deletions(-) create mode 100644 cmd/doggo/api/assets/main.js create mode 100644 cmd/doggo/api/assets/style.css create mode 100644 cmd/doggo/api/index.html diff --git a/cmd/doggo/api/api.go b/cmd/doggo/api/api.go index e8f184f..43b42d9 100644 --- a/cmd/doggo/api/api.go +++ b/cmd/doggo/api/api.go @@ -1,6 +1,8 @@ package main import ( + "embed" + "io/fs" "net/http" "time" @@ -9,6 +11,7 @@ import ( "github.com/sirupsen/logrus" "github.com/go-chi/chi" + "github.com/go-chi/chi/middleware" "github.com/knadh/koanf" ) @@ -18,6 +21,10 @@ var ( // Version and date of the build. This is injected at build-time. buildVersion = "unknown" buildDate = "unknown" + //go:embed assets/* + assetsDir embed.FS + //go:embed index.html + html []byte ) func main() { @@ -26,11 +33,30 @@ func main() { // Initialize app. app := app.New(logger, buildVersion) - // Register handles. + // Register router instance. r := chi.NewRouter() - r.Get("/", wrap(app, handleIndex)) - r.Get("/ping/", wrap(app, handleHealthCheck)) - r.Post("/lookup/", wrap(app, handleLookup)) + + // Register middlewares + r.Use(middleware.RequestID) + r.Use(middleware.RealIP) + r.Use(middleware.Logger) + r.Use(middleware.Recoverer) + + // Frontend Handlers. + assets, _ := fs.Sub(assetsDir, "assets") + r.Get("/assets/*", func(w http.ResponseWriter, r *http.Request) { + fs := http.StripPrefix("/assets/", http.FileServer(http.FS(assets))) + fs.ServeHTTP(w, r) + }) + r.Get("/", func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "text/html") + w.Write(html) + }) + + // API Handlers. + r.Get("/api/", wrap(app, handleIndexAPI)) + r.Get("/api/ping/", wrap(app, handleHealthCheck)) + r.Post("/api/lookup/", wrap(app, handleLookup)) // HTTP Server. srv := &http.Server{ diff --git a/cmd/doggo/api/assets/main.js b/cmd/doggo/api/assets/main.js new file mode 100644 index 0000000..802c542 --- /dev/null +++ b/cmd/doggo/api/assets/main.js @@ -0,0 +1,92 @@ +var app = new Vue({ + el: '#app', + data: { + apiURL: "/api/lookup/", + results: [], + noRecordsFound: false, + emptyNameError: false, + apiErrorMessage: "", + queryName: "", + queryType: "A", + nameserverName: "google", + customNSAddr: "", + nsAddrMap: { + "google": "8.8.8.8", + "cloudflare": "1.1.1.1", + "quad9": "9.9.9.9", + } + }, + created: function () { + }, + computed: { + getNSAddrValue() { + return this.nsAddrMap[this.nameserverName] + }, + isCustomNS() { + if (this.nameserverName == "custom") { + return true + } + return false + } + }, + methods: { + prepareNS() { + switch (this.nameserverName) { + case "google": + return "tcp://8.8.8.8:53" + case "cloudflare": + return "tcp://1.1.1.1:53" + case "quad9": + return "tcp://9.9.9.9:53" + case "custom": + return this.customNSAddr + default: + return "" + } + }, + lookupRecords() { + // reset variables. + this.results = [] + this.noRecordsFound = false + this.emptyNameError = false + this.apiErrorMessage = "" + + if (this.queryName == "") { + this.emptyNameError = true + return + } + + // GET request using fetch with error handling + fetch(this.apiURL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + query: [this.queryName,], + type: [this.queryType,], + nameservers: [this.prepareNS(),], + }), + }).then(async response => { + const res = await response.json(); + + // check for error response + if (!response.ok) { + // get error message from body or default to response statusText + const error = (res && res.message) || response.statusText; + return Promise.reject(error); + } + + if (res.data[0].answers == null) { + this.noRecordsFound = true + } else { + // Set the answers in the results list. + this.results = res.data[0].answers + } + + }).catch(error => { + this.apiErrorMessage = error + }); + } + } +}) diff --git a/cmd/doggo/api/assets/style.css b/cmd/doggo/api/assets/style.css new file mode 100644 index 0000000..e69de29 diff --git a/cmd/doggo/api/handlers.go b/cmd/doggo/api/handlers.go index d7ba361..c9488c9 100644 --- a/cmd/doggo/api/handlers.go +++ b/cmd/doggo/api/handlers.go @@ -19,7 +19,7 @@ type httpResp struct { Data interface{} `json:"data,omitempty"` } -func handleIndex(w http.ResponseWriter, r *http.Request) { +func handleIndexAPI(w http.ResponseWriter, r *http.Request) { sendResponse(w, http.StatusOK, "Welcome to Doggo API.") return } @@ -49,6 +49,7 @@ func handleLookup(w http.ResponseWriter, r *http.Request) { sendErrorResponse(w, fmt.Sprintf("Invalid JSON payload"), http.StatusBadRequest, nil) return } + app.QueryFlags = qFlags // Load fallbacks. app.LoadFallbacks() @@ -56,11 +57,17 @@ func handleLookup(w http.ResponseWriter, r *http.Request) { // Load Questions. app.PrepareQuestions() + if len(app.Questions) == 0 { + sendErrorResponse(w, fmt.Sprintf("Missing field `query`."), http.StatusBadRequest, nil) + return + } + // Load Nameservers. err = app.LoadNameservers() if err != nil { app.Logger.WithError(err).Error("error loading nameservers") - sendErrorResponse(w, fmt.Sprintf("Error lookup up for records"), http.StatusInternalServerError, nil) + sendErrorResponse(w, fmt.Sprintf("Error lookuping up for records."), http.StatusInternalServerError, nil) + return } // Load Resolvers. @@ -75,8 +82,8 @@ func handleLookup(w http.ResponseWriter, r *http.Request) { }) if err != nil { app.Logger.WithError(err).Error("error loading resolver") - sendErrorResponse(w, fmt.Sprintf("Error lookup up for records"), http.StatusInternalServerError, nil) - + sendErrorResponse(w, fmt.Sprintf("Error lookuping up for records."), http.StatusInternalServerError, nil) + return } app.Resolvers = rslvrs @@ -86,7 +93,8 @@ func handleLookup(w http.ResponseWriter, r *http.Request) { resp, err := rslv.Lookup(q) if err != nil { app.Logger.WithError(err).Error("error looking up DNS records") - app.Logger.Exit(2) + sendErrorResponse(w, fmt.Sprintf("Error lookuping up for records."), http.StatusInternalServerError, nil) + return } responses = append(responses, resp) } diff --git a/cmd/doggo/api/index.html b/cmd/doggo/api/index.html new file mode 100644 index 0000000..2929586 --- /dev/null +++ b/cmd/doggo/api/index.html @@ -0,0 +1,195 @@ + + + + + Doggo DNS + + + + + + + + + + + +
+

Doggo DNS

+
+
+
+
+
+
+ + +

Please enter a domain name to + query. +

+
+
+ + +
+
+
+
+ + +
+
+ +
+ +

To use different protocols like DOH, DOT etc. refer + to the + instructions here.

+
+
+ +
+
+
+
+ +
+
+
+ + +
+ + +
    +
  • Name: {{answer.name}}
  • +
  • Address: {{answer.address}}
  • +
  • Type: {{answer.type}}
  • +
  • Nameserver: {{answer.nameserver}}
  • +
  • TTL: {{answer.ttl}}
  • +
  • RTT: {{answer.rtt}}
  • +
+
+ + + + +
+

Oops! Found no records for this query.

+
+ + +
+

{{apiErrorMessage}}

+
+ +
+ + + \ No newline at end of file diff --git a/pkg/models/models.go b/pkg/models/models.go index aa4dc1e..a361b79 100644 --- a/pkg/models/models.go +++ b/pkg/models/models.go @@ -25,8 +25,8 @@ type QueryFlags struct { UseIPv4 bool `koanf:"ipv4" json:"ipv4"` UseIPv6 bool `koanf:"ipv6" json:"ipv6"` Ndots int `koanf:"ndots" json:"ndots"` - Color bool `koanf:"color" json:"color"` Timeout time.Duration `koanf:"timeout" json:"timeout"` + Color bool `koanf:"color" json:"-"` DisplayTimeTaken bool `koanf:"time" json:"-"` ShowJSON bool `koanf:"json" json:"-"` UseSearchList bool `koanf:"search" json:"-"` From 083ba1936351509fd7e399fa7c410b53dfc3657a Mon Sep 17 00:00:00 2001 From: Karan Sharma Date: Tue, 2 Mar 2021 18:39:36 +0530 Subject: [PATCH 05/75] chore: fix margin issue --- cmd/doggo/api/index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/doggo/api/index.html b/cmd/doggo/api/index.html index 2929586..f345099 100644 --- a/cmd/doggo/api/index.html +++ b/cmd/doggo/api/index.html @@ -63,8 +63,8 @@ -
-
+
+
@@ -77,7 +77,7 @@
-
+
From 7d0499c43256fab28967c7b0f0f416e898eef932 Mon Sep 17 00:00:00 2001 From: Karan Sharma Date: Tue, 2 Mar 2021 18:58:49 +0530 Subject: [PATCH 06/75] feat: Build process for 2 binaries (api,cli) --- .goreleaser.yml | 81 +++++++++++++++++++++++++++--------- Dockerfile-api | 7 ++++ Dockerfile => Dockerfile-cli | 0 3 files changed, 68 insertions(+), 20 deletions(-) create mode 100644 Dockerfile-api rename Dockerfile => Dockerfile-cli (100%) diff --git a/.goreleaser.yml b/.goreleaser.yml index 13296c7..2b3b359 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -4,6 +4,7 @@ env: builds: - binary: doggo + id: cli goos: - windows - darwin @@ -16,7 +17,23 @@ builds: - 7 ldflags: - -s -w -X "main.buildVersion={{ .Tag }} ({{ .ShortCommit }} {{ .Date }})" - dir: ./cmd/doggo/ + dir: ./cmd/doggo/cli/ + + - binary: doggo-api.bin + id: api + goos: + - windows + - darwin + - linux + goarch: + - amd64 + - arm64 + goarm: + - 6 + - 7 + ldflags: + - -s -w -X "main.buildVersion={{ .Tag }} ({{ .ShortCommit }} {{ .Date }})" + dir: ./cmd/doggo/api/ archives: - format: tar.gz @@ -34,22 +51,46 @@ snapcrafts: publish: true dockers: -- image_templates: - - "ghcr.io/mr-karan/doggo:{{ .Tag }}" - - "ghcr.io/mr-karan/doggo:latest" - binaries: - - doggo - dockerfile: Dockerfile - build_flag_templates: - - "--build-arg" - - "ARCH=amd64" -- image_templates: - - "ghcr.io/mr-karan/doggo:{{ .Tag }}-arm64v8" - - "ghcr.io/mr-karan/doggo:latest-arm64v8" - binaries: - - doggo - goarch: arm64 - dockerfile: Dockerfile - build_flag_templates: - - "--build-arg" - - "ARCH=arm64v8" + - image_templates: + - "ghcr.io/mr-karan/doggo-cli:{{ .Tag }}" + - "ghcr.io/mr-karan/doggo-cli:latest" + binaries: + - doggo + dockerfile: Dockerfile-cli + build_flag_templates: + - "--build-arg" + - "ARCH=amd64" + - image_templates: + - "ghcr.io/mr-karan/doggo-cli:{{ .Tag }}-arm64v8" + - "ghcr.io/mr-karan/doggo-cli:latest-arm64v8" + binaries: + - doggo + goarch: arm64 + dockerfile: Dockerfile-cli + build_flag_templates: + - "--build-arg" + - "ARCH=arm64v8" + + - image_templates: + - "ghcr.io/mr-karan/doggo-api:{{ .Tag }}" + - "ghcr.io/mr-karan/doggo-api:latest" + binaries: + - doggo-api.bin + dockerfile: Dockerfile-api + build_flag_templates: + - "--build-arg" + - "ARCH=amd64" + extra_files: + - config-api-sample.toml + - image_templates: + - "ghcr.io/mr-karan/doggo-api:{{ .Tag }}-arm64v8" + - "ghcr.io/mr-karan/doggo-api:latest-arm64v8" + binaries: + - doggo-api.bin + goarch: arm64 + dockerfile: Dockerfile-api + build_flag_templates: + - "--build-arg" + - "ARCH=arm64v8" + extra_files: + - config-api-sample.toml diff --git a/Dockerfile-api b/Dockerfile-api new file mode 100644 index 0000000..ce88e92 --- /dev/null +++ b/Dockerfile-api @@ -0,0 +1,7 @@ +# Dockerfile +ARG ARCH +FROM ${ARCH}/alpine +WORKDIR /app +COPY doggo-api.bin . +COPY config-api-sample.toml config.toml +CMD ["./doggo-api.bin"] diff --git a/Dockerfile b/Dockerfile-cli similarity index 100% rename from Dockerfile rename to Dockerfile-cli From d9e80b1d17e5919d5039faa837b3fbb27d867f50 Mon Sep 17 00:00:00 2001 From: Karan Sharma Date: Tue, 2 Mar 2021 19:14:00 +0530 Subject: [PATCH 07/75] fix: minor styling changes --- cmd/doggo/api/index.html | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/cmd/doggo/api/index.html b/cmd/doggo/api/index.html index f345099..d155677 100644 --- a/cmd/doggo/api/index.html +++ b/cmd/doggo/api/index.html @@ -23,11 +23,11 @@
- +

Doggo DNS

-
+
@@ -51,15 +51,15 @@ v-model="queryType"> + - - +
@@ -190,6 +190,15 @@
+ + + \ No newline at end of file From e8a7fc720e577c8c05591e84acf28e0824e941d0 Mon Sep 17 00:00:00 2001 From: Karan Sharma Date: Tue, 2 Mar 2021 19:31:49 +0530 Subject: [PATCH 08/75] chore: remove unused font --- cmd/doggo/api/assets/style.css | 0 cmd/doggo/api/index.html | 3 +-- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 cmd/doggo/api/assets/style.css diff --git a/cmd/doggo/api/assets/style.css b/cmd/doggo/api/assets/style.css deleted file mode 100644 index e69de29..0000000 diff --git a/cmd/doggo/api/index.html b/cmd/doggo/api/index.html index d155677..c5da38f 100644 --- a/cmd/doggo/api/index.html +++ b/cmd/doggo/api/index.html @@ -7,8 +7,7 @@ - + + -