diff --git a/.golangci.yaml b/.golangci.yaml index 9ce9b3f..9eca14e 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -32,10 +32,11 @@ linters: - predeclared - revive - staticcheck - #- testpackage - whitespace - wrapcheck - wsl + disable: + - structcheck linters-settings: govet: diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 70a9ce1..80c67a5 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -19,7 +19,11 @@ builds: universal_binaries: - replace: true archives: - - replacements: + - + files: + - LICENCE + - completions/** + replacements: darwin: macOS linux: Linux windows: Windows diff --git a/cli/cli.go b/cli/cli.go index 2e176f2..8a593cb 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -36,6 +36,8 @@ func ParseCLI(version string) (util.Options, error) { } // CLI flags + // + // Remember, when adding a flag edit the manpage and the completions :) 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` (default: .)", flag.OptShorthand('q')) @@ -51,14 +53,14 @@ func ParseCLI(version string) (util.Options, error) { edns = flag.Bool("no-edns", false, "disable EDNS entirely") ednsVer = flag.Uint8("edns-ver", 0, "set EDNS version") - expire = flag.Bool("expire", false, "set EDNS expire") dnssec = flag.Bool("dnssec", false, "enable DNSSEC", flag.OptShorthand('D')) + expire = flag.Bool("expire", false, "set EDNS expire") nsid = flag.Bool("nsid", false, "set EDNS NSID", flag.OptShorthand('n')) cookie = flag.Bool("no-cookie", false, "disable sending EDNS cookie (default: cookie sent)") tcpKeepAlive = flag.Bool("keep-alive", false, "send EDNS TCP keep-alive") udpBufSize = flag.Uint16("buffer-size", 1232, "set EDNS UDP buffer size", flag.OptShorthand('b')) mbzflag = flag.String("zflag", "0", "set EDNS z-flag `value`") - subnet = flag.String("subnet", "", "set EDNS subnet") + subnet = flag.String("subnet", "", "set EDNS client subnet") padding = flag.Bool("pad", false, "set EDNS padding") truncate = flag.Bool("no-truncate", false, "ignore truncation if a UDP request truncates (default: retry with TCP)") diff --git a/cli/cli_test.go b/cli/cli_test.go index 0250015..27e5fc6 100644 --- a/cli/cli_test.go +++ b/cli/cli_test.go @@ -84,6 +84,12 @@ func TestMBZ(t *testing.T) { //nolint: paralleltest // Race conditions func TestInvalidFlag(t *testing.T) { //nolint: paralleltest // Race conditions old := os.Args + old2 := os.Stdout + old3 := os.Stderr + + os.Stdout = os.NewFile(0, os.DevNull) + os.Stderr = os.NewFile(0, os.DevNull) + os.Args = []string{"awl", "--treebug"} _, err := cli.ParseCLI("TEST") @@ -91,6 +97,8 @@ func TestInvalidFlag(t *testing.T) { //nolint: paralleltest // Race conditions assert.ErrorContains(t, err, "unknown flag") os.Args = old + os.Stdout = old2 + os.Stderr = old3 } func TestInvalidDig(t *testing.T) { //nolint: paralleltest // Race conditions @@ -106,13 +114,18 @@ func TestInvalidDig(t *testing.T) { //nolint: paralleltest // Race conditions func TestVersion(t *testing.T) { //nolint: paralleltest // Race conditions old := os.Args + old2 := os.Stdout + old3 := os.Stderr + os.Args = []string{"awl", "--version"} - _, err := cli.ParseCLI("TEST") + _, err := cli.ParseCLI("test") assert.ErrorType(t, err, cli.ErrNotError) os.Args = old + os.Stdout = old2 + os.Stderr = old3 } func TestTimeout(t *testing.T) { //nolint: paralleltest // Race conditions @@ -154,11 +167,14 @@ func TestRetries(t *testing.T) { //nolint: paralleltest // Race conditions 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) { + // Get rid of outputs + old := os.Args os.Args = []string{"awl", orig} //nolint:errcheck,gosec // Only make sure the program does not crash diff --git a/cli/dig.go b/cli/dig.go index 59f49c0..6194e81 100644 --- a/cli/dig.go +++ b/cli/dig.go @@ -48,8 +48,10 @@ func ParseDig(arg string, opts *util.Options) error { opts.ShowQuery = isNo case "ttlunits": opts.HumanTTL = isNo - case "ttlid": + case "ttl", "ttlid": opts.ShowTTL = isNo + case "class": + opts.ShowClass = isNo // EDNS queries case "dnssec": diff --git a/cli/dig_test.go b/cli/dig_test.go index c21a19e..5293bb2 100644 --- a/cli/dig_test.go +++ b/cli/dig_test.go @@ -58,6 +58,7 @@ func FuzzDig(f *testing.F) { "stats", "nostats", "all", "noall", "idnout", "noidnout", + "class", "noclass", "invalid", } @@ -66,6 +67,10 @@ func FuzzDig(f *testing.F) { } f.Fuzz(func(t *testing.T, orig string) { + // Get rid of outputs + // os.Stdout = os.NewFile(0, os.DevNull) + // os.Stderr = os.NewFile(0, os.DevNull) + opts := new(util.Options) opts.Logger = util.InitLogger(0) if err := cli.ParseDig(orig, opts); err != nil { diff --git a/cli/misc.go b/cli/misc.go index 219c3f5..85228fd 100644 --- a/cli/misc.go +++ b/cli/misc.go @@ -13,7 +13,7 @@ import ( "golang.org/x/net/idna" ) -// ParseMiscArgs parses the wildcard arguments, drill style. +// ParseMiscArgs parses the wildcard arguments, dig style. // Only one command is supported at a time, so any extra information overrides previous. func ParseMiscArgs(args []string, opts *util.Options) error { for _, arg := range args { diff --git a/cli/misc_test.go b/cli/misc_test.go index 16d8b50..286e0e3 100644 --- a/cli/misc_test.go +++ b/cli/misc_test.go @@ -166,6 +166,8 @@ func FuzzParseArgs(f *testing.F) { } f.Fuzz(func(t *testing.T, arg string) { + // Get rid of outputs + args := []string{arg} opts := new(util.Options) opts.Logger = util.InitLogger(0) diff --git a/doc/wiki b/doc/wiki index 4974d31..96f2699 160000 --- a/doc/wiki +++ b/doc/wiki @@ -1 +1 @@ -Subproject commit 4974d316fc3be3c28b54af5188860f8cfb2bdd6b +Subproject commit 96f2699a2f22a5bdcf37a48d0a20fd85c96bcc81 diff --git a/go.mod b/go.mod index b59e56e..c2d9b24 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,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 - golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d // indirect + golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 golang.org/x/text v0.3.7 // indirect diff --git a/go.sum b/go.sum index b602654..502ecb9 100644 --- a/go.sum +++ b/go.sum @@ -79,6 +79,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -115,23 +117,6 @@ 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-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-20220731174439-a90be440212d h1:Sv5ogFZatcgIMMtBSTTAgMYsicp25MXBubjXNDKwm80= -golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 h1:9vYwv7OjYaky/tlAeD7C4oC9EsPTlaFl1H2jS++V+ME= -golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 h1:v1W7bwXHsnLLloWYTVEdvGvA7BHMeBYsPcF0GLDxIRs= -golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2 h1:fqTvyMIIj+HRzMmnzr9NtpHP6uVpvB5fkHcgPDC4nu8= -golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U= -golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220823224334-20c2bfdbfe24 h1:TyKJRhyo17yWxOMCTHKWrc5rddHORMlnZ/j57umaUd8= -golang.org/x/sys v0.0.0-20220823224334-20c2bfdbfe24/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= -golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -157,8 +142,10 @@ google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= @@ -167,6 +154,7 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD 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.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 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= diff --git a/main.go b/main.go index 1b266f4..db41dea 100644 --- a/main.go +++ b/main.go @@ -57,7 +57,10 @@ func run() (opts util.Options, code int, err error) { return opts, 10, fmt.Errorf("format print: %w", err) } } else { - str = query.ToString(resp, opts) + str, err = query.ToString(resp, opts) + if err != nil { + return opts, 15, fmt.Errorf("standard print: %w", err) + } } fmt.Println(str) diff --git a/main_test.go b/main_test.go index a1a7c8a..eb42484 100644 --- a/main_test.go +++ b/main_test.go @@ -11,6 +11,9 @@ import ( ) func TestMain(t *testing.T) { //nolint: paralleltest // Race conditions + os.Stdout = os.NewFile(0, os.DevNull) + os.Stderr = os.NewFile(0, os.DevNull) + old := os.Args os.Args = []string{"awl", "+yaml", "@1.1.1.1"} @@ -30,6 +33,8 @@ func TestMain(t *testing.T) { //nolint: paralleltest // Race conditions func TestHelp(t *testing.T) { old := os.Args + os.Stdout = os.NewFile(0, os.DevNull) + os.Stderr = os.NewFile(0, os.DevNull) os.Args = []string{"awl", "-h"} diff --git a/query/print_test.go b/query/print_test.go index 0e91c2b..8dce5d1 100644 --- a/query/print_test.go +++ b/query/print_test.go @@ -114,9 +114,7 @@ func TestRealPrint(t *testing.T) { ShowQuery: true, RD: true, Verbosity: 0, - ShowTTL: true, - Identify: true, - YAML: false, + ShowTTL: false, Display: util.Displays{ Comments: true, Question: true, @@ -177,11 +175,13 @@ func TestRealPrint(t *testing.T) { assert.NilError(t, err) if test.JSON || test.XML || test.YAML { - str, err := query.PrintSpecial(resp.DNS, test) + str := "" + str, err = query.PrintSpecial(resp.DNS, test) assert.NilError(t, err) assert.Assert(t, str != "") } - str := query.ToString(resp, test) + str, err := query.ToString(resp, test) + assert.NilError(t, err) assert.Assert(t, str != "") }) } @@ -197,5 +197,8 @@ func TestBadFormat(t *testing.T) { func TestEmpty(t *testing.T) { t.Parallel() - assert.Assert(t, query.ToString(util.Response{}, util.Options{}) == " MsgHdr") + str, err := query.ToString(util.Response{}, util.Options{}) + + assert.Error(t, err, "no message") + assert.Assert(t, str == " MsgHdr") } diff --git a/query/query.go b/query/query.go index 673d559..2049b25 100644 --- a/query/query.go +++ b/query/query.go @@ -118,10 +118,14 @@ func CreateQuery(opts util.Options) (util.Response, error) { } else { temp := opts.Display.Statistics opts.Display.Statistics = false - str = ToString(util.Response{ + str, err = ToString(util.Response{ DNS: req, RTT: 0, }, opts) + if err != nil { + return util.Response{}, err + } + opts.Display.Statistics = temp str += "\n;; QUERY SIZE: " + strconv.Itoa(req.Len()) + "\n" } diff --git a/query/query_test.go b/query/query_test.go index b4550a1..b9c2be9 100644 --- a/query/query_test.go +++ b/query/query_test.go @@ -110,10 +110,12 @@ func TestCreateQ(t *testing.T) { assert.Assert(t, res != util.Response{}) str, err := query.PrintSpecial(res.DNS, opt) + assert.NilError(t, err) assert.Assert(t, str != "") - str = query.ToString(res, opt) + str, err = query.ToString(res, opt) + assert.NilError(t, err) assert.Assert(t, str != "") }) } diff --git a/query/struct.go b/query/struct.go index 4a64cf8..908c10f 100644 --- a/query/struct.go +++ b/query/struct.go @@ -3,12 +3,15 @@ package query import ( + "errors" + "fmt" "strconv" "strings" "time" "git.froth.zone/sam/awl/util" "github.com/miekg/dns" + "golang.org/x/net/idna" ) // Message is for overall DNS responses. @@ -45,9 +48,9 @@ type Answer struct { // ToString turns the response into something that looks a lot like dig // // Much of this is taken from https://github.com/miekg/dns/blob/master/msg.go#L900 -func ToString(res util.Response, opts util.Options) string { +func ToString(res util.Response, opts util.Options) (string, error) { if res.DNS == nil { - return " MsgHdr" + return " MsgHdr", errNoMessage } var ( @@ -77,7 +80,12 @@ func ToString(res util.Response, opts util.Options) string { } for _, r := range res.DNS.Question { - s += r.String() + "\n" + str, err := stringParse(r.String(), false, opts) + if err != nil { + return "", fmt.Errorf("%w", err) + } + + s += str + "\n" } } } @@ -90,7 +98,12 @@ func ToString(res util.Response, opts util.Options) string { for _, r := range res.DNS.Answer { if r != nil { - s += r.String() + "\n" + str, err := stringParse(r.String(), true, opts) + if err != nil { + return "", fmt.Errorf("%w", err) + } + + s += str + "\n" } } } @@ -104,7 +117,12 @@ func ToString(res util.Response, opts util.Options) string { for _, r := range res.DNS.Ns { if r != nil { - s += r.String() + "\n" + str, err := stringParse(r.String(), true, opts) + if err != nil { + return "", fmt.Errorf("%w", err) + } + + s += str + "\n" } } } @@ -118,7 +136,12 @@ func ToString(res util.Response, opts util.Options) string { for _, r := range res.DNS.Extra { if r != nil && r.Header().Rrtype != dns.TypeOPT { - s += r.String() + "\n" + str, err := stringParse(r.String(), true, opts) + if err != nil { + return "", fmt.Errorf("%w", err) + } + + s += str + "\n" } } } @@ -163,5 +186,58 @@ func ToString(res util.Response, opts util.Options) string { } } - return s + return s, nil } + +func stringParse(str string, isAns bool, opts util.Options) (string, error) { + split := strings.Split(str, "\t") + + // Make edits if so requested + + // TODO: make less ew? + // This exists because the question section should be left alone EXCEPT for punycode. + + if isAns { + if !opts.ShowTTL { + // Remove from existence + split = append(split[:1], split[2:]...) + } + + if !opts.ShowClass { + // Position depends on if the TTL is there or not. + if opts.ShowTTL { + split = append(split[:2], split[3:]...) + } else { + split = append(split[:1], split[2:]...) + } + } + + if opts.HumanTTL { + ttl, _ := strconv.Atoi(split[1]) + split[1] = (time.Duration(ttl) * time.Second).String() + } + } + + if opts.Display.UcodeTranslate { + var ( + err error + semi string + ) + + if strings.HasPrefix(split[0], ";") { + split[0] = strings.TrimPrefix(split[0], ";") + semi = ";" + } + + split[0], err = idna.ToUnicode(split[0]) + if err != nil { + return "", fmt.Errorf("punycode: %w", err) + } + + split[0] = semi + split[0] + } + + return strings.Join(split, "\t"), nil +} + +var errNoMessage = errors.New("no message") diff --git a/util/options.go b/util/options.go index ff29c65..aaf1a74 100644 --- a/util/options.go +++ b/util/options.go @@ -21,6 +21,7 @@ type Options struct { Display Displays TC bool ShowTTL bool + ShowClass bool ShowQuery bool AA bool AD bool