feat: refactor resolver in separate package
parent
0e195fd9a7
commit
508a8dd7c4
32
Makefile
32
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)
|
HASH := $(shell git rev-parse --short HEAD)
|
||||||
BUILD_DATE := $(shell date '+%Y-%m-%d %H:%M:%S')
|
BUILD_DATE := $(shell date '+%Y-%m-%d %H:%M:%S')
|
||||||
VERSION := ${HASH}
|
VERSION := ${HASH}
|
||||||
|
|
||||||
.PHONY: build
|
.PHONY: build-cli
|
||||||
build:
|
build-cli:
|
||||||
go build -o ${BIN} -ldflags="-X 'main.buildVersion=${VERSION}' -X 'main.buildDate=${BUILD_DATE}'" ./cmd/doggo/
|
go build -o ${CLI_BIN} -ldflags="-X 'main.buildVersion=${VERSION}' -X 'main.buildDate=${BUILD_DATE}'" ./cmd/doggo/cli/
|
||||||
|
|
||||||
.PHONY: run
|
.PHONY: build-api
|
||||||
run: build ## Build and Execute the binary after the build step
|
build-api:
|
||||||
${BIN}
|
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
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
go clean
|
go clean
|
||||||
- rm -f ${BIN}
|
- rm -rf ./bin/
|
||||||
|
|
||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
lint:
|
lint:
|
||||||
golangci-lint run
|
golangci-lint run
|
||||||
|
|
||||||
.PHONY: fresh
|
|
||||||
fresh: clean build
|
|
||||||
|
|
2
TODO.md
2
TODO.md
|
@ -61,7 +61,7 @@
|
||||||
- [ ] zsh
|
- [ ] zsh
|
||||||
- [ ] fish
|
- [ ] fish
|
||||||
- [ ] Add tests for Resolvers.
|
- [ ] Add tests for Resolvers.
|
||||||
- [ ] Add tests for CLI Output.
|
- [ ] Add tests for CLI Output.
|
||||||
- [ ] Homebrew - Goreleaser
|
- [ ] Homebrew - Goreleaser
|
||||||
- [x] Separate Authority/Answer in JSON output.
|
- [x] Separate Authority/Answer in JSON output.
|
||||||
- [x] Error on NXDomain (Related upstream [bug](https://github.com/miekg/dns/issues/1198))
|
- [x] Error on NXDomain (Related upstream [bug](https://github.com/miekg/dns/issues/1198))
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -3,11 +3,13 @@ package main
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/knadh/koanf"
|
"github.com/knadh/koanf"
|
||||||
"github.com/knadh/koanf/providers/posflag"
|
"github.com/knadh/koanf/providers/posflag"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"github.com/mr-karan/doggo/pkg/resolvers"
|
"github.com/mr-karan/doggo/pkg/resolvers"
|
||||||
|
"github.com/mr-karan/doggo/pkg/utils"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
flag "github.com/spf13/pflag"
|
flag "github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
@ -20,7 +22,7 @@ var (
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var (
|
var (
|
||||||
logger = initLogger()
|
logger = utils.InitLogger()
|
||||||
k = koanf.New(".")
|
k = koanf.New(".")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -110,15 +112,21 @@ func main() {
|
||||||
hub.Logger.Exit(2)
|
hub.Logger.Exit(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load Resolver Options.
|
|
||||||
hub.loadResolverOptions()
|
|
||||||
|
|
||||||
// Load Resolvers.
|
// 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 {
|
if err != nil {
|
||||||
hub.Logger.WithError(err).Error("error loading resolver")
|
hub.Logger.WithError(err).Error("error loading resolver")
|
||||||
hub.Logger.Exit(2)
|
hub.Logger.Exit(2)
|
||||||
}
|
}
|
||||||
|
hub.Resolvers = rslvrs
|
||||||
|
|
||||||
// Run the app.
|
// Run the app.
|
||||||
hub.Logger.Debug("Starting doggo 🐶")
|
hub.Logger.Debug("Starting doggo 🐶")
|
||||||
|
@ -130,7 +138,7 @@ func main() {
|
||||||
// Resolve Queries.
|
// Resolve Queries.
|
||||||
var responses []resolvers.Response
|
var responses []resolvers.Response
|
||||||
for _, q := range hub.Questions {
|
for _, q := range hub.Questions {
|
||||||
for _, rslv := range hub.Resolver {
|
for _, rslv := range hub.Resolvers {
|
||||||
resp, err := rslv.Lookup(q)
|
resp, err := rslv.Lookup(q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
hub.Logger.WithError(err).Error("error looking up DNS records")
|
hub.Logger.WithError(err).Error("error looking up DNS records")
|
|
@ -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
|
||||||
|
}
|
|
@ -6,19 +6,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/mr-karan/doggo/pkg/config"
|
"github.com/mr-karan/doggo/pkg/config"
|
||||||
)
|
"github.com/mr-karan/doggo/pkg/models"
|
||||||
|
|
||||||
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"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// loadNameservers reads all the user given
|
// loadNameservers reads all the user given
|
||||||
|
@ -62,56 +50,56 @@ func (hub *Hub) loadNameservers() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultServers() ([]Nameserver, int, []string, error) {
|
func getDefaultServers() ([]models.Nameserver, int, []string, error) {
|
||||||
dnsServers, ndots, search, err := config.GetDefaultServers()
|
dnsServers, ndots, search, err := config.GetDefaultServers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, nil, err
|
return nil, 0, nil, err
|
||||||
}
|
}
|
||||||
servers := make([]Nameserver, 0, len(dnsServers))
|
servers := make([]models.Nameserver, 0, len(dnsServers))
|
||||||
for _, s := range dnsServers {
|
for _, s := range dnsServers {
|
||||||
ns := Nameserver{
|
ns := models.Nameserver{
|
||||||
Type: UDPResolver,
|
Type: models.UDPResolver,
|
||||||
Address: net.JoinHostPort(s, DefaultUDPPort),
|
Address: net.JoinHostPort(s, models.DefaultUDPPort),
|
||||||
}
|
}
|
||||||
servers = append(servers, ns)
|
servers = append(servers, ns)
|
||||||
}
|
}
|
||||||
return servers, ndots, search, nil
|
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.
|
// Instantiate a UDP resolver with default port as a fallback.
|
||||||
ns := Nameserver{
|
ns := models.Nameserver{
|
||||||
Type: UDPResolver,
|
Type: models.UDPResolver,
|
||||||
Address: net.JoinHostPort(n, DefaultUDPPort),
|
Address: net.JoinHostPort(n, models.DefaultUDPPort),
|
||||||
}
|
}
|
||||||
u, err := url.Parse(n)
|
u, err := url.Parse(n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ns, err
|
return ns, err
|
||||||
}
|
}
|
||||||
if u.Scheme == "https" {
|
if u.Scheme == "https" {
|
||||||
ns.Type = DOHResolver
|
ns.Type = models.DOHResolver
|
||||||
ns.Address = u.String()
|
ns.Address = u.String()
|
||||||
}
|
}
|
||||||
if u.Scheme == "tls" {
|
if u.Scheme == "tls" {
|
||||||
ns.Type = DOTResolver
|
ns.Type = models.DOTResolver
|
||||||
if u.Port() == "" {
|
if u.Port() == "" {
|
||||||
ns.Address = net.JoinHostPort(u.Hostname(), DefaultTLSPort)
|
ns.Address = net.JoinHostPort(u.Hostname(), models.DefaultTLSPort)
|
||||||
} else {
|
} else {
|
||||||
ns.Address = net.JoinHostPort(u.Hostname(), u.Port())
|
ns.Address = net.JoinHostPort(u.Hostname(), u.Port())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if u.Scheme == "tcp" {
|
if u.Scheme == "tcp" {
|
||||||
ns.Type = TCPResolver
|
ns.Type = models.TCPResolver
|
||||||
if u.Port() == "" {
|
if u.Port() == "" {
|
||||||
ns.Address = net.JoinHostPort(u.Hostname(), DefaultTCPPort)
|
ns.Address = net.JoinHostPort(u.Hostname(), models.DefaultTCPPort)
|
||||||
} else {
|
} else {
|
||||||
ns.Address = net.JoinHostPort(u.Hostname(), u.Port())
|
ns.Address = net.JoinHostPort(u.Hostname(), u.Port())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if u.Scheme == "udp" {
|
if u.Scheme == "udp" {
|
||||||
ns.Type = UDPResolver
|
ns.Type = models.UDPResolver
|
||||||
if u.Port() == "" {
|
if u.Port() == "" {
|
||||||
ns.Address = net.JoinHostPort(u.Hostname(), DefaultUDPPort)
|
ns.Address = net.JoinHostPort(u.Hostname(), models.DefaultUDPPort)
|
||||||
} else {
|
} else {
|
||||||
ns.Address = net.JoinHostPort(u.Hostname(), u.Port())
|
ns.Address = net.JoinHostPort(u.Hostname(), u.Port())
|
||||||
}
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
package main
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
2
go.mod
2
go.mod
|
@ -4,6 +4,8 @@ go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fatih/color v1.10.0
|
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/knadh/koanf v0.14.0
|
||||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||||
github.com/miekg/dns v1.1.35
|
github.com/miekg/dns v1.1.35
|
||||||
|
|
10
go.sum
10
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/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.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
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/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 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
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 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
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=
|
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 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
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/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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
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/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-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-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-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-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-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-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-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 h1:nc5K6ox/4lTFbMVSL9WRR81ixkcwXThoiF6yf+R9scA=
|
||||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
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/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/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=
|
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/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 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -4,16 +4,20 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
|
"github.com/mr-karan/doggo/pkg/models"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Options represent a set of common options
|
// Options represent a set of common options
|
||||||
// to configure a Resolver.
|
// to configure a Resolver.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
SearchList []string
|
Nameservers []models.Nameserver
|
||||||
Ndots int
|
UseIPv4 bool
|
||||||
Timeout time.Duration
|
UseIPv6 bool
|
||||||
Logger *logrus.Logger
|
SearchList []string
|
||||||
|
Ndots int
|
||||||
|
Timeout time.Duration
|
||||||
|
Logger *logrus.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolver implements the configuration for a DNS
|
// Resolver implements the configuration for a DNS
|
||||||
|
@ -59,3 +63,70 @@ type Authority struct {
|
||||||
RTT string `json:"rtt"`
|
RTT string `json:"rtt"`
|
||||||
Nameserver string `json:"nameserver"`
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
Loading…
Reference in New Issue