From eda2e5b55f8e32b3f5edf23ef1b4fa58c7cfef8e Mon Sep 17 00:00:00 2001 From: Sam Therapy Date: Fri, 22 Jul 2022 23:22:43 +0200 Subject: [PATCH] More work! Signed-off-by: Sam Therapy --- .drone.jsonnet | 3 +- .gitignore | 7 +- Makefile | 62 +++++++++---- Mkfile | 35 ++++++-- README.md | 9 +- cli/cli.go | 45 +++++++--- cli/cli_test.go | 183 +++++--------------------------------- cli/dig.go | 26 +++++- cli/dig_test.go | 10 ++- cli/misc.go | 44 ++++++++- cli/misc_test.go | 55 ++++++++++++ cli/options.go | 24 +++-- conf/plan9_test.go | 1 + doc/awl.1 | 51 ++++++++--- doc/awl.1.md | 47 +++++++--- go.mod | 5 ++ go.sum | 21 +++++ internal/helpers/query.go | 9 +- internal/structs/query.go | 23 ----- main.go | 50 ++++++++++- query/DNSCrypt.go | 58 ++++++++++++ query/DNSCrypt_test.go | 52 +++++++++++ query/HTTPS.go | 17 ++-- query/HTTPS_test.go | 50 +++++++---- query/QUIC.go | 9 +- query/QUIC_test.go | 23 ++++- query/general.go | 13 ++- query/general_test.go | 43 ++++++++- query/query.go | 7 +- query/resolver.go | 33 ++++--- 30 files changed, 692 insertions(+), 323 deletions(-) delete mode 100644 internal/structs/query.go create mode 100644 query/DNSCrypt.go create mode 100644 query/DNSCrypt_test.go diff --git a/.drone.jsonnet b/.drone.jsonnet index 0bcb4dd..fd3c192 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: BSD-3-Clause + local testing(version, arch) = { kind: "pipeline", name: version + "-" + arch , @@ -59,7 +61,6 @@ local release() = { ] }; -// logawl uses generics so 1.18 is the minimum [ testing("1.18", "amd64"), testing("1.18", "arm64"), diff --git a/.gitignore b/.gitignore index 356b47d..fef0267 100644 --- a/.gitignore +++ b/.gitignore @@ -15,5 +15,8 @@ # vendor/ # Go workspace file -go.work -dist/ +go.work +dist/ + +# Test coverage +coverage/ \ No newline at end of file diff --git a/Makefile b/Makefile index 6ecb350..9368065 100644 --- a/Makefile +++ b/Makefile @@ -1,36 +1,62 @@ -# This should only be used for dev environments -GO := go -GOFLAGS := -ldflags '-s -w' -PREFIX := /usr/local -BINPATH = $(PREFIX)/bin +# SPDX-License-Identifier: BSD-3-Clause -.PHONY: clean doc +HASH := $(shell git describe --always --dirty || echo "UNKNOWN") +VER := "git-$(HASH)" + +CGO_ENABLED = 0 +GO := go +GOFLAGS := -ldflags "-s -w -X 'main.version=$(VER)'" +SCDOC := scdoc + +PREFIX := /usr/local +BINPATH := $(PREFIX)/bin +MANPATH := $(PREFIX)/share/man + +PROG := awl +ifeq ($(OS),Windows_NT) + EXE := $(PROG).exe +else + EXE := $(PROG) +endif + +.PHONY: clean lint # hehe -all: awl +all: $(PROG) -awl: . - $(GO) build -o awl $(GOFLAGS) . +$(PROG): lint + $(GO) build -o $(EXE) $(GOFLAGS) . -doc: - scdoc < doc/awl.1.md > doc/awl.1 +doc: doc/awl.1.md + $(SCDOC) < doc/$(PROG).1.md > doc/$(PROG).1 -test: - $(GO) test -v ./... -cover +test: coverage/coverage.out + $(GO) test -v -cover -coverprofile=coverage/coverage.out ./... -fmt: +cover: test + $(GO) tool cover -func=coverage/coverage.out + $(GO) tool cover -html=coverage/coverage.out -o coverage/cover.html + +fmt: *.go gofmt -w -s . -vet: +vet: *.go $(GO) vet ./... lint: fmt vet + -golangci-lint run -install: awl - install awl $(BINPATH) +ifeq ($(OS),Windows_NT) +install: + $(GO) install $(GOFLAGS) . +else +install: $(PROG) + install $(PROG) $(BINPATH) +endif + install_doc: doc - install doc/awl.1 $(PREFIX)/share/man/man1 + install doc/$(PROG).1 $(MANPATH)/man1 @echo "" @echo "Make sure to update your manual database" @echo "'sudo mandb' on Debian/Ubuntu" diff --git a/Mkfile b/Mkfile index c8a0359..3c3649c 100644 --- a/Mkfile +++ b/Mkfile @@ -1,9 +1,34 @@ -GO=GO -awl: awl.go - $GO build -o awl +# SPDX-License-Identifier: BSD-3-Clause + +GO = go +SCDOC = scdoc +PROG = awl +LDFLAGS = '-s -w' +GOFLAGS = -ldflags=$LDFLAGS +CGO_ENABLED = 0 + +$PROG: + $GO build $GOFLAGS -o $PROG . + +doc: doc/$PROG.1.md + $SCDOC < doc/$PROG.1.md > doc/$PROG.1 install: - $GO install . + $GO install $GOFLAGS . + +install_doc: doc + install doc/$PROG.1 /sys/man/1 test: - $GO test ./... + $GO test -v ./... -cover + +fmt: + gofmt -w -s . + +vet: + $GO vet ./... + +lint: fmt vet + +clean: + $GO clean diff --git a/README.md b/README.md index 7aedb20..0dbc84f 100644 --- a/README.md +++ b/README.md @@ -14,12 +14,9 @@ lifting. ## What awl should do -- Optimize everything -- Make the code less spaghetti (partially completed) -- Feature parity with drill - - Making a drop-in replacement for drill? - - What about dig? +- Be a drop-in replacement for dig (and maybe drill) +- Support DNS RFCs ## What awl won't do - Print to files or read from files -- Colour outputs (unless?) +- Colour outputs diff --git a/cli/cli.go b/cli/cli.go index be1e509..f483aed 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -7,6 +7,7 @@ import ( "os" "runtime" "strings" + "time" "git.froth.zone/sam/awl/internal/helpers" "git.froth.zone/sam/awl/util" @@ -44,13 +45,15 @@ func ParseCLI(version string) (Options, error) { ipv6 = flag.Bool("6", false, "force IPv6", flag.OptShorthand('6')) reverse = flag.Bool("reverse", false, "do a reverse lookup", flag.OptShorthand('x')) + timeout = flag.Float32("timeout", 1, "Timeout, in `seconds`") dnssec = flag.Bool("dnssec", false, "enable DNSSEC", flag.OptShorthand('D')) truncate = flag.Bool("no-truncate", false, "ignore truncation if a UDP request truncates (default= retry with TCP)") - tcp = flag.Bool("tcp", false, "use TCP") - tls = flag.Bool("tls", false, "use DNS-over-TLS", flag.OptShorthand('T')) - https = flag.Bool("https", false, "use DNS-over-HTTPS", flag.OptShorthand('H')) - quic = flag.Bool("quic", false, "use DNS-over-QUIC", flag.OptShorthand('Q')) + tcp = flag.Bool("tcp", false, "use TCP") + dnscrypt = flag.Bool("dnscrypt", false, "use DNSCrypt") + tls = flag.Bool("tls", false, "use DNS-over-TLS", flag.OptShorthand('T')) + https = flag.Bool("https", false, "use DNS-over-HTTPS", flag.OptShorthand('H')) + quic = flag.Bool("quic", false, "use DNS-over-QUIC", flag.OptShorthand('Q')) aa = flag.Bool("aa", false, "set/unset AA (Authoratative Answer) flag (default: not set)") ad = flag.Bool("ad", false, "set/unset AD (Authenticated Data) flag (default: not set)") @@ -66,7 +69,13 @@ func ParseCLI(version string) (Options, error) { xml = flag.Bool("xml", false, "print the result(s) as XML", flag.OptShorthand('X')) yaml = flag.Bool("yaml", false, "print the result(s) as yaml", flag.OptShorthand('y')) - verbosity = flag.Int("verbosity", 0, "sets verbosity", flag.OptShorthand('v'), flag.OptNoOptDefVal("3")) + noQ = flag.Bool("no-question", false, "disable printing the question section") + noAns = flag.Bool("no-answer", false, "disable printing the answer section") + noAuth = flag.Bool("no-authority", false, "disable printing the authority section") + noAdd = flag.Bool("no-additional", false, "disable printing the additonal section") + noStats = flag.Bool("no-statistics", false, "disable printing the statistics section") + + verbosity = flag.Int("verbosity", 0, "sets verbosity `level`", flag.OptShorthand('v'), flag.OptNoOptDefVal("2")) versionFlag = flag.Bool("version", false, "print version information", flag.OptShorthand('V')) ) @@ -87,6 +96,7 @@ func ParseCLI(version string) (Options, error) { DNSSEC: *dnssec, Short: *short, TCP: *tcp, + DNSCrypt: *dnscrypt, TLS: *tls, HTTPS: *https, QUIC: *quic, @@ -104,12 +114,23 @@ func ParseCLI(version string) (Options, error) { XML: *xml, YAML: *yaml, Request: helpers.Request{ - Type: dns.StringToType[strings.ToUpper(*qType)], - Class: dns.StringToClass[strings.ToUpper(*class)], - Name: *query, + Type: dns.StringToType[strings.ToUpper(*qType)], + Class: dns.StringToClass[strings.ToUpper(*class)], + Name: *query, + Timeout: time.Duration(*timeout * float32(time.Second)), + }, + Display: Displays{ + Question: !*noQ, + Answer: !*noAns, + Authority: !*noAuth, + Additional: !*noAdd, + Statistics: !*noStats, }, } + opts.Logger.Info("POSIX flags parsed") + opts.Logger.Debug(fmt.Sprintf("%+v", opts)) + if *versionFlag { fmt.Printf("awl version %s, built with %s\n", version, runtime.Version()) return opts, ErrNotError @@ -122,6 +143,8 @@ func ParseCLI(version string) (Options, error) { opts.Logger.Error(err) return opts, err } + opts.Logger.Info("Dig/Drill flags parsed") + opts.Logger.Debug(fmt.Sprintf("%+v", opts)) if opts.Port == 0 { if opts.TLS || opts.QUIC { @@ -130,11 +153,7 @@ func ParseCLI(version string) (Options, error) { opts.Port = 53 } } - - // opts.Request = req - - // Set verbosity to full if just -v is specified - // flag.Lookup("verbosity").NoOptDefVal = "3" + opts.Logger.Info("Port set to", opts.Port) return opts, nil } diff --git a/cli/cli_test.go b/cli/cli_test.go index 36ec6be..e371036 100644 --- a/cli/cli_test.go +++ b/cli/cli_test.go @@ -12,10 +12,20 @@ import ( func TestEmpty(t *testing.T) { old := os.Args - os.Args = []string{""} + os.Args = []string{"awl", "-4"} opts, err := cli.ParseCLI("TEST") assert.NilError(t, err) - assert.Assert(t, opts != cli.Options{}) + assert.Assert(t, (opts.Port == 53)) + assert.Assert(t, opts.IPv4) + os.Args = old +} + +func TestTLSPort(t *testing.T) { + old := os.Args + os.Args = []string{"awl", "-T"} + opts, err := cli.ParseCLI("TEST") + assert.NilError(t, err) + assert.Assert(t, (opts.Port == 853)) os.Args = old } @@ -31,35 +41,17 @@ func TestInvalidDig(t *testing.T) { old := os.Args os.Args = []string{"awl", "+a"} _, err := cli.ParseCLI("TEST") - assert.ErrorContains(t, err, "dig: unknown flag given") + assert.ErrorContains(t, err, "dig: unknown flag") os.Args = old } -// func TestArgParse(t *testing.T) { -// tests := []struct { -// in []string -// want helpers.Request -// }{ -// { -// []string{"@::1", "localhost", "AAAA"}, -// helpers.Request{Server: "::1", Type: dns.TypeAAAA, Name: "localhost"}, -// }, -// { -// []string{"@1.0.0.1", "google.com"}, -// helpers.Request{Server: "1.0.0.1", Type: dns.TypeA, Name: "google.com"}, -// }, -// { -// []string{"@8.8.4.4"}, -// helpers.Request{Server: "8.8.4.4", Type: dns.TypeNS, Name: "."}, -// }, -// } -// for _, test := range tests { -// old := os.Args -// act, err := cli.ParseCLI(test.in) -// assert.Nil(t, err) -// assert.Equal(t, test.want, act) -// } -// } +func TestVersion(t *testing.T) { + old := os.Args + os.Args = []string{"awl", "--version"} + _, err := cli.ParseCLI("TEST") + assert.ErrorType(t, err, cli.ErrNotError) + os.Args = old +} func FuzzFlags(f *testing.F) { testcases := []string{"git.froth.zone", "", "!12345", "google.com.edu.org.fr"} @@ -73,138 +65,3 @@ func FuzzFlags(f *testing.F) { cli.ParseCLI("TEST") }) } - -// func TestArgParse(t *testing.T) { -// t.Parallel() -// tests := []struct { -// in []string -// want helpers.Request -// }{ -// { -// []string{"@::1", "localhost", "AAAA"}, -// helpers.Request{Server: "::1", Type: dns.TypeAAAA, Name: "localhost"}, -// }, -// { -// []string{"@1.0.0.1", "google.com"}, -// helpers.Request{Server: "1.0.0.1", Type: dns.TypeA, Name: "google.com"}, -// }, -// { -// []string{"@8.8.4.4"}, -// helpers.Request{Server: "8.8.4.4", Type: dns.TypeNS, Name: "."}, -// }, -// } -// for _, test := range tests { -// act, err := parseArgs(test.in, &query.Options{}) -// assert.Nil(t, err) -// assert.Equal(t, test.want, act) -// } -// } - -// func TestQuery(t *testing.T) { -// app := prepareCLI() -// args := os.Args[0:1] -// args = append(args, "--Treebug") -// err := app.Run(args) -// assert.NotNil(t, err) -// } - -// func TestNoArgs(t *testing.T) { -// app := prepareCLI() -// args := os.Args[0:1] -// args = append(args, "--no-truncate") -// err := app.Run(args) -// assert.Nil(t, err) -// } - -// func TestFlags(t *testing.T) { -// app := prepareCLI() -// args := os.Args[0:1] -// args = append(args, "--debug") -// args = append(args, "--short") -// args = append(args, "-4") -// err := app.Run(args) -// assert.Nil(t, err) -// } - -// func TestHTTPS(t *testing.T) { -// app := prepareCLI() -// args := os.Args[0:1] -// args = append(args, "-H") -// // args = append(args, "@https://cloudflare-dns.com/dns-query") -// args = append(args, "git.froth.zone") -// err := app.Run(args) -// assert.Nil(t, err) -// } - -// func TestJSON(t *testing.T) { -// app := prepareCLI() -// args := os.Args[0:1] -// args = append(args, "-j") -// args = append(args, "git.froth.zone") -// err := app.Run(args) -// assert.Nil(t, err) -// } - -// func TestTLS(t *testing.T) { -// app := prepareCLI() -// args := os.Args[0:1] -// args = append(args, "-T") -// args = append(args, "git.froth.zone") -// err := app.Run(args) -// assert.Nil(t, err) -// } - -// func TestXML(t *testing.T) { -// app := prepareCLI() -// args := os.Args[0:1] -// args = append(args, "-X") -// args = append(args, "git.froth.zone") -// err := app.Run(args) -// assert.Nil(t, err) -// } - -// func TestYAML(t *testing.T) { -// app := prepareCLI() -// args := os.Args[0:1] -// args = append(args, "-y") -// args = append(args, "git.froth.zone") -// err := app.Run(args) -// assert.Nil(t, err) -// } - -// func TestQUIC(t *testing.T) { -// app := prepareCLI() -// args := os.Args[0:1] -// args = append(args, "-Q") -// // args = append(args, "@dns.adguard.com") -// args = append(args, "git.froth.zone") -// err := app.Run(args) -// assert.Nil(t, err) -// } - -// func TestReverse(t *testing.T) { -// app := prepareCLI() -// args := os.Args[0:1] -// args = append(args, "-x") -// args = append(args, "8.8.8.8") -// err := app.Run(args) -// assert.Nil(t, err) -// } - -// func FuzzCli(f *testing.F) { -// testcases := []string{"git.froth.zone", "", "!12345", "google.com.edu.org.fr"} -// for _, tc := range testcases { -// f.Add(tc) -// } - -// f.Fuzz(func(t *testing.T, orig string) { -// app := prepareCLI() -// args := os.Args[0:1] -// args = append(args, orig) -// err := app.Run(args) -// if err != nil { -// require.ErrorContains(t, err, "domain must be fully qualified") -// } -// require.Nil(t, err) -// }) -// } diff --git a/cli/dig.go b/cli/dig.go index e697ed9..458476e 100644 --- a/cli/dig.go +++ b/cli/dig.go @@ -11,6 +11,8 @@ import ( func ParseDig(arg string, opts *Options) error { // returns true if the flag starts with a no isNo := !strings.HasPrefix(arg, "no") + opts.Logger.Info("Setting", arg, "to", isNo) + switch arg { // Set DNS query flags case "aa", "aaflag", "aaonly", "noaa", "noaaflag", "noaaonly": @@ -49,8 +51,30 @@ func ParseDig(arg string, opts *Options) error { opts.YAML = isNo // End formatting + // Output + // TODO: get this to work + // case "comments", "nocomments": + // opts.Display.Comments = isNo + case "question", "noquestion": + opts.Display.Question = isNo + case "answer", "noanswer": + opts.Display.Answer = isNo + case "authority", "noauthority": + opts.Display.Authority = isNo + case "additional", "noadditional": + opts.Display.Additional = isNo + case "stats", "nostats": + opts.Display.Statistics = isNo + + case "all", "noall": + opts.Display.Question = isNo + opts.Display.Answer = isNo + opts.Display.Authority = isNo + opts.Display.Additional = isNo + opts.Display.Statistics = isNo + default: - return fmt.Errorf("dig: unknown flag given") + return fmt.Errorf("dig: unknown flag %s given", arg) } return nil } diff --git a/cli/dig_test.go b/cli/dig_test.go index 8d1292a..71e69ec 100644 --- a/cli/dig_test.go +++ b/cli/dig_test.go @@ -6,6 +6,7 @@ import ( "testing" "git.froth.zone/sam/awl/cli" + "git.froth.zone/sam/awl/util" "gotest.tools/v3/assert" ) @@ -27,6 +28,12 @@ func FuzzDig(f *testing.F) { "json", "nojson", "xml", "noxml", "yaml", "noyaml", + "question", "noquestion", + "answer", "noanswer", + "authority", "noauthority", + "additional", "noadditional", + "stats", "nostats", + "all", "noall", "invalid", } for _, tc := range seeds { @@ -35,9 +42,10 @@ func FuzzDig(f *testing.F) { f.Fuzz(func(t *testing.T, orig string) { opts := new(cli.Options) + opts.Logger = util.InitLogger(0) err := cli.ParseDig(orig, opts) if err != nil { - assert.ErrorContains(t, err, "unknown flag given") + assert.ErrorContains(t, err, "unknown flag") } }) } diff --git a/cli/misc.go b/cli/misc.go index c334e12..6f0f747 100644 --- a/cli/misc.go +++ b/cli/misc.go @@ -23,23 +23,48 @@ func ParseMiscArgs(args []string, opts *Options) error { switch { // If it starts with @, it's a DNS server case strings.HasPrefix(arg, "@"): - opts.Request.Server = arg[1:] + arg = arg[1:] + // Automatically set flags based on URI header + opts.Logger.Info(arg, "detected as a server") + switch { + case strings.HasPrefix(arg, "tls://"): + opts.TLS = true + opts.Request.Server = arg[6:] + opts.Logger.Info("DNS-over-TLS implicitly set") + case strings.HasPrefix(arg, "https://"): + opts.HTTPS = true + opts.Request.Server = arg + opts.Logger.Info("DNS-over-HTTPS implicitly set") + case strings.HasPrefix(arg, "quic://"): + opts.QUIC = true + opts.Request.Server = arg[7:] + opts.Logger.Info("DNS-over-QUIC implicitly set.") + case strings.HasPrefix(arg, "sdns://"): + opts.DNSCrypt = true + opts.Request.Server = arg + opts.Logger.Info("DNSCrypt implicitly set") + default: + opts.Request.Server = arg + } case strings.Contains(arg, "."): + opts.Logger.Info(arg, "detected as a domain name") opts.Request.Name, err = idna.ToASCII(arg) if err != nil { return err } case ok: + opts.Logger.Info(arg, "detected as a type") // If it's a DNS request, it's a DNS request (obviously) opts.Request.Type = r case strings.HasPrefix(arg, "+"): + opts.Logger.Info(arg, "detected as a dig query") // Dig-style +queries err = ParseDig(strings.ToLower(arg[1:]), opts) if err != nil { return err } default: - + opts.Logger.Info(arg, "is unknown. Assuming domain") opts.Request.Name, err = idna.ToASCII(arg) if err != nil { return err @@ -49,18 +74,25 @@ func ParseMiscArgs(args []string, opts *Options) error { // If nothing was set, set a default if opts.Request.Name == "" { + opts.Logger.Info("Domain not specified, making a default") opts.Request.Name = "." if opts.Request.Type == 0 { opts.Request.Type = dns.StringToType["NS"] } } else { + opts.Logger.Info("Query not specified, making an \"A\" query") if opts.Request.Type == 0 { opts.Request.Type = dns.StringToType["A"] } } // if opts.Request.Server == "" { + opts.Logger.Info("Server not specified, selecting a default") + // Set "defaults" for each if there is no input switch { + case opts.DNSCrypt: + // This is adguard + opts.Request.Server = "sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20" case opts.TLS: opts.Request.Server = "dns.google" case opts.HTTPS: @@ -70,8 +102,11 @@ func ParseMiscArgs(args []string, opts *Options) error { default: resolv, err := conf.GetDNSConfig() if err != nil { - opts.Request.Server = "9.9.9.9" + // :^) + opts.Logger.Error("Could not query system for server. Using default") + opts.Request.Server = "95.216.99.249" } else { + // Make sure that if IPv4 or IPv6 is asked for it actually uses it for _, srv := range resolv.Servers { if opts.IPv4 { if strings.Contains(srv, ".") { @@ -92,9 +127,11 @@ func ParseMiscArgs(args []string, opts *Options) error { } } } + opts.Logger.Info("DNS server set to", opts.Request.Server) // Make reverse adresses proper addresses if opts.Reverse { + opts.Logger.Info("Making reverse DNS query proper *.arpa domain") if dns.TypeToString[opts.Request.Type] == "A" { opts.Request.Type = dns.StringToType["PTR"] } @@ -107,6 +144,7 @@ func ParseMiscArgs(args []string, opts *Options) error { // if the domain is not canonical, make it canonical if !strings.HasSuffix(opts.Request.Name, ".") { opts.Request.Name = fmt.Sprintf("%s.", opts.Request.Name) + opts.Logger.Debug("Domain made canonical") } return nil } diff --git a/cli/misc_test.go b/cli/misc_test.go index bbed34b..929c1b4 100644 --- a/cli/misc_test.go +++ b/cli/misc_test.go @@ -3,9 +3,11 @@ package cli_test import ( + "strconv" "testing" "git.froth.zone/sam/awl/cli" + "git.froth.zone/sam/awl/util" "github.com/miekg/dns" "gotest.tools/v3/assert" @@ -20,6 +22,7 @@ func TestParseArgs(t *testing.T) { "+ignore", } opts := new(cli.Options) + opts.Logger = util.InitLogger(0) err := cli.ParseMiscArgs(args, opts) assert.NilError(t, err) assert.Equal(t, opts.Request.Name, "go.dev.") @@ -32,6 +35,7 @@ func TestParseNoInput(t *testing.T) { t.Parallel() args := []string{} opts := new(cli.Options) + opts.Logger = util.InitLogger(0) err := cli.ParseMiscArgs(args, opts) assert.NilError(t, err) assert.Equal(t, opts.Request.Name, ".") @@ -44,6 +48,7 @@ func TestParseA(t *testing.T) { "golang.org.", } opts := new(cli.Options) + opts.Logger = util.InitLogger(0) err := cli.ParseMiscArgs(args, opts) assert.NilError(t, err) assert.Equal(t, opts.Request.Name, "golang.org.") @@ -54,18 +59,30 @@ func TestParsePTR(t *testing.T) { t.Parallel() args := []string{"8.8.8.8"} opts := new(cli.Options) + opts.Logger = util.InitLogger(0) opts.Reverse = true err := cli.ParseMiscArgs(args, opts) assert.NilError(t, err) assert.Equal(t, opts.Request.Type, dns.StringToType["PTR"]) } +func TestParseInvalidPTR(t *testing.T) { + t.Parallel() + args := []string{"8.88.8"} + opts := new(cli.Options) + opts.Logger = util.InitLogger(0) + opts.Reverse = true + err := cli.ParseMiscArgs(args, opts) + assert.ErrorContains(t, err, "unrecognized address") +} + func TestDefaultServer(t *testing.T) { t.Parallel() tests := []struct { in string want string }{ + {"DNSCRYPT", "sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20"}, {"TLS", "dns.google"}, {"HTTPS", "https://dns.cloudflare.com/dns-query"}, {"QUIC", "dns.adguard.com"}, @@ -76,7 +93,10 @@ func TestDefaultServer(t *testing.T) { t.Parallel() args := []string{} opts := new(cli.Options) + opts.Logger = util.InitLogger(0) switch test.in { + case "DNSCRYPT": + opts.DNSCrypt = true case "TLS": opts.TLS = true case "HTTPS": @@ -91,6 +111,40 @@ func TestDefaultServer(t *testing.T) { } } +func TestFlagSetting(t *testing.T) { + t.Parallel() + tests := []struct { + in []string + }{ + {[]string{"@sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20"}}, + {[]string{"@tls://dns.google"}}, + {[]string{"@https://dns.cloudflare.com/dns-query"}}, + {[]string{"@quic://dns.adguard.com"}}, + } + for i, test := range tests { + test := test + i := i + t.Run(strconv.Itoa(i), func(t *testing.T) { + opts := new(cli.Options) + opts.Logger = util.InitLogger(0) + t.Parallel() + err := cli.ParseMiscArgs(test.in, opts) + assert.NilError(t, err) + switch i { + case 0: + assert.Assert(t, opts.DNSCrypt) + case 1: + assert.Assert(t, opts.TLS) + case 2: + assert.Assert(t, opts.HTTPS) + case 3: + assert.Assert(t, opts.QUIC) + + } + }) + } +} + func FuzzParseArgs(f *testing.F) { cases := []string{ "go.dev", @@ -105,6 +159,7 @@ func FuzzParseArgs(f *testing.F) { f.Fuzz(func(t *testing.T, arg string) { args := []string{arg} opts := new(cli.Options) + opts.Logger = util.InitLogger(0) //nolint:errcheck // Only make sure the program does not crash cli.ParseMiscArgs(args, opts) }) diff --git a/cli/options.go b/cli/options.go index 4f07778..16d0978 100644 --- a/cli/options.go +++ b/cli/options.go @@ -17,6 +17,7 @@ type Options struct { IPv6 bool // Force IPv6 DNSSEC bool // Enable DNSSEC TCP bool // Query with TCP + DNSCrypt bool // Query over DNSCrypt TLS bool // Query over TLS HTTPS bool // Query over HTTPS QUIC bool // Query over QUIC @@ -31,13 +32,24 @@ type Options struct { Z bool // Set Z (Zero) Reverse bool // Make reverse query Verbosity int // Set logawl verbosity - HumanTTL bool // Make TTL human readable - Short bool // Short output - JSON bool // Outout as JSON - XML bool // Output as XML - YAML bool // Output at YAML + // HumanTTL bool // Make TTL human readable + Short bool // Short output + JSON bool // Outout as JSON + XML bool // Output as XML + YAML bool // Output at YAML - Request helpers.Request + Display Displays // Display options + Request helpers.Request // DNS reuqest +} + +// What to (and not to) display +type Displays struct { + // Comments bool + Question bool // QUESTION SECTION + Answer bool // ANSWER SECTION + Authority bool // AUTHORITY SECTION + Additional bool // ADDITIONAL SECTION + Statistics bool // Query time, message size, etc. } var ErrNotError = errors.New("not an error") diff --git a/conf/plan9_test.go b/conf/plan9_test.go index a5123fc..af8379a 100644 --- a/conf/plan9_test.go +++ b/conf/plan9_test.go @@ -6,6 +6,7 @@ import ( "testing" "git.froth.zone/sam/awl/conf" + "gotest.tools/v3/assert" ) diff --git a/doc/awl.1 b/doc/awl.1 index 597ff69..7a36e25 100644 --- a/doc/awl.1 +++ b/doc/awl.1 @@ -5,10 +5,10 @@ .nh .ad l .\" Begin generated content: -.TH "awl" "1" "2022-07-21" +.TH "awl" "1" "2022-07-22" .PP .SH NAME -awl - drill, writ small +awl - DNS lookup tool .PP .SH SYNOPSIS \fBawl\fR [ \fIOPTIONS\fR ] \fIname\fR [ \fI@server\fR ] [ \fItype\fR ] @@ -23,17 +23,17 @@ where .PP .SH DESCRIPTION .PP -\fBawl\fR (\fBa\fRwls \fBw\fRant \fBl\fRicorice) is a simple tool designed to make DNS queries, much -like the venerable \fIdig\fR(1).\& An awl is a tool used to make small holes, typically -used in tannery (leatherworking).\& +\fBawl\fR (\fBa\fRwls \fBw\fRant \fBl\fRicorice) is a simple tool designed to make DNS queries, +much like the venerable \fIdig\fR(1).\& An awl is a tool used to make small holes, +typically used in tannery (leatherworking).\& .PP -Written in Go, \fBawl\fR is designed to be a more "modern" version of \fIdrill\fR(1) by -including some more RFCs and output options.\& \fBawl\fR is still heavily +\fBawl\fR is designed to be a more "modern" version of \fIdrill\fR(1) by including +some more recent RFCs and output options.\& \fBawl\fR is still heavily Work-In-Progress so some features may get added or removed.\& .PP .SH OPTIONS .RS 4 -Dig-like queries are supported, see dig(1) +Dig-like +[no]flags are supported, see dig(1) .PP \fB-D\fR, \fB--dnssec\fR, \fB+dnssec\fR .br @@ -100,9 +100,13 @@ Dig-like queries are supported, see dig(1) .br Use TCP for the query (see \fIRFC 7766\fR) .PP +\fB--dnscrypt\fR +.br + Use DNSCrypt +.PP \fB-T\fR, \fB--tls\fR, \fB+tls\fR .br - Use DNS-over-TLS, implies \fB-t\fR (see \fIRFC 7858\fR) + Use DNS-over-TLS, implies \fB--tcp\fR (see \fIRFC 7858\fR) .PP \fB-H\fR.\& \fB--https\fR, \fB+https\fR .br @@ -155,7 +159,34 @@ Dig-like queries are supported, see dig(1) (Set, Unset) RA (Recursion Available) flag .PP .RE -.SS Output Options +.SS Output Display +.RS 4 +\fB--no-question\fR, \fB+noquestion\fR +.br + Do not display the Question section +.PP +\fB--no-answer\fR, \fB+noanswer\fR +.br + Do not display the Answer section +.PP +\fB--no-answer\fR, \fB+noanswer\fR +.br + Do not display the Answer section +.PP +\fB--no-authority\fR, \fB+noauthority\fR +.br + Do not display the Authority section +.PP +\fB--no-additional\fR, \fB+noadditional\fR +.br + Do not display the Additional section +.PP +\fB--no-statistics\fR, \fB+nostatistics\fR +.br + Do not display the Statistics (additional comments) section +.PP +.RE +.SS Output Formats .RS 4 \fB-j\fR, \fB--json\fR, \fB+json\fR .br diff --git a/doc/awl.1.md b/doc/awl.1.md index e6d3126..560ad6c 100644 --- a/doc/awl.1.md +++ b/doc/awl.1.md @@ -1,7 +1,7 @@ awl(1) # NAME -awl - drill, writ small +awl - DNS lookup tool # SYNOPSIS *awl* [ _OPTIONS_ ] _name_ [ _@server_ ] [ _type_ ]++ @@ -13,16 +13,16 @@ _type_ is the DNS resource type (*example: AAAA*) # DESCRIPTION -*awl* (*a*wls *w*ant *l*icorice) is a simple tool designed to make DNS queries, much -like the venerable _dig_(1). An awl is a tool used to make small holes, typically -used in tannery (leatherworking). +*awl* (*a*wls *w*ant *l*icorice) is a simple tool designed to make DNS queries, +much like the venerable _dig_(1). An awl is a tool used to make small holes, +typically used in tannery (leatherworking). -Written in Go, *awl* is designed to be a more "modern" version of _drill_(1) by -including some more RFCs and output options. *awl* is still heavily +*awl* is designed to be a more "modern" version of _drill_(1) by including +some more recent RFCs and output options. *awl* is still heavily Work-In-Progress so some features may get added or removed. # OPTIONS - Dig-like queries are supported, see dig(1) + Dig-like +[no]flags are supported, see dig(1) *-D*, *--dnssec*, *+dnssec*++ Enable DNSSEC. This needs to be manually enabled. @@ -63,11 +63,14 @@ _Default Ports_: *--no-truncate*, *+ignore*++ Ignore UDP truncation (by default, awl _retries with TCP_) - *-t*, *--tcp*, *+tcp*, *+vc*++ + *--tcp*, *+tcp*, *+vc*++ Use TCP for the query (see _RFC 7766_) + *--dnscrypt*++ + Use DNSCrypt + *-T*, *--tls*, *+tls*++ - Use DNS-over-TLS, implies *-t* (see _RFC 7858_) + Use DNS-over-TLS, implies *--tcp* (see _RFC 7858_) *-H*. *--https*, *+https*++ Use DNS-over-HTTPS (see _RFC 8484_) @@ -79,6 +82,9 @@ _Default Ports_: Do a reverse lookup. Sets default _type_ to PTR.++ *awl* automatically makes an IP or phone number canonical. + *--timeout* _seconds_ + Set the timeout period. Floating point numbers are accepted. + ## DNS Flags *--aa=[false]*, *+[no]aaflag*++ @@ -105,7 +111,26 @@ _Default Ports_: *--ra=[false]*, *+[no]raflag*++ (Set, Unset) RA (Recursion Available) flag -## Output Options +## Output Display + *--no-question*, *+noquestion*++ + Do not display the Question section + + *--no-answer*, *+noanswer*++ + Do not display the Answer section + + *--no-answer*, *+noanswer*++ + Do not display the Answer section + + *--no-authority*, *+noauthority*++ + Do not display the Authority section + + *--no-additional*, *+noadditional*++ + Do not display the Additional section + + *--no-statistics*, *+nostats*++ + Do not display the Statistics (additional comments) section + +## Output Formats *-j*, *--json*, *+json*++ Print the query results as JSON. @@ -116,7 +141,7 @@ _Default Ports_: Print the query results as YAML. *-s*, *--short*, *+short*++ - Print just the results. + Print just the address of the answer. # EXAMPLES ``` diff --git a/go.mod b/go.mod index af04dab..65665ad 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module git.froth.zone/sam/awl go 1.18 require ( + github.com/ameshkov/dnscrypt/v2 v2.2.3 github.com/lucas-clemente/quic-go v0.28.0 github.com/miekg/dns v1.1.50 github.com/stefansundin/go-zflag v1.1.1 @@ -12,6 +13,10 @@ require ( ) require ( + github.com/AdguardTeam/golibs v0.4.2 // indirect + github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect + github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect + github.com/ameshkov/dnsstamps v1.0.1 // indirect github.com/google/go-cmp v0.5.5 // indirect github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 // indirect ) diff --git a/go.sum b/go.sum index a4ef21f..751dcc7 100644 --- a/go.sum +++ b/go.sum @@ -7,7 +7,17 @@ dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBr dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +github.com/AdguardTeam/golibs v0.4.2 h1:7M28oTZFoFwNmp8eGPb3ImmYbxGaJLyQXeIFVHjME0o= +github.com/AdguardTeam/golibs v0.4.2/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= +github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw= +github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us= +github.com/ameshkov/dnscrypt/v2 v2.2.3 h1:X9UP5AHtwp46Ji+sGFfF/1Is6OPI/SjxLqhKpx0P5UI= +github.com/ameshkov/dnscrypt/v2 v2.2.3/go.mod h1:xJB9cE1/GF+NB6EEQqRlkoa4bjcV2w7VYn1G+zVq7Bs= +github.com/ameshkov/dnsstamps v1.0.1 h1:LhGvgWDzhNJh+kBQd/AfUlq1vfVe109huiXw4JhnPug= +github.com/ameshkov/dnsstamps v1.0.1/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= @@ -68,6 +78,7 @@ github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:Fecb github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -92,6 +103,7 @@ github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 h1:7m/WlWcSROrcK5NxuXaxYD32B github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= +github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -153,6 +165,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -169,6 +182,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -176,6 +190,7 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= @@ -191,6 +206,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -220,10 +237,12 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/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-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -256,6 +275,7 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -302,6 +322,7 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/internal/helpers/query.go b/internal/helpers/query.go index a8247f8..a63f3d7 100644 --- a/internal/helpers/query.go +++ b/internal/helpers/query.go @@ -16,8 +16,9 @@ type Response struct { // A structure for a DNS query. type Request struct { - Server string `json:"server"` // The server to make the DNS request from - Type uint16 `json:"request"` // The type of request - Class uint16 `json:"class"` // DNS Class - Name string `json:"name"` // The domain name to make a DNS request for + Server string `json:"server"` // The server to make the DNS request from + Type uint16 `json:"request"` // The type of request + Class uint16 `json:"class"` // DNS Class + Name string `json:"name"` // The domain name to make a DNS request for + Timeout time.Duration // The maximum timeout } diff --git a/internal/structs/query.go b/internal/structs/query.go deleted file mode 100644 index eb743be..0000000 --- a/internal/structs/query.go +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause - -package structs - -import ( - "time" - - "github.com/miekg/dns" -) - -// The DNS response -type Response struct { - DNS *dns.Msg // The full DNS response - RTT time.Duration `json:"rtt"` // The time it took to make the DNS query -} - -// A structure for a DNS query -type Request struct { - Server string `json:"server"` // The server to make the DNS request from - Type uint16 `json:"request"` // The type of request - Class uint16 `json:"class"` // DNS Class - Name string `json:"name"` // The domain name to make a DNS request for -} diff --git a/main.go b/main.go index a4d88ea..9583128 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "os" + "strconv" "strings" "time" @@ -36,6 +37,7 @@ func main() { } switch { case opts.JSON: + opts.Logger.Info("Printing as JSON") json, err := json.MarshalIndent(resp.DNS, "", " ") if err != nil { opts.Logger.Fatal(err) @@ -43,6 +45,7 @@ func main() { } fmt.Println(string(json)) case opts.XML: + opts.Logger.Info("Printing as XML") xml, err := xml.MarshalIndent(resp.DNS, "", " ") if err != nil { opts.Logger.Fatal(err) @@ -50,6 +53,7 @@ func main() { } fmt.Println(string(xml)) case opts.YAML: + opts.Logger.Info("Printing as YAML") yaml, err := yaml.Marshal(resp.DNS) if err != nil { opts.Logger.Fatal(err) @@ -59,11 +63,49 @@ func main() { default: if !opts.Short { // Print everything + + if !opts.Display.Question { + resp.DNS.Question = nil + opts.Logger.Info("Disabled question display") + } + if !opts.Display.Answer { + resp.DNS.Answer = nil + opts.Logger.Info("Disabled answer display") + } + if !opts.Display.Authority { + resp.DNS.Ns = nil + opts.Logger.Info("Disabled authority display") + } + if !opts.Display.Additional { + resp.DNS.Extra = nil + opts.Logger.Info("Disabled additional display") + } + fmt.Println(resp.DNS) - fmt.Println(";; Query time:", resp.RTT) - fmt.Println(";; SERVER:", opts.Request.Server) - fmt.Println(";; WHEN:", time.Now().Format(time.RFC1123Z)) - fmt.Println(";; MSG SIZE rcvd:", resp.DNS.Len()) + + if opts.Display.Statistics { + fmt.Println(";; Query time:", resp.RTT) + + // Add extra information to server string + var extra string + switch { + case opts.TCP: + extra = ":" + strconv.Itoa(opts.Port) + " (TCP)" + case opts.TLS: + extra = ":" + strconv.Itoa(opts.Port) + " (TLS)" + case opts.HTTPS, opts.DNSCrypt: + extra = "" + case opts.QUIC: + extra = ":" + strconv.Itoa(opts.Port) + " (QUIC)" + default: + extra = ":" + strconv.Itoa(opts.Port) + " (UDP)" + } + + fmt.Println(";; SERVER:", opts.Request.Server+extra) + fmt.Println(";; WHEN:", time.Now().Format(time.RFC1123Z)) + fmt.Println(";; MSG SIZE rcvd:", resp.DNS.Len()) + } + } else { // Print just the responses, nothing else for _, res := range resp.DNS.Answer { diff --git a/query/DNSCrypt.go b/query/DNSCrypt.go new file mode 100644 index 0000000..12751f8 --- /dev/null +++ b/query/DNSCrypt.go @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: BSD-3-Clause + +package query + +import ( + "time" + + "git.froth.zone/sam/awl/cli" + "git.froth.zone/sam/awl/internal/helpers" + + "github.com/ameshkov/dnscrypt/v2" + "github.com/miekg/dns" +) + +type DNSCryptResolver struct { + opts cli.Options +} + +func (r *DNSCryptResolver) LookUp(msg *dns.Msg) (helpers.Response, error) { + + client := dnscrypt.Client{ + Timeout: r.opts.Request.Timeout, + UDPSize: 1232, + } + + if r.opts.TCP || r.opts.TLS { + client.Net = "tcp" + } else { + client.Net = "udp" + } + + switch { + case r.opts.IPv4: + client.Net += "4" + case r.opts.IPv6: + client.Net += "6" + } + r.opts.Logger.Debug("Using", client.Net, "for making the request") + + resolverInf, err := client.Dial(r.opts.Request.Server) + if err != nil { + return helpers.Response{}, err + } + + now := time.Now() + res, err := client.Exchange(msg, resolverInf) + rtt := time.Since(now) + + if err != nil { + return helpers.Response{}, err + } + r.opts.Logger.Info("Request successful") + + return helpers.Response{ + DNS: res, + RTT: rtt, + }, nil +} diff --git a/query/DNSCrypt_test.go b/query/DNSCrypt_test.go new file mode 100644 index 0000000..126a073 --- /dev/null +++ b/query/DNSCrypt_test.go @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: BSD-3-Clause + +package query_test + +import ( + "testing" + + "git.froth.zone/sam/awl/cli" + "git.froth.zone/sam/awl/internal/helpers" + "git.froth.zone/sam/awl/query" + "git.froth.zone/sam/awl/util" + + "github.com/miekg/dns" + "gotest.tools/v3/assert" +) + +func TestDNSCrypt(t *testing.T) { + tests := []struct { + opt cli.Options + }{ + { + cli.Options{ + Logger: util.InitLogger(0), + DNSCrypt: true, + Request: helpers.Request{ + Server: "sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20", + Type: dns.TypeA, + Name: "example.com.", + }, + }, + }, + { + cli.Options{ + Logger: util.InitLogger(0), + DNSCrypt: true, + TCP: true, + IPv4: true, + Request: helpers.Request{ + Server: "sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20", + Type: dns.TypeAAAA, + Name: "example.com.", + }, + }, + }, + } + for _, test := range tests { + res, err := query.CreateQuery(test.opt) + assert.NilError(t, err) + assert.Assert(t, res != helpers.Response{}) + } + +} diff --git a/query/HTTPS.go b/query/HTTPS.go index bb8d925..b15dd81 100644 --- a/query/HTTPS.go +++ b/query/HTTPS.go @@ -15,22 +15,23 @@ import ( ) type HTTPSResolver struct { - server string - opts cli.Options + opts cli.Options } func (r *HTTPSResolver) LookUp(msg *dns.Msg) (helpers.Response, error) { var resp helpers.Response - httpR := &http.Client{} + httpR := &http.Client{ + Timeout: r.opts.Request.Timeout, + } buf, err := msg.Pack() if err != nil { return helpers.Response{}, err } r.opts.Logger.Debug("making DoH request") // query := server + "?dns=" + base64.RawURLEncoding.EncodeToString(buf) - req, err := http.NewRequest("POST", r.server, bytes.NewBuffer(buf)) + req, err := http.NewRequest("POST", r.opts.Request.Server, bytes.NewBuffer(buf)) if err != nil { - return helpers.Response{}, fmt.Errorf("DoH: %s", err.Error()) + return helpers.Response{}, fmt.Errorf("DoH: %w", err) } req.Header.Set("Content-Type", "application/dns-message") req.Header.Set("Accept", "application/dns-message") @@ -40,7 +41,7 @@ func (r *HTTPSResolver) LookUp(msg *dns.Msg) (helpers.Response, error) { resp.RTT = time.Since(now) if err != nil { - return helpers.Response{}, fmt.Errorf("DoH HTTP request error: %s", err.Error()) + return helpers.Response{}, fmt.Errorf("DoH HTTP request error: %w", err) } defer res.Body.Close() @@ -50,13 +51,13 @@ func (r *HTTPSResolver) LookUp(msg *dns.Msg) (helpers.Response, error) { fullRes, err := io.ReadAll(res.Body) if err != nil { - return helpers.Response{}, fmt.Errorf("DoH body read error: %s", err.Error()) + return helpers.Response{}, fmt.Errorf("DoH body read error: %w", err) } resp.DNS = &dns.Msg{} r.opts.Logger.Debug("unpacking response") err = resp.DNS.Unpack(fullRes) if err != nil { - return helpers.Response{}, fmt.Errorf("DoH dns message unpack error: %s", err.Error()) + return helpers.Response{}, fmt.Errorf("DoH dns message unpack error: %w", err) } return resp, nil diff --git a/query/HTTPS_test.go b/query/HTTPS_test.go index bb0a5f4..b6efae6 100644 --- a/query/HTTPS_test.go +++ b/query/HTTPS_test.go @@ -22,21 +22,18 @@ func TestResolveHTTPS(t *testing.T) { opts := cli.Options{ HTTPS: true, Logger: util.InitLogger(0), + Request: helpers.Request{ + Server: "https://dns9.quad9.net/dns-query", + Type: dns.TypeA, + Name: "git.froth.zone.", + }, } - testCase := helpers.Request{Server: "dns9.quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone"} - resolver, err := query.LoadResolver(testCase.Server, opts) + // testCase := helpers.Request{Server: "https://dns9.quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone."} + resolver, err := query.LoadResolver(opts) assert.NilError(t, err) - if !strings.HasPrefix(testCase.Server, "https://") { - testCase.Server = "https://" + testCase.Server - } - // if the domain is not canonical, make it canonical - if !strings.HasSuffix(testCase.Name, ".") { - testCase.Name = fmt.Sprintf("%s.", testCase.Name) - } - msg := new(dns.Msg) - msg.SetQuestion(testCase.Name, testCase.Type) + msg.SetQuestion(opts.Request.Name, opts.Request.Type) // msg = msg.SetQuestion(testCase.Name, testCase.Type) res, err := resolver.LookUp(msg) assert.NilError(t, err) @@ -51,7 +48,7 @@ func Test2ResolveHTTPS(t *testing.T) { } var err error testCase := helpers.Request{Server: "dns9.quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone"} - resolver, err := query.LoadResolver(testCase.Server, opts) + resolver, err := query.LoadResolver(opts) assert.NilError(t, err) msg := new(dns.Msg) msg.SetQuestion(testCase.Name, testCase.Type) @@ -69,14 +66,11 @@ func Test3ResolveHTTPS(t *testing.T) { } var err error testCase := helpers.Request{Server: "dns9..quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone."} - if !strings.HasPrefix(testCase.Server, "https://") { - testCase.Server = "https://" + testCase.Server - } // if the domain is not canonical, make it canonical if !strings.HasSuffix(testCase.Name, ".") { testCase.Name = fmt.Sprintf("%s.", testCase.Name) } - resolver, err := query.LoadResolver(testCase.Server, opts) + resolver, err := query.LoadResolver(opts) assert.NilError(t, err) msg := new(dns.Msg) msg.SetQuestion(testCase.Name, testCase.Type) @@ -85,3 +79,27 @@ func Test3ResolveHTTPS(t *testing.T) { assert.ErrorContains(t, err, "request error") assert.Equal(t, res, helpers.Response{}) } + +func Test404ResolveHTTPS(t *testing.T) { + t.Parallel() + var err error + opts := cli.Options{ + HTTPS: true, + Logger: util.InitLogger(0), + Request: helpers.Request{ + Server: "https://dns9.quad9.net/dns", + Type: dns.TypeA, + Name: "git.froth.zone.", + }, + } + // testCase := helpers.Request{Server: "https://dns9.quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone."} + resolver, err := query.LoadResolver(opts) + assert.NilError(t, err) + + msg := new(dns.Msg) + msg.SetQuestion(opts.Request.Name, opts.Request.Type) + // msg = msg.SetQuestion(testCase.Name, testCase.Type) + res, err := resolver.LookUp(msg) + assert.ErrorContains(t, err, "404") + assert.Equal(t, res, helpers.Response{}) +} diff --git a/query/QUIC.go b/query/QUIC.go index e9aa373..e9e5dd1 100644 --- a/query/QUIC.go +++ b/query/QUIC.go @@ -15,8 +15,7 @@ import ( ) type QUICResolver struct { - server string - opts cli.Options + opts cli.Options } func (r *QUICResolver) LookUp(msg *dns.Msg) (helpers.Response, error) { @@ -25,8 +24,12 @@ func (r *QUICResolver) LookUp(msg *dns.Msg) (helpers.Response, error) { MinVersion: tls.VersionTLS12, NextProtos: []string{"doq"}, } + + conf := new(quic.Config) + conf.HandshakeIdleTimeout = r.opts.Request.Timeout + r.opts.Logger.Debug("making DoQ request") - connection, err := quic.DialAddr(r.server, tls, nil) + connection, err := quic.DialAddr(r.opts.Request.Server, tls, conf) if err != nil { return helpers.Response{}, err } diff --git a/query/QUIC_test.go b/query/QUIC_test.go index 751d0b2..b8837d1 100644 --- a/query/QUIC_test.go +++ b/query/QUIC_test.go @@ -8,6 +8,7 @@ import ( "strconv" "strings" "testing" + "time" "git.froth.zone/sam/awl/cli" "git.froth.zone/sam/awl/internal/helpers" @@ -34,7 +35,7 @@ func TestQuic(t *testing.T) { for i := range testCases { switch i { case 0: - resolver, err := query.LoadResolver(testCases[i].Server, opts) + resolver, err := query.LoadResolver(opts) assert.NilError(t, err) // if the domain is not canonical, make it canonical if !strings.HasSuffix(testCase.Name, ".") { @@ -47,7 +48,7 @@ func TestQuic(t *testing.T) { assert.ErrorContains(t, err, "fully qualified") assert.Equal(t, res, helpers.Response{}) case 1: - resolver, err := query.LoadResolver(testCase2.Server, opts) + resolver, err := query.LoadResolver(opts) assert.NilError(t, err) testCase2.Server = net.JoinHostPort(testCase2.Server, strconv.Itoa(opts.Port)) // if the domain is not canonical, make it canonical @@ -63,3 +64,21 @@ func TestQuic(t *testing.T) { } } } + +func TestInvalidQuic(t *testing.T) { + t.Parallel() + opts := cli.Options{ + QUIC: true, + Logger: util.InitLogger(0), + Port: 853, + Request: helpers.Request{Server: "example.com", Type: dns.TypeA, Name: "git.froth.zone", Timeout: 10 * time.Millisecond}, + } + resolver, err := query.LoadResolver(opts) + assert.NilError(t, err) + + msg := new(dns.Msg) + msg.SetQuestion(opts.Request.Name, opts.Request.Type) + res, err := resolver.LookUp(msg) + assert.ErrorContains(t, err, "timeout") + assert.Equal(t, res, helpers.Response{}) +} diff --git a/query/general.go b/query/general.go index 2a29fab..68a5f8d 100644 --- a/query/general.go +++ b/query/general.go @@ -4,6 +4,7 @@ package query import ( "fmt" + "net" "git.froth.zone/sam/awl/cli" "git.froth.zone/sam/awl/internal/helpers" @@ -12,8 +13,7 @@ import ( ) type StandardResolver struct { - server string - opts cli.Options + opts cli.Options } func (r *StandardResolver) LookUp(msg *dns.Msg) (helpers.Response, error) { @@ -22,6 +22,9 @@ func (r *StandardResolver) LookUp(msg *dns.Msg) (helpers.Response, error) { err error ) dnsClient := new(dns.Client) + dnsClient.Dialer = &net.Dialer{ + Timeout: r.opts.Request.Timeout, + } if r.opts.TCP || r.opts.TLS { dnsClient.Net = "tcp" } else { @@ -38,11 +41,13 @@ func (r *StandardResolver) LookUp(msg *dns.Msg) (helpers.Response, error) { if r.opts.TLS { dnsClient.Net += "-tls" } + r.opts.Logger.Debug("Using", dnsClient.Net, "for making the request") - resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, r.server) + resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, r.opts.Request.Server) if err != nil { return helpers.Response{}, err } + r.opts.Logger.Info("Request successful") if resp.DNS.MsgHdr.Truncated && !r.opts.Truncate { fmt.Printf(";; Truncated, retrying with TCP\n\n") @@ -53,7 +58,7 @@ func (r *StandardResolver) LookUp(msg *dns.Msg) (helpers.Response, error) { case r.opts.IPv4: dnsClient.Net += "6" } - resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, r.server) + resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, r.opts.Request.Server) } return resp, err diff --git a/query/general_test.go b/query/general_test.go index 2b8d82f..5b080b9 100644 --- a/query/general_test.go +++ b/query/general_test.go @@ -14,7 +14,6 @@ import ( ) func TestResolve(t *testing.T) { - t.Parallel() opts := cli.Options{ Logger: util.InitLogger(0), Port: 53, @@ -24,7 +23,7 @@ func TestResolve(t *testing.T) { Name: "example.com.", }, } - resolver, err := query.LoadResolver(opts.Request.Server, opts) + resolver, err := query.LoadResolver(opts) assert.NilError(t, err) msg := new(dns.Msg) msg.SetQuestion(opts.Request.Name, opts.Request.Type) @@ -34,9 +33,9 @@ func TestResolve(t *testing.T) { } func TestTruncate(t *testing.T) { - t.Parallel() opts := cli.Options{ Logger: util.InitLogger(0), + IPv4: true, Port: 5301, Request: helpers.Request{ Server: "madns.binarystar.systems", @@ -44,7 +43,7 @@ func TestTruncate(t *testing.T) { Name: "limit.txt.example.", }, } - resolver, err := query.LoadResolver(opts.Request.Server, opts) + resolver, err := query.LoadResolver(opts) assert.NilError(t, err) msg := new(dns.Msg) msg.SetQuestion(opts.Request.Name, opts.Request.Type) @@ -52,3 +51,39 @@ func TestTruncate(t *testing.T) { assert.NilError(t, err) assert.Assert(t, res != helpers.Response{}) } + +func TestResolveAgain(t *testing.T) { + tests := []struct { + opt cli.Options + }{ + { + cli.Options{ + Logger: util.InitLogger(0), + TCP: true, + Port: 53, + Request: helpers.Request{ + Server: "8.8.4.4", + Type: dns.TypeA, + Name: "example.com.", + }, + }, + }, + { + cli.Options{ + Logger: util.InitLogger(0), + Port: 53, + Request: helpers.Request{ + Server: "8.8.4.4", + Type: dns.TypeAAAA, + Name: "example.com.", + }, + }, + }, + } + for _, test := range tests { + res, err := query.CreateQuery(test.opt) + assert.NilError(t, err) + assert.Assert(t, res != helpers.Response{}) + } + +} diff --git a/query/query.go b/query/query.go index f069113..4111207 100644 --- a/query/query.go +++ b/query/query.go @@ -3,6 +3,8 @@ package query import ( + "fmt" + "git.froth.zone/sam/awl/cli" "git.froth.zone/sam/awl/internal/helpers" @@ -28,10 +30,13 @@ func CreateQuery(opts cli.Options) (helpers.Response, error) { res.DNS.SetEdns0(1232, true) } - resolver, err := LoadResolver(opts.Request.Server, opts) + opts.Logger.Debug(fmt.Sprintf("%+v", res)) + + resolver, err := LoadResolver(opts) if err != nil { return helpers.Response{}, err } + opts.Logger.Info("Query successfully loaded") return resolver.LookUp(res.DNS) } diff --git a/query/resolver.go b/query/resolver.go index e5484fd..7c035ee 100644 --- a/query/resolver.go +++ b/query/resolver.go @@ -16,30 +16,35 @@ type Resolver interface { LookUp(*dns.Msg) (helpers.Response, error) } -func LoadResolver(server string, opts cli.Options) (Resolver, error) { +func LoadResolver(opts cli.Options) (Resolver, error) { switch { case opts.HTTPS: - opts.Logger.Debug("loading DoH resolver") - if !strings.HasPrefix(server, "https://") { - server = "https://" + server + opts.Logger.Info("loading DNS-over-HTTPS resolver") + if !strings.HasPrefix(opts.Request.Server, "https://") { + opts.Request.Server = "https://" + opts.Request.Server } return &HTTPSResolver{ - server: server, - opts: opts, + opts: opts, }, nil case opts.QUIC: - opts.Logger.Debug("loading DoQ resolver") - server = net.JoinHostPort(opts.Request.Server, strconv.Itoa(opts.Port)) + opts.Logger.Info("loading DNS-over-QUIC resolver") + opts.Request.Server = net.JoinHostPort(opts.Request.Server, strconv.Itoa(opts.Port)) return &QUICResolver{ - server: server, - opts: opts, + opts: opts, + }, nil + case opts.DNSCrypt: + opts.Logger.Info("loading DNSCrypt resolver") + if !strings.HasPrefix(opts.Request.Server, "sdns://") { + opts.Request.Server = "sdns://" + opts.Request.Server + } + return &DNSCryptResolver{ + opts: opts, }, nil default: - opts.Logger.Debug("loading standard/DoT resolver") - server = net.JoinHostPort(opts.Request.Server, strconv.Itoa(opts.Port)) + opts.Logger.Info("loading standard/DNS-over-TLS resolver") + opts.Request.Server = net.JoinHostPort(opts.Request.Server, strconv.Itoa(opts.Port)) return &StandardResolver{ - server: server, - opts: opts, + opts: opts, }, nil } }