diff --git a/Makefile b/Makefile index 6aba0eb..1e75c74 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,8 @@ DOGGO-BIN := doggo.bin HASH := $(shell git rev-parse --short HEAD) -COMMIT_DATE := $(shell git show -s --format=%ci ${HASH}) BUILD_DATE := $(shell date '+%Y-%m-%d %H:%M:%S') -VERSION := ${HASH} (${COMMIT_DATE}) +VERSION := ${HASH} .PHONY: build build: ## Build the doggo binary diff --git a/TODO.md b/TODO.md index d009dd7..b8645e9 100644 --- a/TODO.md +++ b/TODO.md @@ -1,4 +1,4 @@ -# doggo - v1.0 Milestone +# doggo - Initial Release Milestone ## Resolver - [x] Create a DNS Resolver struct @@ -6,20 +6,22 @@ - [x] Add a resolve method - [x] Make it separate from Hub - [x] Parse output into separate fields -- [ ] Test IPv6 +- [ ] Test IPv6/IPv4 only options - [x] Add DOH support - [x] Add DOT support - [x] Add DNS protocol on TCP mode support. - [ ] Error on NXDomain (Realted upstream [bug](https://github.com/miekg/dns/issues/1198)) +- [ ] Support all DNS records? + - [x] Major records supported ## CLI Features - [ ] `digfile` -- [x] `ndots` support +- [ ] `ndots` support - [x] `search list` support - [x] JSON output - [x] Colorized output - [x] Table output -- [ ] Parsing options free-form +- [x] Parsing options free-form - [x] Remove urfave/cli in favour of `flag` ## CLI Grunt @@ -27,15 +29,27 @@ - [x] Neatly package them to load args in different functions - [x] Upper case is not mandatory for query type/classes - [x] Output +- [ ] Custom Help Text + - [x] Add examples + - [ ] Colorize + - [ ] Add different commands - [x] Add client transport options +- [ ] Fix an issue while loading free form args, where the same records are being added twice ## Tests ## Documentation +- [ ] Mkdocs init project + - [ ] Custom Index (Landing Page) + ## Release Checklist - [ ] Goreleaser - [ ] Snap - [ ] Homebrew - [ ] ARM + +## v1.0 + +- [ ] Support obscure protocal tweaks in `dig` diff --git a/cmd/cli.go b/cmd/cli.go index 7ba14fd..5c51bc8 100644 --- a/cmd/cli.go +++ b/cmd/cli.go @@ -1,11 +1,11 @@ package main import ( - "fmt" "os" "github.com/knadh/koanf" "github.com/knadh/koanf/providers/posflag" + "github.com/sirupsen/logrus" flag "github.com/spf13/pflag" ) @@ -27,10 +27,7 @@ func main() { // Configure Flags // Use the POSIX compliant pflag lib instead of Go's flag lib. f := flag.NewFlagSet("config", flag.ContinueOnError) - f.Usage = func() { - fmt.Println(f.FlagUsages()) - os.Exit(0) - } + f.Usage = renderCustomHelp // Path to one or more config files to load into koanf along with some config params. 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)") @@ -50,26 +47,32 @@ func main() { // Output Options f.BoolP("json", "J", false, "Set the output format as JSON") f.Bool("time", false, "Display how long it took for the response to arrive") + f.Bool("color", true, "Show colored output") f.Bool("debug", false, "Enable debug mode") // Parse and Load Flags f.Parse(os.Args[1:]) if err := k.Load(posflag.Provider(f, ".", k), nil); err != nil { - hub.Logger.Fatalf("error loading flags: %v", err) - fmt.Println(f.FlagUsages()) - os.Exit(0) + hub.Logger.Errorf("error loading flags: %v", err) + f.Usage() + hub.Logger.Exit(2) + } + // set log level + if k.Bool("debug") { + // Set logger level + hub.Logger.SetLevel(logrus.DebugLevel) + } else { + hub.Logger.SetLevel(logrus.InfoLevel) } - // Run the app. - hub.Logger.Debug("Starting doggo...") + hub.Logger.Debug("Starting doggo 🐶") // Parse Query Args hub.loadQueryArgs() // Start App if len(hub.QueryFlags.QNames) == 0 { - fmt.Println(f.FlagUsages()) - os.Exit(0) + f.Usage() } hub.Lookup() diff --git a/cmd/help.go b/cmd/help.go index 6d2b339..39adfbd 100644 --- a/cmd/help.go +++ b/cmd/help.go @@ -1,21 +1,42 @@ package main import ( - "fmt" - "io" + "html/template" + "os" ) -// Override Help Template -var helpTmpl = `NAME: - {{.Name}} - {{ range $key, $value := . }} -
  • {{ $key }}: {{ $value }}
  • -{{ end }} +// AppHelpTemplate is the text template to customise the Help output. +// Uses text/template to render templates. +var AppHelpTemplate = `NAME: + {{.Name}} {{.Description}} + +USAGE: + {{.Name}} [OPTIONS] [--] + +VERSION: + {{.Version}} Built at {{.Date}} + +EXAMPLES: + doggo mrkaran.dev Query a domain using defaults + doggo mrkaran.dev CNAME Looks up for a CNAME record. + doggo mrkaran.dev MX @9.9.9.9 Uses a custom DNS resolver. + doggo -q mrkaran.dev -t MX -n 1.1.1.1 Using named arguments ` -func renderCustomHelp(w io.Writer, templ string, data interface{}) { - var helpTmplVars = map[string]string{} - - helpTmplVars["Name"] = "doggo" - fmt.Fprintf(w, helpTmpl, helpTmplVars) +func renderCustomHelp() { + helpTmplVars := map[string]string{ + "Name": "doggo", + "Description": "DNS Client for Humans", + "Version": buildVersion, + "Date": buildDate, + } + tmpl, err := template.New("test").Parse(AppHelpTemplate) + if err != nil { + panic(err) + } + err = tmpl.Execute(os.Stdout, helpTmplVars) + if err != nil { + panic(err) + } + os.Exit(0) } diff --git a/cmd/hub.go b/cmd/hub.go index ee91d4c..8b2f7ab 100644 --- a/cmd/hub.go +++ b/cmd/hub.go @@ -31,6 +31,7 @@ type QueryFlags struct { ShowJSON bool `koanf:"json"` UseSearchList bool `koanf:"search"` Ndots int `koanf:"ndots"` + Color bool `koanf:"color"` } // NewHub initializes an instance of Hub which holds app wide configuration. diff --git a/cmd/output.go b/cmd/output.go index 8aa2065..3bc9d7c 100644 --- a/cmd/output.go +++ b/cmd/output.go @@ -64,9 +64,15 @@ func (hub *Hub) outputJSON(out []Output, msgs []resolvers.Response) { } func (hub *Hub) outputTerminal(out []Output) { - green := color.New(color.FgGreen).SprintFunc() - blue := color.New(color.FgBlue).SprintFunc() - magenta := color.New(color.FgMagenta).SprintFunc() + green := color.New(color.FgGreen, color.Bold).SprintFunc() + blue := color.New(color.FgBlue, color.Bold).SprintFunc() + yellow := color.New(color.FgYellow, color.Bold).SprintFunc() + cyan := color.New(color.FgCyan, color.Bold).SprintFunc() + red := color.New(color.FgRed, color.Bold).SprintFunc() + + if !hub.QueryFlags.Color { + color.NoColor = true // disables colorized output + } table := tablewriter.NewWriter(os.Stdout) header := []string{"Name", "Type", "Class", "TTL", "Address", "Nameserver"} @@ -87,7 +93,24 @@ func (hub *Hub) outputTerminal(out []Output) { table.SetNoWhiteSpace(true) for _, o := range out { - output := []string{green(o.Name), blue(o.Type), o.Class, o.TTL, magenta(o.Address), o.Nameserver} + var typOut string + switch typ := o.Type; typ { + case "A": + typOut = blue(o.Type) + case "AAAA": + typOut = blue(o.Type) + case "MX": + typOut = cyan(o.Type) + case "NS": + typOut = cyan(o.Type) + case "CNAME": + typOut = yellow(o.Type) + case "TXT": + typOut = yellow(o.Type) + default: + typOut = red(o.Type) + } + output := []string{green(o.Name), typOut, o.Class, o.TTL, o.Address, o.Nameserver} // Print how long it took if hub.QueryFlags.DisplayTimeTaken { output = append(output, o.TimeTaken) @@ -125,10 +148,28 @@ func collectOutput(responses []resolvers.Response) []Output { addr = t.AAAA.String() case *dns.CNAME: addr = t.Target + case *dns.CAA: + addr = t.Tag + " " + t.Value + case *dns.HINFO: + addr = t.Cpu + " " + t.Os + // case *dns.LOC: + // addr = t.String() + case *dns.PTR: + addr = t.Ptr + case *dns.SRV: + addr = strconv.Itoa(int(t.Priority)) + " " + + strconv.Itoa(int(t.Weight)) + " " + + t.Target + ":" + strconv.Itoa(int(t.Port)) + case *dns.TXT: + addr = t.String() + case *dns.NS: + addr = t.String() case *dns.MX: addr = strconv.Itoa(int(t.Preference)) + " " + t.Mx case *dns.SOA: addr = t.String() + case *dns.NAPTR: + addr = t.String() } h := a.Header() diff --git a/cmd/parse.go b/cmd/parse.go index 0961f93..8a35499 100644 --- a/cmd/parse.go +++ b/cmd/parse.go @@ -5,17 +5,9 @@ import ( "strings" "github.com/miekg/dns" - "github.com/sirupsen/logrus" ) func (hub *Hub) loadQueryArgs() error { - // set log level - if k.Bool("debug") { - // Set logger level - hub.Logger.SetLevel(logrus.DebugLevel) - } else { - hub.Logger.SetLevel(logrus.InfoLevel) - } err := hub.loadNamedArgs()