feat: add strategy flag

Ref https://github.com/mr-karan/doggo/issues/35
pull/41/head
Karan Sharma 2022-05-17 10:21:14 +05:30
parent 79c5fc7283
commit 7619cbdeb0
7 changed files with 46 additions and 9 deletions

View File

@ -62,5 +62,8 @@
- [ ] Add tests for CLI Output.
- [ ] Homebrew - Goreleaser
- [ ] Add support for `dig +trace` like functionality.
- [ ] Add `dig +x` short output
- [x] Add `--strategy` for picking nameservers.
- [ ] Explore `dig.rc` kinda file
- [x] Separate Authority/Answer in JSON output.
- [x] Error on NXDomain (Related upstream [bug](https://github.com/miekg/dns/issues/1198))

1
^ 100644
View File

@ -0,0 +1 @@
nameserver 127.0.w0.1

View File

@ -45,6 +45,7 @@ func main() {
f.Int("ndots", -1, "Specify the ndots parameter. 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")
f.String("strategy", "all", "Strategy to query nameservers in resolv.conf file (`all`, `random`, `first`)")
// Output Options
f.BoolP("json", "J", false, "Set the output format as JSON")
@ -126,6 +127,7 @@ func main() {
Ndots: app.ResolverOpts.Ndots,
Timeout: app.QueryFlags.Timeout * time.Second,
Logger: app.Logger,
Strategy: app.QueryFlags.Strategy,
})
if err != nil {
app.Logger.WithError(err).Error("error loading resolver")

View File

@ -47,11 +47,12 @@ var appHelpTextTemplate = `{{ "NAME" | color "" "heading" }}:
{{"-x, --reverse" | color "yellow" ""}} Performs a DNS Lookup for an IPv4 or IPv6 address. Sets the query type and class to PTR and IN respectively.
{{ "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.
{{"--strategy=STRATEGY" | color "yellow" ""}} Specify strategy to query nameserver listed in etc/resolv.conf. ({{"all, random, first" | color "cyan" ""}}).
{{"--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.

View File

@ -2,8 +2,10 @@ package app
import (
"fmt"
"math/rand"
"net"
"net/url"
"time"
"github.com/ameshkov/dnsstamps"
"github.com/mr-karan/doggo/pkg/config"
@ -29,7 +31,7 @@ func (app *App) LoadNameservers() error {
// fallback to system nameserver
// in case no nameserver is specified by user.
if len(app.Nameservers) == 0 {
ns, ndots, search, err := getDefaultServers()
ns, ndots, search, err := getDefaultServers(app.QueryFlags.Strategy)
if err != nil {
return fmt.Errorf("error fetching system default nameserver")
}
@ -117,18 +119,44 @@ func initNameserver(n string) (models.Nameserver, error) {
return ns, nil
}
func getDefaultServers() ([]models.Nameserver, int, []string, error) {
func getDefaultServers(strategy string) ([]models.Nameserver, int, []string, error) {
// Load nameservers from `/etc/resolv.conf`.
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 {
switch strategy {
case "random":
// Choose a random server from the list.
rand.Seed(time.Now().Unix())
srv := dnsServers[rand.Intn(len(dnsServers))]
ns := models.Nameserver{
Type: models.UDPResolver,
Address: net.JoinHostPort(s, models.DefaultUDPPort),
Address: net.JoinHostPort(srv, models.DefaultUDPPort),
}
servers = append(servers, ns)
case "first":
// Choose the first from the list, always.
srv := dnsServers[0]
ns := models.Nameserver{
Type: models.UDPResolver,
Address: net.JoinHostPort(srv, models.DefaultUDPPort),
}
servers = append(servers, ns)
default:
// Default behaviour is to load all nameservers.
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
}

View File

@ -35,6 +35,7 @@ type QueryFlags struct {
ShowJSON bool `koanf:"json" json:"-"`
UseSearchList bool `koanf:"search" json:"-"`
ReverseLookup bool `koanf:"reverse" reverse:"-"`
Strategy string `koanf:"strategy" strategy:"-"`
}
// Nameserver represents the type of Nameserver

View File

@ -18,6 +18,7 @@ type Options struct {
Ndots int
Timeout time.Duration
Logger *logrus.Logger
Strategy string
}
// Resolver implements the configuration for a DNS