feat: refactor resolver in separate package

This commit is contained in:
Karan Sharma 2021-02-26 20:06:46 +05:30
parent 0e195fd9a7
commit 508a8dd7c4
17 changed files with 306 additions and 207 deletions

82
cmd/doggo/api/api.go Normal file
View file

@ -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)
}

View file

@ -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")

36
cmd/doggo/cli/hub.go Normal file
View file

@ -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
}

View file

@ -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())
}

View file

@ -0,0 +1 @@
package main

View file

@ -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
}

View file

@ -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
}