From 1b5d5a3fedb88eb7c41c41eb511a657dfb1e42e6 Mon Sep 17 00:00:00 2001 From: Sam Therapy Date: Wed, 20 Jul 2022 23:14:15 +0200 Subject: [PATCH 01/17] Do "a few things" Signed-off-by: Sam Therapy --- .drone.jsonnet | 53 ++++- .gitignore | 3 +- .goreleaser.yaml | 39 ++++ Makefile | 24 ++- README.md | 24 +-- awl.go | 17 -- cli.go | 216 ------------------- cli/cli.go | 135 ++++++++++++ cli/cli_test.go | 159 ++++++++++++++ cli/dig.go | 57 +++++ cli/docs.go | 5 + cli/misc.go | 122 +++++++++++ cli/options.go | 41 ++++ cli_test.go | 126 ----------- conf/plan9_test.go | 1 + doc/awl.1 | 168 +++++++++++++++ doc/awl.1.md | 115 ++++++++++ go.mod | 7 +- go.sum | 39 +--- internal/structs/query.go | 23 ++ logawl/logger.go | 24 ++- logawl/logging_test.go | 34 +-- main.go | 71 ++++++ query.go | 199 ----------------- query/HTTPS.go | 26 +-- query/HTTPS_test.go | 89 ++++++++ query/QUIC.go | 32 +-- query/QUIC_test.go | 67 ++++++ query/general.go | 60 ++++++ query/query.go | 34 +++ query/query_test.go | 129 ----------- query/resolver.go | 65 ++---- util/logger.go | 9 +- util/{helpers.go => reverseDNS.go} | 0 util/{helpers_test.go => reverseDNS_test.go} | 6 +- 35 files changed, 1363 insertions(+), 856 deletions(-) create mode 100644 .goreleaser.yaml delete mode 100644 awl.go delete mode 100644 cli.go create mode 100644 cli/cli.go create mode 100644 cli/cli_test.go create mode 100644 cli/dig.go create mode 100644 cli/docs.go create mode 100644 cli/misc.go create mode 100644 cli/options.go delete mode 100644 cli_test.go create mode 100644 doc/awl.1 create mode 100644 doc/awl.1.md create mode 100644 internal/structs/query.go create mode 100644 main.go delete mode 100644 query.go create mode 100644 query/HTTPS_test.go create mode 100644 query/QUIC_test.go create mode 100644 query/general.go create mode 100644 query/query.go delete mode 100644 query/query_test.go rename util/{helpers.go => reverseDNS.go} (100%) rename util/{helpers_test.go => reverseDNS_test.go} (93%) diff --git a/.drone.jsonnet b/.drone.jsonnet index 7ac857c..0bcb4dd 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -1,22 +1,67 @@ -local pipeline(version, arch) = { +local testing(version, arch) = { kind: "pipeline", name: version + "-" + arch , platform: { arch: arch }, steps: [ + { + name: "lint", + image: "rancher/drone-golangci-lint:latest" + }, { name: "test", image: "golang:" + version, commands: [ - "go test ./..." + "go test -race -v ./... -cover" ] + }, + ] +}; + +// "Inspired by" https://goreleaser.com/ci/drone/ +local release() = { + kind: "pipeline", + name: "release", + trigger: { + event: "tag" + }, + steps: [ + { + name: "fetch", + image: "docker:git", + commands : [ + "git fetch --tags" + ] + }, + { + name: "test", + image: "golang", + commands: [ + "go test -race -v ./... -cover" + ] + }, + { + name: "release", + image: "goreleaser/goreleaser", + environment: { + "GITEA_TOKEN": { + from_secret: "GITEA_TOKEN" + } + }, + commands: [ + "goreleaser release" + ], + // when: { + // event: "tag" + // } } ] }; // logawl uses generics so 1.18 is the minimum [ - pipeline("1.18", "amd64"), - pipeline("1.18", "arm64"), + testing("1.18", "amd64"), + testing("1.18", "arm64"), + release() ] \ No newline at end of file diff --git a/.gitignore b/.gitignore index 682e852..356b47d 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,5 @@ # vendor/ # Go workspace file -go.work \ No newline at end of file +go.work +dist/ diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..88b95e2 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,39 @@ +# This is an example .goreleaser.yml file with some sensible defaults. +# Make sure to check the documentation at https://goreleaser.com +before: + hooks: + # You may remove this if you don't use go modules. + - go mod tidy + # you may remove this if you don't need go generate + # - go generate ./... +builds: + - env: + - CGO_ENABLED=0 + goos: + - linux + - windows + - darwin + goarch: + - amd64 + - arm64 +universal_binaries: +- replace: true +archives: + - replacements: + darwin: macOS + linux: Linux + windows: Windows + amd64: x86_64 + format_overrides: + - goos: windows + format: zip +checksum: + name_template: 'checksums.txt' +snapshot: + name_template: "{{ incpatch .Version }}-next" +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' diff --git a/Makefile b/Makefile index f2ca423..d9bb0e7 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,12 @@ -GO:=go -GOFLAGS:= -ldflags '-s -w' -PREFIX:=/usr/local -BINPATH=$(PREFIX)/bin +# This should only be used for dev environments +GO := go +GOFLAGS := -ldflags '-s -w' +PREFIX := /usr/local +BINPATH = $(PREFIX)/bin +export CGO_ENABLED=1 +export CC=gcc + +.PHONY: clean doc # hehe all: awl @@ -9,19 +14,22 @@ all: awl awl: . $(GO) build -o awl $(GOFLAGS) . +doc: + scdoc < doc/awl.1.md > doc/awl.1 + test: - $(GO) test ./... + $(GO) test -race -v ./... -cover fmt: - $(GO) fmt + gofmt -w -s . vet: - $(GO) vet + $(GO) vet ./... lint: fmt vet install: awl - install awl $(BINPATH) || echo "You probably need to run `sudo make install`" + install awl $(BINPATH) clean: $(GO) clean \ No newline at end of file diff --git a/README.md b/README.md index 7e37b9c..7aedb20 100644 --- a/README.md +++ b/README.md @@ -12,24 +12,14 @@ This was made as my first major experiment with Go, so there are probably things The excellent [dns](https://github.com/miekg/dns) library for Go does most of the heavy lifting. -## What works +## What awl should do -- UDP -- TCP -- TLS -- HTTPS (maybe) -- QUIC (extreme maybe) - -## What doesn't - -- Your sanity after reading my awful code -- A motivation for making this after finding q and doggo - -## What should change - -- Make the CLI less abysmal (migrate to [cobra](https://github.com/spf13/cobra)? - or just use stdlib's flags) - Optimize everything - Make the code less spaghetti (partially completed) - Feature parity with drill - - Making a drop-in replacement for drill? \ No newline at end of file + - Making a drop-in replacement for drill? + - What about dig? + +## What awl won't do +- Print to files or read from files +- Colour outputs (unless?) diff --git a/awl.go b/awl.go deleted file mode 100644 index 6c5af9c..0000000 --- a/awl.go +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause - -package main - -import ( - "fmt" - "os" -) - -func main() { - app := prepareCLI() - err := app.Run(os.Args) - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } -} diff --git a/cli.go b/cli.go deleted file mode 100644 index e810161..0000000 --- a/cli.go +++ /dev/null @@ -1,216 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause - -package main - -import ( - "fmt" - "math/rand" - "runtime" - "strings" - - "git.froth.zone/sam/awl/conf" - "git.froth.zone/sam/awl/query" - - "github.com/miekg/dns" - "github.com/urfave/cli/v2" - "golang.org/x/net/idna" -) - -// Do all the magic CLI crap -func prepareCLI() *cli.App { - // Custom version string - cli.VersionPrinter = func(c *cli.Context) { - fmt.Printf("%s version %s, built with %s\n", c.App.Name, c.App.Version, runtime.Version()) - } - - cli.VersionFlag = &cli.BoolFlag{ - Name: "v", - Usage: "show version and exit", - } - - cli.HelpFlag = &cli.BoolFlag{ - Name: "h", - Usage: "show this help and exit", - } - - // Hack to get rid of the annoying default on the CLI - oldFlagStringer := cli.FlagStringer - cli.FlagStringer = func(f cli.Flag) string { - return strings.TrimSuffix(oldFlagStringer(f), " (default: false)") - } - - cli.AppHelpTemplate = `{{.Name}} - {{.Usage}} - - Usage: {{.HelpName}} name [@server] [record] - can be a name or an IP address - defaults to A - - arguments can be in any order - {{if .VisibleFlags}} - Options: - {{range .VisibleFlags}}{{.}} - {{end}}{{end}}` - app := &cli.App{ - Name: "awl", - Usage: "drill, writ small", - Version: "v0.2.1", - Flags: []cli.Flag{ - &cli.IntFlag{ - Name: "port", - Aliases: []string{"p"}, - Usage: "`` to make DNS query", - DefaultText: "53 over plain TCP/UDP, 853 over TLS or QUIC", - }, - &cli.BoolFlag{ - Name: "4", - Usage: "force IPv4", - }, - &cli.BoolFlag{ - Name: "6", - Usage: "force IPv6", - }, - &cli.BoolFlag{ - Name: "dnssec", - Aliases: []string{"D"}, - Usage: "enable DNSSEC", - }, - &cli.BoolFlag{ - Name: "json", - Aliases: []string{"j"}, - Usage: "return the result(s) as JSON", - }, - &cli.BoolFlag{ - Name: "short", - Aliases: []string{"s"}, - Usage: "print just the results, equivalent to dig +short", - }, - &cli.BoolFlag{ - Name: "tcp", - Aliases: []string{"t"}, - Usage: "use TCP (default: use UDP)", - }, - &cli.BoolFlag{ - Name: "tls", - Aliases: []string{"T"}, - Usage: "use DNS-over-TLS", - }, - &cli.BoolFlag{ - Name: "https", - Aliases: []string{"H"}, - Usage: "use DNS-over-HTTPS", - }, - &cli.BoolFlag{ - Name: "quic", - Aliases: []string{"Q"}, - Usage: "use DNS-over-QUIC", - }, - &cli.BoolFlag{ - Name: "no-truncate", - Usage: "ignore truncation if a UDP request truncates (default: retry with TCP)", - }, - &cli.BoolFlag{ - Name: "aa", - Usage: "set AA (Authoratative Answer) flag (default: not set)", - }, - &cli.BoolFlag{ - Name: "tc", - Usage: "set TC (TrunCated) flag (default: not set)", - }, - &cli.BoolFlag{ - Name: "z", - Usage: "set Z (Zero) flag (default: not set)", - }, - &cli.BoolFlag{ - Name: "cd", - Usage: "set CD (Checking Disabled) flag (default: not set)", - }, - &cli.BoolFlag{ - Name: "no-rd", - Usage: "UNset RD (Recursion Desired) flag (default: set)", - }, - &cli.BoolFlag{ - Name: "no-ra", - Usage: "UNset RA (Recursion Available) flag (default: set)", - }, - &cli.BoolFlag{ - Name: "reverse", - Aliases: []string{"x"}, - Usage: "do a reverse lookup", - }, - &cli.BoolFlag{ - Name: "debug", - Usage: "enable verbose logging", - }, - }, - Action: doQuery, - } - return app -} - -// Parse the wildcard arguments, drill style -func parseArgs(args []string, opts query.Options) (query.Answers, error) { - var ( - resp query.Response - err error - ) - for _, arg := range args { - r, ok := dns.StringToType[strings.ToUpper(arg)] - switch { - // If it starts with @, it's a DNS server - case strings.HasPrefix(arg, "@"): - resp.Answers.Server = strings.Split(arg, "@")[1] - case strings.Contains(arg, "."): - resp.Answers.Name, err = idna.ToUnicode(arg) - if err != nil { - return query.Answers{}, err - } - case ok: - // If it's a DNS request, it's a DNS request (obviously) - resp.Answers.Request = r - default: - //else, assume it's a name - resp.Answers.Name, err = idna.ToUnicode(arg) - if err != nil { - return query.Answers{}, err - } - - } - } - - // If nothing was set, set a default - if resp.Answers.Name == "" { - resp.Answers.Name = "." - if resp.Answers.Request == 0 { - resp.Answers.Request = dns.StringToType["NS"] - } - } else { - if resp.Answers.Request == 0 { - resp.Answers.Request = dns.StringToType["A"] - } - } - if resp.Answers.Server == "" { - resolv, err := conf.GetDNSConfig() - if err != nil { // Query Google by default - resp.Answers.Server = "8.8.4.4" - } else { - for _, srv := range resolv.Servers { - if opts.IPv4 { - if strings.Contains(srv, ".") { - resp.Answers.Server = srv - break - } - } else if opts.IPv6 { - if strings.Contains(srv, ":") { - resp.Answers.Server = srv - break - } - } else { - resp.Answers.Server = resolv.Servers[rand.Intn(len(resolv.Servers))] - break - } - } - } - } - - return query.Answers{Server: resp.Answers.Server, Request: resp.Answers.Request, Name: resp.Answers.Name}, nil -} diff --git a/cli/cli.go b/cli/cli.go new file mode 100644 index 0000000..c6309f6 --- /dev/null +++ b/cli/cli.go @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: BSD-3-Clause + +package cli + +import ( + "fmt" + "os" + "runtime" + + "git.froth.zone/sam/awl/internal/structs" + "git.froth.zone/sam/awl/util" + + "github.com/miekg/dns" + flag "github.com/stefansundin/go-zflag" +) + +// Parse the arguments passed into awl +func ParseCLI(version string) (Options, error) { + + flag.Usage = func() { + fmt.Println(`awl - drill, writ small + + Usage: awl name [@server] [record] + domain, IP address, phone number + defaults to A + + Arguments may be in any order, including flags. + Dig-like +[no]commands are also supported, see dig(1) or dig -h + + Options:`) + flag.PrintDefaults() + os.Exit(0) + } + + // CLI flag + var ( + port = flag.Int("port", 0, "`port` to make DNS query (default: 53 for UDP/TCP, 853 for TLS/QUIC", flag.OptShorthand('p'), flag.OptDisablePrintDefault(true)) + query = flag.String("query", ".", "domain name to query", flag.OptShorthand('q')) + class = flag.String("class", "IN", "DNS class to query", flag.OptShorthand('c')) + qType = flag.String("qType", "A", "type to query", flag.OptShorthand('t')) + + ipv4 = flag.Bool("4", false, "force IPv4", flag.OptShorthandStr("4")) + ipv6 = flag.Bool("6", false, "force IPv6", flag.OptShorthand('6')) + reverse = flag.Bool("reverse", false, "do a reverse lookup", flag.OptShorthand('x')) + + 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')) + + 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)") + cd = flag.Bool("cd", false, "set/unset CD (Checking Disabled) flag (default: not set)") + qr = flag.Bool("qr", false, "set/unset QR (QueRy) flag (default: set)", flag.OptDisablePrintDefault(true)) + rd = flag.Bool("rd", true, "set/unset RD (Recursion Desired) flag (default: set)", flag.OptDisablePrintDefault(true)) + ra = flag.Bool("ra", false, "set/unset RA (Recursion Available) flag (default: set)", flag.OptDisablePrintDefault(true)) + tc = flag.Bool("tc", false, "set/unset TC (TrunCated) flag (default: not set)") + z = flag.Bool("z", false, "set/unset Z (Zero) flag (default: not set)", flag.OptShorthand('z')) + + short = flag.Bool("short", false, "print just the results, equivalent to dig +short", flag.OptShorthand('s')) + json = flag.Bool("json", false, "print the result(s) as JSON", flag.OptShorthand('j')) + 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")) + versionFlag = flag.Bool("version", false, "print version information", flag.OptShorthand('V')) + ) + + // Don't sort the flags when -h is given + flag.CommandLine.SortFlags = false + + // Parse the flags + flag.Parse() + + opts := Options{ + Logger: util.InitLogger(*verbosity), + Class: dns.StringToClass[*class], + Port: *port, + IPv4: *ipv4, + IPv6: *ipv6, + DNSSEC: *dnssec, + Short: *short, + TCP: *tcp, + TLS: *tls, + HTTPS: *https, + QUIC: *quic, + Truncate: *truncate, + AA: *aa, + AD: *ad, + TC: *tc, + Z: *z, + CD: *cd, + QR: *qr, + RD: *rd, + RA: *ra, + Reverse: *reverse, + JSON: *json, + XML: *xml, + YAML: *yaml, + Request: structs.Request{ + Type: dns.StringToType[*qType], + Name: *query, + }, + } + + if *versionFlag { + fmt.Printf("awl version %s, built with %s\n", version, runtime.Version()) + os.Exit(0) + } + + // Parse all the arguments that don't start with - or -- + // This includes the dig-style (+) options + err := ParseMiscArgs(flag.Args(), &opts) + if err != nil { + opts.Logger.Fatal(err) + } + + if opts.Port == 0 { + if opts.TLS || opts.QUIC { + opts.Port = 853 + } else { + opts.Port = 53 + } + } + + // opts.Request = req + + // Set verbosity to full if just -v is specified + // flag.Lookup("verbosity").NoOptDefVal = "3" + + return opts, nil +} diff --git a/cli/cli_test.go b/cli/cli_test.go new file mode 100644 index 0000000..7ac5666 --- /dev/null +++ b/cli/cli_test.go @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: BSD-3-Clause + +package cli_test + +// TODO: readd these + +// import ( +// "os" +// "testing" + +// "git.froth.zone/sam/awl/internal/structs" +// "git.froth.zone/sam/awl/query" + +// "github.com/miekg/dns" +// "github.com/stretchr/testify/assert" +// "github.com/stretchr/testify/require" +// ) + +// func TestApp(t *testing.T) { +// t.Parallel() +// app := prepareCLI() +// // What more can even be done lmao +// require.NotNil(t, app) +// } + +// func TestArgParse(t *testing.T) { +// t.Parallel() +// tests := []struct { +// in []string +// want structs.Request +// }{ +// { +// []string{"@::1", "localhost", "AAAA"}, +// structs.Request{Server: "::1", Type: dns.TypeAAAA, Name: "localhost"}, +// }, +// { +// []string{"@1.0.0.1", "google.com"}, +// structs.Request{Server: "1.0.0.1", Type: dns.TypeA, Name: "google.com"}, +// }, +// { +// []string{"@8.8.4.4"}, +// structs.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 new file mode 100644 index 0000000..938eff5 --- /dev/null +++ b/cli/dig.go @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: BSD-3-Clause + +package cli + +import ( + "fmt" + "strings" +) + +// Parse dig-like commands and set the options as such +func ParseDig(arg string, opts *Options) error { + // returns true if the flag starts with a no + isNo := !strings.HasPrefix(arg, "no") + switch arg { + // Set DNS query flags + case "aaflag", "aaonly", "noaaflag", "noaaonly": + opts.AA = isNo + case "adflag", "noadflag": + opts.AD = isNo + case "cdflag", "nocdflag": + opts.CD = isNo + case "qrflag", "noqrflag": + opts.QR = isNo + case "raflag", "noraflag": + opts.RA = isNo + case "rdflag", "recurse", "nordflag", "norecurse": + opts.RD = isNo + case "tcflag", "notcflag": + opts.TC = isNo + case "zflag", "nozflag": + opts.Z = isNo + // End DNS query flags + + case "dnssec", "nodnssec": + opts.DNSSEC = isNo + case "tcp", "vc", "notcp", "novc": + opts.TCP = isNo + case "ignore", "noignore": + // Invert (ignore truncation when true) + opts.Truncate = !isNo + + // Formatting + case "short", "noshort": + opts.Short = isNo + case "json", "nojson": + opts.JSON = isNo + case "xml", "noxml": + opts.XML = isNo + case "yaml", "noyaml": + opts.YAML = isNo + // End formatting + + default: + return fmt.Errorf("dig: Unknown flag given") + } + return nil +} diff --git a/cli/docs.go b/cli/docs.go new file mode 100644 index 0000000..dbc972b --- /dev/null +++ b/cli/docs.go @@ -0,0 +1,5 @@ +/* +The CLI part of the package, including both POSIX +flag parsing and dig-like flag parsing. +*/ +package cli diff --git a/cli/misc.go b/cli/misc.go new file mode 100644 index 0000000..2e2f6d9 --- /dev/null +++ b/cli/misc.go @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: BSD-3-Clause + +package cli + +import ( + "fmt" + "math/rand" + "strings" + + "git.froth.zone/sam/awl/conf" + "git.froth.zone/sam/awl/internal/structs" + "git.froth.zone/sam/awl/util" + + "github.com/miekg/dns" + "golang.org/x/net/idna" +) + +// Parse the wildcard arguments, drill style +func ParseMiscArgs(args []string, opts *Options) error { + var err error + + for _, arg := range args { + r, ok := dns.StringToType[strings.ToUpper(arg)] + switch { + // If it starts with @, it's a DNS server + case strings.HasPrefix(arg, "@"): + opts.Request.Server = arg[1:] + case strings.Contains(arg, "."): + opts.Query, err = idna.ToASCII(arg) + if err != nil { + return err + } + case ok: + // If it's a DNS request, it's a DNS request (obviously) + opts.Type = r + case strings.HasPrefix(arg, "+"): + // Dig-style +queries + err = ParseDig(strings.ToLower(arg[1:]), opts) + default: + //else, assume it's a name + opts.Query, err = idna.ToASCII(arg) + if err != nil { + return err + } + + } + } + + // If nothing was set, set a default + if opts.Query == "" { + opts.Query = "." + if opts.Type == 0 { + opts.Type = dns.StringToType["NS"] + } + } else { + if opts.Type == 0 { + opts.Type = dns.StringToType["A"] + } + } + if opts.Request.Server == "" { + switch { + case opts.TLS: + opts.Request.Server = "dns.google" + case opts.HTTPS: + opts.Request.Server = "https://dns.cloudflare.com/dns-query" + case opts.QUIC: + opts.Request.Server = "dns.adguard.com" + default: + resolv, err := conf.GetDNSConfig() + if err != nil { + opts.Request.Server = "9.9.9.9" + } else { + for _, srv := range resolv.Servers { + if opts.IPv4 { + if strings.Contains(srv, ".") { + opts.Request.Server = srv + break + } + } else if opts.IPv6 { + if strings.Contains(srv, ":") { + opts.Request.Server = srv + break + } + } else { + //#nosec -- This isn't used for anything secure + opts.Request.Server = resolv.Servers[rand.Intn(len(resolv.Servers))] + break + } + } + } + } + } + + // Make reverse adresses proper addresses + if opts.Reverse { + if dns.TypeToString[opts.Request.Type] == "A" { + opts.Type = dns.StringToType["PTR"] + } + opts.Query, err = util.ReverseDNS(opts.Query, opts.Request.Type) + if err != nil { + return err + } + } + + if opts.Class == 0 { + opts.Class = dns.StringToClass["IN"] + } + + // if the domain is not canonical, make it canonical + if !strings.HasSuffix(opts.Query, ".") { + opts.Query = fmt.Sprintf("%s.", opts.Query) + } + + opts.Request = structs.Request{ + Server: opts.Request.Server, + Type: opts.Type, + Class: opts.Class, + Name: opts.Query, + } + + return nil +} diff --git a/cli/options.go b/cli/options.go new file mode 100644 index 0000000..c1891c1 --- /dev/null +++ b/cli/options.go @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: BSD-3-Clause + +package cli + +import ( + "git.froth.zone/sam/awl/internal/structs" + "git.froth.zone/sam/awl/logawl" +) + +// CLI options structure +type Options struct { + Logger *logawl.Logger // Logger + Port int // DNS port + Query string // DNS Query + Class uint16 // DNS Class + Type uint16 // DNS Type + IPv4 bool // Force IPv4 + IPv6 bool // Force IPv6 + DNSSEC bool // Enable DNSSEC + TCP bool // Query with TCP + TLS bool // Query over TLS + HTTPS bool // Query over HTTPS + QUIC bool // Query over QUIC + Truncate bool // Ignore truncation + AA bool // Set Authoratative Answer + AD bool // Set Authenticated Data + CD bool // Set CD + QR bool // Set QueRy + RD bool // Set Recursion Desired + RA bool // Set Recursion Available + TC bool // Set TC (TrunCated) + Z bool // Set Z (Zero) + Reverse bool // Make reverse query + Verbosity int // Set logawl verbosity + Short bool // Short output + JSON bool // Outout as JSON + XML bool // Output as XML + YAML bool // Output at YAML + + Request structs.Request +} diff --git a/cli_test.go b/cli_test.go deleted file mode 100644 index ca1fc89..0000000 --- a/cli_test.go +++ /dev/null @@ -1,126 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause - -package main - -import ( - "os" - "testing" - - "git.froth.zone/sam/awl/query" - "github.com/miekg/dns" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestApp(t *testing.T) { - app := prepareCLI() - // What more can even be done lmao - require.NotNil(t, app) -} - -func TestArgParse(t *testing.T) { - tests := []struct { - in []string - want query.Answers - }{ - { - []string{"@::1", "localhost", "AAAA"}, - query.Answers{Server: "::1", Request: dns.TypeAAAA, Name: "localhost"}, - }, - { - []string{"@1.0.0.1", "google.com"}, - query.Answers{Server: "1.0.0.1", Request: dns.TypeA, Name: "google.com"}, - }, - { - []string{"@8.8.4.4"}, - query.Answers{Server: "8.8.4.4", Request: 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 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 { - assert.ErrorContains(t, err, "domain must be fully qualified") - } - assert.Nil(t, err) - }) -} diff --git a/conf/plan9_test.go b/conf/plan9_test.go index fa81fd2..573906d 100644 --- a/conf/plan9_test.go +++ b/conf/plan9_test.go @@ -9,6 +9,7 @@ import ( ) func TestGetPlan9Config(t *testing.T) { + t.Parallel() ndbs := []struct { in string want string diff --git a/doc/awl.1 b/doc/awl.1 new file mode 100644 index 0000000..18cbefa --- /dev/null +++ b/doc/awl.1 @@ -0,0 +1,168 @@ +.\" Generated by scdoc 1.11.2 +.\" Complete documentation for this program is not available as a GNU info page +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.nh +.ad l +.\" Begin generated content: +.TH "AWL" "1" "2022-07-20" +.PP +.SH NAME +awl - drill, writ small +.PP +.SH SYNOPSIS +\fBawl\fR [ \fIOPTIONS\fR ] \fIname\fR [ \fI@server\fR ] [ \fItype\fR ] +.br +where +.PP +\fIname\fR is the query to make (\fBexample: froth.\&zone\fR) +.br +\fI@server\fR is the server to query (\fBexample: dns.\&froth.\&zone\fR) +.br +\fItype\fR is the DNS resource type (\fBexample: AAAA\fR) +.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).\& +.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 Work-In-Progress so some features may get added or removed.\& +.PP +.SH OPTIONS +.RS 4 +\fB-D\fR, \fB--dnssec\fR +.br + Enable DNSSEC.\& This needs to be manually enabled.\& +.PP +\fB--debug\fR +.br + Enable debug logging (currently WIP).\& +.PP +\fB-v\fR +.br + Print the version and exit.\& +.PP +\fB-h\fR +.br + Show a "short" help message.\& +.PP +.RE +.SS Query Options +.RS 4 +\fB-4\fR +.br + Only make query over IPv4 +.PP +\fB-6\fR +.br + Only make query over IPv6 +.PP +\fB-p\fR, \fB--port\fR \fIport\fR +.br + Sets the port to query.\& +.br + +.br +\fIDefault Ports\fR: +.RS 4 +.PD 0 +.IP \(bu 4 +\fI53\fR for \fBUDP\fR and \fBTCP\fR +.IP \(bu 4 +\fI853\fR for \fBTLS\fR and \fBQUIC\fR +.IP \(bu 4 +\fI443\fR for \fBHTTPS\fR +.PD +.PP +.RE +\fB--no-truncate\fR +.br + Ignore UDP truncation (by default, awl \fIretries with TCP\fR) +.PP +\fB-t\fR, \fB--tcp\fR +.br + Use TCP for the query (see \fIRFC 7766\fR) +.PP +\fB-u\fR, \fB--udp\fR +.br + Use UDP for the query (default) +.PP +\fB-T\fR, \fB--tls\fR +.br + Use DNS-over-TLS, implies \fB-t\fR (see \fIRFC 7858\fR) +.PP +\fB-H\fR.\& \fB--https\fR +.br + Use DNS-over-HTTPS (see \fIRFC 8484\fR) +.PP +\fB-Q\fR.\& \fB--quic\fR +.br + Use DNS-over-QUIC (see \fIRFC 9250\fR) +.PP +\fB-x\fR, \fB--reverse\fR +.br + Do a reverse lookup.\& Sets default \fItype\fR to PTR.\& +.br + \fBawl\fR automatically makes an IP or phone number canonical.\& +.PP +.RE +.SS DNS Flags +.PP +.RS 4 +\fB--aa\fR +.br + \fISET\fR Authoritative Answer (default: unset) +.PP +\fB--ad\fR +.br + \fISET\fR Authenticated Data (default: unset) +.PP +\fB--tc\fR +.br + \fISET\fR TC (TrunCated) flag (default: not set) +.PP +\fB-z\fR +.br + \fISET\fR Z (Zero) flag (default: not set) +.PP +\fB--cd\fR +.br + \fISET\fR CD (Checking Disabled) flag (default: not set) +.PP +\fB--no-qr\fR +.br + \fIUNSET\fR QR (QueRy) flag (default: set) +.PP +\fB--no-rd\fR +.br + \fIUNSET\fR RD (Recursion Desired) flag (default: set) +.PP +\fB--no-ra\fR +.br + \fIUNSET\fR RA (Recursion Available) flag (default: set) +.PP +.RE +.SS Output Options +.RS 4 +\fB-j\fR, \fB--json\fR +.br + Print the query results as JSON.\& +.PP +\fB-X\fR, \fB--xml\fR +.br + Print the query results as XML.\& +.PP +\fB-y\fR, \fB--yaml\fR +.br + Print the query results as YAML.\& +.PP +\fB-s\fR, \fB--short\fR +.br + Print just the results.\& +.br + Equivalent to \fBdig +short\fR +.PP +.RE +.SH SEE ALSO +\fIdrill\fR(1), \fIdig\fR(1), the many DNS RFCs \ No newline at end of file diff --git a/doc/awl.1.md b/doc/awl.1.md new file mode 100644 index 0000000..46f3125 --- /dev/null +++ b/doc/awl.1.md @@ -0,0 +1,115 @@ +AWL(1) + +# NAME +awl - drill, writ small + +# SYNOPSIS +*awl* [ _OPTIONS_ ] _name_ [ _@server_ ] [ _type_ ]++ +where + +_name_ is the query to make (*example: froth.zone*)++ +_@server_ is the server to query (*example: dns.froth.zone*)++ +_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). + +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 Work-In-Progress so some features may get added or removed. + +# OPTIONS + _Dig-like queries are supported, see dig(1)_ + + *-D*, *--dnssec*++ + Enable DNSSEC. This needs to be manually enabled. + + *--debug*++ + Enable debug logging (currently WIP). + + *-v*++ + Print the version and exit. + + *-h*++ + Show a "short" help message. + +## Query Options + *-4*++ + Only make query over IPv4 + + *-6*++ + Only make query over IPv6 + + *-p*, *--port* _port_++ + Sets the port to query.++ + ++ +_Default Ports_: + - _53_ for *UDP* and *TCP* + - _853_ for *TLS* and *QUIC* + - _443_ for *HTTPS* + + *--no-truncate*++ + Ignore UDP truncation (by default, awl _retries with TCP_) + + *-t*, *--tcp*++ + Use TCP for the query (see _RFC 7766_) + + *-u*, *--udp*++ + Use UDP for the query (default) + + *-T*, *--tls*++ + Use DNS-over-TLS, implies *-t* (see _RFC 7858_) + + *-H*. *--https*++ + Use DNS-over-HTTPS (see _RFC 8484_) + + *-Q*. *--quic*++ + Use DNS-over-QUIC (see _RFC 9250_) + + *-x*, *--reverse*++ + Do a reverse lookup. Sets default _type_ to PTR.++ + *awl* automatically makes an IP or phone number canonical. + +## DNS Flags + + *--aa*++ + _SET_ Authoritative Answer (default: unset) + + *--ad*++ + _SET_ Authenticated Data (default: unset) + + *--tc*++ + _SET_ TC (TrunCated) flag (default: not set) + + *-z*++ + _SET_ Z (Zero) flag (default: not set) + + *--cd*++ + _SET_ CD (Checking Disabled) flag (default: not set) + + *--no-qr*++ + _UNSET_ QR (QueRy) flag (default: set) + + *--no-rd*++ + _UNSET_ RD (Recursion Desired) flag (default: set) + + *--no-ra*++ + _UNSET_ RA (Recursion Available) flag (default: set) + +## Output Options + *-j*, *--json*++ + Print the query results as JSON. + + *-X*, *--xml*++ + Print the query results as XML. + + *-y*, *--yaml*++ + Print the query results as YAML. + + *-s*, *--short*++ + Print just the results.++ + Equivalent to *dig +short* + +# SEE ALSO +_drill_(1), _dig_(1), the many DNS RFCs \ No newline at end of file diff --git a/go.mod b/go.mod index 1596228..412acf0 100644 --- a/go.mod +++ b/go.mod @@ -5,19 +5,20 @@ go 1.18 require ( github.com/lucas-clemente/quic-go v0.28.0 github.com/miekg/dns v1.1.50 - github.com/urfave/cli/v2 v2.11.0 + github.com/stefansundin/go-zflag v1.1.1 golang.org/x/net v0.0.0-20220708220712-1185a9018129 + gopkg.in/yaml.v2 v2.4.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( github.com/cheekybits/genny v1.0.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect @@ -25,9 +26,7 @@ require ( github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/onsi/ginkgo v1.16.5 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/stretchr/testify v1.8.0 - github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 diff --git a/go.sum b/go.sum index 0753914..5442a9b 100644 --- a/go.sum +++ b/go.sum @@ -8,7 +8,6 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1 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/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 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= @@ -17,8 +16,6 @@ github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitf github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -79,8 +76,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lucas-clemente/quic-go v0.27.2 h1:zsMwwniyybb8B/UDNXRSYee7WpQJVOcjQEGgpw2ikXs= -github.com/lucas-clemente/quic-go v0.27.2/go.mod h1:vXgO/11FBSKM+js1NxoaQ/bPtVFYfB7uxhfHXyMhl1A= github.com/lucas-clemente/quic-go v0.28.0 h1:9eXVRgIkMQQyiyorz/dAaOYIx3TFzXsIFkNFz4cxuJM= github.com/lucas-clemente/quic-go v0.28.0/go.mod h1:oGz5DKK41cJt5+773+BSO9BXDsREY4HLf7+0odGAPO0= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= @@ -124,10 +119,7 @@ github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= @@ -153,6 +145,8 @@ github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYED github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= +github.com/stefansundin/go-zflag v1.1.1 h1:XabhzWS588bVvV1z1UctSa6i8zHkXc5W9otqtnDSHw8= +github.com/stefansundin/go-zflag v1.1.1/go.mod h1:HXX5rABl1AoTcZ2jw+CqJ7R8irczaLquGNZlFabZooc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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= @@ -161,17 +155,10 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/urfave/cli/v2 v2.10.3 h1:oi571Fxz5aHugfBAJd5nkwSk3fzATXtMlpxdLylSCMo= -github.com/urfave/cli/v2 v2.10.3/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo= -github.com/urfave/cli/v2 v2.11.0 h1:c6bD90aLd2iEsokxhxkY5Er0zA2V9fId2aJfwmrF+do= -github.com/urfave/cli/v2 v2.11.0/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= @@ -181,7 +168,6 @@ golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACk 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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 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= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -206,18 +192,10 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL 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= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220630215102-69896b714898 h1:K7wO6V1IrczY9QOQ2WkVpw4JQSwCd52UsxVEirZUfiw= -golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220706163947-c90051bbdb60 h1:8NSylCMxLW4JvserAndSgFL7aPli6A68yf0bYFTcWCM= -golang.org/x/net v0.0.0-20220706163947-c90051bbdb60/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220708220712-1185a9018129 h1:vucSRfWwTsoXro7P+3Cjlr6flUMtzCwzlvkxEQtHHB0= golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -253,21 +231,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/FiaizQEK5Gu4Bq4JE8= -golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220701225701-179beb0bd1a1 h1:+Lm8wRwJpsVpTHuM4tHTwgxjPzv/bjxsHt2cW5EY7XU= -golang.org/x/sys v0.0.0-20220701225701-179beb0bd1a1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220702020025-31831981b65f h1:xdsejrW/0Wf2diT5CPp3XmKUNbr7Xvw8kYilQ+6qjRY= -golang.org/x/sys v0.0.0-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e h1:CsOuNlbOuf0mzxJIefr6Q4uAUetRUwZE4qt7VfzP+xo= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d h1:/m5NbqQelATgoSPVC2Z23sR4kVNokFwDDyWh/3rGY+I= -golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e h1:NHvCuwuS43lGnYhten69ZWqi2QOj/CiDNcKbVqwVoew= -golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/internal/structs/query.go b/internal/structs/query.go new file mode 100644 index 0000000..eb743be --- /dev/null +++ b/internal/structs/query.go @@ -0,0 +1,23 @@ +// 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/logawl/logger.go b/logawl/logger.go index 607f516..6376a7b 100644 --- a/logawl/logger.go +++ b/logawl/logger.go @@ -28,15 +28,27 @@ func (l *Logger) Println(level Level, v ...any) { if l.IsLevel(level) { switch level { //Goes through log levels and does stuff based on them (Fatal os.Exit...etc) case 0: - l.Printer(0, fmt.Sprintln(v...)) //Fatal level + err := l.Printer(0, fmt.Sprintln(v...)) //Fatal level + if err != nil { + panic(err) + } os.Exit(1) case 1: - l.Printer(1, fmt.Sprintln(v...)) //Error level + err := l.Printer(1, fmt.Sprintln(v...)) //Error level + if err != nil { + panic(err) + } os.Exit(2) case 2: - l.Printer(2, fmt.Sprintln(v...)) //Info level + err := l.Printer(2, fmt.Sprintln(v...)) //Info level + if err != nil { + panic(err) + } case 3: - l.Printer(3, fmt.Sprintln(v...)) //Debug level + err := l.Printer(3, fmt.Sprintln(v...)) //Debug level + if err != nil { + panic(err) + } default: break } @@ -44,7 +56,7 @@ func (l *Logger) Println(level Level, v ...any) { } // Formats the log header as such YYYY/MM/DD HH:MM:SS (local time) -func (l *Logger) formatHeader(buf *[]byte, t time.Time, line int, level Level) error { +func (l *Logger) FormatHeader(buf *[]byte, t time.Time, line int, level Level) error { if lvl, err := l.UnMarshalLevel(level); err == nil { // This is ugly but functional // maybe there can be an append func or something in the future @@ -80,7 +92,7 @@ func (l *Logger) Printer(level Level, s string) error { defer l.Mu.Unlock() l.buf = l.buf[:0] - l.formatHeader(&l.buf, now, line, level) + l.FormatHeader(&l.buf, now, line, level) l.buf = append(l.buf, s...) if len(s) == 0 || s[len(s)-1] != '\n' { l.buf = append(l.buf, '\n') diff --git a/logawl/logging_test.go b/logawl/logging_test.go index 606cd5c..9b187dc 100644 --- a/logawl/logging_test.go +++ b/logawl/logging_test.go @@ -1,52 +1,59 @@ -package logawl +// SPDX-License-Identifier: BSD-3-Clause + +package logawl_test import ( "bytes" "testing" "time" + "git.froth.zone/sam/awl/logawl" + "github.com/stretchr/testify/assert" ) -var logger = New() +var logger = logawl.New() func TestLogawl(t *testing.T) { + t.Parallel() - assert.Equal(t, Level(2), logger.Level) //cast 2 (int) to 2 (level) + assert.Equal(t, logawl.Level(2), logger.Level) //cast 2 (int) to 2 (level) //Validate setting and getting levels from memory works - for i := range AllLevels { - logger.SetLevel(Level(i)) - assert.Equal(t, Level(i), logger.GetLevel()) + for i := range logawl.AllLevels { + logger.SetLevel(logawl.Level(i)) + assert.Equal(t, logawl.Level(i), logger.GetLevel()) } } func TestUnmarshalLevels(t *testing.T) { + t.Parallel() m := make(map[int]string) var err error //Fill map with unmarshalled level info - for i := range AllLevels { - m[i], err = logger.UnMarshalLevel(Level(i)) + for i := range logawl.AllLevels { + m[i], err = logger.UnMarshalLevel(logawl.Level(i)) assert.Nil(t, err) } //iterate over map and assert equal - for i := range AllLevels { - lv, err := logger.UnMarshalLevel(Level(i)) + for i := range logawl.AllLevels { + lv, err := logger.UnMarshalLevel(logawl.Level(i)) assert.Nil(t, err) assert.Equal(t, m[i], lv) } - lv, err := logger.UnMarshalLevel(Level(9001)) + lv, err := logger.UnMarshalLevel(logawl.Level(9001)) assert.NotNil(t, err) assert.Equal(t, "", lv) assert.ErrorContains(t, err, "invalid log level choice") } func TestLogger(t *testing.T) { + t.Parallel() - for i := range AllLevels { + for i := range logawl.AllLevels { // only test non-exiting log levels switch i { case 1: @@ -76,8 +83,9 @@ func TestLogger(t *testing.T) { } func TestFmt(t *testing.T) { + t.Parallel() ti := time.Now() test := []byte("test") - assert.NotNil(t, logger.formatHeader(&test, ti, 0, Level(9001))) //make sure error is error + assert.NotNil(t, logger.FormatHeader(&test, ti, 0, logawl.Level(9001))) //make sure error is error } diff --git a/main.go b/main.go new file mode 100644 index 0000000..991be6b --- /dev/null +++ b/main.go @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: BSD-3-Clause + +package main + +import ( + "encoding/json" + "encoding/xml" + "fmt" + "os" + "strings" + "time" + + "git.froth.zone/sam/awl/cli" + "git.froth.zone/sam/awl/query" + + "gopkg.in/yaml.v2" +) + +var version = "DEV" + +func main() { + opts, err := cli.ParseCLI(version) + if err != nil { + opts.Logger.Fatal(err) + os.Exit(1) + } + resp, err := query.CreateQuery(opts) + if err != nil { + opts.Logger.Fatal(err) + os.Exit(1) + } + switch { + case opts.JSON: + json, err := json.MarshalIndent(resp.DNS, "", " ") + if err != nil { + opts.Logger.Fatal(err) + os.Exit(1) + } + fmt.Println(string(json)) + case opts.XML: + xml, err := xml.MarshalIndent(resp.DNS, "", " ") + if err != nil { + opts.Logger.Fatal(err) + os.Exit(1) + } + fmt.Println(string(xml)) + case opts.YAML: + yaml, err := yaml.Marshal(resp.DNS) + if err != nil { + opts.Logger.Fatal(err) + os.Exit(1) + } + fmt.Println(string(yaml)) + default: + if !opts.Short { + // Print everything + 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()) + } else { + // Print just the responses, nothing else + for _, res := range resp.DNS.Answer { + temp := strings.Split(res.String(), "\t") + fmt.Println(temp[len(temp)-1]) + } + } + } + +} diff --git a/query.go b/query.go deleted file mode 100644 index e90f933..0000000 --- a/query.go +++ /dev/null @@ -1,199 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause - -package main - -import ( - "encoding/json" - "fmt" - "net" - "strconv" - "strings" - "time" - - "git.froth.zone/sam/awl/query" - "git.froth.zone/sam/awl/util" - "github.com/miekg/dns" - "github.com/urfave/cli/v2" -) - -func doQuery(c *cli.Context) error { - var ( - err error - ) - // load cli flags into options struct - Options := query.Options{ - Logger: util.InitLogger(c.Bool("debug")), - Port: c.Int("port"), - IPv4: c.Bool("4"), - IPv6: c.Bool("6"), - DNSSEC: c.Bool("dnssec"), - Short: c.Bool("short"), - TCP: c.Bool("tcp"), - TLS: c.Bool("tls"), - HTTPS: c.Bool("https"), - QUIC: c.Bool("quic"), - Truncate: c.Bool("no-truncate"), - AA: c.Bool("aa"), - TC: c.Bool("tc"), - Z: c.Bool("z"), - CD: c.Bool("cd"), - NoRD: c.Bool("no-rd"), - NoRA: c.Bool("no-ra"), - Reverse: c.Bool("reverse"), - Debug: c.Bool("debug"), - } - Options.Answers, err = parseArgs(c.Args().Slice(), Options) - if err != nil { - Options.Logger.Error("Unable to parse args") - return err - } - msg := new(dns.Msg) - - if Options.Reverse { - if dns.TypeToString[Options.Answers.Request] == "A" { - Options.Answers.Request = dns.StringToType["PTR"] - } - Options.Answers.Name, err = util.ReverseDNS(Options.Answers.Name, Options.Answers.Request) - if err != nil { - return err - } - } - - // if the domain is not canonical, make it canonical - if !strings.HasSuffix(Options.Answers.Name, ".") { - Options.Answers.Name = fmt.Sprintf("%s.", Options.Answers.Name) - } - msg.SetQuestion(Options.Answers.Name, Options.Answers.Request) - // If port is not set, set it - if Options.Port == 0 { - if Options.TLS || Options.QUIC { - Options.Port = 853 - } else { - Options.Port = 53 - } - } - Options.Logger.Debug("setting any message flags") - // Make this authoritative (does this do anything?) - if Options.AA { - Options.Logger.Debug("making message authorative") - msg.Authoritative = true - } - // Set truncated flag (why) - if Options.TC { - msg.Truncated = true - } - // Set the zero flag if requested (does nothing) - if Options.Z { - Options.Logger.Debug("setting to zero") - msg.Zero = true - } - // Disable DNSSEC validation - if Options.CD { - Options.Logger.Debug("disabling DNSSEC validation") - msg.CheckingDisabled = true - } - // Disable wanting recursion - if Options.NoRD { - Options.Logger.Debug("disabling recursion") - msg.RecursionDesired = false - } - // Disable recursion being available (I don't think this does anything) - if Options.NoRA { - msg.RecursionAvailable = false - } - // Set DNSSEC if requested - if Options.DNSSEC { - Options.Logger.Debug("using DNSSEC") - msg.SetEdns0(1232, true) - } - - resolver, err := query.LoadResolver(Options.Answers.Server, Options) - if err != nil { - return err - } - - if Options.Debug { - Options.Logger.SetLevel(3) - } - - Options.Logger.Debug("Starting awl") - - var in = Options.Answers.DNS - - // Make the DNS request - if Options.HTTPS { - in, Options.Answers.RTT, err = resolver.LookUp(msg) - } else if Options.QUIC { - in, Options.Answers.RTT, err = resolver.LookUp(msg) - } else { - Options.Answers.Server = net.JoinHostPort(Options.Answers.Server, strconv.Itoa(Options.Port)) - d := new(dns.Client) - - // Set TCP/UDP, depending on flags - if Options.TCP || Options.TLS { - d.Net = "tcp" - } else { - Options.Logger.Debug("using udp") - d.Net = "udp" - } - - // Set IPv4 or IPv6, depending on flags - switch { - case Options.IPv4: - d.Net += "4" - case Options.IPv6: - d.Net += "6" - } - - // Add TLS, if requested - if Options.TLS { - d.Net += "-tls" - } - - in, Options.Answers.RTT, err = d.Exchange(msg, Options.Answers.Server) - if err != nil { - return err - } - // If UDP truncates, use TCP instead (unless truncation is to be ignored) - if in.MsgHdr.Truncated && !Options.Truncate { - fmt.Printf(";; Truncated, retrying with TCP\n\n") - d.Net = "tcp" - switch { - case Options.IPv4: - d.Net += "4" - case Options.IPv4: - d.Net += "6" - } - in, Options.Answers.RTT, err = d.Exchange(msg, Options.Answers.Server) - } - } - - if err != nil { - return err - } - - if c.Bool("json") { - json, err := json.MarshalIndent(in, "", " ") - if err != nil { - return err - } - fmt.Println(string(json)) - } else { - if !c.Bool("short") { - // Print everything - fmt.Println(in) - fmt.Println(";; Query time:", Options.Answers.RTT) - fmt.Println(";; SERVER:", Options.Answers.Server) - fmt.Println(";; WHEN:", time.Now().Format(time.RFC1123Z)) - fmt.Println(";; MSG SIZE rcvd:", in.Len()) - } else { - // Print just the responses, nothing else - for _, res := range in.Answer { - temp := strings.Split(res.String(), "\t") - fmt.Println(temp[len(temp)-1]) - } - } - } - - return nil -} diff --git a/query/HTTPS.go b/query/HTTPS.go index f72470c..df0bc6b 100644 --- a/query/HTTPS.go +++ b/query/HTTPS.go @@ -9,53 +9,55 @@ import ( "net/http" "time" + "git.froth.zone/sam/awl/cli" + "git.froth.zone/sam/awl/internal/structs" "github.com/miekg/dns" ) type HTTPSResolver struct { server string - opts Options + opts cli.Options } -func (r *HTTPSResolver) LookUp(msg *dns.Msg) (*dns.Msg, time.Duration, error) { - var resp Response +func (r *HTTPSResolver) LookUp(msg *dns.Msg) (structs.Response, error) { + var resp structs.Response httpR := &http.Client{} buf, err := msg.Pack() if err != nil { - return nil, 0, err + return structs.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)) if err != nil { - return nil, 0, fmt.Errorf("DoH: %s", err.Error()) + return structs.Response{}, fmt.Errorf("DoH: %s", err.Error()) } req.Header.Set("Content-Type", "application/dns-message") req.Header.Set("Accept", "application/dns-message") now := time.Now() res, err := httpR.Do(req) - resp.Answers.RTT = time.Since(now) + resp.RTT = time.Since(now) if err != nil { - return nil, 0, fmt.Errorf("DoH HTTP request error: %s", err.Error()) + return structs.Response{}, fmt.Errorf("DoH HTTP request error: %s", err.Error()) } defer res.Body.Close() if res.StatusCode != http.StatusOK { - return nil, 0, fmt.Errorf("DoH server responded with HTTP %d", res.StatusCode) + return structs.Response{}, fmt.Errorf("DoH server responded with HTTP %d", res.StatusCode) } fullRes, err := io.ReadAll(res.Body) if err != nil { - return nil, 0, fmt.Errorf("DoH body read error: %s", err.Error()) + return structs.Response{}, fmt.Errorf("DoH body read error: %s", err.Error()) } - resp.DNS = dns.Msg{} + resp.DNS = &dns.Msg{} r.opts.Logger.Debug("unpacking response") err = resp.DNS.Unpack(fullRes) if err != nil { - return nil, 0, fmt.Errorf("DoH dns message unpack error: %s", err.Error()) + return structs.Response{}, fmt.Errorf("DoH dns message unpack error: %s", err.Error()) } - return &resp.DNS, resp.Answers.RTT, nil + return resp, nil } diff --git a/query/HTTPS_test.go b/query/HTTPS_test.go new file mode 100644 index 0000000..8309554 --- /dev/null +++ b/query/HTTPS_test.go @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: BSD-3-Clause + +package query_test + +import ( + "fmt" + "strings" + "testing" + + "git.froth.zone/sam/awl/cli" + "git.froth.zone/sam/awl/internal/structs" + "git.froth.zone/sam/awl/query" + "git.froth.zone/sam/awl/util" + + "github.com/miekg/dns" + "github.com/stretchr/testify/assert" +) + +func TestResolveHTTPS(t *testing.T) { + t.Parallel() + var err error + opts := cli.Options{ + HTTPS: true, + Logger: util.InitLogger(0), + } + testCase := structs.Request{Server: "dns9.quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone"} + resolver, err := query.LoadResolver(testCase.Server, opts) + assert.Nil(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 = msg.SetQuestion(testCase.Name, testCase.Type) + res, err := resolver.LookUp(msg) + assert.Nil(t, err) + assert.NotNil(t, res) + +} + +func Test2ResolveHTTPS(t *testing.T) { + t.Parallel() + opts := cli.Options{ + HTTPS: true, + Logger: util.InitLogger(0), + } + var err error + testCase := structs.Request{Server: "dns9.quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone"} + resolver, err := query.LoadResolver(testCase.Server, opts) + assert.Nil(t, err) + msg := new(dns.Msg) + msg.SetQuestion(testCase.Name, testCase.Type) + msg = msg.SetQuestion(testCase.Name, testCase.Type) + res, err := resolver.LookUp(msg) + assert.Error(t, err) + assert.Equal(t, res, structs.Response{}) + +} +func Test3ResolveHTTPS(t *testing.T) { + t.Parallel() + opts := cli.Options{ + HTTPS: true, + Logger: util.InitLogger(0), + } + var err error + testCase := structs.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) + assert.Nil(t, err) + msg := new(dns.Msg) + msg.SetQuestion(testCase.Name, testCase.Type) + msg = msg.SetQuestion(testCase.Name, testCase.Type) + res, err := resolver.LookUp(msg) + assert.Error(t, err) + assert.Equal(t, res, structs.Response{}) + +} diff --git a/query/QUIC.go b/query/QUIC.go index 98f29f2..3026cee 100644 --- a/query/QUIC.go +++ b/query/QUIC.go @@ -7,63 +7,67 @@ import ( "io" "time" + "git.froth.zone/sam/awl/cli" + "git.froth.zone/sam/awl/internal/structs" + "github.com/lucas-clemente/quic-go" "github.com/miekg/dns" ) type QUICResolver struct { server string - opts Options + opts cli.Options } -func (r *QUICResolver) LookUp(msg *dns.Msg) (*dns.Msg, time.Duration, error) { - var resp Response +func (r *QUICResolver) LookUp(msg *dns.Msg) (structs.Response, error) { + var resp structs.Response tls := &tls.Config{ + MinVersion: tls.VersionTLS12, NextProtos: []string{"doq"}, } r.opts.Logger.Debug("making DoQ request") connection, err := quic.DialAddr(r.server, tls, nil) if err != nil { - return nil, 0, err + return structs.Response{}, err } // Compress request to over-the-wire buf, err := msg.Pack() if err != nil { - return nil, 0, err + return structs.Response{}, err } t := time.Now() stream, err := connection.OpenStream() if err != nil { - return nil, 0, err + return structs.Response{}, err } _, err = stream.Write(buf) if err != nil { - return nil, 0, err + return structs.Response{}, err } fullRes, err := io.ReadAll(stream) if err != nil { - return nil, 0, err + return structs.Response{}, err } - resp.Answers.RTT = time.Since(t) + resp.RTT = time.Since(t) // Close with error: no error err = connection.CloseWithError(0, "") if err != nil { - return nil, 0, err + return structs.Response{}, err } err = stream.Close() if err != nil { - return nil, 0, err + return structs.Response{}, err } - resp.DNS = dns.Msg{} + resp.DNS = &dns.Msg{} r.opts.Logger.Debug("unpacking DoQ response") err = resp.DNS.Unpack(fullRes) if err != nil { - return nil, 0, err + return structs.Response{}, err } - return &resp.DNS, resp.Answers.RTT, nil + return resp, nil } diff --git a/query/QUIC_test.go b/query/QUIC_test.go new file mode 100644 index 0000000..bc765f7 --- /dev/null +++ b/query/QUIC_test.go @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: BSD-3-Clause + +package query_test + +import ( + "fmt" + "net" + "strconv" + "strings" + "testing" + + "git.froth.zone/sam/awl/cli" + "git.froth.zone/sam/awl/internal/structs" + "git.froth.zone/sam/awl/query" + "git.froth.zone/sam/awl/util" + + "github.com/miekg/dns" + "github.com/stretchr/testify/assert" +) + +func TestQuic(t *testing.T) { + t.Parallel() + opts := cli.Options{ + QUIC: true, + Logger: util.InitLogger(0), + Port: 853, + Request: structs.Request{Server: "dns.adguard.com"}, + } + testCase := structs.Request{Server: "dns.//./,,adguard.com", Type: dns.TypeA, Name: "git.froth.zone"} + testCase2 := structs.Request{Server: "dns.adguard.com", Type: dns.TypeA, Name: "git.froth.zone"} + var testCases []structs.Request + testCases = append(testCases, testCase) + testCases = append(testCases, testCase2) + for i := range testCases { + switch i { + case 0: + resolver, err := query.LoadResolver(testCases[i].Server, opts) + assert.Nil(t, err) + // if the domain is not canonical, make it canonical + if !strings.HasSuffix(testCase.Name, ".") { + testCases[i].Name = fmt.Sprintf("%s.", testCases[i].Name) + } + msg := new(dns.Msg) + msg.SetQuestion(testCase.Name, testCase.Type) + msg = msg.SetQuestion(testCase.Name, testCase.Type) + res, err := resolver.LookUp(msg) + assert.NotNil(t, err) + assert.Equal(t, res, structs.Response{}) + case 1: + resolver, err := query.LoadResolver(testCase2.Server, opts) + assert.Nil(t, err) + testCase2.Server = net.JoinHostPort(testCase2.Server, strconv.Itoa(opts.Port)) + // if the domain is not canonical, make it canonical + if !strings.HasSuffix(testCase2.Name, ".") { + testCase2.Name = fmt.Sprintf("%s.", testCase2.Name) + } + msg := new(dns.Msg) + msg.SetQuestion(testCase2.Name, testCase2.Type) + msg = msg.SetQuestion(testCase2.Name, testCase2.Type) + res, err := resolver.LookUp(msg) + assert.Nil(t, err) + assert.NotNil(t, res) + } + + } + +} diff --git a/query/general.go b/query/general.go new file mode 100644 index 0000000..b92783a --- /dev/null +++ b/query/general.go @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: BSD-3-Clause + +package query + +import ( + "fmt" + + "git.froth.zone/sam/awl/cli" + "git.froth.zone/sam/awl/internal/structs" + + "github.com/miekg/dns" +) + +type StandardResolver struct { + server string + opts cli.Options +} + +func (r *StandardResolver) LookUp(msg *dns.Msg) (structs.Response, error) { + var ( + resp structs.Response + err error + ) + dnsClient := new(dns.Client) + if r.opts.TCP || r.opts.TLS { + dnsClient.Net = "tcp" + } else { + dnsClient.Net = "udp" + } + + switch { + case r.opts.IPv4: + dnsClient.Net += "4" + case r.opts.IPv6: + dnsClient.Net += "6" + } + + if r.opts.TLS { + dnsClient.Net += "-tls" + } + + resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, r.server) + if err != nil { + return structs.Response{}, err + } + + if resp.DNS.MsgHdr.Truncated && !r.opts.Truncate { + fmt.Printf(";; Truncated, retrying with TCP\n\n") + dnsClient.Net = "tcp" + switch { + case r.opts.IPv4: + dnsClient.Net += "4" + case r.opts.IPv4: + dnsClient.Net += "6" + } + resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, r.server) + } + + return resp, err +} diff --git a/query/query.go b/query/query.go new file mode 100644 index 0000000..4421bd9 --- /dev/null +++ b/query/query.go @@ -0,0 +1,34 @@ +package query + +import ( + "git.froth.zone/sam/awl/cli" + "git.froth.zone/sam/awl/internal/structs" + + "github.com/miekg/dns" +) + +func CreateQuery(opts cli.Options) (structs.Response, error) { + var res structs.Response + res.DNS = new(dns.Msg) + res.DNS.SetQuestion(opts.Query, opts.Type) + + res.DNS.MsgHdr.Response = opts.QR + res.DNS.MsgHdr.Authoritative = opts.AA + res.DNS.MsgHdr.Truncated = opts.TC + res.DNS.MsgHdr.RecursionDesired = opts.RD + res.DNS.MsgHdr.RecursionAvailable = opts.RA + res.DNS.MsgHdr.Zero = opts.Z + res.DNS.MsgHdr.AuthenticatedData = opts.AD + res.DNS.MsgHdr.CheckingDisabled = opts.CD + + if opts.DNSSEC { + res.DNS.SetEdns0(1232, true) + } + + resolver, err := LoadResolver(opts.Request.Server, opts) + if err != nil { + return structs.Response{}, err + } + + return resolver.LookUp(res.DNS) +} diff --git a/query/query_test.go b/query/query_test.go deleted file mode 100644 index e9591dd..0000000 --- a/query/query_test.go +++ /dev/null @@ -1,129 +0,0 @@ -package query - -import ( - "fmt" - "net" - "strconv" - "strings" - "testing" - - "git.froth.zone/sam/awl/util" - "github.com/miekg/dns" - "github.com/stretchr/testify/assert" -) - -func TestResolveHTTPS(t *testing.T) { - var err error - opts := Options{ - HTTPS: true, - Logger: util.InitLogger(false), - } - testCase := Answers{Server: "dns9.quad9.net/dns-query", Request: dns.TypeA, Name: "git.froth.zone"} - resolver, err := LoadResolver(testCase.Server, opts) - - 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.Request) - msg = msg.SetQuestion(testCase.Name, testCase.Request) - var in *dns.Msg - in, testCase.RTT, err = resolver.LookUp(msg) - assert.Nil(t, err) - assert.NotNil(t, in) - -} - -func Test2ResolveHTTPS(t *testing.T) { - opts := Options{ - HTTPS: true, - Logger: util.InitLogger(false), - } - var err error - testCase := Answers{Server: "dns9.quad9.net/dns-query", Request: dns.TypeA, Name: "git.froth.zone"} - resolver, err := LoadResolver(testCase.Server, opts) - msg := new(dns.Msg) - msg.SetQuestion(testCase.Name, testCase.Request) - msg = msg.SetQuestion(testCase.Name, testCase.Request) - var in *dns.Msg - in, testCase.RTT, err = resolver.LookUp(msg) - assert.NotNil(t, err) - assert.Nil(t, in) - -} -func Test3ResolveHTTPS(t *testing.T) { - opts := Options{ - HTTPS: true, - Logger: util.InitLogger(false), - } - var err error - testCase := Answers{Server: "dns9..quad9.net/dns-query", Request: 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 := LoadResolver(testCase.Server, opts) - msg := new(dns.Msg) - msg.SetQuestion(testCase.Name, testCase.Request) - msg = msg.SetQuestion(testCase.Name, testCase.Request) - var in *dns.Msg - in, testCase.RTT, err = resolver.LookUp(msg) - assert.NotNil(t, err) - assert.Nil(t, in) - -} - -func TestQuic(t *testing.T) { - opts := Options{ - QUIC: true, - Logger: util.InitLogger(false), - Port: 853, - Answers: Answers{Server: "dns.adguard.com"}, - } - testCase := Answers{Server: "dns.//./,,adguard.com", Request: dns.TypeA, Name: "git.froth.zone"} - testCase2 := Answers{Server: "dns.adguard.com", Request: dns.TypeA, Name: "git.froth.zone"} - var testCases []Answers - testCases = append(testCases, testCase) - testCases = append(testCases, testCase2) - for i := range testCases { - switch i { - case 0: - resolver, err := LoadResolver(testCases[i].Server, opts) - // if the domain is not canonical, make it canonical - if !strings.HasSuffix(testCase.Name, ".") { - testCases[i].Name = fmt.Sprintf("%s.", testCases[i].Name) - } - msg := new(dns.Msg) - msg.SetQuestion(testCase.Name, testCase.Request) - msg = msg.SetQuestion(testCase.Name, testCase.Request) - var in *dns.Msg - in, testCase.RTT, err = resolver.LookUp(msg) - assert.NotNil(t, err) - assert.Nil(t, in) - case 1: - resolver, err := LoadResolver(testCase2.Server, opts) - testCase2.Server = net.JoinHostPort(testCase2.Server, strconv.Itoa(opts.Port)) - // if the domain is not canonical, make it canonical - if !strings.HasSuffix(testCase2.Name, ".") { - testCase2.Name = fmt.Sprintf("%s.", testCase2.Name) - } - msg := new(dns.Msg) - msg.SetQuestion(testCase2.Name, testCase2.Request) - msg = msg.SetQuestion(testCase2.Name, testCase2.Request) - var in *dns.Msg - in, testCase.RTT, err = resolver.LookUp(msg) - assert.Nil(t, err) - assert.NotNil(t, in) - } - - } - -} diff --git a/query/resolver.go b/query/resolver.go index f1ac190..f6df45e 100644 --- a/query/resolver.go +++ b/query/resolver.go @@ -1,60 +1,24 @@ +// SPDX-License-Identifier: BSD-3-Clause + package query import ( "net" "strconv" "strings" - "time" - "git.froth.zone/sam/awl/logawl" + "git.froth.zone/sam/awl/cli" + "git.froth.zone/sam/awl/internal/structs" "github.com/miekg/dns" ) -// represent all CLI flags -type Options struct { - Logger *logawl.Logger - - Port int - IPv4 bool - IPv6 bool - DNSSEC bool - Short bool - TCP bool - TLS bool - HTTPS bool - QUIC bool - Truncate bool - AA bool - TC bool - Z bool - CD bool - NoRD bool - NoRA bool - Reverse bool - Debug bool - Answers Answers -} -type Response struct { - Answers Answers `json:"Response"` // These be DNS query answers - DNS dns.Msg -} - -// The Answers struct is the basic structure of a DNS request -// to be returned to the user upon making a request -type Answers struct { - Server string `json:"Server"` // The server to make the DNS request from - DNS *dns.Msg - Request uint16 `json:"Request"` // The type of request - Name string `json:"Name"` // The domain name to make a DNS request for - RTT time.Duration `json:"RTT"` // The time it took to make the DNS query -} - type Resolver interface { - LookUp(*dns.Msg) (*dns.Msg, time.Duration, error) + LookUp(*dns.Msg) (structs.Response, error) } -func LoadResolver(server string, opts Options) (Resolver, error) { - if opts.HTTPS { +func LoadResolver(server string, opts cli.Options) (Resolver, error) { + switch { + case opts.HTTPS: opts.Logger.Debug("loading DoH resolver") if !strings.HasPrefix(server, "https://") { server = "https://" + server @@ -63,14 +27,19 @@ func LoadResolver(server string, opts Options) (Resolver, error) { server: server, opts: opts, }, nil - } else if opts.QUIC { + case opts.QUIC: opts.Logger.Debug("loading DoQ resolver") - server = net.JoinHostPort(opts.Answers.Server, strconv.Itoa(opts.Port)) + server = net.JoinHostPort(opts.Request.Server, strconv.Itoa(opts.Port)) return &QUICResolver{ server: server, opts: opts, }, nil + default: + opts.Logger.Debug("loading standard/DoT resolver") + server = net.JoinHostPort(opts.Request.Server, strconv.Itoa(opts.Port)) + return &StandardResolver{ + server: server, + opts: opts, + }, nil } - - return nil, nil } diff --git a/util/logger.go b/util/logger.go index 3d39476..4a3b2a7 100644 --- a/util/logger.go +++ b/util/logger.go @@ -1,13 +1,14 @@ +// SPDX-License-Identifier: BSD-3-Clause + package util import "git.froth.zone/sam/awl/logawl" -func InitLogger(debug bool) (Logger *logawl.Logger) { +// Initialize the logawl instance +func InitLogger(verbosity int) (Logger *logawl.Logger) { Logger = logawl.New() - if debug { - Logger.SetLevel(3) - } + Logger.SetLevel(logawl.Level(verbosity)) return } diff --git a/util/helpers.go b/util/reverseDNS.go similarity index 100% rename from util/helpers.go rename to util/reverseDNS.go diff --git a/util/helpers_test.go b/util/reverseDNS_test.go similarity index 93% rename from util/helpers_test.go rename to util/reverseDNS_test.go index a53a1e4..fe1d16f 100644 --- a/util/helpers_test.go +++ b/util/reverseDNS_test.go @@ -15,18 +15,21 @@ var ( ) func TestIPv4(t *testing.T) { + t.Parallel() act, err := ReverseDNS("8.8.4.4", PTR) assert.Nil(t, err) assert.Equal(t, act, "4.4.8.8.in-addr.arpa.", "IPv4 reverse") } func TestIPv6(t *testing.T) { + t.Parallel() act, err := ReverseDNS("2606:4700:4700::1111", PTR) assert.Nil(t, err) assert.Equal(t, act, "1.1.1.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.7.4.0.0.7.4.6.0.6.2.ip6.arpa.", "IPv6 reverse") } func TestNAPTR(t *testing.T) { + t.Parallel() tests := []struct { in string want string @@ -44,6 +47,7 @@ func TestNAPTR(t *testing.T) { } func TestInvalid(t *testing.T) { + t.Parallel() _, err := ReverseDNS("AAAAA", 1) - assert.NotNil(t, err) + assert.Error(t, err) } -- 2.43.0 From b35f1b5c67c5f608c2f505f2c621b49bf63ccc31 Mon Sep 17 00:00:00 2001 From: Sam Therapy Date: Wed, 20 Jul 2022 23:17:27 +0200 Subject: [PATCH 02/17] Lint Signed-off-by: Sam Therapy --- cli/misc.go | 3 +++ logawl/logger.go | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/cli/misc.go b/cli/misc.go index 2e2f6d9..693ecda 100644 --- a/cli/misc.go +++ b/cli/misc.go @@ -36,6 +36,9 @@ func ParseMiscArgs(args []string, opts *Options) error { case strings.HasPrefix(arg, "+"): // Dig-style +queries err = ParseDig(strings.ToLower(arg[1:]), opts) + if err != nil { + return err + } default: //else, assume it's a name opts.Query, err = idna.ToASCII(arg) diff --git a/logawl/logger.go b/logawl/logger.go index 6376a7b..0a9939c 100644 --- a/logawl/logger.go +++ b/logawl/logger.go @@ -92,12 +92,15 @@ func (l *Logger) Printer(level Level, s string) error { defer l.Mu.Unlock() l.buf = l.buf[:0] - l.FormatHeader(&l.buf, now, line, level) + err := l.FormatHeader(&l.buf, now, line, level) + if err != nil { + return err + } l.buf = append(l.buf, s...) if len(s) == 0 || s[len(s)-1] != '\n' { l.buf = append(l.buf, '\n') } - _, err := l.Out.Write(l.buf) + _, err = l.Out.Write(l.buf) return err } -- 2.43.0 From 92812c337fc93c6607bcec9174e43881c1bc8ae2 Mon Sep 17 00:00:00 2001 From: Sam Therapy Date: Thu, 21 Jul 2022 19:06:46 -0500 Subject: [PATCH 03/17] Another day's work Signed-off-by: Sam Therapy --- Makefile | 10 ++- cli/cli.go | 43 +++++++------ cli/cli_test.go | 89 ++++++++++++++++++++------ cli/dig.go | 23 ++++--- cli/dig_test.go | 43 +++++++++++++ cli/misc.go | 45 +++++-------- cli/misc_test.go | 111 ++++++++++++++++++++++++++++++++ cli/options.go | 14 ++-- conf/plan9.go | 4 +- conf/plan9_test.go | 21 +++--- conf/{notwin.go => unix.go} | 3 +- conf/unix_test.go | 21 ++++++ conf/win_test.go | 21 ++++++ doc/awl.1 | 113 +++++++++++++++++++++------------ doc/awl.1.md | 104 ++++++++++++++++++------------ go.mod | 7 +- go.sum | 7 ++ internal/helpers/docs.go | 4 ++ internal/helpers/query.go | 23 +++++++ internal/helpers/query_test.go | 13 ++++ logawl/docs.go | 2 +- logawl/logawl.go | 34 +++++----- logawl/logger.go | 4 ++ logawl/logging_test.go | 17 ++--- main.go | 6 +- query/HTTPS.go | 18 +++--- query/HTTPS_test.go | 38 ++++++----- query/QUIC.go | 22 +++---- query/QUIC_test.go | 26 ++++---- query/general.go | 8 +-- query/general_test.go | 54 ++++++++++++++++ query/query.go | 13 ++-- query/query_test.go | 34 ++++++++++ query/resolver.go | 4 +- util/logger.go | 2 +- util/logger_test.go | 16 +++++ util/reverseDNS.go | 5 +- util/reverseDNS_test.go | 29 +++++---- 38 files changed, 757 insertions(+), 294 deletions(-) create mode 100644 cli/dig_test.go create mode 100644 cli/misc_test.go rename conf/{notwin.go => unix.go} (88%) create mode 100644 conf/unix_test.go create mode 100644 conf/win_test.go create mode 100644 internal/helpers/docs.go create mode 100644 internal/helpers/query.go create mode 100644 internal/helpers/query_test.go create mode 100644 query/general_test.go create mode 100644 query/query_test.go create mode 100644 util/logger_test.go diff --git a/Makefile b/Makefile index d9bb0e7..6ecb350 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,6 @@ GO := go GOFLAGS := -ldflags '-s -w' PREFIX := /usr/local BINPATH = $(PREFIX)/bin -export CGO_ENABLED=1 -export CC=gcc .PHONY: clean doc @@ -18,7 +16,7 @@ doc: scdoc < doc/awl.1.md > doc/awl.1 test: - $(GO) test -race -v ./... -cover + $(GO) test -v ./... -cover fmt: gofmt -w -s . @@ -30,6 +28,12 @@ lint: fmt vet install: awl install awl $(BINPATH) + +install_doc: doc + install doc/awl.1 $(PREFIX)/share/man/man1 + @echo "" + @echo "Make sure to update your manual database" + @echo "'sudo mandb' on Debian/Ubuntu" clean: $(GO) clean \ No newline at end of file diff --git a/cli/cli.go b/cli/cli.go index c6309f6..be1e509 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -6,16 +6,18 @@ import ( "fmt" "os" "runtime" + "strings" - "git.froth.zone/sam/awl/internal/structs" + "git.froth.zone/sam/awl/internal/helpers" "git.froth.zone/sam/awl/util" "github.com/miekg/dns" flag "github.com/stefansundin/go-zflag" ) -// Parse the arguments passed into awl +// Parse the arguments passed into awl. func ParseCLI(version string) (Options, error) { + flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError) flag.Usage = func() { fmt.Println(`awl - drill, writ small @@ -24,20 +26,19 @@ func ParseCLI(version string) (Options, error) { domain, IP address, phone number defaults to A - Arguments may be in any order, including flags. - Dig-like +[no]commands are also supported, see dig(1) or dig -h + Arguments may be in any order, including flags. + Dig-like +[no]commands are also supported, see dig(1) or dig -h Options:`) flag.PrintDefaults() - os.Exit(0) } // CLI flag var ( - port = flag.Int("port", 0, "`port` to make DNS query (default: 53 for UDP/TCP, 853 for TLS/QUIC", flag.OptShorthand('p'), flag.OptDisablePrintDefault(true)) - query = flag.String("query", ".", "domain name to query", flag.OptShorthand('q')) - class = flag.String("class", "IN", "DNS class to query", flag.OptShorthand('c')) - qType = flag.String("qType", "A", "type to query", flag.OptShorthand('t')) + port = flag.Int("port", 0, "`port` to make DNS query (default: 53 for UDP/TCP, 853 for TLS/QUIC)", flag.OptShorthand('p'), flag.OptDisablePrintDefault(true)) + query = flag.String("query", "", "domain name to `query` (default: .)", flag.OptShorthand('q')) + class = flag.String("class", "IN", "DNS `class` to query", flag.OptShorthand('c')) + qType = flag.String("qType", "", "`type` to query (default: A)", flag.OptShorthand('t')) ipv4 = flag.Bool("4", false, "force IPv4", flag.OptShorthandStr("4")) ipv6 = flag.Bool("6", false, "force IPv6", flag.OptShorthand('6')) @@ -54,9 +55,9 @@ func ParseCLI(version string) (Options, error) { 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)") cd = flag.Bool("cd", false, "set/unset CD (Checking Disabled) flag (default: not set)") - qr = flag.Bool("qr", false, "set/unset QR (QueRy) flag (default: set)", flag.OptDisablePrintDefault(true)) + qr = flag.Bool("qr", false, "set/unset QR (QueRy) flag (default: not set)") rd = flag.Bool("rd", true, "set/unset RD (Recursion Desired) flag (default: set)", flag.OptDisablePrintDefault(true)) - ra = flag.Bool("ra", false, "set/unset RA (Recursion Available) flag (default: set)", flag.OptDisablePrintDefault(true)) + ra = flag.Bool("ra", false, "set/unset RA (Recursion Available) flag (default: not set)") tc = flag.Bool("tc", false, "set/unset TC (TrunCated) flag (default: not set)") z = flag.Bool("z", false, "set/unset Z (Zero) flag (default: not set)", flag.OptShorthand('z')) @@ -73,11 +74,13 @@ func ParseCLI(version string) (Options, error) { flag.CommandLine.SortFlags = false // Parse the flags - flag.Parse() + err := flag.CommandLine.Parse(os.Args[1:]) + if err != nil { + return Options{Logger: util.InitLogger(*verbosity)}, err + } opts := Options{ Logger: util.InitLogger(*verbosity), - Class: dns.StringToClass[*class], Port: *port, IPv4: *ipv4, IPv6: *ipv6, @@ -100,22 +103,24 @@ func ParseCLI(version string) (Options, error) { JSON: *json, XML: *xml, YAML: *yaml, - Request: structs.Request{ - Type: dns.StringToType[*qType], - Name: *query, + Request: helpers.Request{ + Type: dns.StringToType[strings.ToUpper(*qType)], + Class: dns.StringToClass[strings.ToUpper(*class)], + Name: *query, }, } if *versionFlag { fmt.Printf("awl version %s, built with %s\n", version, runtime.Version()) - os.Exit(0) + return opts, ErrNotError } // Parse all the arguments that don't start with - or -- // This includes the dig-style (+) options - err := ParseMiscArgs(flag.Args(), &opts) + err = ParseMiscArgs(flag.Args(), &opts) if err != nil { - opts.Logger.Fatal(err) + opts.Logger.Error(err) + return opts, err } if opts.Port == 0 { diff --git a/cli/cli_test.go b/cli/cli_test.go index 7ac5666..36ec6be 100644 --- a/cli/cli_test.go +++ b/cli/cli_test.go @@ -2,44 +2,95 @@ package cli_test -// TODO: readd these +import ( + "os" + "testing" -// import ( -// "os" -// "testing" + "git.froth.zone/sam/awl/cli" + "gotest.tools/v3/assert" +) -// "git.froth.zone/sam/awl/internal/structs" -// "git.froth.zone/sam/awl/query" +func TestEmpty(t *testing.T) { + old := os.Args + os.Args = []string{""} + opts, err := cli.ParseCLI("TEST") + assert.NilError(t, err) + assert.Assert(t, opts != cli.Options{}) + os.Args = old +} -// "github.com/miekg/dns" -// "github.com/stretchr/testify/assert" -// "github.com/stretchr/testify/require" -// ) +func TestInvalidFlag(t *testing.T) { + old := os.Args + os.Args = []string{"awl", "--treebug"} + _, err := cli.ParseCLI("TEST") + assert.ErrorContains(t, err, "unknown flag") + os.Args = old +} -// func TestApp(t *testing.T) { -// t.Parallel() -// app := prepareCLI() -// // What more can even be done lmao -// require.NotNil(t, app) +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") + 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 FuzzFlags(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) { + os.Args = []string{orig} + //nolint:errcheck // Only make sure the program does not crash + cli.ParseCLI("TEST") + }) +} + // func TestArgParse(t *testing.T) { // t.Parallel() // tests := []struct { // in []string -// want structs.Request +// want helpers.Request // }{ // { // []string{"@::1", "localhost", "AAAA"}, -// structs.Request{Server: "::1", Type: dns.TypeAAAA, Name: "localhost"}, +// helpers.Request{Server: "::1", Type: dns.TypeAAAA, Name: "localhost"}, // }, // { // []string{"@1.0.0.1", "google.com"}, -// structs.Request{Server: "1.0.0.1", Type: dns.TypeA, Name: "google.com"}, +// helpers.Request{Server: "1.0.0.1", Type: dns.TypeA, Name: "google.com"}, // }, // { // []string{"@8.8.4.4"}, -// structs.Request{Server: "8.8.4.4", Type: dns.TypeNS, Name: "."}, +// helpers.Request{Server: "8.8.4.4", Type: dns.TypeNS, Name: "."}, // }, // } // for _, test := range tests { diff --git a/cli/dig.go b/cli/dig.go index 938eff5..e697ed9 100644 --- a/cli/dig.go +++ b/cli/dig.go @@ -7,27 +7,27 @@ import ( "strings" ) -// Parse dig-like commands and set the options as such +// Parse dig-like commands and set the options as such. func ParseDig(arg string, opts *Options) error { // returns true if the flag starts with a no isNo := !strings.HasPrefix(arg, "no") switch arg { // Set DNS query flags - case "aaflag", "aaonly", "noaaflag", "noaaonly": + case "aa", "aaflag", "aaonly", "noaa", "noaaflag", "noaaonly": opts.AA = isNo - case "adflag", "noadflag": + case "ad", "adflag", "noad", "noadflag": opts.AD = isNo - case "cdflag", "nocdflag": + case "cd", "cdflag", "nocd", "nocdflag": opts.CD = isNo - case "qrflag", "noqrflag": + case "qr", "qrflag", "noqr", "noqrflag": opts.QR = isNo - case "raflag", "noraflag": + case "ra", "raflag", "nora", "noraflag": opts.RA = isNo - case "rdflag", "recurse", "nordflag", "norecurse": + case "rd", "rdflag", "recurse", "nord", "nordflag", "norecurse": opts.RD = isNo - case "tcflag", "notcflag": + case "tc", "tcflag", "notc", "notcflag": opts.TC = isNo - case "zflag", "nozflag": + case "z", "zflag", "noz", "nozflag": opts.Z = isNo // End DNS query flags @@ -36,8 +36,7 @@ func ParseDig(arg string, opts *Options) error { case "tcp", "vc", "notcp", "novc": opts.TCP = isNo case "ignore", "noignore": - // Invert (ignore truncation when true) - opts.Truncate = !isNo + opts.Truncate = isNo // Formatting case "short", "noshort": @@ -51,7 +50,7 @@ func ParseDig(arg string, opts *Options) error { // End formatting default: - return fmt.Errorf("dig: Unknown flag given") + return fmt.Errorf("dig: unknown flag given") } return nil } diff --git a/cli/dig_test.go b/cli/dig_test.go new file mode 100644 index 0000000..8d1292a --- /dev/null +++ b/cli/dig_test.go @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: BSD-3-Clause + +package cli_test + +import ( + "testing" + + "git.froth.zone/sam/awl/cli" + "gotest.tools/v3/assert" +) + +func FuzzDig(f *testing.F) { + f.Log("ParseDig Fuzzing") + seeds := []string{ + "aaflag", "aaonly", "noaaflag", "noaaonly", + "adflag", "noadflag", + "cdflag", "nocdflag", + "qrflag", "noqrflag", + "raflag", "noraflag", + "rdflag", "recurse", "nordflag", "norecurse", + "tcflag", "notcflag", + "zflag", "nozflag", + "dnssec", "nodnssec", + "tcp", "vc", "notcp", "novc", + "ignore", "noignore", + "short", "noshort", + "json", "nojson", + "xml", "noxml", + "yaml", "noyaml", + "invalid", + } + for _, tc := range seeds { + f.Add(tc) + } + + f.Fuzz(func(t *testing.T, orig string) { + opts := new(cli.Options) + err := cli.ParseDig(orig, opts) + if err != nil { + assert.ErrorContains(t, err, "unknown flag given") + } + }) +} diff --git a/cli/misc.go b/cli/misc.go index 693ecda..c334e12 100644 --- a/cli/misc.go +++ b/cli/misc.go @@ -8,14 +8,13 @@ import ( "strings" "git.froth.zone/sam/awl/conf" - "git.froth.zone/sam/awl/internal/structs" "git.froth.zone/sam/awl/util" "github.com/miekg/dns" "golang.org/x/net/idna" ) -// Parse the wildcard arguments, drill style +// Parse the wildcard arguments, drill style. func ParseMiscArgs(args []string, opts *Options) error { var err error @@ -26,13 +25,13 @@ func ParseMiscArgs(args []string, opts *Options) error { case strings.HasPrefix(arg, "@"): opts.Request.Server = arg[1:] case strings.Contains(arg, "."): - opts.Query, err = idna.ToASCII(arg) + opts.Request.Name, err = idna.ToASCII(arg) if err != nil { return err } case ok: // If it's a DNS request, it's a DNS request (obviously) - opts.Type = r + opts.Request.Type = r case strings.HasPrefix(arg, "+"): // Dig-style +queries err = ParseDig(strings.ToLower(arg[1:]), opts) @@ -40,26 +39,26 @@ func ParseMiscArgs(args []string, opts *Options) error { return err } default: - //else, assume it's a name - opts.Query, err = idna.ToASCII(arg) + + opts.Request.Name, err = idna.ToASCII(arg) if err != nil { return err } - } } // If nothing was set, set a default - if opts.Query == "" { - opts.Query = "." - if opts.Type == 0 { - opts.Type = dns.StringToType["NS"] + if opts.Request.Name == "" { + opts.Request.Name = "." + if opts.Request.Type == 0 { + opts.Request.Type = dns.StringToType["NS"] } } else { - if opts.Type == 0 { - opts.Type = dns.StringToType["A"] + if opts.Request.Type == 0 { + opts.Request.Type = dns.StringToType["A"] } } + // if opts.Request.Server == "" { switch { case opts.TLS: @@ -97,29 +96,17 @@ func ParseMiscArgs(args []string, opts *Options) error { // Make reverse adresses proper addresses if opts.Reverse { if dns.TypeToString[opts.Request.Type] == "A" { - opts.Type = dns.StringToType["PTR"] + opts.Request.Type = dns.StringToType["PTR"] } - opts.Query, err = util.ReverseDNS(opts.Query, opts.Request.Type) + opts.Request.Name, err = util.ReverseDNS(opts.Request.Name, opts.Request.Type) if err != nil { return err } } - if opts.Class == 0 { - opts.Class = dns.StringToClass["IN"] - } - // if the domain is not canonical, make it canonical - if !strings.HasSuffix(opts.Query, ".") { - opts.Query = fmt.Sprintf("%s.", opts.Query) + if !strings.HasSuffix(opts.Request.Name, ".") { + opts.Request.Name = fmt.Sprintf("%s.", opts.Request.Name) } - - opts.Request = structs.Request{ - Server: opts.Request.Server, - Type: opts.Type, - Class: opts.Class, - Name: opts.Query, - } - return nil } diff --git a/cli/misc_test.go b/cli/misc_test.go new file mode 100644 index 0000000..bbed34b --- /dev/null +++ b/cli/misc_test.go @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: BSD-3-Clause + +package cli_test + +import ( + "testing" + + "git.froth.zone/sam/awl/cli" + + "github.com/miekg/dns" + "gotest.tools/v3/assert" +) + +func TestParseArgs(t *testing.T) { + t.Parallel() + args := []string{ + "go.dev", + "AAAA", + "@1.1.1.1", + "+ignore", + } + opts := new(cli.Options) + err := cli.ParseMiscArgs(args, opts) + assert.NilError(t, err) + assert.Equal(t, opts.Request.Name, "go.dev.") + assert.Equal(t, opts.Request.Type, dns.StringToType["AAAA"]) + assert.Equal(t, opts.Request.Server, "1.1.1.1") + assert.Equal(t, opts.Truncate, true) +} + +func TestParseNoInput(t *testing.T) { + t.Parallel() + args := []string{} + opts := new(cli.Options) + err := cli.ParseMiscArgs(args, opts) + assert.NilError(t, err) + assert.Equal(t, opts.Request.Name, ".") + assert.Equal(t, opts.Request.Type, dns.StringToType["NS"]) +} + +func TestParseA(t *testing.T) { + t.Parallel() + args := []string{ + "golang.org.", + } + opts := new(cli.Options) + err := cli.ParseMiscArgs(args, opts) + assert.NilError(t, err) + assert.Equal(t, opts.Request.Name, "golang.org.") + assert.Equal(t, opts.Request.Type, dns.StringToType["A"]) +} + +func TestParsePTR(t *testing.T) { + t.Parallel() + args := []string{"8.8.8.8"} + opts := new(cli.Options) + opts.Reverse = true + err := cli.ParseMiscArgs(args, opts) + assert.NilError(t, err) + assert.Equal(t, opts.Request.Type, dns.StringToType["PTR"]) +} + +func TestDefaultServer(t *testing.T) { + t.Parallel() + tests := []struct { + in string + want string + }{ + {"TLS", "dns.google"}, + {"HTTPS", "https://dns.cloudflare.com/dns-query"}, + {"QUIC", "dns.adguard.com"}, + } + for _, test := range tests { + test := test + t.Run(test.in, func(t *testing.T) { + t.Parallel() + args := []string{} + opts := new(cli.Options) + switch test.in { + case "TLS": + opts.TLS = true + case "HTTPS": + opts.HTTPS = true + case "QUIC": + opts.QUIC = true + } + err := cli.ParseMiscArgs(args, opts) + assert.NilError(t, err) + assert.Equal(t, opts.Request.Server, test.want) + }) + } +} + +func FuzzParseArgs(f *testing.F) { + cases := []string{ + "go.dev", + "AAAA", + "@1.1.1.1", + "+ignore", + "e", + } + for _, tc := range cases { + f.Add(tc) + } + f.Fuzz(func(t *testing.T, arg string) { + args := []string{arg} + opts := new(cli.Options) + //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 c1891c1..4f07778 100644 --- a/cli/options.go +++ b/cli/options.go @@ -3,17 +3,16 @@ package cli import ( - "git.froth.zone/sam/awl/internal/structs" + "errors" + + "git.froth.zone/sam/awl/internal/helpers" "git.froth.zone/sam/awl/logawl" ) -// CLI options structure +// CLI options structure. type Options struct { Logger *logawl.Logger // Logger Port int // DNS port - Query string // DNS Query - Class uint16 // DNS Class - Type uint16 // DNS Type IPv4 bool // Force IPv4 IPv6 bool // Force IPv6 DNSSEC bool // Enable DNSSEC @@ -32,10 +31,13 @@ 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 - Request structs.Request + Request helpers.Request } + +var ErrNotError = errors.New("not an error") diff --git a/conf/plan9.go b/conf/plan9.go index cb507fb..2819c92 100644 --- a/conf/plan9.go +++ b/conf/plan9.go @@ -13,7 +13,7 @@ import ( // Yoink it and use it. // // See ndb(7). -func getPlan9Config(str string) (*dns.ClientConfig, error) { +func GetPlan9Config(str string) (*dns.ClientConfig, error) { str = strings.ReplaceAll(str, "\n", "") spl := strings.FieldsFunc(str, splitChars) var servers []string @@ -34,7 +34,7 @@ func getPlan9Config(str string) (*dns.ClientConfig, error) { }, nil } -// Split the string at either space or tabs +// Split the string at either space or tabs. func splitChars(r rune) bool { return r == ' ' || r == '\t' } diff --git a/conf/plan9_test.go b/conf/plan9_test.go index 573906d..a5123fc 100644 --- a/conf/plan9_test.go +++ b/conf/plan9_test.go @@ -1,11 +1,12 @@ // SPDX-License-Identifier: BSD-3-Clause -package conf +package conf_test import ( "testing" - "github.com/stretchr/testify/assert" + "git.froth.zone/sam/awl/conf" + "gotest.tools/v3/assert" ) func TestGetPlan9Config(t *testing.T) { @@ -28,9 +29,14 @@ func TestGetPlan9Config(t *testing.T) { } for _, ndb := range ndbs { - act, err := getPlan9Config(ndb.in) - assert.Nil(t, err) - assert.Equal(t, ndb.want, act.Servers[0]) + // Go is a little quirky + ndb := ndb + t.Run(ndb.want, func(t *testing.T) { + t.Parallel() + act, err := conf.GetPlan9Config(ndb.in) + assert.NilError(t, err) + assert.Equal(t, ndb.want, act.Servers[0]) + }) } invalid := `sys = spindle @@ -39,8 +45,7 @@ func TestGetPlan9Config(t *testing.T) { ip=135.104.117.32 ether=080069020677 proto=il` - act, err := getPlan9Config(invalid) + act, err := conf.GetPlan9Config(invalid) assert.ErrorContains(t, err, "no DNS servers found") - assert.Nil(t, act) - + assert.Assert(t, act == nil) } diff --git a/conf/notwin.go b/conf/unix.go similarity index 88% rename from conf/notwin.go rename to conf/unix.go index 7f8aa7a..12eb49f 100644 --- a/conf/notwin.go +++ b/conf/unix.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: BSD-3-Clause //go:build !windows -// +build !windows package conf @@ -18,7 +17,7 @@ func GetDNSConfig() (*dns.ClientConfig, error) { if err != nil { return nil, err } - return getPlan9Config(string(dat)) + return GetPlan9Config(string(dat)) } else { return dns.ClientConfigFromFile("/etc/resolv.conf") } diff --git a/conf/unix_test.go b/conf/unix_test.go new file mode 100644 index 0000000..9899c3a --- /dev/null +++ b/conf/unix_test.go @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: BSD-3-Clause +//go:build !windows + +package conf_test + +import ( + "runtime" + "testing" + + "git.froth.zone/sam/awl/conf" + "gotest.tools/v3/assert" +) + +func TestNonWinConfig(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Not running Windows, skipping") + } + conf, err := conf.GetDNSConfig() + assert.NilError(t, err) + assert.Assert(t, len(conf.Servers) != 0) +} diff --git a/conf/win_test.go b/conf/win_test.go new file mode 100644 index 0000000..0b1531e --- /dev/null +++ b/conf/win_test.go @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: BSD-3-Clause +//go:build windows + +package conf_test + +import ( + "runtime" + "testing" + + "git.froth.zone/sam/awl/conf" + "gotest.tools/v3/assert" +) + +func TestWinConfig(t *testing.T) { + if runtime.GOOS != "windows" { + t.Skip("Not running Windows, skipping") + } + conf, err := conf.GetDNSConfig() + assert.NilError(t, err) + assert.Assert(t, len(conf.Servers) != 0) +} diff --git a/doc/awl.1 b/doc/awl.1 index 18cbefa..597ff69 100644 --- a/doc/awl.1 +++ b/doc/awl.1 @@ -5,7 +5,7 @@ .nh .ad l .\" Begin generated content: -.TH "AWL" "1" "2022-07-20" +.TH "awl" "1" "2022-07-21" .PP .SH NAME awl - drill, writ small @@ -23,23 +23,27 @@ 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 Work-In-Progress so some features may get added or removed.\& +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 +Work-In-Progress so some features may get added or removed.\& .PP .SH OPTIONS .RS 4 -\fB-D\fR, \fB--dnssec\fR +Dig-like queries are supported, see dig(1) +.PP +\fB-D\fR, \fB--dnssec\fR, \fB+dnssec\fR .br Enable DNSSEC.\& This needs to be manually enabled.\& .PP -\fB--debug\fR +\fB-v\fR \fIvalue\fR .br - Enable debug logging (currently WIP).\& + Set verbosity (currently WIP) .PP -\fB-v\fR +\fB-V\fR .br Print the version and exit.\& .PP @@ -76,27 +80,35 @@ Written in Go, \fBawl\fR is designed to be a more "modern" version of \fIdrill\f .PD .PP .RE -\fB--no-truncate\fR +\fB-q\fR, \fB--query\fR \fIdomain\fR +.br + Domain to query (eg.\& example.\&com) +.PP +\fB-c\fR, \fB--class\fR \fIclass\fR +.br + DNS class to query (eg.\& IN, CH) +.PP +\fB-t\fR, \fB--qType\fR \fItype\fR +.br + DNS type to query (eg.\& A, NS) +.PP +\fB--no-truncate\fR, \fB+ignore\fR .br Ignore UDP truncation (by default, awl \fIretries with TCP\fR) .PP -\fB-t\fR, \fB--tcp\fR +\fB-t\fR, \fB--tcp\fR, \fB+tcp\fR, \fB+vc\fR .br Use TCP for the query (see \fIRFC 7766\fR) .PP -\fB-u\fR, \fB--udp\fR -.br - Use UDP for the query (default) -.PP -\fB-T\fR, \fB--tls\fR +\fB-T\fR, \fB--tls\fR, \fB+tls\fR .br Use DNS-over-TLS, implies \fB-t\fR (see \fIRFC 7858\fR) .PP -\fB-H\fR.\& \fB--https\fR +\fB-H\fR.\& \fB--https\fR, \fB+https\fR .br Use DNS-over-HTTPS (see \fIRFC 8484\fR) .PP -\fB-Q\fR.\& \fB--quic\fR +\fB-Q\fR.\& \fB--quic\fR, \fB+quic\fR .br Use DNS-over-QUIC (see \fIRFC 9250\fR) .PP @@ -110,59 +122,80 @@ Written in Go, \fBawl\fR is designed to be a more "modern" version of \fIdrill\f .SS DNS Flags .PP .RS 4 -\fB--aa\fR +\fB--aa=[false]\fR, \fB+[no]aaflag\fR .br - \fISET\fR Authoritative Answer (default: unset) + (Set, Unset) AA (Authoritative Answer) flag .PP -\fB--ad\fR +\fB--ad=[false]\fR, \fB+[no]adflag\fR .br - \fISET\fR Authenticated Data (default: unset) + (Set, Unset) AD (Authenticated Data) flag .PP -\fB--tc\fR +\fB--tc=[false]\fR, \fB+[no]tcflag\fR .br - \fISET\fR TC (TrunCated) flag (default: not set) + (Set, Unset) TC (TrunCated) flag .PP -\fB-z\fR +\fB-z=[false]\fR, \fB+[no]zflag\fR .br - \fISET\fR Z (Zero) flag (default: not set) + (Set, Unset) Z (Zero) flag .PP -\fB--cd\fR +\fB--cd=[false]\fR, \fB+[no]cdflag\fR .br - \fISET\fR CD (Checking Disabled) flag (default: not set) + (Set, Unset) CD (Checking Disabled) flag .PP -\fB--no-qr\fR +\fB--qr=[false]\fR, \fB+[no]qrflag\fR .br - \fIUNSET\fR QR (QueRy) flag (default: set) + (Set, Unset) QR (QueRy) flag .PP -\fB--no-rd\fR +\fB--rd=[true]\fR, \fB+[no]rdflag\fR .br - \fIUNSET\fR RD (Recursion Desired) flag (default: set) + (Set, Unset) RD (Recursion Desired) flag .PP -\fB--no-ra\fR +\fB--ra=[false]\fR, \fB+[no]raflag\fR .br - \fIUNSET\fR RA (Recursion Available) flag (default: set) + (Set, Unset) RA (Recursion Available) flag .PP .RE .SS Output Options .RS 4 -\fB-j\fR, \fB--json\fR +\fB-j\fR, \fB--json\fR, \fB+json\fR .br Print the query results as JSON.\& .PP -\fB-X\fR, \fB--xml\fR +\fB-X\fR, \fB--xml\fR, \fB+xml\fR .br Print the query results as XML.\& .PP -\fB-y\fR, \fB--yaml\fR +\fB-y\fR, \fB--yaml\fR, \fB+yaml\fR .br Print the query results as YAML.\& .PP -\fB-s\fR, \fB--short\fR +\fB-s\fR, \fB--short\fR, \fB+short\fR .br Print just the results.\& -.br - Equivalent to \fBdig +short\fR .PP .RE +.SH EXAMPLES +.nf +.RS 4 +awl grumbulon\&.xyz -j +cd +.fi +.RE +Run a query of your local resolver for the A records of grumbulon.\&xyz, print +them as JSON and disable DNSSEC verification.\& +.PP +.nf +.RS 4 +awl +short example\&.com AAAA @1\&.1\&.1\&.1 +.fi +.RE +Query 1.\&1.\&1.\&1 for the AAAA records of example.\&com, print just the answers +.PP +.nf +.RS 4 +awl -xT PTR 8\&.8\&.4\&.4 @dns\&.google +.fi +.RE +Query dns.\&google over TLS for the PTR record to the IP address 8.\&8.\&4.\&4 +.PP .SH SEE ALSO \fIdrill\fR(1), \fIdig\fR(1), the many DNS RFCs \ No newline at end of file diff --git a/doc/awl.1.md b/doc/awl.1.md index 46f3125..e6d3126 100644 --- a/doc/awl.1.md +++ b/doc/awl.1.md @@ -1,4 +1,4 @@ -AWL(1) +awl(1) # NAME awl - drill, writ small @@ -13,22 +13,24 @@ _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 Work-In-Progress so some features may get added or removed. +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 +Work-In-Progress so some features may get added or removed. # OPTIONS - _Dig-like queries are supported, see dig(1)_ + Dig-like queries are supported, see dig(1) - *-D*, *--dnssec*++ + *-D*, *--dnssec*, *+dnssec*++ Enable DNSSEC. This needs to be manually enabled. - *--debug*++ - Enable debug logging (currently WIP). + *-v* _value_++ + Set verbosity (currently WIP) - *-v*++ + *-V*++ Print the version and exit. *-h*++ @@ -49,22 +51,28 @@ _Default Ports_: - _853_ for *TLS* and *QUIC* - _443_ for *HTTPS* - *--no-truncate*++ + *-q*, *--query* _domain_++ + Domain to query (eg. example.com) + + *-c*, *--class* _class_++ + DNS class to query (eg. IN, CH) + + *-t*, *--qType* _type_++ + DNS type to query (eg. A, NS) + + *--no-truncate*, *+ignore*++ Ignore UDP truncation (by default, awl _retries with TCP_) - *-t*, *--tcp*++ + *-t*, *--tcp*, *+tcp*, *+vc*++ Use TCP for the query (see _RFC 7766_) - *-u*, *--udp*++ - Use UDP for the query (default) - - *-T*, *--tls*++ + *-T*, *--tls*, *+tls*++ Use DNS-over-TLS, implies *-t* (see _RFC 7858_) - *-H*. *--https*++ + *-H*. *--https*, *+https*++ Use DNS-over-HTTPS (see _RFC 8484_) - *-Q*. *--quic*++ + *-Q*. *--quic*, *+quic*++ Use DNS-over-QUIC (see _RFC 9250_) *-x*, *--reverse*++ @@ -73,43 +81,59 @@ _Default Ports_: ## DNS Flags - *--aa*++ - _SET_ Authoritative Answer (default: unset) + *--aa=[false]*, *+[no]aaflag*++ + (Set, Unset) AA (Authoritative Answer) flag - *--ad*++ - _SET_ Authenticated Data (default: unset) + *--ad=[false]*, *+[no]adflag*++ + (Set, Unset) AD (Authenticated Data) flag - *--tc*++ - _SET_ TC (TrunCated) flag (default: not set) + *--tc=[false]*, *+[no]tcflag*++ + (Set, Unset) TC (TrunCated) flag - *-z*++ - _SET_ Z (Zero) flag (default: not set) + *-z=[false]*, *+[no]zflag*++ + (Set, Unset) Z (Zero) flag - *--cd*++ - _SET_ CD (Checking Disabled) flag (default: not set) + *--cd=[false]*, *+[no]cdflag*++ + (Set, Unset) CD (Checking Disabled) flag - *--no-qr*++ - _UNSET_ QR (QueRy) flag (default: set) + *--qr=[false]*, *+[no]qrflag*++ + (Set, Unset) QR (QueRy) flag - *--no-rd*++ - _UNSET_ RD (Recursion Desired) flag (default: set) + *--rd=[true]*, *+[no]rdflag*++ + (Set, Unset) RD (Recursion Desired) flag - *--no-ra*++ - _UNSET_ RA (Recursion Available) flag (default: set) + *--ra=[false]*, *+[no]raflag*++ + (Set, Unset) RA (Recursion Available) flag ## Output Options - *-j*, *--json*++ + *-j*, *--json*, *+json*++ Print the query results as JSON. - *-X*, *--xml*++ + *-X*, *--xml*, *+xml*++ Print the query results as XML. - *-y*, *--yaml*++ + *-y*, *--yaml*, *+yaml*++ Print the query results as YAML. - *-s*, *--short*++ - Print just the results.++ - Equivalent to *dig +short* + *-s*, *--short*, *+short*++ + Print just the results. + +# EXAMPLES +``` +awl grumbulon.xyz -j +cd +``` +Run a query of your local resolver for the A records of grumbulon.xyz, print +them as JSON and disable DNSSEC verification. + +``` +awl +short example.com AAAA @1.1.1.1 +``` +Query 1.1.1.1 for the AAAA records of example.com, print just the answers + +``` +awl -xT PTR 8.8.4.4 @dns.google +``` +Query dns.google over TLS for the PTR record to the IP address 8.8.4.4 # SEE ALSO _drill_(1), _dig_(1), the many DNS RFCs \ No newline at end of file diff --git a/go.mod b/go.mod index 412acf0..af04dab 100644 --- a/go.mod +++ b/go.mod @@ -8,13 +8,12 @@ require ( github.com/stefansundin/go-zflag v1.1.1 golang.org/x/net v0.0.0-20220708220712-1185a9018129 gopkg.in/yaml.v2 v2.4.0 + gotest.tools/v3 v3.3.0 ) require ( - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/google/go-cmp v0.5.5 // indirect github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( @@ -26,7 +25,7 @@ require ( github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/onsi/ginkgo v1.16.5 // indirect - github.com/stretchr/testify v1.8.0 + github.com/stretchr/testify v1.8.0 // indirect golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 diff --git a/go.sum b/go.sum index 5442a9b..a4ef21f 100644 --- a/go.sum +++ b/go.sum @@ -55,6 +55,7 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -145,6 +146,7 @@ github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYED github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stefansundin/go-zflag v1.1.1 h1:XabhzWS588bVvV1z1UctSa6i8zHkXc5W9otqtnDSHw8= github.com/stefansundin/go-zflag v1.1.1/go.mod h1:HXX5rABl1AoTcZ2jw+CqJ7R8irczaLquGNZlFabZooc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -226,6 +228,7 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w 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= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -254,6 +257,7 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 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-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= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY= @@ -261,6 +265,7 @@ golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= @@ -303,6 +308,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo= +gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/helpers/docs.go b/internal/helpers/docs.go new file mode 100644 index 0000000..945e5a6 --- /dev/null +++ b/internal/helpers/docs.go @@ -0,0 +1,4 @@ +/* +Useful structs used everywhere I couldn't find a better place to shove +*/ +package helpers diff --git a/internal/helpers/query.go b/internal/helpers/query.go new file mode 100644 index 0000000..a8247f8 --- /dev/null +++ b/internal/helpers/query.go @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: BSD-3-Clause + +package helpers + +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/internal/helpers/query_test.go b/internal/helpers/query_test.go new file mode 100644 index 0000000..f3f5da5 --- /dev/null +++ b/internal/helpers/query_test.go @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: BSD-3-Clause + +package helpers_test + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestNothing(t *testing.T) { + assert.Equal(t, 0, 0) +} diff --git a/logawl/docs.go b/logawl/docs.go index b67c5ea..ac20e97 100644 --- a/logawl/docs.go +++ b/logawl/docs.go @@ -20,7 +20,7 @@ because awl is a cli utility it writes directly to std err. // // Logger.SetLevel(3) // This allows you to change the default level (Info) and prevent log messages from being posted at higher verbosity levels -//for example if +// for example if // Logger.SetLevel(3) // is not called and you call // Logger.Debug() diff --git a/logawl/logawl.go b/logawl/logawl.go index f5c996e..5cbe445 100644 --- a/logawl/logawl.go +++ b/logawl/logawl.go @@ -9,32 +9,34 @@ import ( "sync/atomic" ) -type Level int32 -type Logger struct { - Mu sync.Mutex - Level Level - Prefix string - Out io.Writer - buf []byte - isDiscard int32 -} +type ( + Level int32 + Logger struct { + Mu sync.Mutex + Level Level + Prefix string + Out io.Writer + buf []byte + isDiscard int32 + } +) -// Stores whatever input value is in mem address of l.level +// Stores whatever input value is in mem address of l.level. func (l *Logger) SetLevel(level Level) { atomic.StoreInt32((*int32)(&l.Level), int32(level)) } -// Mostly nothing +// Mostly nothing. func (l *Logger) GetLevel() Level { return l.level() } -// Retrieves whatever was stored in mem address of l.level +// Retrieves whatever was stored in mem address of l.level. func (l *Logger) level() Level { return Level(atomic.LoadInt32((*int32)(&l.Level))) } -// Unmarshalls the int value of level for writing the header +// Unmarshalls the int value of level for writing the header. func (l *Logger) UnMarshalLevel(lv Level) (string, error) { switch lv { case 0: @@ -61,13 +63,13 @@ var AllLevels = []Level{ } const ( - // Fatal logs (will call exit(1)) + // Fatal logs (will call exit(1)). FatalLevel Level = iota - // Error logs + // Error logs. ErrorLevel - // What is going on level + // What is going on level. InfoLevel // Verbose log level. DebugLevel diff --git a/logawl/logger.go b/logawl/logger.go index 0a9939c..c2311c8 100644 --- a/logawl/logger.go +++ b/logawl/logger.go @@ -30,23 +30,27 @@ func (l *Logger) Println(level Level, v ...any) { case 0: err := l.Printer(0, fmt.Sprintln(v...)) //Fatal level if err != nil { + // TODO: Make this not a panic panic(err) } os.Exit(1) case 1: err := l.Printer(1, fmt.Sprintln(v...)) //Error level if err != nil { + // TODO: Make this not a panic panic(err) } os.Exit(2) case 2: err := l.Printer(2, fmt.Sprintln(v...)) //Info level if err != nil { + // TODO: Make this not a panic panic(err) } case 3: err := l.Printer(3, fmt.Sprintln(v...)) //Debug level if err != nil { + // TODO: Make this not a panic panic(err) } default: diff --git a/logawl/logging_test.go b/logawl/logging_test.go index 9b187dc..202feca 100644 --- a/logawl/logging_test.go +++ b/logawl/logging_test.go @@ -9,7 +9,7 @@ import ( "git.froth.zone/sam/awl/logawl" - "github.com/stretchr/testify/assert" + "gotest.tools/v3/assert" ) var logger = logawl.New() @@ -17,35 +17,29 @@ var logger = logawl.New() func TestLogawl(t *testing.T) { t.Parallel() - assert.Equal(t, logawl.Level(2), logger.Level) //cast 2 (int) to 2 (level) - - //Validate setting and getting levels from memory works for i := range logawl.AllLevels { logger.SetLevel(logawl.Level(i)) assert.Equal(t, logawl.Level(i), logger.GetLevel()) } - } func TestUnmarshalLevels(t *testing.T) { t.Parallel() m := make(map[int]string) var err error - //Fill map with unmarshalled level info + for i := range logawl.AllLevels { m[i], err = logger.UnMarshalLevel(logawl.Level(i)) - assert.Nil(t, err) + assert.NilError(t, err) } - //iterate over map and assert equal for i := range logawl.AllLevels { lv, err := logger.UnMarshalLevel(logawl.Level(i)) - assert.Nil(t, err) + assert.NilError(t, err) assert.Equal(t, m[i], lv) } lv, err := logger.UnMarshalLevel(logawl.Level(9001)) - assert.NotNil(t, err) assert.Equal(t, "", lv) assert.ErrorContains(t, err, "invalid log level choice") } @@ -79,13 +73,12 @@ func TestLogger(t *testing.T) { fn() } } - } func TestFmt(t *testing.T) { t.Parallel() ti := time.Now() test := []byte("test") - assert.NotNil(t, logger.FormatHeader(&test, ti, 0, logawl.Level(9001))) //make sure error is error + assert.ErrorContains(t, logger.FormatHeader(&test, ti, 0, 9001), "invalid log level") //make sure error is error } diff --git a/main.go b/main.go index 991be6b..a4d88ea 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ package main import ( "encoding/json" "encoding/xml" + "errors" "fmt" "os" "strings" @@ -21,6 +22,10 @@ var version = "DEV" func main() { opts, err := cli.ParseCLI(version) if err != nil { + // TODO: Make not ew + if errors.Is(err, cli.ErrNotError) || strings.Contains(err.Error(), "help requested") { + os.Exit(0) + } opts.Logger.Fatal(err) os.Exit(1) } @@ -67,5 +72,4 @@ func main() { } } } - } diff --git a/query/HTTPS.go b/query/HTTPS.go index df0bc6b..bb8d925 100644 --- a/query/HTTPS.go +++ b/query/HTTPS.go @@ -10,7 +10,7 @@ import ( "time" "git.froth.zone/sam/awl/cli" - "git.froth.zone/sam/awl/internal/structs" + "git.froth.zone/sam/awl/internal/helpers" "github.com/miekg/dns" ) @@ -19,18 +19,18 @@ type HTTPSResolver struct { opts cli.Options } -func (r *HTTPSResolver) LookUp(msg *dns.Msg) (structs.Response, error) { - var resp structs.Response +func (r *HTTPSResolver) LookUp(msg *dns.Msg) (helpers.Response, error) { + var resp helpers.Response httpR := &http.Client{} buf, err := msg.Pack() if err != nil { - return structs.Response{}, err + 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)) if err != nil { - return structs.Response{}, fmt.Errorf("DoH: %s", err.Error()) + return helpers.Response{}, fmt.Errorf("DoH: %s", err.Error()) } req.Header.Set("Content-Type", "application/dns-message") req.Header.Set("Accept", "application/dns-message") @@ -40,23 +40,23 @@ func (r *HTTPSResolver) LookUp(msg *dns.Msg) (structs.Response, error) { resp.RTT = time.Since(now) if err != nil { - return structs.Response{}, fmt.Errorf("DoH HTTP request error: %s", err.Error()) + return helpers.Response{}, fmt.Errorf("DoH HTTP request error: %s", err.Error()) } defer res.Body.Close() if res.StatusCode != http.StatusOK { - return structs.Response{}, fmt.Errorf("DoH server responded with HTTP %d", res.StatusCode) + return helpers.Response{}, fmt.Errorf("DoH server responded with HTTP %d", res.StatusCode) } fullRes, err := io.ReadAll(res.Body) if err != nil { - return structs.Response{}, fmt.Errorf("DoH body read error: %s", err.Error()) + return helpers.Response{}, fmt.Errorf("DoH body read error: %s", err.Error()) } resp.DNS = &dns.Msg{} r.opts.Logger.Debug("unpacking response") err = resp.DNS.Unpack(fullRes) if err != nil { - return structs.Response{}, fmt.Errorf("DoH dns message unpack error: %s", err.Error()) + return helpers.Response{}, fmt.Errorf("DoH dns message unpack error: %s", err.Error()) } return resp, nil diff --git a/query/HTTPS_test.go b/query/HTTPS_test.go index 8309554..bb0a5f4 100644 --- a/query/HTTPS_test.go +++ b/query/HTTPS_test.go @@ -8,12 +8,12 @@ import ( "testing" "git.froth.zone/sam/awl/cli" - "git.froth.zone/sam/awl/internal/structs" + "git.froth.zone/sam/awl/internal/helpers" "git.froth.zone/sam/awl/query" "git.froth.zone/sam/awl/util" "github.com/miekg/dns" - "github.com/stretchr/testify/assert" + "gotest.tools/v3/assert" ) func TestResolveHTTPS(t *testing.T) { @@ -23,9 +23,9 @@ func TestResolveHTTPS(t *testing.T) { HTTPS: true, Logger: util.InitLogger(0), } - testCase := structs.Request{Server: "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) - assert.Nil(t, err) + assert.NilError(t, err) if !strings.HasPrefix(testCase.Server, "https://") { testCase.Server = "https://" + testCase.Server @@ -37,11 +37,10 @@ func TestResolveHTTPS(t *testing.T) { msg := new(dns.Msg) msg.SetQuestion(testCase.Name, testCase.Type) - msg = msg.SetQuestion(testCase.Name, testCase.Type) + // msg = msg.SetQuestion(testCase.Name, testCase.Type) res, err := resolver.LookUp(msg) - assert.Nil(t, err) - assert.NotNil(t, res) - + assert.NilError(t, err) + assert.Assert(t, res != helpers.Response{}) } func Test2ResolveHTTPS(t *testing.T) { @@ -51,17 +50,17 @@ func Test2ResolveHTTPS(t *testing.T) { Logger: util.InitLogger(0), } var err error - testCase := structs.Request{Server: "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) - assert.Nil(t, err) + assert.NilError(t, err) msg := new(dns.Msg) msg.SetQuestion(testCase.Name, testCase.Type) - msg = msg.SetQuestion(testCase.Name, testCase.Type) + // msg = msg.SetQuestion(testCase.Name, testCase.Type) res, err := resolver.LookUp(msg) - assert.Error(t, err) - assert.Equal(t, res, structs.Response{}) - + assert.ErrorContains(t, err, "fully qualified") + assert.Equal(t, res, helpers.Response{}) } + func Test3ResolveHTTPS(t *testing.T) { t.Parallel() opts := cli.Options{ @@ -69,7 +68,7 @@ func Test3ResolveHTTPS(t *testing.T) { Logger: util.InitLogger(0), } var err error - testCase := structs.Request{Server: "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."} if !strings.HasPrefix(testCase.Server, "https://") { testCase.Server = "https://" + testCase.Server } @@ -78,12 +77,11 @@ func Test3ResolveHTTPS(t *testing.T) { testCase.Name = fmt.Sprintf("%s.", testCase.Name) } resolver, err := query.LoadResolver(testCase.Server, opts) - assert.Nil(t, err) + assert.NilError(t, err) msg := new(dns.Msg) msg.SetQuestion(testCase.Name, testCase.Type) - msg = msg.SetQuestion(testCase.Name, testCase.Type) + // msg = msg.SetQuestion(testCase.Name, testCase.Type) res, err := resolver.LookUp(msg) - assert.Error(t, err) - assert.Equal(t, res, structs.Response{}) - + assert.ErrorContains(t, err, "request error") + assert.Equal(t, res, helpers.Response{}) } diff --git a/query/QUIC.go b/query/QUIC.go index 3026cee..e9aa373 100644 --- a/query/QUIC.go +++ b/query/QUIC.go @@ -8,7 +8,7 @@ import ( "time" "git.froth.zone/sam/awl/cli" - "git.froth.zone/sam/awl/internal/structs" + "git.froth.zone/sam/awl/internal/helpers" "github.com/lucas-clemente/quic-go" "github.com/miekg/dns" @@ -19,8 +19,8 @@ type QUICResolver struct { opts cli.Options } -func (r *QUICResolver) LookUp(msg *dns.Msg) (structs.Response, error) { - var resp structs.Response +func (r *QUICResolver) LookUp(msg *dns.Msg) (helpers.Response, error) { + var resp helpers.Response tls := &tls.Config{ MinVersion: tls.VersionTLS12, NextProtos: []string{"doq"}, @@ -28,46 +28,46 @@ func (r *QUICResolver) LookUp(msg *dns.Msg) (structs.Response, error) { r.opts.Logger.Debug("making DoQ request") connection, err := quic.DialAddr(r.server, tls, nil) if err != nil { - return structs.Response{}, err + return helpers.Response{}, err } // Compress request to over-the-wire buf, err := msg.Pack() if err != nil { - return structs.Response{}, err + return helpers.Response{}, err } t := time.Now() stream, err := connection.OpenStream() if err != nil { - return structs.Response{}, err + return helpers.Response{}, err } _, err = stream.Write(buf) if err != nil { - return structs.Response{}, err + return helpers.Response{}, err } fullRes, err := io.ReadAll(stream) if err != nil { - return structs.Response{}, err + return helpers.Response{}, err } resp.RTT = time.Since(t) // Close with error: no error err = connection.CloseWithError(0, "") if err != nil { - return structs.Response{}, err + return helpers.Response{}, err } err = stream.Close() if err != nil { - return structs.Response{}, err + return helpers.Response{}, err } resp.DNS = &dns.Msg{} r.opts.Logger.Debug("unpacking DoQ response") err = resp.DNS.Unpack(fullRes) if err != nil { - return structs.Response{}, err + return helpers.Response{}, err } return resp, nil } diff --git a/query/QUIC_test.go b/query/QUIC_test.go index bc765f7..751d0b2 100644 --- a/query/QUIC_test.go +++ b/query/QUIC_test.go @@ -10,12 +10,12 @@ import ( "testing" "git.froth.zone/sam/awl/cli" - "git.froth.zone/sam/awl/internal/structs" + "git.froth.zone/sam/awl/internal/helpers" "git.froth.zone/sam/awl/query" "git.froth.zone/sam/awl/util" "github.com/miekg/dns" - "github.com/stretchr/testify/assert" + "gotest.tools/v3/assert" ) func TestQuic(t *testing.T) { @@ -24,18 +24,18 @@ func TestQuic(t *testing.T) { QUIC: true, Logger: util.InitLogger(0), Port: 853, - Request: structs.Request{Server: "dns.adguard.com"}, + Request: helpers.Request{Server: "dns.adguard.com"}, } - testCase := structs.Request{Server: "dns.//./,,adguard.com", Type: dns.TypeA, Name: "git.froth.zone"} - testCase2 := structs.Request{Server: "dns.adguard.com", Type: dns.TypeA, Name: "git.froth.zone"} - var testCases []structs.Request + testCase := helpers.Request{Server: "dns.//./,,adguard.com", Type: dns.TypeA, Name: "git.froth.zone"} + testCase2 := helpers.Request{Server: "dns.adguard.com", Type: dns.TypeA, Name: "git.froth.zone"} + var testCases []helpers.Request testCases = append(testCases, testCase) testCases = append(testCases, testCase2) for i := range testCases { switch i { case 0: resolver, err := query.LoadResolver(testCases[i].Server, opts) - assert.Nil(t, err) + assert.NilError(t, err) // if the domain is not canonical, make it canonical if !strings.HasSuffix(testCase.Name, ".") { testCases[i].Name = fmt.Sprintf("%s.", testCases[i].Name) @@ -44,11 +44,11 @@ func TestQuic(t *testing.T) { msg.SetQuestion(testCase.Name, testCase.Type) msg = msg.SetQuestion(testCase.Name, testCase.Type) res, err := resolver.LookUp(msg) - assert.NotNil(t, err) - assert.Equal(t, res, structs.Response{}) + assert.ErrorContains(t, err, "fully qualified") + assert.Equal(t, res, helpers.Response{}) case 1: resolver, err := query.LoadResolver(testCase2.Server, opts) - assert.Nil(t, err) + assert.NilError(t, err) testCase2.Server = net.JoinHostPort(testCase2.Server, strconv.Itoa(opts.Port)) // if the domain is not canonical, make it canonical if !strings.HasSuffix(testCase2.Name, ".") { @@ -58,10 +58,8 @@ func TestQuic(t *testing.T) { msg.SetQuestion(testCase2.Name, testCase2.Type) msg = msg.SetQuestion(testCase2.Name, testCase2.Type) res, err := resolver.LookUp(msg) - assert.Nil(t, err) - assert.NotNil(t, res) + assert.NilError(t, err) + assert.Assert(t, res != helpers.Response{}) } - } - } diff --git a/query/general.go b/query/general.go index b92783a..2a29fab 100644 --- a/query/general.go +++ b/query/general.go @@ -6,7 +6,7 @@ import ( "fmt" "git.froth.zone/sam/awl/cli" - "git.froth.zone/sam/awl/internal/structs" + "git.froth.zone/sam/awl/internal/helpers" "github.com/miekg/dns" ) @@ -16,9 +16,9 @@ type StandardResolver struct { opts cli.Options } -func (r *StandardResolver) LookUp(msg *dns.Msg) (structs.Response, error) { +func (r *StandardResolver) LookUp(msg *dns.Msg) (helpers.Response, error) { var ( - resp structs.Response + resp helpers.Response err error ) dnsClient := new(dns.Client) @@ -41,7 +41,7 @@ func (r *StandardResolver) LookUp(msg *dns.Msg) (structs.Response, error) { resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, r.server) if err != nil { - return structs.Response{}, err + return helpers.Response{}, err } if resp.DNS.MsgHdr.Truncated && !r.opts.Truncate { diff --git a/query/general_test.go b/query/general_test.go new file mode 100644 index 0000000..2b8d82f --- /dev/null +++ b/query/general_test.go @@ -0,0 +1,54 @@ +// 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 TestResolve(t *testing.T) { + t.Parallel() + opts := cli.Options{ + Logger: util.InitLogger(0), + Port: 53, + Request: helpers.Request{ + Server: "8.8.4.4", + Type: dns.TypeA, + Name: "example.com.", + }, + } + resolver, err := query.LoadResolver(opts.Request.Server, opts) + assert.NilError(t, err) + msg := new(dns.Msg) + msg.SetQuestion(opts.Request.Name, opts.Request.Type) + res, err := resolver.LookUp(msg) + assert.NilError(t, err) + assert.Assert(t, res != helpers.Response{}) +} + +func TestTruncate(t *testing.T) { + t.Parallel() + opts := cli.Options{ + Logger: util.InitLogger(0), + Port: 5301, + Request: helpers.Request{ + Server: "madns.binarystar.systems", + Type: dns.TypeTXT, + Name: "limit.txt.example.", + }, + } + resolver, err := query.LoadResolver(opts.Request.Server, opts) + assert.NilError(t, err) + msg := new(dns.Msg) + msg.SetQuestion(opts.Request.Name, opts.Request.Type) + res, err := resolver.LookUp(msg) + assert.NilError(t, err) + assert.Assert(t, res != helpers.Response{}) +} diff --git a/query/query.go b/query/query.go index 4421bd9..f069113 100644 --- a/query/query.go +++ b/query/query.go @@ -1,16 +1,19 @@ +// SPDX-License-Identifier: BSD-3-Clause + package query import ( "git.froth.zone/sam/awl/cli" - "git.froth.zone/sam/awl/internal/structs" + "git.froth.zone/sam/awl/internal/helpers" "github.com/miekg/dns" ) -func CreateQuery(opts cli.Options) (structs.Response, error) { - var res structs.Response +func CreateQuery(opts cli.Options) (helpers.Response, error) { + var res helpers.Response res.DNS = new(dns.Msg) - res.DNS.SetQuestion(opts.Query, opts.Type) + res.DNS.SetQuestion(opts.Request.Name, opts.Request.Type) + res.DNS.Question[0].Qclass = opts.Request.Class res.DNS.MsgHdr.Response = opts.QR res.DNS.MsgHdr.Authoritative = opts.AA @@ -27,7 +30,7 @@ func CreateQuery(opts cli.Options) (structs.Response, error) { resolver, err := LoadResolver(opts.Request.Server, opts) if err != nil { - return structs.Response{}, err + return helpers.Response{}, err } return resolver.LookUp(res.DNS) diff --git a/query/query_test.go b/query/query_test.go new file mode 100644 index 0000000..ab00cfa --- /dev/null +++ b/query/query_test.go @@ -0,0 +1,34 @@ +// 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 TestCreateQ(t *testing.T) { + opts := cli.Options{ + Logger: util.InitLogger(0), + Port: 53, + QR: false, + Z: true, + RD: false, + DNSSEC: true, + Request: helpers.Request{ + Server: "8.8.4.4", + Type: dns.TypeA, + Name: "example.com.", + }, + } + res, err := query.CreateQuery(opts) + assert.NilError(t, err) + assert.Assert(t, res != helpers.Response{}) +} diff --git a/query/resolver.go b/query/resolver.go index f6df45e..e5484fd 100644 --- a/query/resolver.go +++ b/query/resolver.go @@ -8,12 +8,12 @@ import ( "strings" "git.froth.zone/sam/awl/cli" - "git.froth.zone/sam/awl/internal/structs" + "git.froth.zone/sam/awl/internal/helpers" "github.com/miekg/dns" ) type Resolver interface { - LookUp(*dns.Msg) (structs.Response, error) + LookUp(*dns.Msg) (helpers.Response, error) } func LoadResolver(server string, opts cli.Options) (Resolver, error) { diff --git a/util/logger.go b/util/logger.go index 4a3b2a7..037d4d7 100644 --- a/util/logger.go +++ b/util/logger.go @@ -4,7 +4,7 @@ package util import "git.froth.zone/sam/awl/logawl" -// Initialize the logawl instance +// Initialize the logawl instance. func InitLogger(verbosity int) (Logger *logawl.Logger) { Logger = logawl.New() diff --git a/util/logger_test.go b/util/logger_test.go new file mode 100644 index 0000000..a9502be --- /dev/null +++ b/util/logger_test.go @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: BSD-3-Clause + +package util_test + +import ( + "testing" + + "git.froth.zone/sam/awl/logawl" + "git.froth.zone/sam/awl/util" + "gotest.tools/v3/assert" +) + +func TestInitLogger(t *testing.T) { + logger := util.InitLogger(0) + assert.Equal(t, logger.Level, logawl.Level(0)) +} diff --git a/util/reverseDNS.go b/util/reverseDNS.go index bd153e9..57d800e 100644 --- a/util/reverseDNS.go +++ b/util/reverseDNS.go @@ -10,7 +10,7 @@ import ( "github.com/miekg/dns" ) -// Given an IP or phone number, return a canonical string to be queried +// Given an IP or phone number, return a canonical string to be queried. func ReverseDNS(address string, querInt uint16) (string, error) { query := dns.TypeToString[querInt] if query == "PTR" { @@ -33,11 +33,10 @@ func ReverseDNS(address string, querInt uint16) (string, error) { return "", errors.New("ReverseDNS: -x flag given but no IP found") } -// Reverse a string, return the string in reverse +// Reverse a string, return the string in reverse. func reverse(s string) string { rns := []rune(s) for i, j := 0, len(rns)-1; i < j; i, j = i+1, j-1 { - rns[i], rns[j] = rns[j], rns[i] } return string(rns) diff --git a/util/reverseDNS_test.go b/util/reverseDNS_test.go index fe1d16f..7841e40 100644 --- a/util/reverseDNS_test.go +++ b/util/reverseDNS_test.go @@ -1,12 +1,14 @@ // SPDX-License-Identifier: BSD-3-Clause -package util +package util_test import ( "testing" + "git.froth.zone/sam/awl/util" + "github.com/miekg/dns" - "github.com/stretchr/testify/assert" + "gotest.tools/v3/assert" ) var ( @@ -16,15 +18,15 @@ var ( func TestIPv4(t *testing.T) { t.Parallel() - act, err := ReverseDNS("8.8.4.4", PTR) - assert.Nil(t, err) + act, err := util.ReverseDNS("8.8.4.4", PTR) + assert.NilError(t, err) assert.Equal(t, act, "4.4.8.8.in-addr.arpa.", "IPv4 reverse") } func TestIPv6(t *testing.T) { t.Parallel() - act, err := ReverseDNS("2606:4700:4700::1111", PTR) - assert.Nil(t, err) + act, err := util.ReverseDNS("2606:4700:4700::1111", PTR) + assert.NilError(t, err) assert.Equal(t, act, "1.1.1.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.7.4.0.0.7.4.6.0.6.2.ip6.arpa.", "IPv6 reverse") } @@ -40,14 +42,19 @@ func TestNAPTR(t *testing.T) { {"17705551212", "2.1.2.1.5.5.5.0.7.7.1.e164.arpa."}, } for _, test := range tests { - act, err := ReverseDNS(test.in, NAPTR) - assert.Nil(t, err) - assert.Equal(t, test.want, act) + // Thanks Goroutines, very cool! + test := test + t.Run(test.in, func(t *testing.T) { + t.Parallel() + act, err := util.ReverseDNS(test.in, NAPTR) + assert.NilError(t, err) + assert.Equal(t, test.want, act) + }) } } func TestInvalid(t *testing.T) { t.Parallel() - _, err := ReverseDNS("AAAAA", 1) - assert.Error(t, err) + _, err := util.ReverseDNS("AAAAA", 1) + assert.ErrorContains(t, err, "no IP found") } -- 2.43.0 From eda2e5b55f8e32b3f5edf23ef1b4fa58c7cfef8e Mon Sep 17 00:00:00 2001 From: Sam Therapy Date: Fri, 22 Jul 2022 23:22:43 +0200 Subject: [PATCH 04/17] 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 } } -- 2.43.0 From 7ed7d9bf814723f079240ada66dbc0f56cf5cd54 Mon Sep 17 00:00:00 2001 From: Sam Therapy Date: Sat, 23 Jul 2022 00:47:16 +0200 Subject: [PATCH 05/17] Fix mkfile Signed-off-by: Sam Therapy --- Mkfile => mkfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) rename Mkfile => mkfile (88%) diff --git a/Mkfile b/mkfile similarity index 88% rename from Mkfile rename to mkfile index 3c3649c..64aa154 100644 --- a/Mkfile +++ b/mkfile @@ -5,6 +5,7 @@ SCDOC = scdoc PROG = awl LDFLAGS = '-s -w' GOFLAGS = -ldflags=$LDFLAGS + CGO_ENABLED = 0 $PROG: @@ -16,8 +17,8 @@ doc: doc/$PROG.1.md install: $GO install $GOFLAGS . -install_doc: doc - install doc/$PROG.1 /sys/man/1 +install_doc: + cp doc/$PROG.1 /sys/man/1/$PROG test: $GO test -v ./... -cover -- 2.43.0 From 0dfab9589f5474e04d82970cc77c178c9cda07fd Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 23 Jul 2022 00:54:38 +0000 Subject: [PATCH 06/17] fix(deps): update golang.org/x/sys digest to 8c9f86f --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 65665ad..82507ca 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( github.com/stretchr/testify v1.8.0 // indirect golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 + golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.11 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect diff --git a/go.sum b/go.sum index 751dcc7..1ca6e12 100644 --- a/go.sum +++ b/go.sum @@ -257,6 +257,8 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -- 2.43.0 From c3198326eca3da6f185eee42a8e8c2f034142c1c Mon Sep 17 00:00:00 2001 From: Sam Therapy Date: Sun, 24 Jul 2022 18:54:10 +0200 Subject: [PATCH 07/17] logger: change fatal to error and make the logger not make the program exit Signed-off-by: Sam Therapy --- logawl/logawl.go | 14 +++++++------- logawl/logger.go | 18 ++++++------------ logawl/logging_test.go | 2 +- main.go | 8 ++++---- 4 files changed, 18 insertions(+), 24 deletions(-) diff --git a/logawl/logawl.go b/logawl/logawl.go index 5cbe445..70c9416 100644 --- a/logawl/logawl.go +++ b/logawl/logawl.go @@ -40,15 +40,15 @@ func (l *Logger) level() Level { func (l *Logger) UnMarshalLevel(lv Level) (string, error) { switch lv { case 0: - return "FATAL ", nil - case 1: return "ERROR ", nil + case 1: + return "WARN ", nil case 2: return "INFO ", nil case 3: return "DEBUG ", nil } - return "", fmt.Errorf("invalid log level choice") + return "", fmt.Errorf("invalid log level") } func (l *Logger) IsLevel(level Level) bool { @@ -56,18 +56,18 @@ func (l *Logger) IsLevel(level Level) bool { } var AllLevels = []Level{ - FatalLevel, - ErrorLevel, + ErrLevel, + WarnLevel, InfoLevel, DebugLevel, } const ( // Fatal logs (will call exit(1)). - FatalLevel Level = iota + ErrLevel Level = iota // Error logs. - ErrorLevel + WarnLevel // What is going on level. InfoLevel diff --git a/logawl/logger.go b/logawl/logger.go index c2311c8..ddae454 100644 --- a/logawl/logger.go +++ b/logawl/logger.go @@ -30,28 +30,22 @@ func (l *Logger) Println(level Level, v ...any) { case 0: err := l.Printer(0, fmt.Sprintln(v...)) //Fatal level if err != nil { - // TODO: Make this not a panic - panic(err) + fmt.Fprintln(os.Stderr, "FATAL: Logger failed: ", err) } - os.Exit(1) case 1: err := l.Printer(1, fmt.Sprintln(v...)) //Error level if err != nil { - // TODO: Make this not a panic - panic(err) + fmt.Fprintln(os.Stderr, "FATAL: Logger failed: ", err) } - os.Exit(2) case 2: err := l.Printer(2, fmt.Sprintln(v...)) //Info level if err != nil { - // TODO: Make this not a panic - panic(err) + fmt.Fprintln(os.Stderr, "FATAL: Logger failed: ", err) } case 3: err := l.Printer(3, fmt.Sprintln(v...)) //Debug level if err != nil { - // TODO: Make this not a panic - panic(err) + fmt.Fprintln(os.Stderr, "FATAL: Logger failed: ", err) } default: break @@ -141,10 +135,10 @@ func (l *Logger) Info(v ...any) { // Call print directly with Error level func (l *Logger) Error(v ...any) { - l.Println(ErrorLevel, v...) + l.Println(WarnLevel, v...) } // Call print directly with Fatal level func (l *Logger) Fatal(v ...any) { - l.Println(FatalLevel, v...) + l.Println(ErrLevel, v...) } diff --git a/logawl/logging_test.go b/logawl/logging_test.go index 202feca..ffbcf60 100644 --- a/logawl/logging_test.go +++ b/logawl/logging_test.go @@ -41,7 +41,7 @@ func TestUnmarshalLevels(t *testing.T) { lv, err := logger.UnMarshalLevel(logawl.Level(9001)) assert.Equal(t, "", lv) - assert.ErrorContains(t, err, "invalid log level choice") + assert.ErrorContains(t, err, "invalid log level") } func TestLogger(t *testing.T) { diff --git a/main.go b/main.go index 9583128..923045a 100644 --- a/main.go +++ b/main.go @@ -33,7 +33,7 @@ func main() { resp, err := query.CreateQuery(opts) if err != nil { opts.Logger.Fatal(err) - os.Exit(1) + os.Exit(9) } switch { case opts.JSON: @@ -41,7 +41,7 @@ func main() { json, err := json.MarshalIndent(resp.DNS, "", " ") if err != nil { opts.Logger.Fatal(err) - os.Exit(1) + os.Exit(10) } fmt.Println(string(json)) case opts.XML: @@ -49,7 +49,7 @@ func main() { xml, err := xml.MarshalIndent(resp.DNS, "", " ") if err != nil { opts.Logger.Fatal(err) - os.Exit(1) + os.Exit(10) } fmt.Println(string(xml)) case opts.YAML: @@ -57,7 +57,7 @@ func main() { yaml, err := yaml.Marshal(resp.DNS) if err != nil { opts.Logger.Fatal(err) - os.Exit(1) + os.Exit(10) } fmt.Println(string(yaml)) default: -- 2.43.0 From 20d3196a8c74de2e7d9ec6b99e6bfe12802d64bf Mon Sep 17 00:00:00 2001 From: Sam Therapy Date: Sun, 24 Jul 2022 19:34:51 +0200 Subject: [PATCH 08/17] Add a retry flag Signed-off-by: Sam Therapy --- .vscode/launch.json | 16 ++ cli/cli.go | 13 +- cli/dig.go | 41 +++- cli/dig_test.go | 4 + cli/misc.go | 2 +- doc/awl.1 | 494 ++++++++++++++++++++------------------ doc/awl.1.md | 11 +- internal/helpers/query.go | 1 + logawl/logger.go | 8 +- main.go | 25 +- 10 files changed, 367 insertions(+), 248 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..f1f5f5e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${fileDirname}", + "args": ["+timeout=1"] + } + ] +} \ No newline at end of file diff --git a/cli/cli.go b/cli/cli.go index f483aed..80dc2a7 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -46,6 +46,7 @@ func ParseCLI(version string) (Options, error) { reverse = flag.Bool("reverse", false, "do a reverse lookup", flag.OptShorthand('x')) timeout = flag.Float32("timeout", 1, "Timeout, in `seconds`") + retry = flag.Int("retry", 2, "number of `times` to retry") 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)") @@ -118,6 +119,7 @@ func ParseCLI(version string) (Options, error) { Class: dns.StringToClass[strings.ToUpper(*class)], Name: *query, Timeout: time.Duration(*timeout * float32(time.Second)), + Retries: *retry, }, Display: Displays{ Question: !*noQ, @@ -128,6 +130,15 @@ func ParseCLI(version string) (Options, error) { }, } + // Set timeout to 0.5 seconds if set below 0.5 + if float32(opts.Request.Timeout) < (0.5 * float32(time.Second)) { + opts.Request.Timeout = (time.Second / 2) + } + + if opts.Request.Retries < 0 { + opts.Request.Timeout = 0 + } + opts.Logger.Info("POSIX flags parsed") opts.Logger.Debug(fmt.Sprintf("%+v", opts)) @@ -140,7 +151,7 @@ func ParseCLI(version string) (Options, error) { // This includes the dig-style (+) options err = ParseMiscArgs(flag.Args(), &opts) if err != nil { - opts.Logger.Error(err) + opts.Logger.Warn(err) return opts, err } opts.Logger.Info("Dig/Drill flags parsed") diff --git a/cli/dig.go b/cli/dig.go index 458476e..32b4193 100644 --- a/cli/dig.go +++ b/cli/dig.go @@ -4,14 +4,16 @@ package cli import ( "fmt" + "strconv" "strings" + "time" ) // Parse dig-like commands and set the options as such. 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) + opts.Logger.Info("Setting", arg) switch arg { // Set DNS query flags @@ -33,12 +35,22 @@ func ParseDig(arg string, opts *Options) error { opts.Z = isNo // End DNS query flags + // DNS-over-X case "dnssec", "nodnssec": opts.DNSSEC = isNo case "tcp", "vc", "notcp", "novc": opts.TCP = isNo case "ignore", "noignore": opts.Truncate = isNo + case "tls", "notls": + opts.TLS = isNo + case "dnscrypt", "nodnscrypt": + opts.DNSCrypt = isNo + case "https", "nohttps": + opts.HTTPS = isNo + case "quic", "noquic": + opts.QUIC = isNo + // End DNS-over-X // Formatting case "short", "noshort": @@ -74,7 +86,32 @@ func ParseDig(arg string, opts *Options) error { opts.Display.Statistics = isNo default: - return fmt.Errorf("dig: unknown flag %s given", arg) + // Recursive switch statements WOO + switch { + case strings.HasPrefix(arg, "timeout"): + timeout, err := strconv.Atoi(strings.Split(arg, "=")[1]) + + if err != nil { + return fmt.Errorf("dig: Invalid timeout value") + } + + opts.Request.Timeout = time.Duration(timeout) + + case strings.HasPrefix(arg, "retry"), strings.HasPrefix(arg, "tries"): + tries, err := strconv.Atoi(strings.Split(arg, "=")[1]) + if err != nil { + return fmt.Errorf("dig: Invalid retry value") + } + + if strings.HasPrefix(arg, "tries") { + tries++ + } + + opts.Request.Retries = tries + + default: + return fmt.Errorf("dig: unknown flag %s given", arg) + } } return nil } diff --git a/cli/dig_test.go b/cli/dig_test.go index 71e69ec..4b12db4 100644 --- a/cli/dig_test.go +++ b/cli/dig_test.go @@ -24,6 +24,10 @@ func FuzzDig(f *testing.F) { "dnssec", "nodnssec", "tcp", "vc", "notcp", "novc", "ignore", "noignore", + "tls", "notls", + "dnscrypt", "nodnscrypt", + "https", "nohttps", + "quic", "noquic", "short", "noshort", "json", "nojson", "xml", "noxml", diff --git a/cli/misc.go b/cli/misc.go index 6f0f747..c2bbb94 100644 --- a/cli/misc.go +++ b/cli/misc.go @@ -103,7 +103,7 @@ func ParseMiscArgs(args []string, opts *Options) error { resolv, err := conf.GetDNSConfig() if err != nil { // :^) - opts.Logger.Error("Could not query system for server. Using default") + opts.Logger.Warn("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 diff --git a/doc/awl.1 b/doc/awl.1 index 7a36e25..a2246e3 100644 --- a/doc/awl.1 +++ b/doc/awl.1 @@ -1,232 +1,264 @@ -.\" Generated by scdoc 1.11.2 -.\" Complete documentation for this program is not available as a GNU info page -.ie \n(.g .ds Aq \(aq -.el .ds Aq ' -.nh -.ad l -.\" Begin generated content: -.TH "awl" "1" "2022-07-22" -.PP -.SH NAME -awl - DNS lookup tool -.PP -.SH SYNOPSIS -\fBawl\fR [ \fIOPTIONS\fR ] \fIname\fR [ \fI@server\fR ] [ \fItype\fR ] -.br -where -.PP -\fIname\fR is the query to make (\fBexample: froth.\&zone\fR) -.br -\fI@server\fR is the server to query (\fBexample: dns.\&froth.\&zone\fR) -.br -\fItype\fR is the DNS resource type (\fBexample: AAAA\fR) -.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).\& -.PP -\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 +[no]flags are supported, see dig(1) -.PP -\fB-D\fR, \fB--dnssec\fR, \fB+dnssec\fR -.br - Enable DNSSEC.\& This needs to be manually enabled.\& -.PP -\fB-v\fR \fIvalue\fR -.br - Set verbosity (currently WIP) -.PP -\fB-V\fR -.br - Print the version and exit.\& -.PP -\fB-h\fR -.br - Show a "short" help message.\& -.PP -.RE -.SS Query Options -.RS 4 -\fB-4\fR -.br - Only make query over IPv4 -.PP -\fB-6\fR -.br - Only make query over IPv6 -.PP -\fB-p\fR, \fB--port\fR \fIport\fR -.br - Sets the port to query.\& -.br - -.br -\fIDefault Ports\fR: -.RS 4 -.PD 0 -.IP \(bu 4 -\fI53\fR for \fBUDP\fR and \fBTCP\fR -.IP \(bu 4 -\fI853\fR for \fBTLS\fR and \fBQUIC\fR -.IP \(bu 4 -\fI443\fR for \fBHTTPS\fR -.PD -.PP -.RE -\fB-q\fR, \fB--query\fR \fIdomain\fR -.br - Domain to query (eg.\& example.\&com) -.PP -\fB-c\fR, \fB--class\fR \fIclass\fR -.br - DNS class to query (eg.\& IN, CH) -.PP -\fB-t\fR, \fB--qType\fR \fItype\fR -.br - DNS type to query (eg.\& A, NS) -.PP -\fB--no-truncate\fR, \fB+ignore\fR -.br - Ignore UDP truncation (by default, awl \fIretries with TCP\fR) -.PP -\fB-t\fR, \fB--tcp\fR, \fB+tcp\fR, \fB+vc\fR -.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--tcp\fR (see \fIRFC 7858\fR) -.PP -\fB-H\fR.\& \fB--https\fR, \fB+https\fR -.br - Use DNS-over-HTTPS (see \fIRFC 8484\fR) -.PP -\fB-Q\fR.\& \fB--quic\fR, \fB+quic\fR -.br - Use DNS-over-QUIC (see \fIRFC 9250\fR) -.PP -\fB-x\fR, \fB--reverse\fR -.br - Do a reverse lookup.\& Sets default \fItype\fR to PTR.\& -.br - \fBawl\fR automatically makes an IP or phone number canonical.\& -.PP -.RE -.SS DNS Flags -.PP -.RS 4 -\fB--aa=[false]\fR, \fB+[no]aaflag\fR -.br - (Set, Unset) AA (Authoritative Answer) flag -.PP -\fB--ad=[false]\fR, \fB+[no]adflag\fR -.br - (Set, Unset) AD (Authenticated Data) flag -.PP -\fB--tc=[false]\fR, \fB+[no]tcflag\fR -.br - (Set, Unset) TC (TrunCated) flag -.PP -\fB-z=[false]\fR, \fB+[no]zflag\fR -.br - (Set, Unset) Z (Zero) flag -.PP -\fB--cd=[false]\fR, \fB+[no]cdflag\fR -.br - (Set, Unset) CD (Checking Disabled) flag -.PP -\fB--qr=[false]\fR, \fB+[no]qrflag\fR -.br - (Set, Unset) QR (QueRy) flag -.PP -\fB--rd=[true]\fR, \fB+[no]rdflag\fR -.br - (Set, Unset) RD (Recursion Desired) flag -.PP -\fB--ra=[false]\fR, \fB+[no]raflag\fR -.br - (Set, Unset) RA (Recursion Available) flag -.PP -.RE -.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 - Print the query results as JSON.\& -.PP -\fB-X\fR, \fB--xml\fR, \fB+xml\fR -.br - Print the query results as XML.\& -.PP -\fB-y\fR, \fB--yaml\fR, \fB+yaml\fR -.br - Print the query results as YAML.\& -.PP -\fB-s\fR, \fB--short\fR, \fB+short\fR -.br - Print just the results.\& -.PP -.RE -.SH EXAMPLES -.nf -.RS 4 -awl grumbulon\&.xyz -j +cd -.fi -.RE -Run a query of your local resolver for the A records of grumbulon.\&xyz, print -them as JSON and disable DNSSEC verification.\& -.PP -.nf -.RS 4 -awl +short example\&.com AAAA @1\&.1\&.1\&.1 -.fi -.RE -Query 1.\&1.\&1.\&1 for the AAAA records of example.\&com, print just the answers -.PP -.nf -.RS 4 -awl -xT PTR 8\&.8\&.4\&.4 @dns\&.google -.fi -.RE -Query dns.\&google over TLS for the PTR record to the IP address 8.\&8.\&4.\&4 -.PP -.SH SEE ALSO +.\" Generated by scdoc 1.11.2 +.\" Complete documentation for this program is not available as a GNU info page +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.nh +.ad l +.\" Begin generated content: +.TH "awl" "1" "2022-07-24" +.P +.SH NAME +awl - DNS lookup tool +.P +.SH SYNOPSIS +\fBawl\fR [ \fIOPTIONS\fR ] \fIname\fR [ \fI@server\fR ] [ \fItype\fR ] +.br +where +.P +\fIname\fR is the query to make (\fBexample: froth.\&zone\fR) +.br +\fI@server\fR is the server to query (\fBexample: dns.\&froth.\&zone\fR) +.br +\fItype\fR is the DNS resource type (\fBexample: AAAA\fR) +.P +.SH DESCRIPTION +.P +\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).\& +.P +\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.\& +.P +.SH OPTIONS +.RS 4 +Dig-like +[no]flags are supported, see dig(1) +.P +\fB-D\fR, \fB--dnssec\fR, \fB+dnssec\fR +.br + Enable DNSSEC.\& This needs to be manually enabled.\& +.P +\fB-v\fR \fIvalue\fR +.br + Set verbosity (currently WIP) +.P +\fB-V\fR +.br + Print the version and exit.\& +.P +\fB-h\fR +.br + Show a "short" help message.\& +.P +.RE +.SS Query Options +.RS 4 +\fB-4\fR +.br + Only make query over IPv4 +.P +\fB-6\fR +.br + Only make query over IPv6 +.P +\fB-p\fR, \fB--port\fR \fIport\fR +.br + Sets the port to query.\& +.br + +.br +\fIDefault Ports\fR: +.RS 4 +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.IP \(bu 4 +.\} +\fI53\fR for \fBUDP\fR and \fBTCP\fR +.RE +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.IP \(bu 4 +.\} +\fI853\fR for \fBTLS\fR and \fBQUIC\fR +.RE +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.IP \(bu 4 +.\} +\fI443\fR for \fBHTTPS\fR + +.RE +.P +.RE +\fB-q\fR, \fB--query\fR \fIdomain\fR +.br + Domain to query (eg.\& example.\&com) +.P +\fB-c\fR, \fB--class\fR \fIclass\fR +.br + DNS class to query (eg.\& IN, CH) +.P +\fB-t\fR, \fB--qType\fR \fItype\fR +.br + DNS type to query (eg.\& A, NS) +.P +\fB--no-truncate\fR, \fB+ignore\fR +.br + Ignore UDP truncation (by default, awl \fIretries with TCP\fR) +.P +\fB--tcp\fR, \fB+tcp\fR, \fB+vc\fR +.br + Use TCP for the query (see \fIRFC 7766\fR) +.P +\fB--dnscrypt\fR, \fB+dnscrypt\fR +.br + Use DNSCrypt +.P +\fB-T\fR, \fB--tls\fR, \fB+tls\fR +.br + Use DNS-over-TLS, implies \fB--tcp\fR (see \fIRFC 7858\fR) +.P +\fB-H\fR.\& \fB--https\fR, \fB+https\fR +.br + Use DNS-over-HTTPS (see \fIRFC 8484\fR) +.P +\fB-Q\fR.\& \fB--quic\fR, \fB+quic\fR +.br + Use DNS-over-QUIC (see \fIRFC 9250\fR) +.P +\fB-x\fR, \fB--reverse\fR +.br + Do a reverse lookup.\& Sets default \fItype\fR to PTR.\& +.br + \fBawl\fR automatically makes an IP or phone number canonical.\& +.P +\fB--timeout\fR \fIseconds\fR, \fB+timeout=\fR\fIseconds\fR +.br + Set the timeout period.\& Floating point numbers are accepted.\& +.br + 0.\&5 seconds is the minimum.\& +.P +\fB--retry\fR \fIint\fR, \fB+tries\fR=\fIint\fR, \fB+ retry\fR=\fIint\fR +.br + Set the number of retries.\& +.br + Retry is one more than tries, dig style +.P +.RE +.SS DNS Flags +.P +.RS 4 +\fB--aa=[false]\fR, \fB+[no]aaflag\fR +.br + (Set, Unset) AA (Authoritative Answer) flag +.P +\fB--ad=[false]\fR, \fB+[no]adflag\fR +.br + (Set, Unset) AD (Authenticated Data) flag +.P +\fB--tc=[false]\fR, \fB+[no]tcflag\fR +.br + (Set, Unset) TC (TrunCated) flag +.P +\fB-z=[false]\fR, \fB+[no]zflag\fR +.br + (Set, Unset) Z (Zero) flag +.P +\fB--cd=[false]\fR, \fB+[no]cdflag\fR +.br + (Set, Unset) CD (Checking Disabled) flag +.P +\fB--qr=[false]\fR, \fB+[no]qrflag\fR +.br + (Set, Unset) QR (QueRy) flag +.P +\fB--rd=[true]\fR, \fB+[no]rdflag\fR +.br + (Set, Unset) RD (Recursion Desired) flag +.P +\fB--ra=[false]\fR, \fB+[no]raflag\fR +.br + (Set, Unset) RA (Recursion Available) flag +.P +.RE +.SS Output Display +.RS 4 +\fB--no-question\fR, \fB+noquestion\fR +.br + Do not display the Question section +.P +\fB--no-answer\fR, \fB+noanswer\fR +.br + Do not display the Answer section +.P +\fB--no-answer\fR, \fB+noanswer\fR +.br + Do not display the Answer section +.P +\fB--no-authority\fR, \fB+noauthority\fR +.br + Do not display the Authority section +.P +\fB--no-additional\fR, \fB+noadditional\fR +.br + Do not display the Additional section +.P +\fB--no-statistics\fR, \fB+nostats\fR +.br + Do not display the Statistics (additional comments) section +.P +.RE +.SS Output Formats +.RS 4 +\fB-j\fR, \fB--json\fR, \fB+json\fR +.br + Print the query results as JSON.\& +.P +\fB-X\fR, \fB--xml\fR, \fB+xml\fR +.br + Print the query results as XML.\& +.P +\fB-y\fR, \fB--yaml\fR, \fB+yaml\fR +.br + Print the query results as YAML.\& +.P +\fB-s\fR, \fB--short\fR, \fB+short\fR +.br + Print just the address of the answer.\& +.P +.RE +.SH EXAMPLES +.nf +.RS 4 +awl grumbulon\&.xyz -j +cd +.fi +.RE +Run a query of your local resolver for the A records of grumbulon.\&xyz, print +them as JSON and disable DNSSEC verification.\& +.P +.nf +.RS 4 +awl +short example\&.com AAAA @1\&.1\&.1\&.1 +.fi +.RE +Query 1.\&1.\&1.\&1 for the AAAA records of example.\&com, print just the answers +.P +.nf +.RS 4 +awl -xT PTR 8\&.8\&.4\&.4 @dns\&.google +.fi +.RE +Query dns.\&google over TLS for the PTR record to the IP address 8.\&8.\&4.\&4 +.P +.SH SEE ALSO \fIdrill\fR(1), \fIdig\fR(1), the many DNS RFCs \ No newline at end of file diff --git a/doc/awl.1.md b/doc/awl.1.md index 560ad6c..623fd95 100644 --- a/doc/awl.1.md +++ b/doc/awl.1.md @@ -66,7 +66,7 @@ _Default Ports_: *--tcp*, *+tcp*, *+vc*++ Use TCP for the query (see _RFC 7766_) - *--dnscrypt*++ + *--dnscrypt*, *+dnscrypt*++ Use DNSCrypt *-T*, *--tls*, *+tls*++ @@ -82,8 +82,13 @@ _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. + *--timeout* _seconds_, *+timeout=*_seconds_++ + Set the timeout period. Floating point numbers are accepted.++ + 0.5 seconds is the minimum. + + *--retry* _int_, *+tries*=_int_, *+ retry*=_int_++ + Set the number of retries.++ + Retry is one more than tries, dig style ## DNS Flags diff --git a/internal/helpers/query.go b/internal/helpers/query.go index a63f3d7..ddc0943 100644 --- a/internal/helpers/query.go +++ b/internal/helpers/query.go @@ -21,4 +21,5 @@ type Request struct { 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 + Retries int // Number of queries to retry } diff --git a/logawl/logger.go b/logawl/logger.go index ddae454..b703ba2 100644 --- a/logawl/logger.go +++ b/logawl/logger.go @@ -133,12 +133,12 @@ func (l *Logger) Info(v ...any) { l.Println(InfoLevel, v...) } -// Call print directly with Error level -func (l *Logger) Error(v ...any) { +// Call print directly with Warn level +func (l *Logger) Warn(v ...any) { l.Println(WarnLevel, v...) } -// Call print directly with Fatal level -func (l *Logger) Fatal(v ...any) { +// Call print directly with Error level +func (l *Logger) Error(v ...any) { l.Println(ErrLevel, v...) } diff --git a/main.go b/main.go index 923045a..8d9c4e3 100644 --- a/main.go +++ b/main.go @@ -13,6 +13,7 @@ import ( "time" "git.froth.zone/sam/awl/cli" + "git.froth.zone/sam/awl/internal/helpers" "git.froth.zone/sam/awl/query" "gopkg.in/yaml.v2" @@ -27,12 +28,24 @@ func main() { if errors.Is(err, cli.ErrNotError) || strings.Contains(err.Error(), "help requested") { os.Exit(0) } - opts.Logger.Fatal(err) + opts.Logger.Error(err) os.Exit(1) } - resp, err := query.CreateQuery(opts) + var resp helpers.Response + + // Retry queries if a query fails + for i := 0; i < opts.Request.Retries; i++ { + resp, err = query.CreateQuery(opts) + if err == nil { + break + } else { + opts.Logger.Warn("Retrying") + } + } + + // Query failed, make it fail if err != nil { - opts.Logger.Fatal(err) + opts.Logger.Error(err) os.Exit(9) } switch { @@ -40,7 +53,7 @@ func main() { opts.Logger.Info("Printing as JSON") json, err := json.MarshalIndent(resp.DNS, "", " ") if err != nil { - opts.Logger.Fatal(err) + opts.Logger.Error(err) os.Exit(10) } fmt.Println(string(json)) @@ -48,7 +61,7 @@ func main() { opts.Logger.Info("Printing as XML") xml, err := xml.MarshalIndent(resp.DNS, "", " ") if err != nil { - opts.Logger.Fatal(err) + opts.Logger.Error(err) os.Exit(10) } fmt.Println(string(xml)) @@ -56,7 +69,7 @@ func main() { opts.Logger.Info("Printing as YAML") yaml, err := yaml.Marshal(resp.DNS) if err != nil { - opts.Logger.Fatal(err) + opts.Logger.Error(err) os.Exit(10) } fmt.Println(string(yaml)) -- 2.43.0 From d820b75db19bd5aa84541d7cf7f9dcc3ddb8ed7a Mon Sep 17 00:00:00 2001 From: Sam Therapy Date: Sun, 24 Jul 2022 19:47:51 +0200 Subject: [PATCH 09/17] Add (and fix from) tests Signed-off-by: Sam Therapy --- cli/cli.go | 20 ++++++++++---------- cli/cli_test.go | 32 ++++++++++++++++++++++++++++++++ doc/awl.1.md | 2 +- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/cli/cli.go b/cli/cli.go index 80dc2a7..f14e0e2 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -46,7 +46,7 @@ func ParseCLI(version string) (Options, error) { reverse = flag.Bool("reverse", false, "do a reverse lookup", flag.OptShorthand('x')) timeout = flag.Float32("timeout", 1, "Timeout, in `seconds`") - retry = flag.Int("retry", 2, "number of `times` to retry") + retry = flag.Int("retries", 2, "number of `times` to retry") 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)") @@ -130,15 +130,6 @@ func ParseCLI(version string) (Options, error) { }, } - // Set timeout to 0.5 seconds if set below 0.5 - if float32(opts.Request.Timeout) < (0.5 * float32(time.Second)) { - opts.Request.Timeout = (time.Second / 2) - } - - if opts.Request.Retries < 0 { - opts.Request.Timeout = 0 - } - opts.Logger.Info("POSIX flags parsed") opts.Logger.Debug(fmt.Sprintf("%+v", opts)) @@ -166,5 +157,14 @@ func ParseCLI(version string) (Options, error) { } opts.Logger.Info("Port set to", opts.Port) + // Set timeout to 0.5 seconds if set below 0.5 + if opts.Request.Timeout < (time.Second / 2) { + opts.Request.Timeout = (time.Second / 2) + } + + if opts.Request.Retries < 0 { + opts.Request.Retries = 0 + } + return opts, nil } diff --git a/cli/cli_test.go b/cli/cli_test.go index e371036..21b9e63 100644 --- a/cli/cli_test.go +++ b/cli/cli_test.go @@ -5,6 +5,7 @@ package cli_test import ( "os" "testing" + "time" "git.froth.zone/sam/awl/cli" "gotest.tools/v3/assert" @@ -53,6 +54,37 @@ func TestVersion(t *testing.T) { os.Args = old } +func TestTimeout(t *testing.T) { + args := [][]string{ + {"awl", "+timeout=0"}, + {"awl", "--timeout", "0"}, + } + for _, test := range args { + old := os.Args + os.Args = test + opt, err := cli.ParseCLI("TEST") + assert.NilError(t, err) + assert.Equal(t, opt.Request.Timeout, time.Second/2) + os.Args = old + } +} + +func TestRetries(t *testing.T) { + args := [][]string{ + {"awl", "+retry=-2"}, + {"awl", "+tries=-2"}, + {"awl", "--retries", "-2"}, + } + for _, test := range args { + old := os.Args + os.Args = test + opt, err := cli.ParseCLI("TEST") + assert.NilError(t, err) + assert.Equal(t, opt.Request.Retries, 0) + os.Args = old + } +} + func FuzzFlags(f *testing.F) { testcases := []string{"git.froth.zone", "", "!12345", "google.com.edu.org.fr"} for _, tc := range testcases { diff --git a/doc/awl.1.md b/doc/awl.1.md index 623fd95..3f22798 100644 --- a/doc/awl.1.md +++ b/doc/awl.1.md @@ -86,7 +86,7 @@ _Default Ports_: Set the timeout period. Floating point numbers are accepted.++ 0.5 seconds is the minimum. - *--retry* _int_, *+tries*=_int_, *+ retry*=_int_++ + *--retries* _int_, *+tries*=_int_, *+ retry*=_int_++ Set the number of retries.++ Retry is one more than tries, dig style -- 2.43.0 From 2f9851baeae18dd1bacadaa7dd12776be15f8eb5 Mon Sep 17 00:00:00 2001 From: Sam Therapy Date: Sun, 24 Jul 2022 19:48:58 +0200 Subject: [PATCH 10/17] make doc Signed-off-by: Sam Therapy --- doc/awl.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/awl.1 b/doc/awl.1 index a2246e3..e0138ef 100644 --- a/doc/awl.1 +++ b/doc/awl.1 @@ -148,7 +148,7 @@ Dig-like +[no]flags are supported, see dig(1) .br 0.\&5 seconds is the minimum.\& .P -\fB--retry\fR \fIint\fR, \fB+tries\fR=\fIint\fR, \fB+ retry\fR=\fIint\fR +\fB--retries\fR \fIint\fR, \fB+tries\fR=\fIint\fR, \fB+ retry\fR=\fIint\fR .br Set the number of retries.\& .br -- 2.43.0 From 5787ee571f4594bbfde5180a79cb0367054920f1 Mon Sep 17 00:00:00 2001 From: Sam Therapy Date: Sun, 24 Jul 2022 19:59:29 +0200 Subject: [PATCH 11/17] fix makefile Signed-off-by: Sam Therapy --- Makefile | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 9368065..06dafe9 100644 --- a/Makefile +++ b/Makefile @@ -46,15 +46,9 @@ vet: *.go lint: fmt vet -golangci-lint run -ifeq ($(OS),Windows_NT) install: $(GO) install $(GOFLAGS) . -else -install: $(PROG) - install $(PROG) $(BINPATH) -endif - - + install_doc: doc install doc/$(PROG).1 $(MANPATH)/man1 @echo "" -- 2.43.0 From e482663af7d719ca04fb723b02ed4d7e546929c1 Mon Sep 17 00:00:00 2001 From: Sam Therapy Date: Sun, 24 Jul 2022 20:08:36 +0200 Subject: [PATCH 12/17] (man) fix manpath Signed-off-by: Sam Therapy --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 06dafe9..131732a 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ SCDOC := scdoc PREFIX := /usr/local BINPATH := $(PREFIX)/bin -MANPATH := $(PREFIX)/share/man +MANPATH := /usr/share/man PROG := awl ifeq ($(OS),Windows_NT) -- 2.43.0 From 21920e6b394c02dbde23981f032ae030a65f3a1e Mon Sep 17 00:00:00 2001 From: Sam Therapy Date: Mon, 25 Jul 2022 00:27:42 +0200 Subject: [PATCH 13/17] make warn error better Signed-off-by: Sam Therapy --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 8d9c4e3..68587f0 100644 --- a/main.go +++ b/main.go @@ -39,7 +39,7 @@ func main() { if err == nil { break } else { - opts.Logger.Warn("Retrying") + opts.Logger.Warn("Retrying request, error", err) } } -- 2.43.0 From 9ddaf4910d04f14edc0a8c9cf45347f55c8406e9 Mon Sep 17 00:00:00 2001 From: Sam Therapy Date: Mon, 25 Jul 2022 01:15:43 +0200 Subject: [PATCH 14/17] Add documentation and fix a typo Signed-off-by: Sam Therapy --- LICENCE => LICENSE | 0 README.md | 24 ++++++++++++++---------- go.mod | 10 +++++----- go.sum | 8 ++++++++ 4 files changed, 27 insertions(+), 15 deletions(-) rename LICENCE => LICENSE (100%) diff --git a/LICENCE b/LICENSE similarity index 100% rename from LICENCE rename to LICENSE diff --git a/README.md b/README.md index 0dbc84f..0a8d847 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,26 @@ # awl +[![Build Status](https://ci.git.froth.zone/api/badges/sam/awl/status.svg)](https://ci.git.froth.zone/sam/awl) + `awl` is a command-line DNS client, much like [`drill`](https://github.com/NLnetLabs/ldns), [`dig`](https://bind9.readthedocs.io/en/v9_18_3/manpages.html#dig-dns-lookup-utility), [`dog`](https://github.com/ogham/dog), [`doggo`](https://github.com/mr-karan/doggo), -or [`q`](https://github.com/natesales/q) +or [`q`](https://github.com/natesales/q). -This was made as my first major experiment with Go, so there are probably things that can be improved +`awl` is designed to be a drop-in replacement for the venerable dig, but support newer RFC query types, such as DNS-over-HTTPS and DNS-over-QUIC. -The excellent [dns](https://github.com/miekg/dns) library for Go does most of the heavy -lifting. +## Usage +* [Install](https://git.froth.zone/sam/awl/wiki/Install) +* [Manpage](https://git.froth.zone/sam/awl/wiki/Man) -## What awl should do +## Contributing +Send a [pull request](https://git.froth.zone/sam/awl/pulls) our way. +Prefer emails? Send a patch to the [mailing list](https://lists.sr.ht/~sammefishe/awl-dev). -- Be a drop-in replacement for dig (and maybe drill) -- Support DNS RFCs +Found a bug or want a new feature? +Create an issue [here](https://git.froth.zone/sam/awl/issues). -## What awl won't do -- Print to files or read from files -- Colour outputs +### License +See [LICENSE](./LICENSE) \ No newline at end of file diff --git a/go.mod b/go.mod index 82507ca..729a775 100644 --- a/go.mod +++ b/go.mod @@ -4,19 +4,19 @@ go 1.18 require ( github.com/ameshkov/dnscrypt/v2 v2.2.3 - github.com/lucas-clemente/quic-go v0.28.0 + github.com/lucas-clemente/quic-go v0.28.1 github.com/miekg/dns v1.1.50 github.com/stefansundin/go-zflag v1.1.1 - golang.org/x/net v0.0.0-20220708220712-1185a9018129 + golang.org/x/net v0.0.0-20220722155237-a158d28d115b gopkg.in/yaml.v2 v2.4.0 gotest.tools/v3 v3.3.0 ) require ( - github.com/AdguardTeam/golibs v0.4.2 // indirect + github.com/AdguardTeam/golibs v0.10.9 // 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/ameshkov/dnsstamps v1.0.3 // indirect github.com/google/go-cmp v0.5.5 // indirect github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 // indirect ) @@ -31,7 +31,7 @@ require ( github.com/nxadm/tail v1.4.8 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/stretchr/testify v1.8.0 // indirect - golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect + golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f golang.org/x/text v0.3.7 // indirect diff --git a/go.sum b/go.sum index b5ac0a6..9cc11c9 100644 --- a/go.sum +++ b/go.sum @@ -9,6 +9,8 @@ dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D 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/AdguardTeam/golibs v0.10.9 h1:F9oP2da0dQ9RQDM1lGR7LxUTfUWu8hEFOs4icwAkKM0= +github.com/AdguardTeam/golibs v0.10.9/go.mod h1:W+5rznZa1cSNSFt+gPS7f4Wytnr9fOrd5ZYqwadPw14= 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= @@ -18,6 +20,8 @@ github.com/ameshkov/dnscrypt/v2 v2.2.3 h1:X9UP5AHtwp46Ji+sGFfF/1Is6OPI/SjxLqhKpx 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/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo= +github.com/ameshkov/dnsstamps v1.0.3/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= @@ -90,6 +94,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lucas-clemente/quic-go v0.28.0 h1:9eXVRgIkMQQyiyorz/dAaOYIx3TFzXsIFkNFz4cxuJM= github.com/lucas-clemente/quic-go v0.28.0/go.mod h1:oGz5DKK41cJt5+773+BSO9BXDsREY4HLf7+0odGAPO0= +github.com/lucas-clemente/quic-go v0.28.1 h1:Uo0lvVxWg5la9gflIF9lwa39ONq85Xq2D91YNEIslzU= +github.com/lucas-clemente/quic-go v0.28.1/go.mod h1:oGz5DKK41cJt5+773+BSO9BXDsREY4HLf7+0odGAPO0= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= @@ -186,6 +192,8 @@ golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPh 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= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 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= -- 2.43.0 From ded9890ac3c1e0fb7d0238e54ee7d7bbb3659466 Mon Sep 17 00:00:00 2001 From: Sam Therapy Date: Mon, 25 Jul 2022 23:09:52 +0200 Subject: [PATCH 15/17] MORE Signed-off-by: Sam Therapy --- .drone.jsonnet | 4 +- .gitignore | 3 +- .gitmodules | 3 + .vscode/launch.json | 16 -- GNUmakefile | 24 +++ Makefile | 63 +----- README.md | 92 +++++--- coverage/.gitkeep | 0 doc/awl.1 | 506 +++++++++++++++++++++----------------------- doc/awl.1.md | 169 --------------- doc/wiki | 1 + go.sum | 10 - mkfile | 15 +- template.mk | 56 +++++ 14 files changed, 411 insertions(+), 551 deletions(-) create mode 100644 .gitmodules delete mode 100644 .vscode/launch.json create mode 100644 GNUmakefile create mode 100644 coverage/.gitkeep delete mode 100644 doc/awl.1.md create mode 160000 doc/wiki create mode 100644 template.mk diff --git a/.drone.jsonnet b/.drone.jsonnet index fd3c192..b769190 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -15,7 +15,7 @@ local testing(version, arch) = { name: "test", image: "golang:" + version, commands: [ - "go test -race -v ./... -cover" + "go test -race ./... -cover" ] }, ] @@ -40,7 +40,7 @@ local release() = { name: "test", image: "golang", commands: [ - "go test -race -v ./... -cover" + "go test -race ./... -cover" ] }, { diff --git a/.gitignore b/.gitignore index fef0267..be06a1f 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,5 @@ go.work dist/ # Test coverage -coverage/ \ No newline at end of file +coverage/* +!coverage/.gitkeep diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..1b94379 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "doc/wiki"] + path = doc/wiki + url = ../awl.wiki diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index f1f5f5e..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Launch Package", - "type": "go", - "request": "launch", - "mode": "auto", - "program": "${fileDirname}", - "args": ["+timeout=1"] - } - ] -} \ No newline at end of file diff --git a/GNUmakefile b/GNUmakefile new file mode 100644 index 0000000..7abf056 --- /dev/null +++ b/GNUmakefile @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: BSD-3-Clause + +include template.mk + +ifeq ($(OS),Windows_NT) + EXE := $(PROG).exe +else + EXE := $(PROG) +endif + + +$(PROG): + $(GO) build -o $(EXE) $(GOFLAGS) . + +## install: installs awl +install: all +ifeq ($(OS),Windows_NT) + $(GO) install $(GOFLAGS) . +else + install -m755 $(PROG) $(PREFIX)/$(BIN) + install -m644 doc/$(PROG).1 $(MAN)/man1 +endif + +.PHONY: install \ No newline at end of file diff --git a/Makefile b/Makefile index 131732a..d45f0fd 100644 --- a/Makefile +++ b/Makefile @@ -1,59 +1,14 @@ # SPDX-License-Identifier: BSD-3-Clause +# BSD/POSIX makefile -HASH := $(shell git describe --always --dirty || echo "UNKNOWN") -VER := "git-$(HASH)" +include template.mk -CGO_ENABLED = 0 -GO := go -GOFLAGS := -ldflags "-s -w -X 'main.version=$(VER)'" -SCDOC := scdoc +$(PROG): + $(GO) build -o $(PROG) $(GOFLAGS) . -PREFIX := /usr/local -BINPATH := $(PREFIX)/bin -MANPATH := /usr/share/man +## install: installs awl +install: all + install -m755 $(PROG) $(PREFIX)/$(BIN) + install -m644 doc/$(PROG).1 $(MAN)/man1 -PROG := awl -ifeq ($(OS),Windows_NT) - EXE := $(PROG).exe -else - EXE := $(PROG) -endif - -.PHONY: clean lint - -# hehe -all: $(PROG) - -$(PROG): lint - $(GO) build -o $(EXE) $(GOFLAGS) . - -doc: doc/awl.1.md - $(SCDOC) < doc/$(PROG).1.md > doc/$(PROG).1 - -test: coverage/coverage.out - $(GO) test -v -cover -coverprofile=coverage/coverage.out ./... - -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: *.go - $(GO) vet ./... - -lint: fmt vet - -golangci-lint run - -install: - $(GO) install $(GOFLAGS) . - -install_doc: doc - install doc/$(PROG).1 $(MANPATH)/man1 - @echo "" - @echo "Make sure to update your manual database" - @echo "'sudo mandb' on Debian/Ubuntu" - -clean: - $(GO) clean \ No newline at end of file +.PHONY: install \ No newline at end of file diff --git a/README.md b/README.md index 0a8d847..b47afa6 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,66 @@ -# awl - -[![Build Status](https://ci.git.froth.zone/api/badges/sam/awl/status.svg)](https://ci.git.froth.zone/sam/awl) - -`awl` is a command-line DNS client, much like -[`drill`](https://github.com/NLnetLabs/ldns), -[`dig`](https://bind9.readthedocs.io/en/v9_18_3/manpages.html#dig-dns-lookup-utility), -[`dog`](https://github.com/ogham/dog), -[`doggo`](https://github.com/mr-karan/doggo), -or [`q`](https://github.com/natesales/q). - -`awl` is designed to be a drop-in replacement for the venerable dig, but support newer RFC query types, such as DNS-over-HTTPS and DNS-over-QUIC. - -## Usage -* [Install](https://git.froth.zone/sam/awl/wiki/Install) -* [Manpage](https://git.froth.zone/sam/awl/wiki/Man) - -## Contributing -Send a [pull request](https://git.froth.zone/sam/awl/pulls) our way. -Prefer emails? Send a patch to the [mailing list](https://lists.sr.ht/~sammefishe/awl-dev). - -Found a bug or want a new feature? -Create an issue [here](https://git.froth.zone/sam/awl/issues). - -### License -See [LICENSE](./LICENSE) \ No newline at end of file +# awl + +[![Build Status](https://ci.git.froth.zone/api/badges/sam/awl/status.svg)](https://ci.git.froth.zone/sam/awl) + +`awl` is a command-line DNS client, much like +[`drill`](https://github.com/NLnetLabs/ldns), +[`dig`](https://bind9.readthedocs.io/en/v9_18_3/manpages.html#dig-dns-lookup-utility), +[`dog`](https://github.com/ogham/dog), +[`doggo`](https://github.com/mr-karan/doggo), or +[`q`](https://github.com/natesales/q). + +`awl` is designed to be a drop-in replacement for the venerable dig, but support +newer RFC query types, such as DNS-over-HTTPS and DNS-over-QUIC. + +## Usage + +- [Feature wiki](https://git.froth.zone/sam/awl/wiki/Supported.md) +- [Manpage](https://git.froth.zone/sam/awl/wiki/awl.1.md) + + +## Building and installing + +### From releases + +Grab a prebuilt binary from the +[release](https://git.froth.zone/sam/awl/releases) section. + +### From source + +Dependencies: + +- Go >= 1.18 +- GNU/BSD make or Plan 9 mk +- [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (optional, for manpage) + +Make sure to recursively clone the repo: + +```sh +git clone --recursive https://git.froth.zone/sam/awl +``` + +Using the makefile: + +```sh +make +sudo make install +``` + +Alternatively, using `go install`: + +```sh +go install git.froth.zone/sam/awl@latest +``` + +## Contributing + +Send a [pull request](https://git.froth.zone/sam/awl/pulls) our way. Prefer +emails? Send a patch to the +[mailing list](https://lists.sr.ht/~sammefishe/awl-dev). + +Found a bug or want a new feature? Create an issue +[here](https://git.froth.zone/sam/awl/issues). + +### License + +See [LICENSE](./LICENSE) diff --git a/coverage/.gitkeep b/coverage/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/doc/awl.1 b/doc/awl.1 index e0138ef..3145c0f 100644 --- a/doc/awl.1 +++ b/doc/awl.1 @@ -1,264 +1,244 @@ -.\" Generated by scdoc 1.11.2 -.\" Complete documentation for this program is not available as a GNU info page -.ie \n(.g .ds Aq \(aq -.el .ds Aq ' -.nh -.ad l -.\" Begin generated content: -.TH "awl" "1" "2022-07-24" -.P -.SH NAME -awl - DNS lookup tool -.P -.SH SYNOPSIS -\fBawl\fR [ \fIOPTIONS\fR ] \fIname\fR [ \fI@server\fR ] [ \fItype\fR ] -.br -where -.P -\fIname\fR is the query to make (\fBexample: froth.\&zone\fR) -.br -\fI@server\fR is the server to query (\fBexample: dns.\&froth.\&zone\fR) -.br -\fItype\fR is the DNS resource type (\fBexample: AAAA\fR) -.P -.SH DESCRIPTION -.P -\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).\& -.P -\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.\& -.P -.SH OPTIONS -.RS 4 -Dig-like +[no]flags are supported, see dig(1) -.P -\fB-D\fR, \fB--dnssec\fR, \fB+dnssec\fR -.br - Enable DNSSEC.\& This needs to be manually enabled.\& -.P -\fB-v\fR \fIvalue\fR -.br - Set verbosity (currently WIP) -.P -\fB-V\fR -.br - Print the version and exit.\& -.P -\fB-h\fR -.br - Show a "short" help message.\& -.P -.RE -.SS Query Options -.RS 4 -\fB-4\fR -.br - Only make query over IPv4 -.P -\fB-6\fR -.br - Only make query over IPv6 -.P -\fB-p\fR, \fB--port\fR \fIport\fR -.br - Sets the port to query.\& -.br - -.br -\fIDefault Ports\fR: -.RS 4 -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.IP \(bu 4 -.\} -\fI53\fR for \fBUDP\fR and \fBTCP\fR -.RE -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.IP \(bu 4 -.\} -\fI853\fR for \fBTLS\fR and \fBQUIC\fR -.RE -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.IP \(bu 4 -.\} -\fI443\fR for \fBHTTPS\fR - -.RE -.P -.RE -\fB-q\fR, \fB--query\fR \fIdomain\fR -.br - Domain to query (eg.\& example.\&com) -.P -\fB-c\fR, \fB--class\fR \fIclass\fR -.br - DNS class to query (eg.\& IN, CH) -.P -\fB-t\fR, \fB--qType\fR \fItype\fR -.br - DNS type to query (eg.\& A, NS) -.P -\fB--no-truncate\fR, \fB+ignore\fR -.br - Ignore UDP truncation (by default, awl \fIretries with TCP\fR) -.P -\fB--tcp\fR, \fB+tcp\fR, \fB+vc\fR -.br - Use TCP for the query (see \fIRFC 7766\fR) -.P -\fB--dnscrypt\fR, \fB+dnscrypt\fR -.br - Use DNSCrypt -.P -\fB-T\fR, \fB--tls\fR, \fB+tls\fR -.br - Use DNS-over-TLS, implies \fB--tcp\fR (see \fIRFC 7858\fR) -.P -\fB-H\fR.\& \fB--https\fR, \fB+https\fR -.br - Use DNS-over-HTTPS (see \fIRFC 8484\fR) -.P -\fB-Q\fR.\& \fB--quic\fR, \fB+quic\fR -.br - Use DNS-over-QUIC (see \fIRFC 9250\fR) -.P -\fB-x\fR, \fB--reverse\fR -.br - Do a reverse lookup.\& Sets default \fItype\fR to PTR.\& -.br - \fBawl\fR automatically makes an IP or phone number canonical.\& -.P -\fB--timeout\fR \fIseconds\fR, \fB+timeout=\fR\fIseconds\fR -.br - Set the timeout period.\& Floating point numbers are accepted.\& -.br - 0.\&5 seconds is the minimum.\& -.P -\fB--retries\fR \fIint\fR, \fB+tries\fR=\fIint\fR, \fB+ retry\fR=\fIint\fR -.br - Set the number of retries.\& -.br - Retry is one more than tries, dig style -.P -.RE -.SS DNS Flags -.P -.RS 4 -\fB--aa=[false]\fR, \fB+[no]aaflag\fR -.br - (Set, Unset) AA (Authoritative Answer) flag -.P -\fB--ad=[false]\fR, \fB+[no]adflag\fR -.br - (Set, Unset) AD (Authenticated Data) flag -.P -\fB--tc=[false]\fR, \fB+[no]tcflag\fR -.br - (Set, Unset) TC (TrunCated) flag -.P -\fB-z=[false]\fR, \fB+[no]zflag\fR -.br - (Set, Unset) Z (Zero) flag -.P -\fB--cd=[false]\fR, \fB+[no]cdflag\fR -.br - (Set, Unset) CD (Checking Disabled) flag -.P -\fB--qr=[false]\fR, \fB+[no]qrflag\fR -.br - (Set, Unset) QR (QueRy) flag -.P -\fB--rd=[true]\fR, \fB+[no]rdflag\fR -.br - (Set, Unset) RD (Recursion Desired) flag -.P -\fB--ra=[false]\fR, \fB+[no]raflag\fR -.br - (Set, Unset) RA (Recursion Available) flag -.P -.RE -.SS Output Display -.RS 4 -\fB--no-question\fR, \fB+noquestion\fR -.br - Do not display the Question section -.P -\fB--no-answer\fR, \fB+noanswer\fR -.br - Do not display the Answer section -.P -\fB--no-answer\fR, \fB+noanswer\fR -.br - Do not display the Answer section -.P -\fB--no-authority\fR, \fB+noauthority\fR -.br - Do not display the Authority section -.P -\fB--no-additional\fR, \fB+noadditional\fR -.br - Do not display the Additional section -.P -\fB--no-statistics\fR, \fB+nostats\fR -.br - Do not display the Statistics (additional comments) section -.P -.RE -.SS Output Formats -.RS 4 -\fB-j\fR, \fB--json\fR, \fB+json\fR -.br - Print the query results as JSON.\& -.P -\fB-X\fR, \fB--xml\fR, \fB+xml\fR -.br - Print the query results as XML.\& -.P -\fB-y\fR, \fB--yaml\fR, \fB+yaml\fR -.br - Print the query results as YAML.\& -.P -\fB-s\fR, \fB--short\fR, \fB+short\fR -.br - Print just the address of the answer.\& -.P -.RE -.SH EXAMPLES -.nf -.RS 4 -awl grumbulon\&.xyz -j +cd -.fi -.RE -Run a query of your local resolver for the A records of grumbulon.\&xyz, print -them as JSON and disable DNSSEC verification.\& -.P -.nf -.RS 4 -awl +short example\&.com AAAA @1\&.1\&.1\&.1 -.fi -.RE -Query 1.\&1.\&1.\&1 for the AAAA records of example.\&com, print just the answers -.P -.nf -.RS 4 -awl -xT PTR 8\&.8\&.4\&.4 @dns\&.google -.fi -.RE -Query dns.\&google over TLS for the PTR record to the IP address 8.\&8.\&4.\&4 -.P -.SH SEE ALSO +.\" Generated by scdoc 1.11.2 +.\" Complete documentation for this program is not available as a GNU info page +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.nh +.ad l +.\" Begin generated content: +.TH "awl" "1" "2022-07-25" +.PP +.SH NAME +awl - DNS lookup tool +.PP +.SH SYNOPSIS +\fBawl\fR [ \fIOPTIONS\fR ] \fIname\fR [ \fI@server\fR ] [ \fItype\fR ] +.br +where +.PP +\fIname\fR is the query to make (\fBexample: froth.\&zone\fR) +.br +\fI@server\fR is the server to query (\fBexample: dns.\&froth.\&zone\fR) +.br +\fItype\fR is the DNS resource type (\fBexample: AAAA\fR) +.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 leatherworking.\& +.PP +\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 +[no]flags are supported, see dig(1) +.PP +\fB-D\fR, \fB--dnssec\fR, \fB+dnssec\fR +.br + Enable DNSSEC.\& This needs to be manually enabled.\& +.PP +\fB-v\fR \fIvalue\fR +.br + Set verbosity (currently WIP) +.PP +\fB-V\fR +.br + Print the version and exit.\& +.PP +\fB-h\fR +.br + Show a "short" help message.\& +.PP +.RE +.SS Query Options +.RS 4 +\fB-4\fR +.br + Only make query over IPv4 +.PP +\fB-6\fR +.br + Only make query over IPv6 +.PP +\fB-p\fR, \fB--port\fR \fIport\fR +.br + Sets the port to query.\& +.br + +.br +\fIDefault Ports\fR: +.RS 4 +.PD 0 +.IP \(bu 4 +\fI53\fR for \fBUDP\fR and \fBTCP\fR +.IP \(bu 4 +\fI853\fR for \fBTLS\fR and \fBQUIC\fR +.IP \(bu 4 +\fI443\fR for \fBHTTPS\fR +.PD +.PP +.RE +\fB-q\fR, \fB--query\fR \fIdomain\fR +.br + Domain to query (eg.\& example.\&com) +.PP +\fB-c\fR, \fB--class\fR \fIclass\fR +.br + DNS class to query (eg.\& IN, CH) +.PP +\fB-t\fR, \fB--qType\fR \fItype\fR +.br + DNS type to query (eg.\& A, NS) +.PP +\fB--no-truncate\fR, \fB+ignore\fR +.br + Ignore UDP truncation (by default, awl \fIretries with TCP\fR) +.PP +\fB--tcp\fR, \fB+tcp\fR, \fB+vc\fR +.br + Use TCP for the query (see \fIRFC 7766\fR) +.PP +\fB--dnscrypt\fR, \fB+dnscrypt\fR +.br + Use DNSCrypt +.PP +\fB-T\fR, \fB--tls\fR, \fB+tls\fR +.br + Use DNS-over-TLS, implies \fB--tcp\fR (see \fIRFC 7858\fR) +.PP +\fB-H\fR.\& \fB--https\fR, \fB+https\fR +.br + Use DNS-over-HTTPS (see \fIRFC 8484\fR) +.PP +\fB-Q\fR.\& \fB--quic\fR, \fB+quic\fR +.br + Use DNS-over-QUIC (see \fIRFC 9250\fR) +.PP +\fB-x\fR, \fB--reverse\fR +.br + Do a reverse lookup.\& Sets default \fItype\fR to PTR.\& +.br + \fBawl\fR automatically makes an IP or phone number canonical.\& +.PP +\fB--timeout\fR \fIseconds\fR, \fB+timeout=\fR\fIseconds\fR +.br + Set the timeout period.\& Floating point numbers are accepted.\& +.br + 0.\&5 seconds is the minimum.\& +.PP +\fB--retries\fR \fIint\fR, \fB+tries\fR=\fIint\fR, \fB+ retry\fR=\fIint\fR +.br + Set the number of retries.\& +.br + Retry is one more than tries, dig style +.PP +.RE +.SS DNS Flags +.PP +.RS 4 +\fB--aa=[false]\fR, \fB+[no]aaflag\fR +.br + (Set, Unset) AA (Authoritative Answer) flag +.PP +\fB--ad=[false]\fR, \fB+[no]adflag\fR +.br + (Set, Unset) AD (Authenticated Data) flag +.PP +\fB--tc=[false]\fR, \fB+[no]tcflag\fR +.br + (Set, Unset) TC (TrunCated) flag +.PP +\fB-z=[false]\fR, \fB+[no]zflag\fR +.br + (Set, Unset) Z (Zero) flag +.PP +\fB--cd=[false]\fR, \fB+[no]cdflag\fR +.br + (Set, Unset) CD (Checking Disabled) flag +.PP +\fB--qr=[false]\fR, \fB+[no]qrflag\fR +.br + (Set, Unset) QR (QueRy) flag +.PP +\fB--rd=[true]\fR, \fB+[no]rdflag\fR +.br + (Set, Unset) RD (Recursion Desired) flag +.PP +\fB--ra=[false]\fR, \fB+[no]raflag\fR +.br + (Set, Unset) RA (Recursion Available) flag +.PP +.RE +.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+nostats\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 + Print the query results as JSON.\& +.PP +\fB-X\fR, \fB--xml\fR, \fB+xml\fR +.br + Print the query results as XML.\& +.PP +\fB-y\fR, \fB--yaml\fR, \fB+yaml\fR +.br + Print the query results as YAML.\& +.PP +\fB-s\fR, \fB--short\fR, \fB+short\fR +.br + Print just the address of the answer.\& +.PP +.RE +.SH EXAMPLES +.nf +.RS 4 +awl grumbulon\&.xyz -j +cd +.fi +.RE +Run a query of your local resolver for the A records of grumbulon.\&xyz, print +them as JSON and disable DNSSEC verification.\& +.PP +.nf +.RS 4 +awl +short example\&.com AAAA @1\&.1\&.1\&.1 +.fi +.RE +Query 1.\&1.\&1.\&1 for the AAAA records of example.\&com, print just the answers +.PP +.nf +.RS 4 +awl -xT PTR 8\&.8\&.4\&.4 @dns\&.google +.fi +.RE +Query dns.\&google over TLS for the PTR record to the IP address 8.\&8.\&4.\&4 +.PP +.SH SEE ALSO \fIdrill\fR(1), \fIdig\fR(1), the many DNS RFCs \ No newline at end of file diff --git a/doc/awl.1.md b/doc/awl.1.md deleted file mode 100644 index 3f22798..0000000 --- a/doc/awl.1.md +++ /dev/null @@ -1,169 +0,0 @@ -awl(1) - -# NAME -awl - DNS lookup tool - -# SYNOPSIS -*awl* [ _OPTIONS_ ] _name_ [ _@server_ ] [ _type_ ]++ -where - -_name_ is the query to make (*example: froth.zone*)++ -_@server_ is the server to query (*example: dns.froth.zone*)++ -_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* 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 +[no]flags are supported, see dig(1) - - *-D*, *--dnssec*, *+dnssec*++ - Enable DNSSEC. This needs to be manually enabled. - - *-v* _value_++ - Set verbosity (currently WIP) - - *-V*++ - Print the version and exit. - - *-h*++ - Show a "short" help message. - -## Query Options - *-4*++ - Only make query over IPv4 - - *-6*++ - Only make query over IPv6 - - *-p*, *--port* _port_++ - Sets the port to query.++ - ++ -_Default Ports_: - - _53_ for *UDP* and *TCP* - - _853_ for *TLS* and *QUIC* - - _443_ for *HTTPS* - - *-q*, *--query* _domain_++ - Domain to query (eg. example.com) - - *-c*, *--class* _class_++ - DNS class to query (eg. IN, CH) - - *-t*, *--qType* _type_++ - DNS type to query (eg. A, NS) - - *--no-truncate*, *+ignore*++ - Ignore UDP truncation (by default, awl _retries with TCP_) - - *--tcp*, *+tcp*, *+vc*++ - Use TCP for the query (see _RFC 7766_) - - *--dnscrypt*, *+dnscrypt*++ - Use DNSCrypt - - *-T*, *--tls*, *+tls*++ - Use DNS-over-TLS, implies *--tcp* (see _RFC 7858_) - - *-H*. *--https*, *+https*++ - Use DNS-over-HTTPS (see _RFC 8484_) - - *-Q*. *--quic*, *+quic*++ - Use DNS-over-QUIC (see _RFC 9250_) - - *-x*, *--reverse*++ - Do a reverse lookup. Sets default _type_ to PTR.++ - *awl* automatically makes an IP or phone number canonical. - - *--timeout* _seconds_, *+timeout=*_seconds_++ - Set the timeout period. Floating point numbers are accepted.++ - 0.5 seconds is the minimum. - - *--retries* _int_, *+tries*=_int_, *+ retry*=_int_++ - Set the number of retries.++ - Retry is one more than tries, dig style - -## DNS Flags - - *--aa=[false]*, *+[no]aaflag*++ - (Set, Unset) AA (Authoritative Answer) flag - - *--ad=[false]*, *+[no]adflag*++ - (Set, Unset) AD (Authenticated Data) flag - - *--tc=[false]*, *+[no]tcflag*++ - (Set, Unset) TC (TrunCated) flag - - *-z=[false]*, *+[no]zflag*++ - (Set, Unset) Z (Zero) flag - - *--cd=[false]*, *+[no]cdflag*++ - (Set, Unset) CD (Checking Disabled) flag - - *--qr=[false]*, *+[no]qrflag*++ - (Set, Unset) QR (QueRy) flag - - *--rd=[true]*, *+[no]rdflag*++ - (Set, Unset) RD (Recursion Desired) flag - - *--ra=[false]*, *+[no]raflag*++ - (Set, Unset) RA (Recursion Available) flag - -## 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. - - *-X*, *--xml*, *+xml*++ - Print the query results as XML. - - *-y*, *--yaml*, *+yaml*++ - Print the query results as YAML. - - *-s*, *--short*, *+short*++ - Print just the address of the answer. - -# EXAMPLES -``` -awl grumbulon.xyz -j +cd -``` -Run a query of your local resolver for the A records of grumbulon.xyz, print -them as JSON and disable DNSSEC verification. - -``` -awl +short example.com AAAA @1.1.1.1 -``` -Query 1.1.1.1 for the AAAA records of example.com, print just the answers - -``` -awl -xT PTR 8.8.4.4 @dns.google -``` -Query dns.google over TLS for the PTR record to the IP address 8.8.4.4 - -# SEE ALSO -_drill_(1), _dig_(1), the many DNS RFCs \ No newline at end of file diff --git a/doc/wiki b/doc/wiki new file mode 160000 index 0000000..b900550 --- /dev/null +++ b/doc/wiki @@ -0,0 +1 @@ +Subproject commit b900550a3c7eb7977feb9da5590c67fa08798a83 diff --git a/go.sum b/go.sum index 9cc11c9..4bd4389 100644 --- a/go.sum +++ b/go.sum @@ -7,7 +7,6 @@ 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/AdguardTeam/golibs v0.10.9 h1:F9oP2da0dQ9RQDM1lGR7LxUTfUWu8hEFOs4icwAkKM0= github.com/AdguardTeam/golibs v0.10.9/go.mod h1:W+5rznZa1cSNSFt+gPS7f4Wytnr9fOrd5ZYqwadPw14= @@ -18,7 +17,6 @@ github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyY 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/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo= github.com/ameshkov/dnsstamps v1.0.3/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A= @@ -92,8 +90,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lucas-clemente/quic-go v0.28.0 h1:9eXVRgIkMQQyiyorz/dAaOYIx3TFzXsIFkNFz4cxuJM= -github.com/lucas-clemente/quic-go v0.28.0/go.mod h1:oGz5DKK41cJt5+773+BSO9BXDsREY4HLf7+0odGAPO0= github.com/lucas-clemente/quic-go v0.28.1 h1:Uo0lvVxWg5la9gflIF9lwa39ONq85Xq2D91YNEIslzU= github.com/lucas-clemente/quic-go v0.28.1/go.mod h1:oGz5DKK41cJt5+773+BSO9BXDsREY4HLf7+0odGAPO0= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= @@ -190,8 +186,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U 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= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -223,8 +217,6 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220708220712-1185a9018129 h1:vucSRfWwTsoXro7P+3Cjlr6flUMtzCwzlvkxEQtHHB0= -golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -265,8 +257,6 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/mkfile b/mkfile index 64aa154..053cea2 100644 --- a/mkfile +++ b/mkfile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: BSD-3-Clause +# Plan 9 mkfile GO = go -SCDOC = scdoc PROG = awl LDFLAGS = '-s -w' GOFLAGS = -ldflags=$LDFLAGS @@ -9,19 +9,14 @@ GOFLAGS = -ldflags=$LDFLAGS CGO_ENABLED = 0 $PROG: - $GO build $GOFLAGS -o $PROG . + $GO build $GOFLAGS -o $PROG '-buildvcs=false' . -doc: doc/$PROG.1.md - $SCDOC < doc/$PROG.1.md > doc/$PROG.1 - -install: +install: $PROG $GO install $GOFLAGS . - -install_doc: - cp doc/$PROG.1 /sys/man/1/$PROG + cp doc/$PROG.1 /sys/man/1/$PROG test: - $GO test -v ./... -cover + $GO test -v -cover -coverprofile=coverage/coverage.out ./... fmt: gofmt -w -s . diff --git a/template.mk b/template.mk new file mode 100644 index 0000000..1698df2 --- /dev/null +++ b/template.mk @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Template for the BSD/GNU makefiles + +HASH ?= $(shell git describe --always --dirty || echo "UNKNOWN") +VER ?= "git-$(HASH)" + +CGO_ENABLED ?= 0 +GO ?= go +GOFLAGS ?= -ldflags "-s -w -X 'main.version=$(VER)'" -buildvcs=false + + +PREFIX ?= /usr/local +BIN ?= bin + +SCDOC ?= scdoc +MAN ?= $(PREFIX)/share/man + +PROG ?= awl + +# hehe +all: $(PROG) doc/$(PROG).1 + + +doc/$(PROG).1: doc/wiki/$(PROG).1.md + @cp doc/awl.1 doc/awl.bak + $(SCDOC) doc/$(PROG).1 2>/dev/null && rm doc/awl.bak || mv doc/awl.bak doc/awl.1 + +## test: run go test +test: + $(GO) test -cover -coverprofile=coverage/coverage.out ./... + +## cover: generates test coverage, output as HTML +cover: test + $(GO) tool cover -func=coverage/coverage.out + $(GO) tool cover -html=coverage/coverage.out -o coverage/cover.html + +fmt: + gofmt -w -s . + +vet: + $(GO) vet ./... + +## lint: lint awl, using fmt, vet and golangci-lint +lint: fmt vet + -golangci-lint run + +## clean: clean the build files +clean: + $(GO) clean + +## help: Prints this help message +help: + @echo "Usage: " + @sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /' + +.PHONY: clean lint test fmt vet help \ No newline at end of file -- 2.43.0 From aeef727608a1bc3ad1cd71128fb3d80ab243ec29 Mon Sep 17 00:00:00 2001 From: Sam Therapy Date: Mon, 25 Jul 2022 23:17:53 +0200 Subject: [PATCH 16/17] submod Signed-off-by: Sam Therapy --- doc/wiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/wiki b/doc/wiki index b900550..0fba1fb 160000 --- a/doc/wiki +++ b/doc/wiki @@ -1 +1 @@ -Subproject commit b900550a3c7eb7977feb9da5590c67fa08798a83 +Subproject commit 0fba1fbe4b12e8c88514b3f7d98be3e75a5a034d -- 2.43.0 From d1751fd67870f3ab038315146db5376ccb9a01f4 Mon Sep 17 00:00:00 2001 From: Sam Therapy Date: Tue, 26 Jul 2022 00:22:12 +0200 Subject: [PATCH 17/17] readme: fix links Signed-off-by: Sam Therapy --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b47afa6..c1ca76f 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,8 @@ newer RFC query types, such as DNS-over-HTTPS and DNS-over-QUIC. ## Usage -- [Feature wiki](https://git.froth.zone/sam/awl/wiki/Supported.md) -- [Manpage](https://git.froth.zone/sam/awl/wiki/awl.1.md) +- [Feature wiki](https://git.froth.zone/sam/awl/wiki/Supported) +- [Manpage](https://git.froth.zone/sam/awl/wiki/awl.1) ## Building and installing -- 2.43.0