diff --git a/.goreleaser.yaml b/.goreleaser.yaml index ff26ef9..da446bc 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -13,9 +13,22 @@ builds: - linux - windows - darwin + - freebsd goarch: - amd64 + - arm - arm64 + ignore: + # Windows on ARM, maybe someday + - goos: windows + goarch: arm64 + - goos: windows + goarch: arm + + - goos: darwin + goarch: arm + - goos: freebsd + goarch: arm universal_binaries: - replace: true diff --git a/GNUmakefile b/GNUmakefile index 5e7fab3..66aacd1 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -9,6 +9,13 @@ else EXE := $(PROG) endif +doc/$(PROG).1: doc/$(PROG).1.scd + $(SCDOC) <$< >$@ + +doc/wiki/$(PROG).1.md: doc/$(PROG).1 + pandoc --from man --to gfm -o $@ $< + + ## install: installs awl .PHONY: install ifeq ($(OS),Windows_NT) diff --git a/Makefile b/Makefile index d8d8c68..b5483dd 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,12 @@ include template.mk EXE := $(PROG) +doc/$(PROG).1: doc/$(PROG).1.scd + $(SCDOC) $@ + +doc/wiki/$(PROG).1.md: doc/$(PROG).1 + pandoc --from man --to gfm -o $@ doc/$(PROG).1 + ## install: installs awl .PHONY: install install: all diff --git a/cli/cli.go b/cli/cli.go index f002afe..e093ece 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -62,7 +62,8 @@ func ParseCLI(args []string, version string) (util.Options, error) { subnet = flag.String("subnet", "", "set EDNS client subnet") padding = flag.Bool("pad", false, "set EDNS padding") - truncate = flag.Bool("no-truncate", false, "ignore truncation if a UDP request truncates (default: retry with TCP)") + badCookie = flag.Bool("no-bad-cookie", false, "ignore BADCOOKIE EDNS responses (default: retry with correct cookie") + truncate = flag.Bool("no-truncate", false, "ignore truncation if a UDP request truncates (default: retry with TCP)") tcp = flag.Bool("tcp", false, "use TCP") dnscrypt = flag.Bool("dnscrypt", false, "use DNSCrypt") @@ -126,6 +127,7 @@ func ParseCLI(args []string, version string) (util.Options, error) { HTTPS: *https, QUIC: *quic, Truncate: *truncate, + BadCookie: *badCookie, Reverse: *reverse, JSON: *json, XML: *xml, diff --git a/cli/dig.go b/cli/dig.go index 00ee8a6..5ac8477 100644 --- a/cli/dig.go +++ b/cli/dig.go @@ -73,6 +73,8 @@ func ParseDig(arg string, opts *util.Options) error { opts.TCP = isNo case "ignore": opts.Truncate = isNo + case "badcookie": + opts.BadCookie = !isNo case "tls": opts.TLS = isNo case "dnscrypt": diff --git a/cli/dig_test.go b/cli/dig_test.go index 5293bb2..572872e 100644 --- a/cli/dig_test.go +++ b/cli/dig_test.go @@ -40,6 +40,7 @@ func FuzzDig(f *testing.F) { "tries=2", "tries=b", "tries", "tcp", "vc", "notcp", "novc", "ignore", "noignore", + "badcookie", "nobadcookie", "tls", "notls", "dnscrypt", "nodnscrypt", "https", "nohttps", diff --git a/cli/misc.go b/cli/misc.go index 85228fd..7a14854 100644 --- a/cli/misc.go +++ b/cli/misc.go @@ -29,7 +29,7 @@ func ParseMiscArgs(args []string, opts *util.Options) error { switch { case strings.HasPrefix(arg, "tls://"): opts.TLS = true - opts.Request.Server = strings.TrimPrefix(opts.Request.Server, "tls://") + opts.Request.Server = strings.TrimPrefix(arg, "tls://") opts.Logger.Info("DNS-over-TLS implicitly set") case strings.HasPrefix(arg, "https://"): opts.HTTPS = true @@ -37,12 +37,18 @@ func ParseMiscArgs(args []string, opts *util.Options) error { opts.Logger.Info("DNS-over-HTTPS implicitly set") case strings.HasPrefix(arg, "quic://"): opts.QUIC = true - opts.Request.Server = strings.TrimPrefix(opts.Request.Server, "quic://") + opts.Request.Server = strings.TrimPrefix(arg, "quic://") 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") + case strings.HasPrefix(arg, "tcp://"): + opts.TCP = true + opts.Request.Server = strings.TrimPrefix(arg, "udp://") + opts.Logger.Info("TCP implicitly set") + case strings.HasPrefix(arg, "udp://"): + opts.Request.Server = strings.TrimPrefix(arg, "udp://") default: opts.Request.Server = arg } @@ -117,7 +123,7 @@ func ParseMiscArgs(args []string, opts *util.Options) error { if err != nil { // :^) - opts.Logger.Warn("Could not query system for server. Using localhost") + opts.Logger.Warn("Could not query system for server. Using localhost\n", "Error:", err) opts.Request.Server = "127.0.0.1" } else { // Make sure that if IPv4 or IPv6 is asked for it actually uses it diff --git a/cli/misc_test.go b/cli/misc_test.go index 286e0e3..c3f60c3 100644 --- a/cli/misc_test.go +++ b/cli/misc_test.go @@ -127,6 +127,8 @@ func TestFlagSetting(t *testing.T) { {[]string{"@tls://dns.google"}}, {[]string{"@https://dns.cloudflare.com/dns-query"}}, {[]string{"@quic://dns.adguard.com"}}, + {[]string{"@tcp://dns.froth.zone"}}, + {[]string{"@udp://dns.example.com"}}, } for i, test := range tests { @@ -147,6 +149,10 @@ func TestFlagSetting(t *testing.T) { assert.Assert(t, opts.HTTPS) case 3: assert.Assert(t, opts.QUIC) + case 4: + assert.Assert(t, opts.TCP) + case 5: + assert.Assert(t, true) } }) } diff --git a/completions/zsh.zsh b/completions/zsh.zsh index 8463dbf..64afd0c 100644 --- a/completions/zsh.zsh +++ b/completions/zsh.zsh @@ -13,6 +13,7 @@ local -a alts args '*+'{no,}'aaonly[set aa flag in the query]' '*+'{no,}'additional[print additional section of a reply]' '*+'{no,}'adflag[set the AD (authentic data) bit in the query]' + '*+'{no,}'badcookie[retry BADCOOKIE responses]' '*+'{no,}'cdflag[set the CD (checking disabled) bit in the query]' '*+'{no,}'cookie[add a COOKIE option to the request]' '*+edns=[specify EDNS version for query]:version (0-255)' diff --git a/doc/awl.1.scd b/doc/awl.1.scd index bc0110a..de88719 100644 --- a/doc/awl.1.scd +++ b/doc/awl.1.scd @@ -80,6 +80,9 @@ Anything in [brackets] is optional. *--no-truncate*, *+ignore* Ignore UDP truncation (by default, awl *retries with TCP*). +*--no-bad-cookie*, *+[no]badcookie* + \[Do not\] ignore BADCOOKIE responses + *--tcp*, *+tcp*, *+vc* Use TCP for the query (see RFC 7766). diff --git a/doc/wiki b/doc/wiki index 6f3070f..d41f619 160000 --- a/doc/wiki +++ b/doc/wiki @@ -1 +1 @@ -Subproject commit 6f3070f5933d0cc48bc8bb5290a1d0cc1825cd75 +Subproject commit d41f6197e638d7f0c0b203bd8b7f1fa0fc4b7f97 diff --git a/logawl/docs.go b/logawl/docs.go index fa0d5f9..d9940e3 100644 --- a/logawl/docs.go +++ b/logawl/docs.go @@ -14,7 +14,7 @@ because awl is a cli utility it writes directly to std err. // You can call specific logging levels from your new logger using // // logger.Debug("Message to log") -// logger.Fatal("Message to log") +// logger.Warning("Message to log") // logger.Info("Message to log") // logger.Error("Message to log") // diff --git a/query/general.go b/query/general.go index 8e0973b..6f4c6fa 100644 --- a/query/general.go +++ b/query/general.go @@ -52,13 +52,31 @@ func (r *StandardResolver) LookUp(msg *dns.Msg) (util.Response, error) { } } - r.opts.Logger.Debug("Using", dnsClient.Net, "for making the request") + r.opts.Logger.Info("Using", dnsClient.Net, "for making the request") resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, r.opts.Request.Server) if err != nil { return util.Response{}, fmt.Errorf("standard: DNS exchange: %w", err) } + switch dns.RcodeToString[resp.DNS.MsgHdr.Rcode] { + case "BADCOOKIE": + if !r.opts.BadCookie { + fmt.Printf(";; BADCOOKIE, retrying.\n\n") + + msg.Extra = resp.DNS.Extra + + resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, r.opts.Request.Server) + + if err != nil { + return util.Response{}, fmt.Errorf("badcookie: DNS exchange: %w", err) + } + } + + case "NOERR": + break + } + r.opts.Logger.Info("Request successful") if resp.DNS.MsgHdr.Truncated && !r.opts.Truncate { diff --git a/query/general_test.go b/query/general_test.go index fe06c12..396e87b 100644 --- a/query/general_test.go +++ b/query/general_test.go @@ -19,7 +19,7 @@ func TestResolve(t *testing.T) { Logger: util.InitLogger(0), Request: util.Request{ Server: "8.8.4.1", - Port: 53, + Port: 1, Type: dns.TypeA, Name: "example.com.", Timeout: time.Second / 2, diff --git a/query/query.go b/query/query.go index 6bccb34..1bca238 100644 --- a/query/query.go +++ b/query/query.go @@ -35,64 +35,64 @@ func CreateQuery(opts util.Options) (util.Response, error) { // EDNS time :) if opts.EDNS.EnableEDNS { - o := new(dns.OPT) - o.Hdr.Name = "." - o.Hdr.Rrtype = dns.TypeOPT + edns := new(dns.OPT) + edns.Hdr.Name = "." + edns.Hdr.Rrtype = dns.TypeOPT - o.SetVersion(opts.EDNS.Version) + edns.SetVersion(opts.EDNS.Version) if opts.EDNS.Cookie { - e := new(dns.EDNS0_COOKIE) - e.Code = dns.EDNS0COOKIE - e.Cookie = uniuri.NewLenChars(8, []byte("1234567890abcdef")) - o.Option = append(o.Option, e) + cookie := new(dns.EDNS0_COOKIE) + cookie.Code = dns.EDNS0COOKIE + cookie.Cookie = uniuri.NewLenChars(16, []byte("1234567890abcdef")) + edns.Option = append(edns.Option, cookie) - opts.Logger.Info("Setting EDNS cookie to", e.Cookie) + opts.Logger.Info("Setting EDNS cookie to", cookie.Cookie) } if opts.EDNS.Expire { - o.Option = append(o.Option, new(dns.EDNS0_EXPIRE)) + edns.Option = append(edns.Option, new(dns.EDNS0_EXPIRE)) opts.Logger.Info("Setting EDNS Expire option") } if opts.EDNS.KeepOpen { - o.Option = append(o.Option, new(dns.EDNS0_TCP_KEEPALIVE)) + edns.Option = append(edns.Option, new(dns.EDNS0_TCP_KEEPALIVE)) opts.Logger.Info("Setting EDNS TCP Keepalive option") } if opts.EDNS.Nsid { - o.Option = append(o.Option, new(dns.EDNS0_NSID)) + edns.Option = append(edns.Option, new(dns.EDNS0_NSID)) opts.Logger.Info("Setting EDNS NSID option") } if opts.EDNS.Padding { - o.Option = append(o.Option, new(dns.EDNS0_PADDING)) + edns.Option = append(edns.Option, new(dns.EDNS0_PADDING)) opts.Logger.Info("Setting EDNS padding") } - o.SetUDPSize(opts.EDNS.BufSize) + edns.SetUDPSize(opts.EDNS.BufSize) opts.Logger.Info("EDNS UDP buffer set to", opts.EDNS.BufSize) - o.SetZ(opts.EDNS.ZFlag) + edns.SetZ(opts.EDNS.ZFlag) opts.Logger.Info("EDNS Z flag set to", opts.EDNS.ZFlag) if opts.EDNS.DNSSEC { - o.SetDo() + edns.SetDo() opts.Logger.Info("EDNS DNSSEC OK set") } if opts.EDNS.Subnet.Address != nil { - o.Option = append(o.Option, &opts.EDNS.Subnet) + edns.Option = append(edns.Option, &opts.EDNS.Subnet) } - req.Extra = append(req.Extra, o) + req.Extra = append(req.Extra, edns) } else if opts.EDNS.DNSSEC { req.SetEdns0(1232, true) opts.Logger.Warn("DNSSEC implies EDNS, EDNS enabled") diff --git a/template.mk b/template.mk index 0223929..41e441e 100644 --- a/template.mk +++ b/template.mk @@ -28,12 +28,6 @@ all: $(PROG) doc/$(PROG).1 $(PROG): $(SOURCES) $(GO) build -o $(EXE) $(GOFLAGS) . -doc/$(PROG).1: doc/$(PROG).1.scd - $(SCDOC) <$< >$@ - -doc/wiki/$(PROG).1.md: doc/$(PROG).1 - pandoc --from man --to gfm -o $@ $< - ## update_doc: update documentation (requires pandoc) update_doc: doc/wiki/$(PROG).1.md diff --git a/util/options.go b/util/options.go index c406801..1abd2f4 100644 --- a/util/options.go +++ b/util/options.go @@ -27,6 +27,8 @@ type Options struct { Display Display // Ignore Truncation Truncate bool `json:"ignoreTruncate" example:"false"` + // Ignore BADCOOKIE + BadCookie bool `json:"ignoreBadCookie" example:"false"` // Print only the answer Short bool `json:"short" example:"false"` // When Short is true, display where the query came from