diff --git a/README.md b/README.md
index 5152e17..b2378a0 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,189 @@
-# doggo
+
+
+
+
+ 🐶 Command-line DNS client for humans +
+ + -_Command-line DNS client written in Golang_ +--- -![](www/static/main.png) +`doggo` is a modern command-line DNS client (like _dig_) written in Golang. It outputs information in a neat concise manner and supports protocols like DoH, DoT as well. +It's totally inspired from [dog](https://github.com/ogham/dog/) which is written in Rust. I wanted to add some features to it but since I don't know Rust, I found it as a nice oppurtunity +to experiment with writing a DNS Client from scratch in `Go` myself. Hence the name `dog` +`go` => `doggo`. + +## Features + +- Human readable output - Supports colors and tabular format. +- Supports JSON format - useful for writing scripts. +- Has support for multiple transport protocols: + - DNS over **HTTPS** (DoH) + - DNS over **TLS** (DoT) + - DNS over **TCP** + - DNS over **UDP** +- Supports **ndots** and **search** configurations from `resolv.conf` or command-line arguments. +- Supports multiple resolvers in one go. +- Supports IPv4 **and** IPv6 _both_. + +## Installation + +### Binary (Recommended) + +### Docker + +### Snap + +## Usage Examples + +**Do a simple DNS Lookup for `mrkaran.dev`** + +```bash +$ doggo mrkaran.dev +NAME TYPE CLASS TTL ADDRESS NAMESERVER +mrkaran.dev. A IN 20s 13.250.205.9 127.0.0.1:53 +mrkaran.dev. A IN 20s 206.189.89.118 127.0.0.1:53 +``` + +**Query MX records for `github.com` using `9.9.9.9` resolver** + +``` +doggo MX github.com @9.9.9.9 +NAME TYPE CLASS TTL ADDRESS NAMESERVER +github.com. MX IN 3600s 10 alt3.aspmx.l.google.com. 9.9.9.9:53 +github.com. MX IN 3600s 5 alt1.aspmx.l.google.com. 9.9.9.9:53 +github.com. MX IN 3600s 10 alt4.aspmx.l.google.com. 9.9.9.9:53 +github.com. MX IN 3600s 5 alt2.aspmx.l.google.com. 9.9.9.9:53 +github.com. MX IN 3600s 1 aspmx.l.google.com. 9.9.9.9:53 +``` + +or using _named parameters_: + +```bash +$ doggo -t MX -n 9.9.9.9 github.com +NAME TYPE CLASS TTL ADDRESS NAMESERVER +github.com. MX IN 3600s 10 alt3.aspmx.l.google.com. 9.9.9.9:53 +github.com. MX IN 3600s 5 alt1.aspmx.l.google.com. 9.9.9.9:53 +github.com. MX IN 3600s 10 alt4.aspmx.l.google.com. 9.9.9.9:53 +github.com. MX IN 3600s 5 alt2.aspmx.l.google.com. 9.9.9.9:53 +github.com. MX IN 3600s 1 aspmx.l.google.com. 9.9.9.9:53 +``` + +**Query DNS records for archive.org using Cloudflare DoH resolver** + +```bash +$ doggo archive.org @https://cloudflare-dns.com/dns-query +NAME TYPE CLASS TTL ADDRESS NAMESERVER +archive.org. A IN 41s 207.241.224.2 https://cloudflare-dns.com/dns-query +``` + +**Query DNS records for internetfreedom.in with JSON output** + +```bash +$ doggo internetfreedom.in --json | jq +{ + "responses": { + "answers": [ + { + "name": "internetfreedom.in.", + "type": "A", + "class": "IN", + "ttl": "22s", + "address": "104.27.158.96", + "rtt": "37ms", + "nameserver": "127.0.0.1:53" + }, + { + "name": "internetfreedom.in.", + "type": "A", + "class": "IN", + "ttl": "22s", + "address": "104.27.159.96", + "rtt": "37ms", + "nameserver": "127.0.0.1:53" + }, + { + "name": "internetfreedom.in.", + "type": "A", + "class": "IN", + "ttl": "22s", + "address": "172.67.202.77", + "rtt": "37ms", + "nameserver": "127.0.0.1:53" + } + ], + "queries": [ + { + "name": "internetfreedom.in.", + "type": "A", + "class": "IN" + } + ] + } +} +``` + +**Query DNS records for duckduckgo.com and show RTT (Round Trip Time)** + +```bash +$ doggo duckduckgo.com --time +NAME TYPE CLASS TTL ADDRESS NAMESERVER TIME TAKEN +duckduckgo.com. A IN 30s 40.81.94.43 127.0.0.1:53 45ms +``` + +## Command-line Arguments + +![](www/static/help.png) + +### Transport Options + +URL scheme of the server is used to identify which resolver to use for lookups. If no scheme is specified, defaults to `udp`. + +``` + @udp:// eg: @1.1.1.1 initiates a UDP resolver for 1.1.1.1:53. + @tcp:// eg: @1.1.1.1 initiates a TCP resolver for 1.1.1.1:53. + @https:// eg: @https://cloudflare-dns.com/dns-query initiates a DOH resolver for Cloudflare DoH server. + @tls:// eg: @1.1.1.1 initiates a DoT resolver for 1.1.1.1:853. +``` + +### Query Options + +``` + -q, --query=HOSTNAME Hostname to query the DNS records for (eg mrkaran.dev). + -t, --type=TYPE Type of the DNS Record (A, MX, NS etc). + -n, --nameserver=ADDR Address of a specific nameserver to send queries to (9.9.9.9, 8.8.8.8 etc). + -c, --class=CLASS Network class of the DNS record (IN, CH, HS etc). +``` + +### Resolver Options + +``` + --ndots=INT Specify ndots parameter. Takes value from /etc/resolv.conf if using the system namesever or 1 otherwise. + --search Use the search list defined in resolv.conf. Defaults to true. Set --search=false to disable search list. + --timeout Specify timeout (in seconds) for the resolver to return a response. + -4 --ipv4 Use IPv4 only. + -6 --ipv6 Use IPv6 only. +``` + + +### Output Options + +``` + -J, --json Format the output as JSON. + --color Defaults to true. Set --color=false to disable colored output. + --debug Enable debug logging. + --time Shows how long the response took from the server. +``` + +--- + +## Contributing + +I'm open to accept feature requests and/or issues. I understand `doggo` is a very new DNS Client in the town and there might be some edge cases I am not handling. Please feel free to open issues if you ever come across such a case. +For now I am focussing more on [planned features](TODO.md) for a **stable** v1.0 release _soon_. + +## License + +[LICENSE](LICENSE) diff --git a/TODO.md b/TODO.md index c40641f..8be89c8 100644 --- a/TODO.md +++ b/TODO.md @@ -39,25 +39,18 @@ - [x] Remove urfave/cli in favour of `pflag + koanf` - [x] Flags - Remove uneeded ones -## Tests -- [ ] Add tests for Resolvers. -- [ ] Add tests for CLI Output. - ## Documentation -- [ ] README - - [ ] Usage - - [ ] Installation - - [ ] Features -- [ ] Mkdocs init project - - [ ] Custom Index (Landing Page) +- [x] README + - [x] Usage + - [x] Installation + - [x] Features + ## Release Checklist - [ ] Goreleaser - [ ] Snap - [ ] Homebrew - - [ ] ARM - [ ] Docker - --- # Future Release @@ -70,4 +63,7 @@ - [ ] zsh - [ ] fish - [ ] Support non RFC Compliant DOH Google response (_ugh_) - +- [ ] Add tests for Resolvers. +- [ ] Add tests for CLI Output. +- [ ] Mkdocs init project + - [ ] Custom Index (Landing Page) \ No newline at end of file diff --git a/cmd/cli.go b/cmd/cli.go index 2b65cf8..f2c2603 100644 --- a/cmd/cli.go +++ b/cmd/cli.go @@ -40,7 +40,7 @@ func main() { // Resolver Options f.Int("timeout", 5, "Sets the timeout for a query to T seconds. The default timeout is 5 seconds.") f.Bool("search", true, "Use the search list provided in resolv.conf. It sets the `ndots` parameter as well unless overriden by `ndots` flag.") - f.Int("ndots", 1, "Specify the ndots paramter. Default value is taken from resolv.conf and fallbacks to 1 if ndots statement is missing in resolv.conf") + f.Int("ndots", 0, "Specify the ndots paramter. Default value is taken from resolv.conf and fallbacks to 1 if ndots statement is missing in resolv.conf") f.BoolP("ipv4", "4", false, "Use IPv4 only") f.BoolP("ipv6", "6", false, "Use IPv6 only") diff --git a/cmd/help.go b/cmd/help.go index 9a3d7c7..cc05b69 100644 --- a/cmd/help.go +++ b/cmd/help.go @@ -13,32 +13,48 @@ var appHelpTextTemplate = `{{ "NAME" | color "" "heading" }}: {{ .Name | color "green" "bold" }} 🐶 {{.Description}} {{ "USAGE" | color "" "heading" }}: - {{ .Name | color "green" "" }} [--] {{ "[query options]" | color "yellow" "" }} {{ "[arguments...]" | color "cyan" "" }} + {{ .Name | color "green" "bold" }} [--] {{ "[query options]" | color "yellow" "" }} {{ "[arguments...]" | color "cyan" "" }} {{ "VERSION" | color "" "heading" }}: {{.Version | color "red" "" }} - {{.Date | color "red" ""}} {{ "EXAMPLES" | color "" "heading" }}: - {{ .Name | color "green" "" }} {{ "mrkaran.dev" | color "cyan" "" }} Query a domain using defaults - {{ .Name | color "green" "" }} {{ "mrkaran.dev CNAME" | color "cyan" "" }} Looks up for a CNAME record - {{ .Name | color "green" "" }} {{ "mrkaran.dev MX @9.9.9.9" | color "cyan" "" }} Uses a custom DNS resolver - {{ .Name | color "green" "" }} {{"-q mrkaran.dev -t MX -n 1.1.1.1" | color "yellow" ""}} Using named arguments + {{ .Name | color "green" "bold" }} {{ "mrkaran.dev" | color "cyan" "" }} Query a domain using defaults. + {{ .Name | color "green" "bold" }} {{ "mrkaran.dev CNAME" | color "cyan" "" }} Looks up for a CNAME record. + {{ .Name | color "green" "bold" }} {{ "mrkaran.dev MX @9.9.9.9" | color "cyan" "" }} Uses a custom DNS resolver. + {{ .Name | color "green" "bold" }} {{"-q mrkaran.dev -t MX -n 1.1.1.1" | color "yellow" ""}} Using named arguments. {{ "Free Form Arguments" | color "" "heading" }}: Supply hostnames, query types, classes without any flag. For eg: - {{ .Name | color "green" "" }} {{"mrkaran.dev A @1.1.1.1" | color "cyan" "" }} + {{ .Name | color "green" "bold" }} {{"mrkaran.dev A @1.1.1.1" | color "cyan" "" }} + +{{ "Transport Options" | color "" "heading" }}: + Based on the URL scheme the correct resolver is chosen. + Fallbacks to UDP resolver if no scheme is present. + + {{"@udp://" | color "yellow" ""}} eg: @1.1.1.1 initiates a {{"UDP" | color "cyan" ""}} resolver for 1.1.1.1:53. + {{"@tcp://" | color "yellow" ""}} eg: @1.1.1.1 initiates a {{"TCP" | color "cyan" ""}} resolver for 1.1.1.1:53. + {{"@https://" | color "yellow" ""}} eg: @https://cloudflare-dns.com/dns-query initiates a {{"DOH" | color "cyan" ""}} resolver for Cloudflare DoH server. + {{"@tls://" | color "yellow" ""}} eg: @1.1.1.1 initiates a {{"DoT" | color "cyan" ""}} resolver for 1.1.1.1:853. {{ "Query Options" | color "" "heading" }}: - {{"-q, --query=HOSTNAME" | color "yellow" ""}} Hostname to query the DNS records for - {{"-t, --type=TYPE" | color "yellow" ""}} Type of the DNS Record (A, MX, NS etc) - {{"-n, --nameserver=ADDR" | color "yellow" ""}} Address of a specific nameserver to send queries to (9.9.9.9, 1.1.1.1 etc) - {{"-c, --class=CLASS" | color "yellow" ""}} Network class of the DNS record (IN, CH, HS etc) + {{"-q, --query=HOSTNAME" | color "yellow" ""}} Hostname to query the DNS records for (eg {{"mrkaran.dev" | color "cyan" ""}}). + {{"-t, --type=TYPE" | color "yellow" ""}} Type of the DNS Record ({{"A, MX, NS" | color "cyan" ""}} etc). + {{"-n, --nameserver=ADDR" | color "yellow" ""}} Address of a specific nameserver to send queries to ({{"9.9.9.9, 8.8.8.8" | color "cyan" ""}} etc). + {{"-c, --class=CLASS" | color "yellow" ""}} Network class of the DNS record ({{"IN, CH, HS" | color "cyan" ""}} etc). + +{{ "Resolver Options" | color "" "heading" }}: + {{"--ndots=INT" | color "yellow" ""}} Specify ndots parameter. Takes value from /etc/resolv.conf if using the system namesever or 1 otherwise. + {{"--search" | color "yellow" ""}} Use the search list defined in resolv.conf. Defaults to true. Set --search=false to disable search list. + {{"--timeout" | color "yellow" ""}} Specify timeout (in seconds) for the resolver to return a response. + {{"-4 --ipv4" | color "yellow" ""}} Use IPv4 only. + {{"-6 --ipv6" | color "yellow" ""}} Use IPv6 only. {{ "Output Options" | color "" "heading" }}: - {{"-J, --json " | color "yellow" ""}} Format the output as JSON - {{"--color " | color "yellow" ""}} Defaults to true. Set --color=false to disable colored output - {{"--debug " | color "yellow" ""}} Enable debug logging - {{"--time" | color "yellow" ""}} Shows how long the response took from the server + {{"-J, --json " | color "yellow" ""}} Format the output as JSON. + {{"--color " | color "yellow" ""}} Defaults to true. Set --color=false to disable colored output. + {{"--debug " | color "yellow" ""}} Enable debug logging. + {{"--time" | color "yellow" ""}} Shows how long the response took from the server. ` func renderCustomHelp() { @@ -67,8 +83,7 @@ func renderCustomHelp() { case "underline": formatter = formatter.Add(color.Underline) case "heading": - formatter = formatter.Add(color.Bold) - formatter = formatter.Add(color.Underline) + formatter = formatter.Add(color.Bold, color.Underline) } return formatter.SprintFunc()(str) }, diff --git a/cmd/nameservers.go b/cmd/nameservers.go index bfcde49..dccb6ba 100644 --- a/cmd/nameservers.go +++ b/cmd/nameservers.go @@ -46,7 +46,7 @@ func (hub *Hub) loadNameservers() error { if err != nil { return fmt.Errorf("error fetching system default nameserver") } - if !hub.QueryFlags.isNdotsSet { + if hub.QueryFlags.Ndots == 0 { hub.QueryFlags.Ndots = ndots } hub.Nameservers = append(hub.Nameservers, ns...) diff --git a/www/static/help.png b/www/static/help.png new file mode 100644 index 0000000..64c9d61 Binary files /dev/null and b/www/static/help.png differ diff --git a/www/static/main.png b/www/static/main.png deleted file mode 100644 index 7e508fa..0000000 Binary files a/www/static/main.png and /dev/null differ diff --git a/www/static/usage.png b/www/static/usage.png new file mode 100644 index 0000000..14af461 Binary files /dev/null and b/www/static/usage.png differ