From 0d011bb097826d1e7eead44753b630954712fdf3 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 13 Sep 2022 18:00:18 +0000 Subject: [PATCH 1/5] fix(deps): update golang.org/x/sys digest to 63ea559 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 9f7fcb1..fa831c7 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( github.com/onsi/ginkgo v1.16.5 // 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-20220909162455-aba9fc2a8ff2 + golang.org/x/sys v0.0.0-20220913175220-63ea55921009 golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.12 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect diff --git a/go.sum b/go.sum index f7afb59..4863d95 100644 --- a/go.sum +++ b/go.sum @@ -125,6 +125,8 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956 h1:XeJjHH1KiLpKGb6lvMiksZ9l0 golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 h1:wM1k/lXfpc5HdkJJyW9GELpd8ERGdnh8sMGL6Gzq3Ho= golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220913175220-63ea55921009 h1:PuvuRMeLWqsf/ZdT1UUZz0syhioyv1mzuFZsXs4fvhw= +golang.org/x/sys v0.0.0-20220913175220-63ea55921009/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= From 9746ae0a6f099b29944bd55303af642d77af8e70 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 15 Sep 2022 11:02:43 +0000 Subject: [PATCH 2/5] feat: more API work Reviewed-on: https://git.froth.zone/sam/awl/pulls/94 Reviewed-by: grumbulon --- .drone.jsonnet | 12 +- .editorconfig | 12 ++ .golangci.yaml | 11 ++ cli/cli.go | 46 ++++---- cli/cli_test.go | 117 ++++++------------ cli/dig.go | 8 +- go.mod | 13 +- go.sum | 14 +-- logawl/logger.go | 20 ++++ logawl/logging_test.go | 5 + main.go | 8 +- main_test.go | 22 +--- pkg/awl-dns-git | 2 +- query/QUIC_test.go | 8 +- query/general_test.go | 11 +- query/print.go | 262 ++++++++++++++++++++++++++++++++++++++--- query/print_test.go | 111 +++++++++-------- query/query.go | 6 +- query/query_test.go | 42 ++++--- query/resolver.go | 4 +- query/struct.go | 262 +++++++---------------------------------- template.mk | 4 +- util/options.go | 175 ++++++++++++++++++--------- util/query.go | 26 ++-- 24 files changed, 661 insertions(+), 540 deletions(-) create mode 100644 .editorconfig diff --git a/.drone.jsonnet b/.drone.jsonnet index 6ac4e64..95a30fb 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -8,19 +8,9 @@ local testing(version, arch) = { arch: arch }, steps: [ - { - name: "compile", - image: "golang:" + version, - commands: [ - "make awl" - ], - }, { name: "lint", image: "rancher/drone-golangci-lint:latest", - depends_on: [ - "compile", - ], }, { name: "test", @@ -74,7 +64,7 @@ local release() = { name: "test", image: "golang", commands: [ - "make test" + "make test-ci" ] }, { diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ebe51d3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/.golangci.yaml b/.golangci.yaml index 9eca14e..badf518 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -32,6 +32,7 @@ linters: - predeclared - revive - staticcheck + - tagliatelle - whitespace - wrapcheck - wsl @@ -70,6 +71,16 @@ linters-settings: - name: unexported-return - name: var-declaration - name: var-naming + linters-settings: + tagliatelle: + case: + use-field-name: false + rules: + # Any struct tag type can be used. + # Support string case: `camel`, `pascal`, `kebab`, `snake`, `goCamel`, `goPascal`, `goKebab`, `goSnake`, `upper`, `lower` + json: goCamel + yaml: goCamel + xml: goCamel issues: exclude-use-default: false diff --git a/cli/cli.go b/cli/cli.go index 8a593cb..f002afe 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -5,7 +5,6 @@ package cli import ( "errors" "fmt" - "os" "runtime" "strconv" "strings" @@ -18,8 +17,8 @@ import ( // ParseCLI parses arguments given from the CLI and passes them into an `Options` // struct. -func ParseCLI(version string) (util.Options, error) { - flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError) +func ParseCLI(args []string, version string) (util.Options, error) { + flag.CommandLine = flag.NewFlagSet(args[0], flag.ContinueOnError) flag.Usage = func() { fmt.Println(`awl - drill, writ small @@ -104,7 +103,7 @@ func ParseCLI(version string) (util.Options, error) { flag.CommandLine.SortFlags = false // Parse the flags - if err := flag.CommandLine.Parse(os.Args[1:]); err != nil { + if err := flag.CommandLine.Parse(args[1:]); err != nil { return util.Options{Logger: util.InitLogger(*verbosity)}, fmt.Errorf("flag: %w", err) } @@ -116,7 +115,6 @@ func ParseCLI(version string) (util.Options, error) { opts := util.Options{ Logger: util.InitLogger(*verbosity), - Port: *port, IPv4: *ipv4, IPv6: *ipv6, Short: *short, @@ -128,29 +126,29 @@ func ParseCLI(version string) (util.Options, error) { HTTPS: *https, QUIC: *quic, Truncate: *truncate, - ShowQuery: false, - AA: *aaflag, - AD: *adflag, - TC: *tcflag, - Z: *zflag, - CD: *cdflag, - QR: *qrflag, - RD: *rdflag, - RA: *raflag, Reverse: *reverse, - HumanTTL: false, - ShowTTL: true, JSON: *json, XML: *xml, YAML: *yaml, + HeaderFlags: util.HeaderFlags{ + AA: *aaflag, + AD: *adflag, + TC: *tcflag, + Z: *zflag, + CD: *cdflag, + QR: *qrflag, + RD: *rdflag, + RA: *raflag, + }, Request: util.Request{ Type: dns.StringToType[strings.ToUpper(*qType)], Class: dns.StringToClass[strings.ToUpper(*class)], Name: *query, Timeout: time.Duration(*timeout * float32(time.Second)), Retries: *retry, + Port: *port, }, - Display: util.Displays{ + Display: util.Display{ Comments: !*noC, Question: !*noQ, Opt: !*noOpt, @@ -158,6 +156,9 @@ func ParseCLI(version string) (util.Options, error) { Authority: !*noAuth, Additional: !*noAdd, Statistics: !*noStats, + HumanTTL: false, + ShowQuery: false, + TTL: true, }, EDNS: util.EDNS{ EnableEDNS: !*edns, @@ -199,15 +200,15 @@ func ParseCLI(version string) (util.Options, error) { opts.Logger.Info("Dig/Drill flags parsed") opts.Logger.Debug(fmt.Sprintf("%+v", opts)) - if opts.Port == 0 { + if opts.Request.Port == 0 { if opts.TLS || opts.QUIC { - opts.Port = 853 + opts.Request.Port = 853 } else { - opts.Port = 53 + opts.Request.Port = 53 } } - opts.Logger.Info("Port set to", opts.Port) + opts.Logger.Info("Port set to", opts.Request.Port) // Set timeout to 0.5 seconds if set below 0.5 if opts.Request.Timeout < (time.Second / 2) { @@ -218,6 +219,9 @@ func ParseCLI(version string) (util.Options, error) { opts.Request.Retries = 0 } + opts.Logger.Info("Options fully populated") + opts.Logger.Debug(fmt.Sprintf("%+v", opts)) + return opts, nil } diff --git a/cli/cli_test.go b/cli/cli_test.go index 0930fbe..e830c2f 100644 --- a/cli/cli_test.go +++ b/cli/cli_test.go @@ -3,7 +3,6 @@ package cli_test import ( - "os" "testing" "time" @@ -12,156 +11,110 @@ import ( ) func TestEmpty(t *testing.T) { - args := os.Args - os.Args = []string{"awl", "-4"} + args := []string{"awl", "-4"} - opts, err := cli.ParseCLI("TEST") + opts, err := cli.ParseCLI(args, "TEST") assert.NilError(t, err) - assert.Equal(t, opts.Port, 53) + assert.Equal(t, opts.Request.Port, 53) assert.Assert(t, opts.IPv4) - - os.Args = args } func TestTLSPort(t *testing.T) { - args := os.Args - os.Args = []string{"awl", "-T"} + args := []string{"awl", "-T"} - opts, err := cli.ParseCLI("TEST") + opts, err := cli.ParseCLI(args, "TEST") assert.NilError(t, err) - assert.Equal(t, opts.Port, 853) - - os.Args = args + assert.Equal(t, opts.Request.Port, 853) } func TestSubnet(t *testing.T) { - args := os.Args - os.Args = []string{"awl", "--subnet", "127.0.0.1/32"} + args := []string{"awl", "--subnet", "127.0.0.1/32"} - opts, err := cli.ParseCLI("TEST") + opts, err := cli.ParseCLI(args, "TEST") assert.NilError(t, err) assert.Equal(t, opts.EDNS.Subnet.Family, uint16(1)) - os.Args = args + args = []string{"awl", "--subnet", "0"} - os.Args = []string{"awl", "--subnet", "0"} - - opts, err = cli.ParseCLI("TEST") + opts, err = cli.ParseCLI(args, "TEST") assert.NilError(t, err) assert.Equal(t, opts.EDNS.Subnet.Family, uint16(1)) - os.Args = args + args = []string{"awl", "--subnet", "::/0"} - os.Args = []string{"awl", "--subnet", "::/0"} - - opts, err = cli.ParseCLI("TEST") + opts, err = cli.ParseCLI(args, "TEST") assert.NilError(t, err) assert.Equal(t, opts.EDNS.Subnet.Family, uint16(2)) - os.Args = args + args = []string{"awl", "--subnet", "/"} - os.Args = []string{"awl", "--subnet", "/"} - - opts, err = cli.ParseCLI("TEST") + opts, err = cli.ParseCLI(args, "TEST") assert.ErrorContains(t, err, "EDNS subnet") - - os.Args = args } -func TestMBZ(t *testing.T) { //nolint: paralleltest // Race conditions - args := os.Args - os.Args = []string{"awl", "--zflag", "G"} +func TestMBZ(t *testing.T) { + args := []string{"awl", "--zflag", "G"} - _, err := cli.ParseCLI("TEST") + _, err := cli.ParseCLI(args, "TEST") assert.ErrorContains(t, err, "EDNS MBZ") - - os.Args = args } -func TestInvalidFlag(t *testing.T) { //nolint: paralleltest // Race conditions - args := os.Args - stdout := os.Stdout - stderr := os.Stderr +func TestInvalidFlag(t *testing.T) { + args := []string{"awl", "--treebug"} - os.Stdout = os.NewFile(0, os.DevNull) - os.Stderr = os.NewFile(0, os.DevNull) - - os.Args = []string{"awl", "--treebug"} - - _, err := cli.ParseCLI("TEST") + _, err := cli.ParseCLI(args, "TEST") assert.ErrorContains(t, err, "unknown flag") - - os.Args = args - os.Stdout = stdout - os.Stderr = stderr } -func TestInvalidDig(t *testing.T) { //nolint: paralleltest // Race conditions - args := os.Args - os.Args = []string{"awl", "+a"} +func TestInvalidDig(t *testing.T) { + args := []string{"awl", "+a"} - _, err := cli.ParseCLI("TEST") + _, err := cli.ParseCLI(args, "TEST") assert.ErrorContains(t, err, "digflags: invalid argument") - - os.Args = args } -func TestVersion(t *testing.T) { //nolint: paralleltest // Race conditions - args := os.Args - stdout := os.Stdout - stderr := os.Stderr +func TestVersion(t *testing.T) { + args := []string{"awl", "--version"} - os.Args = []string{"awl", "--version"} - - _, err := cli.ParseCLI("test") + _, err := cli.ParseCLI(args, "test") assert.ErrorType(t, err, cli.ErrNotError) - - os.Args = args - os.Stdout = stdout - os.Stderr = stderr } -func TestTimeout(t *testing.T) { //nolint: paralleltest // Race conditions +func TestTimeout(t *testing.T) { args := [][]string{ {"awl", "+timeout=0"}, {"awl", "--timeout", "0"}, } for _, test := range args { - args := os.Args - os.Args = test + test := test - opt, err := cli.ParseCLI("TEST") + opt, err := cli.ParseCLI(test, "TEST") assert.NilError(t, err) assert.Equal(t, opt.Request.Timeout, time.Second/2) - - os.Args = args } } -func TestRetries(t *testing.T) { //nolint: paralleltest // Race conditions +func TestRetries(t *testing.T) { args := [][]string{ {"awl", "+retry=-2"}, {"awl", "+tries=-2"}, {"awl", "--retries", "-2"}, } for _, test := range args { - args := os.Args - os.Args = test + test := test - opt, err := cli.ParseCLI("TEST") + opt, err := cli.ParseCLI(test, "TEST") assert.NilError(t, err) assert.Equal(t, opt.Request.Retries, 0) - - os.Args = args } } @@ -175,10 +128,8 @@ func FuzzFlags(f *testing.F) { f.Fuzz(func(t *testing.T, orig string) { // Get rid of outputs - args := os.Args - os.Args = []string{"awl", orig} + args := []string{"awl", orig} //nolint:errcheck,gosec // Only make sure the program does not crash - cli.ParseCLI("TEST") - os.Args = args + cli.ParseCLI(args, "TEST") }) } diff --git a/cli/dig.go b/cli/dig.go index 6d75b6c..00ee8a6 100644 --- a/cli/dig.go +++ b/cli/dig.go @@ -45,13 +45,13 @@ func ParseDig(arg string, opts *util.Options) error { // End DNS query flags case "qr": - opts.ShowQuery = isNo + opts.Display.ShowQuery = isNo case "ttlunits": - opts.HumanTTL = isNo + opts.Display.HumanTTL = isNo case "ttl", "ttlid": - opts.ShowTTL = isNo + opts.Display.TTL = isNo case "class": - opts.ShowClass = isNo + opts.Display.ShowClass = isNo // EDNS queries case "dnssec": diff --git a/go.mod b/go.mod index fa831c7..9b6a2f8 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/miekg/dns v1.1.50 github.com/stefansundin/go-zflag v1.1.1 golang.org/x/net v0.0.0-20220909164309-bea034e7d591 + golang.org/x/sys v0.0.0-20220913175220-63ea55921009 gopkg.in/yaml.v3 v3.0.1 gotest.tools/v3 v3.3.0 ) @@ -18,22 +19,18 @@ require ( 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.3 // indirect + github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/golang/mock v1.6.0 // indirect github.com/google/go-cmp v0.5.8 // indirect github.com/kr/pretty v0.3.0 // indirect - github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect - golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 // indirect -) - -require ( - 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-18 v0.1.2 // indirect + github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/onsi/ginkgo v1.16.5 // indirect golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect + golang.org/x/exp v0.0.0-20220914170420-dc92f8653013 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/sys v0.0.0-20220913175220-63ea55921009 golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.12 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect diff --git a/go.sum b/go.sum index 4863d95..5defd9f 100644 --- a/go.sum +++ b/go.sum @@ -79,10 +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-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/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= -golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/exp v0.0.0-20220914170420-dc92f8653013 h1:ZjglnWxEUdPyXl4o/j4T89SRCI+4X6NW6185PNLEOF4= +golang.org/x/exp v0.0.0-20220914170420-dc92f8653013/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= 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= @@ -94,8 +92,6 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220907135653-1e95f45603a7 h1:1WGATo9HAhkWMbfyuVU0tEFP88OIkUvwaHFveQPvzCQ= -golang.org/x/net v0.0.0-20220907135653-1e95f45603a7/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI= golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -119,12 +115,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-20220907062415-87db552b00fd h1:AZeIEzg+8RCELJYq8w+ODLVxFgLMMigSwO/ffKPEd9U= -golang.org/x/sys v0.0.0-20220907062415-87db552b00fd/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956 h1:XeJjHH1KiLpKGb6lvMiksZ9l0fVUh+AmGcm0nOMEBOY= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 h1:wM1k/lXfpc5HdkJJyW9GELpd8ERGdnh8sMGL6Gzq3Ho= -golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220913175220-63ea55921009 h1:PuvuRMeLWqsf/ZdT1UUZz0syhioyv1mzuFZsXs4fvhw= golang.org/x/sys v0.0.0-20220913175220-63ea55921009/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/logawl/logger.go b/logawl/logger.go index e6aebfd..b0993ea 100644 --- a/logawl/logger.go +++ b/logawl/logger.go @@ -139,17 +139,37 @@ func (l *Logger) Debug(v ...any) { l.Println(DebugLevel, v...) } +// Debugf calls print after formatting the string with Debug level. +func (l *Logger) Debugf(format string, v ...any) { + l.Println(ErrLevel, fmt.Sprintf(format, v...)) +} + // Info calls print directly with Info level. func (l *Logger) Info(v ...any) { l.Println(InfoLevel, v...) } +// Infof calls print after formatting the string with Info level. +func (l *Logger) Infof(format string, v ...any) { + l.Println(ErrLevel, fmt.Sprintf(format, v...)) +} + // Warn calls print directly with Warn level. func (l *Logger) Warn(v ...any) { l.Println(WarnLevel, v...) } +// Warnf calls print after formatting the string with Warn level. +func (l *Logger) Warnf(format string, v ...any) { + l.Println(WarnLevel, fmt.Sprintf(format, v...)) +} + // Error calls print directly with Error level. func (l *Logger) Error(v ...any) { l.Println(ErrLevel, v...) } + +// Errorf calls print after formatting the string with Error level. +func (l *Logger) Errorf(format string, v ...any) { + l.Println(ErrLevel, fmt.Sprintf(format, v...)) +} diff --git a/logawl/logging_test.go b/logawl/logging_test.go index 1b63b01..24f001c 100644 --- a/logawl/logging_test.go +++ b/logawl/logging_test.go @@ -52,6 +52,7 @@ func TestLogger(t *testing.T) { case 0: fn := func() { logger.Error("Test", "E") + logger.Errorf("%s", "Test") } var buffer bytes.Buffer @@ -62,6 +63,7 @@ func TestLogger(t *testing.T) { case 1: fn := func() { logger.Warn("Test") + logger.Warnf("%s", "Test") } var buffer bytes.Buffer @@ -72,6 +74,7 @@ func TestLogger(t *testing.T) { case 2: fn := func() { logger.Info("Test") + logger.Infof("%s", "Test") } var buffer bytes.Buffer @@ -83,6 +86,8 @@ func TestLogger(t *testing.T) { fn := func() { logger.Debug("Test") logger.Debug("Test 2") + logger.Debugf("%s", "Test") + logger.Debugf("%s %d", "Test", 2) } var buffer bytes.Buffer diff --git a/main.go b/main.go index 641deac..7771998 100644 --- a/main.go +++ b/main.go @@ -17,7 +17,7 @@ import ( var version = "DEV" func main() { - if opts, code, err := run(); err != nil { + if opts, code, err := run(os.Args); err != nil { // TODO: Make not ew if errors.Is(err, cli.ErrNotError) || strings.Contains(err.Error(), "help requested") { os.Exit(0) @@ -28,8 +28,8 @@ func main() { } } -func run() (opts util.Options, code int, err error) { - opts, err = cli.ParseCLI(version) +func run(args []string) (opts util.Options, code int, err error) { + opts, err = cli.ParseCLI(args, version) if err != nil { return opts, 1, fmt.Errorf("parse: %w", err) } @@ -53,7 +53,7 @@ func run() (opts util.Options, code int, err error) { var str string if opts.JSON || opts.XML || opts.YAML { - str, err = query.PrintSpecial(resp.DNS, opts) + str, err = query.PrintSpecial(resp, opts) if err != nil { return opts, 10, fmt.Errorf("format print: %w", err) } diff --git a/main_test.go b/main_test.go index eb42484..57186c9 100644 --- a/main_test.go +++ b/main_test.go @@ -14,33 +14,23 @@ 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 + args := []string{"awl", "+yaml", "@1.1.1.1"} - os.Args = []string{"awl", "+yaml", "@1.1.1.1"} - - _, code, err := run() + _, code, err := run(args) assert.NilError(t, err) assert.Equal(t, code, 0) - os.Args = []string{"awl", "+short", "@1.1.1.1"} + args = []string{"awl", "+short", "@1.1.1.1"} - _, code, err = run() + _, code, err = run(args) assert.NilError(t, err) assert.Equal(t, code, 0) - - os.Args = old } func TestHelp(t *testing.T) { - old := os.Args - os.Stdout = os.NewFile(0, os.DevNull) - os.Stderr = os.NewFile(0, os.DevNull) + args := []string{"awl", "-h"} - os.Args = []string{"awl", "-h"} - - _, code, err := run() + _, code, err := run(args) assert.ErrorIs(t, err, zflag.ErrHelp) assert.Equal(t, code, 1) - - os.Args = old } diff --git a/pkg/awl-dns-git b/pkg/awl-dns-git index c8686b1..d876d6d 160000 --- a/pkg/awl-dns-git +++ b/pkg/awl-dns-git @@ -1 +1 @@ -Subproject commit c8686b1e149b6c42db019c77caf36475aafadd03 +Subproject commit d876d6de34a78298ed041f575662015fb7eccdb5 diff --git a/query/QUIC_test.go b/query/QUIC_test.go index 6158254..6d83199 100644 --- a/query/QUIC_test.go +++ b/query/QUIC_test.go @@ -22,8 +22,7 @@ func TestQuic(t *testing.T) { opts := util.Options{ QUIC: true, Logger: util.InitLogger(0), - Port: 853, - Request: util.Request{Server: "dns.adguard.com"}, + Request: util.Request{Server: "dns.adguard.com", Port: 853}, } testCase := util.Request{Server: "dns.//./,,adguard.com", Type: dns.TypeA, Name: "git.froth.zone"} testCase2 := util.Request{Server: "dns.adguard.com", Type: dns.TypeA, Name: "git.froth.zone"} @@ -54,7 +53,7 @@ func TestQuic(t *testing.T) { resolver, err := query.LoadResolver(opts) assert.NilError(t, err) - testCase2.Server = net.JoinHostPort(testCase2.Server, strconv.Itoa(opts.Port)) + testCase2.Server = net.JoinHostPort(testCase2.Server, strconv.Itoa(opts.Request.Port)) // if the domain is not canonical, make it canonical if !strings.HasSuffix(testCase2.Name, ".") { @@ -78,8 +77,7 @@ func TestInvalidQuic(t *testing.T) { opts := util.Options{ QUIC: true, Logger: util.InitLogger(0), - Port: 853, - Request: util.Request{Server: "example.com", Type: dns.TypeA, Name: "git.froth.zone", Timeout: 10 * time.Millisecond}, + Request: util.Request{Server: "example.com", Port: 853, Type: dns.TypeA, Name: "git.froth.zone", Timeout: 10 * time.Millisecond}, } resolver, err := query.LoadResolver(opts) assert.NilError(t, err) diff --git a/query/general_test.go b/query/general_test.go index 312cf60..fe06c12 100644 --- a/query/general_test.go +++ b/query/general_test.go @@ -17,9 +17,9 @@ func TestResolve(t *testing.T) { opts := util.Options{ Logger: util.InitLogger(0), - Port: 53, Request: util.Request{ Server: "8.8.4.1", + Port: 53, Type: dns.TypeA, Name: "example.com.", Timeout: time.Second / 2, @@ -42,9 +42,9 @@ func TestTruncate(t *testing.T) { opts := util.Options{ Logger: util.InitLogger(0), IPv4: true, - Port: 5301, Request: util.Request{ Server: "madns.binarystar.systems", + Port: 5301, Type: dns.TypeTXT, Name: "limit.txt.example.", }, @@ -70,9 +70,10 @@ func TestResolveAgain(t *testing.T) { util.Options{ Logger: util.InitLogger(0), TCP: true, - Port: 53, + Request: util.Request{ Server: "8.8.4.4", + Port: 53, Type: dns.TypeA, Name: "example.com.", }, @@ -81,9 +82,9 @@ func TestResolveAgain(t *testing.T) { { util.Options{ Logger: util.InitLogger(0), - Port: 53, Request: util.Request{ Server: "8.8.4.4", + Port: 53, Type: dns.TypeAAAA, Name: "example.com.", }, @@ -93,9 +94,9 @@ func TestResolveAgain(t *testing.T) { util.Options{ Logger: util.InitLogger(0), TLS: true, - Port: 853, Request: util.Request{ Server: "dns.google", + Port: 853, Type: dns.TypeAAAA, Name: "example.com.", }, diff --git a/query/print.go b/query/print.go index 5eaa6bf..d3a79c3 100644 --- a/query/print.go +++ b/query/print.go @@ -18,10 +18,210 @@ import ( "gopkg.in/yaml.v3" ) +// 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, error) { + if res.DNS == nil { + return " MsgHdr", errNoMessage + } + + var ( + s string + opt *dns.OPT + ) + + if !opts.Short { + if opts.Display.Comments { + s += res.DNS.MsgHdr.String() + " " + s += "QUERY: " + strconv.Itoa(len(res.DNS.Question)) + ", " + s += "ANSWER: " + strconv.Itoa(len(res.DNS.Answer)) + ", " + s += "AUTHORITY: " + strconv.Itoa(len(res.DNS.Ns)) + ", " + s += "ADDITIONAL: " + strconv.Itoa(len(res.DNS.Extra)) + "\n" + opt = res.DNS.IsEdns0() + + if opt != nil && opts.Display.Opt { + // OPT PSEUDOSECTION + s += opt.String() + "\n" + } + } + + if opts.Display.Question { + if len(res.DNS.Question) > 0 { + if opts.Display.Comments { + s += "\n;; QUESTION SECTION:\n" + } + + for _, r := range res.DNS.Question { + str, err := stringParse(r.String(), false, opts) + if err != nil { + return "", fmt.Errorf("%w", err) + } + + s += str + "\n" + } + } + } + + if opts.Display.Answer { + if len(res.DNS.Answer) > 0 { + if opts.Display.Comments { + s += "\n;; ANSWER SECTION:\n" + } + + for _, r := range res.DNS.Answer { + if r != nil { + str, err := stringParse(r.String(), true, opts) + if err != nil { + return "", fmt.Errorf("%w", err) + } + + s += str + "\n" + } + } + } + } + + if opts.Display.Authority { + if len(res.DNS.Ns) > 0 { + if opts.Display.Comments { + s += "\n;; AUTHORITY SECTION:\n" + } + + for _, r := range res.DNS.Ns { + if r != nil { + str, err := stringParse(r.String(), true, opts) + if err != nil { + return "", fmt.Errorf("%w", err) + } + + s += str + "\n" + } + } + } + } + + if opts.Display.Additional { + if len(res.DNS.Extra) > 0 && (opt == nil || len(res.DNS.Extra) > 1) { + if opts.Display.Comments { + s += "\n;; ADDITIONAL SECTION:\n" + } + + for _, r := range res.DNS.Extra { + if r != nil && r.Header().Rrtype != dns.TypeOPT { + str, err := stringParse(r.String(), true, opts) + if err != nil { + return "", fmt.Errorf("%w", err) + } + + s += str + "\n" + } + } + } + } + + if opts.Display.Statistics { + s += "\n;; Query time: " + res.RTT.String() + s += "\n;; SERVER: " + opts.Request.Server + serverExtra(opts) + s += "\n;; WHEN: " + time.Now().Format(time.RFC1123Z) + s += "\n;; MSG SIZE rcvd: " + strconv.Itoa(res.DNS.Len()) + "\n" + } + } else { + // Print just the responses, nothing else + for i, resp := range res.DNS.Answer { + temp := strings.Split(resp.String(), "\t") + s += temp[len(temp)-1] + + if opts.Identify { + s += " from server " + opts.Request.Server + " in " + res.RTT.String() + } + + // Don't print newline on last line + if i != len(res.DNS.Answer)-1 { + s += "\n" + } + } + } + + return s, nil +} + +func serverExtra(opts util.Options) string { + // Add extra information to server string + var extra string + + switch { + case opts.TCP: + extra = ":" + strconv.Itoa(opts.Request.Port) + " (TCP)" + case opts.TLS: + extra = ":" + strconv.Itoa(opts.Request.Port) + " (TLS)" + case opts.HTTPS, opts.DNSCrypt: + extra = "" + case opts.QUIC: + extra = ":" + strconv.Itoa(opts.Request.Port) + " (QUIC)" + default: + extra = ":" + strconv.Itoa(opts.Request.Port) + " (UDP)" + } + + return extra +} + +// stringParse edits the raw responses to user requests. +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.Display.TTL { + // Remove from existence + split = append(split[:1], split[2:]...) + } + + if !opts.Display.ShowClass { + // Position depends on if the TTL is there or not. + if opts.Display.TTL { + split = append(split[:2], split[3:]...) + } else { + split = append(split[:1], split[2:]...) + } + } + + if opts.Display.TTL && opts.Display.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 +} + // PrintSpecial is for printing as JSON, XML or YAML. // As of now JSON and XML use the stdlib version. -func PrintSpecial(msg *dns.Msg, opts util.Options) (string, error) { - formatted, err := MakePrintable(msg, opts) +func PrintSpecial(res util.Response, opts util.Options) (string, error) { + formatted, err := MakePrintable(res, opts) if err != nil { return "", err } @@ -52,11 +252,34 @@ func PrintSpecial(msg *dns.Msg, opts util.Options) (string, error) { // MakePrintable takes a DNS message and makes it nicer to be printed as JSON,YAML, // and XML. Little is changed beyond naming. -func MakePrintable(msg *dns.Msg, opts util.Options) (*Message, error) { - var err error - +func MakePrintable(res util.Response, opts util.Options) (*Message, error) { + var ( + err error + msg = res.DNS + ) + // The things I do for compatibility ret := Message{ - Header: msg.MsgHdr, + Header: Header{ + ID: msg.Id, + Response: msg.Response, + Opcode: dns.OpcodeToString[msg.Opcode], + Authoritative: msg.Authoritative, + Truncated: msg.Truncated, + RecursionDesired: msg.RecursionDesired, + RecursionAvailable: msg.RecursionAvailable, + Zero: msg.Zero, + AuthenticatedData: msg.AuthenticatedData, + CheckingDisabled: msg.CheckingDisabled, + Status: dns.RcodeToString[msg.Rcode], + }, + } + + opt := msg.IsEdns0() + if opt != nil && opts.Display.Opt { + ret.Opt, err = parseOpt(*opt) + if err != nil { + return nil, fmt.Errorf("edns print: %w", err) + } } for _, question := range msg.Question { @@ -85,8 +308,8 @@ func MakePrintable(msg *dns.Msg, opts util.Options) (*Message, error) { name string ) - if opts.ShowTTL { - if opts.HumanTTL { + if opts.Display.TTL { + if opts.Display.HumanTTL { ttl = (time.Duration(answer.Header().Ttl) * time.Second).String() } else { ttl = strconv.Itoa(int(answer.Header().Ttl)) @@ -122,8 +345,8 @@ func MakePrintable(msg *dns.Msg, opts util.Options) (*Message, error) { name string ) - if opts.ShowTTL { - if opts.HumanTTL { + if opts.Display.TTL { + if opts.Display.HumanTTL { ttl = (time.Duration(ns.Header().Ttl) * time.Second).String() } else { ttl = strconv.Itoa(int(ns.Header().Ttl)) @@ -139,7 +362,7 @@ func MakePrintable(msg *dns.Msg, opts util.Options) (*Message, error) { name = ns.Header().Name } - ret.Ns = append(ret.Ns, Answer{ + ret.Authority = append(ret.Authority, Answer{ RRHeader: RRHeader{ Name: name, Type: dns.TypeToString[ns.Header().Rrtype], @@ -162,8 +385,8 @@ func MakePrintable(msg *dns.Msg, opts util.Options) (*Message, error) { name string ) - if opts.ShowTTL { - if opts.HumanTTL { + if opts.Display.TTL { + if opts.Display.HumanTTL { ttl = (time.Duration(additional.Header().Ttl) * time.Second).String() } else { ttl = strconv.Itoa(int(additional.Header().Ttl)) @@ -192,12 +415,15 @@ func MakePrintable(msg *dns.Msg, opts util.Options) (*Message, error) { } } - opt := msg.IsEdns0() - if opt != nil && opts.Display.Opt { - ret.Opt, err = parseOpt(*opt) - if err != nil { - return nil, fmt.Errorf("edns print: %w", err) + if opts.Display.Statistics { + ret.Statistics = Statistics{ + RTT: res.RTT.String(), + Server: opts.Request.Server + serverExtra(opts), + When: time.Now().Format(time.RFC1123Z), + MsgSize: res.DNS.Len(), } + } else { + ret.Statistics = Statistics{} } return &ret, nil diff --git a/query/print_test.go b/query/print_test.go index 8dce5d1..3f88e3c 100644 --- a/query/print_test.go +++ b/query/print_test.go @@ -16,15 +16,16 @@ func TestRealPrint(t *testing.T) { opts := []util.Options{ { - Logger: util.InitLogger(0), - Port: 53, - TCP: true, - ShowQuery: true, - RD: true, - ShowTTL: true, - HumanTTL: true, - JSON: true, - Display: util.Displays{ + Logger: util.InitLogger(0), + + TCP: true, + + HeaderFlags: util.HeaderFlags{ + RD: true, + }, + + JSON: true, + Display: util.Display{ Comments: true, Question: true, Answer: true, @@ -32,9 +33,13 @@ func TestRealPrint(t *testing.T) { Additional: true, Statistics: true, UcodeTranslate: true, + TTL: true, + HumanTTL: true, + ShowQuery: true, }, Request: util.Request{ Server: "a.gtld-servers.net", + Port: 53, Type: dns.StringToType["NS"], Class: 1, Name: "google.com.", @@ -44,17 +49,18 @@ func TestRealPrint(t *testing.T) { }, }, { - Logger: util.InitLogger(0), - Port: 53, - TCP: true, - ShowQuery: true, - RD: true, + Logger: util.InitLogger(0), + + TCP: true, + HeaderFlags: util.HeaderFlags{ + RD: true, + }, Verbosity: 0, - ShowTTL: true, - Short: true, - Identify: true, - YAML: false, - Display: util.Displays{ + + Short: true, + Identify: true, + YAML: false, + Display: util.Display{ Comments: true, Question: true, Answer: true, @@ -62,9 +68,12 @@ func TestRealPrint(t *testing.T) { Additional: true, Statistics: true, UcodeTranslate: true, + TTL: true, + ShowQuery: true, }, Request: util.Request{ Server: "ns1.google.com", + Port: 53, Type: dns.StringToType["NS"], Class: 1, Name: "google.com.", @@ -76,16 +85,14 @@ func TestRealPrint(t *testing.T) { }, }, { - Logger: util.InitLogger(0), - Port: 53, - HTTPS: true, - ShowQuery: true, - RD: true, - ShowTTL: true, - HumanTTL: true, - Identify: true, - XML: true, - Display: util.Displays{ + Logger: util.InitLogger(0), + HTTPS: true, + HeaderFlags: util.HeaderFlags{ + RD: true, + }, + Identify: true, + XML: true, + Display: util.Display{ Comments: true, Question: true, Answer: true, @@ -93,9 +100,13 @@ func TestRealPrint(t *testing.T) { Additional: true, Statistics: true, UcodeTranslate: false, + TTL: true, + HumanTTL: true, + ShowQuery: true, }, Request: util.Request{ Server: "https://dns.froth.zone/dns-query", + Port: 443, Type: dns.StringToType["NS"], Class: 1, Name: "freecumextremist.com.", @@ -108,14 +119,13 @@ func TestRealPrint(t *testing.T) { }, }, { - Logger: util.InitLogger(0), - Port: 853, - TLS: true, - ShowQuery: true, - RD: true, + Logger: util.InitLogger(0), + TLS: true, + HeaderFlags: util.HeaderFlags{ + RD: true, + }, Verbosity: 0, - ShowTTL: false, - Display: util.Displays{ + Display: util.Display{ Comments: true, Question: true, Answer: true, @@ -123,25 +133,29 @@ func TestRealPrint(t *testing.T) { Additional: true, Statistics: true, UcodeTranslate: true, + TTL: false, + ShowQuery: true, }, Request: util.Request{ Server: "dns.google", + Port: 853, Type: dns.StringToType["NS"], Class: 1, Name: "freecumextremist.com.", }, }, { - Logger: util.InitLogger(0), - Port: 53, - TCP: true, - ShowQuery: true, - AA: true, - RD: true, + Logger: util.InitLogger(0), + TCP: true, + + HeaderFlags: util.HeaderFlags{ + AA: true, + RD: true, + }, Verbosity: 0, - ShowTTL: true, - YAML: true, - Display: util.Displays{ + + YAML: true, + Display: util.Display{ Comments: true, Question: true, Answer: true, @@ -149,9 +163,12 @@ func TestRealPrint(t *testing.T) { Additional: true, Statistics: true, UcodeTranslate: false, + TTL: true, + ShowQuery: true, }, Request: util.Request{ Server: "rin.froth.zone", + Port: 53, Type: dns.StringToType["A"], Class: 1, Name: "froth.zone.", @@ -176,7 +193,7 @@ func TestRealPrint(t *testing.T) { if test.JSON || test.XML || test.YAML { str := "" - str, err = query.PrintSpecial(resp.DNS, test) + str, err = query.PrintSpecial(resp, test) assert.NilError(t, err) assert.Assert(t, str != "") } @@ -190,7 +207,7 @@ func TestRealPrint(t *testing.T) { func TestBadFormat(t *testing.T) { t.Parallel() - _, err := query.PrintSpecial(new(dns.Msg), util.Options{}) + _, err := query.PrintSpecial(util.Response{DNS: new(dns.Msg)}, util.Options{}) assert.ErrorContains(t, err, "never happen") } diff --git a/query/query.go b/query/query.go index 9b1cda9..6bccb34 100644 --- a/query/query.go +++ b/query/query.go @@ -102,7 +102,7 @@ func CreateQuery(opts util.Options) (util.Response, error) { opts.Logger.Debug(req) if !opts.Short { - if opts.ShowQuery { + if opts.Display.ShowQuery { opts.Logger.Info("Printing constructed query") var ( @@ -111,7 +111,7 @@ func CreateQuery(opts util.Options) (util.Response, error) { ) if opts.JSON || opts.XML || opts.YAML { - str, err = PrintSpecial(req, opts) + str, err = PrintSpecial(util.Response{DNS: req}, opts) if err != nil { return util.Response{}, err } @@ -133,7 +133,7 @@ func CreateQuery(opts util.Options) (util.Response, error) { fmt.Println(str) - opts.ShowQuery = false + opts.Display.ShowQuery = false } } diff --git a/query/query_test.go b/query/query_test.go index 6b4d504..5a5183f 100644 --- a/query/query_test.go +++ b/query/query_test.go @@ -16,18 +16,20 @@ func TestCreateQ(t *testing.T) { in := []util.Options{ { - Logger: util.InitLogger(0), - Port: 53, - Z: true, - ShowQuery: true, - YAML: true, + Logger: util.InitLogger(0), + HeaderFlags: util.HeaderFlags{ + Z: true, + }, + + YAML: true, Request: util.Request{ Server: "8.8.4.4", + Port: 53, Type: dns.TypeA, Name: "example.com.", }, - Display: util.Displays{ + Display: util.Display{ Comments: true, Question: true, Opt: true, @@ -35,6 +37,7 @@ func TestCreateQ(t *testing.T) { Authority: true, Additional: true, Statistics: true, + ShowQuery: true, }, EDNS: util.EDNS{ ZFlag: 1, @@ -50,18 +53,19 @@ func TestCreateQ(t *testing.T) { }, }, { - Logger: util.InitLogger(0), - Port: 53, - Z: true, - ShowQuery: true, - XML: true, + Logger: util.InitLogger(0), + HeaderFlags: util.HeaderFlags{ + Z: true, + }, + XML: true, Request: util.Request{ Server: "8.8.4.4", + Port: 53, Type: dns.TypeA, Name: "example.com.", }, - Display: util.Displays{ + Display: util.Display{ Comments: true, Question: true, Opt: true, @@ -70,22 +74,21 @@ func TestCreateQ(t *testing.T) { Additional: true, Statistics: true, UcodeTranslate: true, + ShowQuery: true, }, }, { Logger: util.InitLogger(0), - Port: 853, - // Z: true, - ShowQuery: true, - JSON: true, - QUIC: true, + JSON: true, + QUIC: true, Request: util.Request{ Server: "dns.adguard.com", + Port: 853, Type: dns.TypeA, Name: "example.com.", }, - Display: util.Displays{ + Display: util.Display{ Comments: true, Question: true, Opt: true, @@ -93,6 +96,7 @@ func TestCreateQ(t *testing.T) { Authority: true, Additional: true, Statistics: true, + ShowQuery: true, }, EDNS: util.EDNS{ EnableEDNS: true, @@ -113,7 +117,7 @@ func TestCreateQ(t *testing.T) { assert.NilError(t, err) assert.Assert(t, res != util.Response{}) - str, err := query.PrintSpecial(res.DNS, opt) + str, err := query.PrintSpecial(res, opt) assert.NilError(t, err) assert.Assert(t, str != "") diff --git a/query/resolver.go b/query/resolver.go index 2ede1db..049522e 100644 --- a/query/resolver.go +++ b/query/resolver.go @@ -31,7 +31,7 @@ func LoadResolver(opts util.Options) (Resolver, error) { }, nil case opts.QUIC: opts.Logger.Info("loading DNS-over-QUIC resolver") - opts.Request.Server = net.JoinHostPort(opts.Request.Server, strconv.Itoa(opts.Port)) + opts.Request.Server = net.JoinHostPort(opts.Request.Server, strconv.Itoa(opts.Request.Port)) return &QUICResolver{ opts: opts, @@ -48,7 +48,7 @@ func LoadResolver(opts util.Options) (Resolver, error) { }, nil default: opts.Logger.Info("loading standard/DNS-over-TLS resolver") - opts.Request.Server = net.JoinHostPort(opts.Request.Server, strconv.Itoa(opts.Port)) + opts.Request.Server = net.JoinHostPort(opts.Request.Server, strconv.Itoa(opts.Request.Port)) return &StandardResolver{ opts: opts, diff --git a/query/struct.go b/query/struct.go index 2617b4f..3ad2159 100644 --- a/query/struct.go +++ b/query/struct.go @@ -4,48 +4,63 @@ 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. // -//nolint:govet // Better output is worth 32 bytes. +//nolint:govet // Better looking output is worth a few bytes. type Message struct { - Header dns.MsgHdr `json:"header,omitempty" xml:"header,omitempty" yaml:",omitempty"` - Opt []Opts `json:"opt,omitempty" xml:"opt,omitempty" yaml:"opt,omitempty"` - Question []Question `json:"question,omitempty" xml:"question,omitempty" yaml:",omitempty"` - Answer []Answer `json:"answer,omitempty" xml:"answer,omitempty" yaml:",omitempty"` - Ns []Answer `json:"ns,omitempty" xml:"ns,omitempty" yaml:",omitempty"` - Additional []Answer `json:"additional,omitempty" xml:"additional,omitempty" yaml:",omitempty"` + // Header section + Header `json:"header,omitempty" xml:"header,omitempty" yaml:"header,omitempty"` + // Opt Pseudosection + Opt []Opts `json:"opt,omitempty" xml:"opt,omitempty" yaml:"opt,omitempty"` + // Question Section + Question []Question `json:"question,omitempty" xml:"question,omitempty" yaml:"question,omitempty"` + // Answer Section + Answer []Answer `json:"answer,omitempty" xml:"answer,omitempty" yaml:"answer,omitempty"` + // Authority Section + Authority []Answer `json:"authority,omitempty" xml:"authority,omitempty" yaml:"authority,omitempty"` + // Additional Section + Additional []Answer `json:"additional,omitempty" xml:"additional,omitempty" yaml:"additional,omitempty"` + // Statistics :) + Statistics `json:"statistics,omitempty" xml:"statistics,omitempty" yaml:"statistics,omitempty"` +} + +// Header is the header. +type Header struct { + Opcode string `json:"opcode," xml:"opcode," yaml:"opcode" example:"QUERY"` + Status string `json:"status," xml:"status," yaml:"status" example:"NOERR"` + ID uint16 `json:"id," xml:"id," yaml:"id" example:"12"` + Response bool `json:"response," xml:"response," yaml:"response" example:"true"` + Authoritative bool `json:"authoritative," xml:"authoritative," yaml:"authoritative" example:"false"` + Truncated bool `json:"truncated," xml:"truncated," yaml:"truncated" example:"false"` + RecursionDesired bool `json:"recursionDesired," xml:"recursionDesired," yaml:"recursionDesired" example:"true"` + RecursionAvailable bool `json:"recursionAvailable," xml:"recursionAvailable," yaml:"recursionAvailable" example:"true"` + Zero bool `json:"zero," xml:"zero," yaml:"zero" example:"false"` + AuthenticatedData bool `json:"authenticatedData," xml:"authenticatedData," yaml:"authenticatedData" example:"false"` + CheckingDisabled bool `json:"checkingDisabled," xml:"checkingDisabled," yaml:"checkingDisabled" example:"false"` } // Question is a DNS Query. type Question struct { - Name string `json:"name,omitempty" xml:"name,omitempty" yaml:",omitempty"` - Class string `json:"class,omitempty" xml:"class,omitempty" yaml:",omitempty"` - Type string `json:"type,omitempty" xml:"type,omitempty" yaml:",omitempty"` + Name string `json:"name,omitempty" xml:"name,omitempty" yaml:"name,omitempty" example:"localhost"` + Class string `json:"class,omitempty" xml:"class,omitempty" yaml:"class,omitempty" example:"A"` + Type string `json:"type,omitempty" xml:"type,omitempty" yaml:"type,omitempty" example:"IN"` } // RRHeader is for DNS Resource Headers. type RRHeader struct { - Name string `json:"name,omitempty" xml:"name,omitempty" yaml:",omitempty"` - TTL string `json:"ttl,omitempty" xml:"ttl,omitempty" yaml:",omitempty"` - Class string `json:"class,omitempty" xml:"class,omitempty" yaml:",omitempty"` - Type string `json:"type,omitempty" xml:"type,omitempty" yaml:",omitempty"` + Name string `json:"name,omitempty" xml:"name,omitempty" yaml:"name,omitempty" example:"127.0.0.1"` + TTL string `json:"ttl,omitempty" xml:"ttl,omitempty" yaml:"ttl,omitempty" example:"0ms"` + Class string `json:"class,omitempty" xml:"class,omitempty" yaml:"class,omitempty" example:"A"` + Type string `json:"type,omitempty" xml:"type,omitempty" yaml:"type,omitempty" example:"IN"` Rdlength uint16 `json:"-" xml:"-" yaml:"-"` } // Opts is for the OPT pseudosection, nearly exclusively for EDNS. type Opts struct { - Name string `json:"name,omitempty" xml:"name,omitempty" yaml:",omitempty"` - Value string `json:"value" xml:"value" yaml:""` + Name string `json:"name,omitempty" xml:"name,omitempty" yaml:"name,omitempty"` + Value string `json:"value" xml:"value" yaml:"value"` } // Answer is for a DNS Response. @@ -54,199 +69,12 @@ type Answer struct { RRHeader `json:"header,omitempty" xml:"header,omitempty" yaml:"header,omitempty"` } -// 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, error) { - if res.DNS == nil { - return " MsgHdr", errNoMessage - } - - var ( - s string - opt *dns.OPT - ) - - if !opts.Short { - if opts.Display.Comments { - s += res.DNS.MsgHdr.String() + " " - s += "QUERY: " + strconv.Itoa(len(res.DNS.Question)) + ", " - s += "ANSWER: " + strconv.Itoa(len(res.DNS.Answer)) + ", " - s += "AUTHORITY: " + strconv.Itoa(len(res.DNS.Ns)) + ", " - s += "ADDITIONAL: " + strconv.Itoa(len(res.DNS.Extra)) + "\n" - opt = res.DNS.IsEdns0() - - if opt != nil && opts.Display.Opt { - // OPT PSEUDOSECTION - s += opt.String() + "\n" - } - } - - if opts.Display.Question { - if len(res.DNS.Question) > 0 { - if opts.Display.Comments { - s += "\n;; QUESTION SECTION:\n" - } - - for _, r := range res.DNS.Question { - str, err := stringParse(r.String(), false, opts) - if err != nil { - return "", fmt.Errorf("%w", err) - } - - s += str + "\n" - } - } - } - - if opts.Display.Answer { - if len(res.DNS.Answer) > 0 { - if opts.Display.Comments { - s += "\n;; ANSWER SECTION:\n" - } - - for _, r := range res.DNS.Answer { - if r != nil { - str, err := stringParse(r.String(), true, opts) - if err != nil { - return "", fmt.Errorf("%w", err) - } - - s += str + "\n" - } - } - } - } - - if opts.Display.Authority { - if len(res.DNS.Ns) > 0 { - if opts.Display.Comments { - s += "\n;; AUTHORITY SECTION:\n" - } - - for _, r := range res.DNS.Ns { - if r != nil { - str, err := stringParse(r.String(), true, opts) - if err != nil { - return "", fmt.Errorf("%w", err) - } - - s += str + "\n" - } - } - } - } - - if opts.Display.Additional { - if len(res.DNS.Extra) > 0 && (opt == nil || len(res.DNS.Extra) > 1) { - if opts.Display.Comments { - s += "\n;; ADDITIONAL SECTION:\n" - } - - for _, r := range res.DNS.Extra { - if r != nil && r.Header().Rrtype != dns.TypeOPT { - str, err := stringParse(r.String(), true, opts) - if err != nil { - return "", fmt.Errorf("%w", err) - } - - s += str + "\n" - } - } - } - } - - if opts.Display.Statistics { - s += "\n;; Query time: " + res.RTT.String() - // 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)" - } - - s += "\n;; SERVER: " + opts.Request.Server + extra - s += "\n;; WHEN: " + time.Now().Format(time.RFC1123Z) - s += "\n;; MSG SIZE rcvd: " + strconv.Itoa(res.DNS.Len()) + "\n" - } - } else { - // Print just the responses, nothing else - for i, resp := range res.DNS.Answer { - temp := strings.Split(resp.String(), "\t") - s += temp[len(temp)-1] - - if opts.Identify { - s += " from server " + opts.Request.Server + " in " + res.RTT.String() - } - - // Don't print newline on last line - if i != len(res.DNS.Answer)-1 { - s += "\n" - } - } - } - - 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.ShowTTL && 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 +// Statistics is the little bit at the bottom :). +type Statistics struct { + RTT string `json:"queryTime,omitempty" xml:"queryTime,omitempty" yaml:"queryTime,omitempty"` + Server string `json:"server,omitempty" xml:"server,omitempty" yaml:"server,omitempty"` + When string `json:"when,omitempty" xml:"when,omitempty" yaml:"when,omitempty"` + MsgSize int `json:"msgSize,omitempty" xml:"msgSize,omitempty" yaml:"msgSize,omitempty"` } var errNoMessage = errors.New("no message") diff --git a/template.mk b/template.mk index fe820f5..0223929 100644 --- a/template.mk +++ b/template.mk @@ -1,7 +1,7 @@ # SPDX-License-Identifier: BSD-3-Clause # Template for the BSD/GNU makefiles -HASH ?= `git describe --always --dirty --broken | sed 's/\([^-]*-g\)/r\1/;s/-/./g' || echo "UNKNOWN"` +HASH ?= `git describe --always --dirty --broken | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g' || echo "UNKNOWN"` SOURCES ?= $(shell find . -name "*.go" -type f ! -name '*_test*') TEST_SOURCES ?= $(shell find . -name "*_test.go" -type f) @@ -90,4 +90,4 @@ clean: .PHONY: help help: @echo "Usage: " - @sed -n 's/^##//p' $(MAKEFILE_LIST) | column -t -s ':' | sed -e 's/^/ /' \ No newline at end of file + @sed -n 's/^##//p' $(MAKEFILE_LIST) | column -t -s ':' | sed -e 's/^/ /' diff --git a/util/options.go b/util/options.go index 91e5f14..c406801 100644 --- a/util/options.go +++ b/util/options.go @@ -12,67 +12,134 @@ import ( // Options is the grand structure for all query options. type Options struct { - Logger *logawl.Logger - TLSHost string + // The logger + Logger *logawl.Logger `json:"-"` + // Host to verify TLS cert with + TLSHost string `json:"tlsHost" example:""` + // EDNS Options EDNS - Request Request - Port int - Verbosity int - Display Displays - TC bool - ShowTTL bool - ShowClass bool - ShowQuery bool - AA bool - AD bool - CD bool - QR bool - RD bool - RA bool - IPv4 bool - Z bool - Reverse bool - HumanTTL bool - Truncate bool - Short bool - Identify bool - JSON bool - XML bool - YAML bool - QUIC bool - HTTPS bool - TLSNoVerify bool - TLS bool - DNSCrypt bool - TCP bool - IPv6 bool + // DNS request :) + Request Request + + // Verbosity levels, see [logawl.AllLevels] + Verbosity int `json:"-" example:"0"` + // Display options + Display Display + // Ignore Truncation + Truncate bool `json:"ignoreTruncate" example:"false"` + // Print only the answer + Short bool `json:"short" example:"false"` + // When Short is true, display where the query came from + Identify bool `json:"identify" example:"false"` + // Perform a reverse DNS query when true + Reverse bool `json:"reverse" example:"false"` + + HeaderFlags + + // Display resposne as JSON + JSON bool `json:"-" xml:"-" yaml:"-"` + // Display response as XML + XML bool `json:"-" xml:"-" yaml:"-"` + // Display response as YAML + YAML bool `json:"-" xml:"-" yaml:"-"` + + // Use TCP instead of UDP to make the query + TCP bool `json:"tcp" example:"false"` + // Use DNS-over-TLS to make the query + TLS bool `json:"dnsOverTLS" example:"false"` + // When using TLS, ignore certificates + TLSNoVerify bool `json:"tlsNoVerify" example:"false"` + // Use DNS-over-HTTPS to make the query + HTTPS bool `json:"dnsOverHTTPS" example:"false"` + // Use DNS-over-QUIC to make the query + //nolint:tagliatelle // QUIC is an acronym + QUIC bool `json:"dnsOverQUIC" example:"false"` + // Use DNSCrypt to make the query + DNSCrypt bool `json:"dnscrypt" example:"false"` + + // Force IPv4 only + IPv4 bool `json:"forceIPv4" example:"false"` + // Force IPv6 only + IPv6 bool `json:"forceIPv6" example:"false"` } -// Displays contains toggles for what to (and not to) display. -type Displays struct { - Comments bool - Question bool // QUESTION SECTION - Opt bool // OPT PSEUDOSECTION - Answer bool // ANSWER SECTION - Authority bool // AUTHORITY SECTION - Additional bool // ADDITIONAL SECTION - Statistics bool // Query time, message size, etc. - UcodeTranslate bool // Translate Punycode back to Unicode +// HeaderFlags are the flags that are in DNS headers. +type HeaderFlags struct { + // Authoritative Answer DNS query flag + AA bool `json:"authoritative" example:"false"` + // Authenticated Data DNS query flag + AD bool `json:"authenticatedData" example:"false"` + // Checking Disabled DNS query flag + CD bool `json:"checkingDisabled" example:"false"` + // QueRy DNS query flag + QR bool `json:"query" example:"false"` + // Recursion Desired DNS query flag + RD bool `json:"recursionDesired" example:"true"` + // Recursion Available DNS query flag + RA bool `json:"recursionAvailable" example:"false"` + // TrunCated DNS query flag + TC bool `json:"truncated" example:"false"` + // Zero DNS query flag + Z bool `json:"zero" example:"false"` +} + +// Display contains toggles for what to (and not to) display. +type Display struct { + /* Section displaying */ + + // Comments? + Comments bool `json:"comments" example:"true"` + // QUESTION SECTION + Question bool `json:"question" example:"true"` + // OPT PSEUDOSECTION + Opt bool `json:"opt" example:"true"` + // ANSWER SECTION + Answer bool `json:"answer" example:"true"` + // AUTHORITY SECTION + Authority bool `json:"authority" example:"true"` + // ADDITIONAL SECTION + Additional bool `json:"additional" example:"true"` + // Query time, message size, etc. + Statistics bool `json:"statistics" example:"true"` + // Display TTL in response + TTL bool `json:"ttl" example:"true"` + + /* Answer formatting */ + + // Display Class in response + ShowClass bool `json:"showClass" example:"true"` + // Display query before it is sent + ShowQuery bool `json:"showQuery" example:"false"` + // Display TTL as human-readable + HumanTTL bool `json:"humanTTL" example:"false"` + // Translate Punycode back to Unicode + UcodeTranslate bool `json:"unicode" example:"true"` } // EDNS contains toggles for various EDNS options. type EDNS struct { - Subnet dns.EDNS0_SUBNET - ZFlag uint16 - BufSize uint16 - EnableEDNS bool - Cookie bool - DNSSEC bool - Expire bool - KeepOpen bool - Nsid bool - Padding bool - Version uint8 + // Subnet to originate query from. + Subnet dns.EDNS0_SUBNET `json:"subnet"` + // Must Be Zero flag + ZFlag uint16 `json:"zflag" example:"0"` + // UDP buffer size + BufSize uint16 `json:"bufSize" example:"1232"` + // Enable/Disable EDNS entirely + EnableEDNS bool `json:"edns" example:"false"` + // Sending EDNS cookie + Cookie bool `json:"cookie" example:"true"` + // Enabling DNSSEC + DNSSEC bool `json:"dnssec" example:"false"` + // Sending EDNS Expire + Expire bool `json:"expire" example:"false"` + // Sending EDNS TCP keepopen + KeepOpen bool `json:"keepOpen" example:"false"` + // Sending EDNS NSID + Nsid bool `json:"nsid" example:"false"` + // Send EDNS Padding + Padding bool `json:"padding" example:"false"` + // Set EDNS version (default: 0) + Version uint8 `json:"version" example:"0"` } // ParseSubnet takes a subnet argument and makes it into one that the DNS library diff --git a/util/query.go b/util/query.go index 20851b6..8e1dd72 100644 --- a/util/query.go +++ b/util/query.go @@ -10,16 +10,26 @@ import ( // Response is 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 + // The full DNS response + DNS *dns.Msg `json:"response"` + // The time it took to make the DNS query + RTT time.Duration `json:"rtt" example:"2000000000"` } // Request is a structure for a DNS query. type Request struct { - Server string `json:"server"` - Name string `json:"name"` - Timeout time.Duration - Retries int - Type uint16 `json:"request"` - Class uint16 `json:"class"` + // Server to query + Server string `json:"server" example:"1.0.0.1"` + // Domain to query + Name string `json:"name" example:"example.com"` + // Duration to wait until marking request as failed + Timeout time.Duration `json:"timeout" example:"2000000000"` + // Port to make DNS request on + Port int `json:"port" example:"53"` + // Number of failures to make before giving up + Retries int `json:"retries" example:"2"` + // Request type, eg. A, AAAA, NAPTR + Type uint16 `json:"type" example:"1"` + // Request class, eg. IN + Class uint16 `json:"class" example:"1"` } From 55b965d08490eda2e2e9859a4372a4665c76ab57 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 15 Sep 2022 21:00:17 +0000 Subject: [PATCH 3/5] fix(deps): update golang.org/x/sys digest to 7b5979e --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 9b6a2f8..fd8f97d 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/miekg/dns v1.1.50 github.com/stefansundin/go-zflag v1.1.1 golang.org/x/net v0.0.0-20220909164309-bea034e7d591 - golang.org/x/sys v0.0.0-20220913175220-63ea55921009 + golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41 gopkg.in/yaml.v3 v3.0.1 gotest.tools/v3 v3.3.0 ) diff --git a/go.sum b/go.sum index 5defd9f..71264eb 100644 --- a/go.sum +++ b/go.sum @@ -117,6 +117,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220913175220-63ea55921009 h1:PuvuRMeLWqsf/ZdT1UUZz0syhioyv1mzuFZsXs4fvhw= golang.org/x/sys v0.0.0-20220913175220-63ea55921009/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41 h1:ohgcoMbSofXygzo6AD2I1kz3BFmW1QArPYTtwEM3UXc= +golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= From ddd0277de9ea9b497ffdaf0e031983c480adb92b Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 16 Sep 2022 23:25:56 +0000 Subject: [PATCH 4/5] [SKIP CI] ci: add caching (#99) Reviewed-on: https://git.froth.zone/sam/awl/pulls/99 Reviewed-by: grumbulon --- .drone.jsonnet | 142 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 97 insertions(+), 45 deletions(-) diff --git a/.drone.jsonnet b/.drone.jsonnet index 95a30fb..e692f51 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -1,92 +1,144 @@ // SPDX-License-Identifier: BSD-3-Clause local testing(version, arch) = { - kind: "pipeline", - type: "docker", - name: version + "-" + arch , + kind: 'pipeline', + type: 'docker', + name: version + '-' + arch, platform: { - arch: arch + arch: arch, }, steps: [ { - name: "lint", - image: "rancher/drone-golangci-lint:latest", + name: 'lint', + image: 'rancher/drone-golangci-lint:latest', }, { - name: "test", - image: "golang:" + version, + name: 'cache', + image: 'golang:' + version, commands: [ - "make test-ci" + 'go mod tidy' ], depends_on: [ - "lint", + 'lint', + ], + volumes: [ + { + name: 'cache', + path: '/go', + }, ], }, { - name: "fuzz", - image: "golang:" + version, + name: 'test', + image: 'golang:' + version, commands: [ - "make fuzz-ci", + 'make test-ci', ], depends_on: [ - "lint", + 'cache', + ], + volumes: [ + { + name: 'cache', + path: '/go', + }, + ], + }, + { + name: 'fuzz', + image: 'golang:' + version, + commands: [ + 'make fuzz-ci', + ], + depends_on: [ + 'cache', + ], + volumes: [ + { + name: 'cache', + path: '/go', + }, ], }, ], trigger: { event: { exclude: [ - "tag" + 'tag', ], - } + }, }, + volumes: [ + { + name: 'cache', + temp: {}, + }, + ], }; // "Inspired by" https://goreleaser.com/ci/drone/ local release() = { - kind: "pipeline", - type: "docker", - name: "release", + kind: 'pipeline', + type: 'docker', + name: 'release', trigger: { event: [ - "tag" + 'tag', ], }, steps: [ { - name: "fetch", - image: "alpine/git", - commands : [ - "git fetch --tags", - ] - }, - { - name: "test", - image: "golang", + name: 'fetch', + image: 'alpine/git', commands: [ - "make test-ci" - ] + 'git fetch --tags', + ], }, { - name: "release", - image: "goreleaser/goreleaser", + name: 'test', + image: 'golang', + commands: [ + 'make test-ci', + ], + volumes: [ + { + name: 'cache', + path: '/go', + }, + ], + }, + { + name: 'release', + image: 'goreleaser/goreleaser', environment: { - "GITEA_TOKEN": { - from_secret: "GITEA_TOKEN" - } + GITEA_TOKEN: { + from_secret: 'GITEA_TOKEN', + }, }, commands: [ - "goreleaser release" + 'goreleaser release', ], - } - ] + volumes: [ + { + name: 'cache', + path: '/go', + }, + ], + }, + ], + volumes: [ + { + name: 'cache', + temp: {}, + }, + ], }; [ - testing("1.19", "amd64"), - testing("1.19", "arm64"), - testing("1.18", "amd64"), - testing("1.18", "arm64"), + testing('1.19', 'amd64'), + testing('1.19', 'arm64'), + testing('1.18', 'amd64'), + testing('1.18', 'arm64'), - release() -] \ No newline at end of file + release(), +] From 8df03478910f09ede1abf36305cdd9147a23fd07 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 16 Sep 2022 23:26:10 +0000 Subject: [PATCH 5/5] fix: EDNS cookies work properly (#98) also slight refactors :) closes #96 Co-authored-by: Sam Therapy Reviewed-on: https://git.froth.zone/sam/awl/pulls/98 Reviewed-by: grumbulon --- .goreleaser.yaml | 13 +++++++++++++ GNUmakefile | 7 +++++++ Makefile | 6 ++++++ cli/cli.go | 4 +++- cli/dig.go | 2 ++ cli/dig_test.go | 1 + cli/misc.go | 12 +++++++++--- cli/misc_test.go | 6 ++++++ completions/zsh.zsh | 1 + doc/awl.1.scd | 3 +++ doc/wiki | 2 +- logawl/docs.go | 2 +- query/general.go | 20 +++++++++++++++++++- query/general_test.go | 2 +- query/query.go | 36 ++++++++++++++++++------------------ template.mk | 6 ------ util/options.go | 2 ++ 17 files changed, 93 insertions(+), 32 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index ff26ef9..da446bc 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -13,9 +13,22 @@ builds: - linux - windows - darwin + - freebsd goarch: - amd64 + - arm - arm64 + ignore: + # Windows on ARM, maybe someday + - goos: windows + goarch: arm64 + - goos: windows + goarch: arm + + - goos: darwin + goarch: arm + - goos: freebsd + goarch: arm universal_binaries: - replace: true diff --git a/GNUmakefile b/GNUmakefile index 5e7fab3..66aacd1 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -9,6 +9,13 @@ else EXE := $(PROG) endif +doc/$(PROG).1: doc/$(PROG).1.scd + $(SCDOC) <$< >$@ + +doc/wiki/$(PROG).1.md: doc/$(PROG).1 + pandoc --from man --to gfm -o $@ $< + + ## install: installs awl .PHONY: install ifeq ($(OS),Windows_NT) diff --git a/Makefile b/Makefile index d8d8c68..b5483dd 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,12 @@ include template.mk EXE := $(PROG) +doc/$(PROG).1: doc/$(PROG).1.scd + $(SCDOC) $@ + +doc/wiki/$(PROG).1.md: doc/$(PROG).1 + pandoc --from man --to gfm -o $@ doc/$(PROG).1 + ## install: installs awl .PHONY: install install: all diff --git a/cli/cli.go b/cli/cli.go index f002afe..e093ece 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -62,7 +62,8 @@ func ParseCLI(args []string, version string) (util.Options, error) { subnet = flag.String("subnet", "", "set EDNS client subnet") padding = flag.Bool("pad", false, "set EDNS padding") - truncate = flag.Bool("no-truncate", false, "ignore truncation if a UDP request truncates (default: retry with TCP)") + badCookie = flag.Bool("no-bad-cookie", false, "ignore BADCOOKIE EDNS responses (default: retry with correct cookie") + truncate = flag.Bool("no-truncate", false, "ignore truncation if a UDP request truncates (default: retry with TCP)") tcp = flag.Bool("tcp", false, "use TCP") dnscrypt = flag.Bool("dnscrypt", false, "use DNSCrypt") @@ -126,6 +127,7 @@ func ParseCLI(args []string, version string) (util.Options, error) { HTTPS: *https, QUIC: *quic, Truncate: *truncate, + BadCookie: *badCookie, Reverse: *reverse, JSON: *json, XML: *xml, diff --git a/cli/dig.go b/cli/dig.go index 00ee8a6..5ac8477 100644 --- a/cli/dig.go +++ b/cli/dig.go @@ -73,6 +73,8 @@ func ParseDig(arg string, opts *util.Options) error { opts.TCP = isNo case "ignore": opts.Truncate = isNo + case "badcookie": + opts.BadCookie = !isNo case "tls": opts.TLS = isNo case "dnscrypt": diff --git a/cli/dig_test.go b/cli/dig_test.go index 5293bb2..572872e 100644 --- a/cli/dig_test.go +++ b/cli/dig_test.go @@ -40,6 +40,7 @@ func FuzzDig(f *testing.F) { "tries=2", "tries=b", "tries", "tcp", "vc", "notcp", "novc", "ignore", "noignore", + "badcookie", "nobadcookie", "tls", "notls", "dnscrypt", "nodnscrypt", "https", "nohttps", diff --git a/cli/misc.go b/cli/misc.go index 85228fd..7a14854 100644 --- a/cli/misc.go +++ b/cli/misc.go @@ -29,7 +29,7 @@ func ParseMiscArgs(args []string, opts *util.Options) error { switch { case strings.HasPrefix(arg, "tls://"): opts.TLS = true - opts.Request.Server = strings.TrimPrefix(opts.Request.Server, "tls://") + opts.Request.Server = strings.TrimPrefix(arg, "tls://") opts.Logger.Info("DNS-over-TLS implicitly set") case strings.HasPrefix(arg, "https://"): opts.HTTPS = true @@ -37,12 +37,18 @@ func ParseMiscArgs(args []string, opts *util.Options) error { opts.Logger.Info("DNS-over-HTTPS implicitly set") case strings.HasPrefix(arg, "quic://"): opts.QUIC = true - opts.Request.Server = strings.TrimPrefix(opts.Request.Server, "quic://") + opts.Request.Server = strings.TrimPrefix(arg, "quic://") opts.Logger.Info("DNS-over-QUIC implicitly set.") case strings.HasPrefix(arg, "sdns://"): opts.DNSCrypt = true opts.Request.Server = arg opts.Logger.Info("DNSCrypt implicitly set") + case strings.HasPrefix(arg, "tcp://"): + opts.TCP = true + opts.Request.Server = strings.TrimPrefix(arg, "udp://") + opts.Logger.Info("TCP implicitly set") + case strings.HasPrefix(arg, "udp://"): + opts.Request.Server = strings.TrimPrefix(arg, "udp://") default: opts.Request.Server = arg } @@ -117,7 +123,7 @@ func ParseMiscArgs(args []string, opts *util.Options) error { if err != nil { // :^) - opts.Logger.Warn("Could not query system for server. Using localhost") + opts.Logger.Warn("Could not query system for server. Using localhost\n", "Error:", err) opts.Request.Server = "127.0.0.1" } else { // Make sure that if IPv4 or IPv6 is asked for it actually uses it diff --git a/cli/misc_test.go b/cli/misc_test.go index 286e0e3..c3f60c3 100644 --- a/cli/misc_test.go +++ b/cli/misc_test.go @@ -127,6 +127,8 @@ func TestFlagSetting(t *testing.T) { {[]string{"@tls://dns.google"}}, {[]string{"@https://dns.cloudflare.com/dns-query"}}, {[]string{"@quic://dns.adguard.com"}}, + {[]string{"@tcp://dns.froth.zone"}}, + {[]string{"@udp://dns.example.com"}}, } for i, test := range tests { @@ -147,6 +149,10 @@ func TestFlagSetting(t *testing.T) { assert.Assert(t, opts.HTTPS) case 3: assert.Assert(t, opts.QUIC) + case 4: + assert.Assert(t, opts.TCP) + case 5: + assert.Assert(t, true) } }) } diff --git a/completions/zsh.zsh b/completions/zsh.zsh index 8463dbf..64afd0c 100644 --- a/completions/zsh.zsh +++ b/completions/zsh.zsh @@ -13,6 +13,7 @@ local -a alts args '*+'{no,}'aaonly[set aa flag in the query]' '*+'{no,}'additional[print additional section of a reply]' '*+'{no,}'adflag[set the AD (authentic data) bit in the query]' + '*+'{no,}'badcookie[retry BADCOOKIE responses]' '*+'{no,}'cdflag[set the CD (checking disabled) bit in the query]' '*+'{no,}'cookie[add a COOKIE option to the request]' '*+edns=[specify EDNS version for query]:version (0-255)' diff --git a/doc/awl.1.scd b/doc/awl.1.scd index bc0110a..de88719 100644 --- a/doc/awl.1.scd +++ b/doc/awl.1.scd @@ -80,6 +80,9 @@ Anything in [brackets] is optional. *--no-truncate*, *+ignore* Ignore UDP truncation (by default, awl *retries with TCP*). +*--no-bad-cookie*, *+[no]badcookie* + \[Do not\] ignore BADCOOKIE responses + *--tcp*, *+tcp*, *+vc* Use TCP for the query (see RFC 7766). diff --git a/doc/wiki b/doc/wiki index 6f3070f..d41f619 160000 --- a/doc/wiki +++ b/doc/wiki @@ -1 +1 @@ -Subproject commit 6f3070f5933d0cc48bc8bb5290a1d0cc1825cd75 +Subproject commit d41f6197e638d7f0c0b203bd8b7f1fa0fc4b7f97 diff --git a/logawl/docs.go b/logawl/docs.go index fa0d5f9..d9940e3 100644 --- a/logawl/docs.go +++ b/logawl/docs.go @@ -14,7 +14,7 @@ because awl is a cli utility it writes directly to std err. // You can call specific logging levels from your new logger using // // logger.Debug("Message to log") -// logger.Fatal("Message to log") +// logger.Warning("Message to log") // logger.Info("Message to log") // logger.Error("Message to log") // diff --git a/query/general.go b/query/general.go index 8e0973b..6f4c6fa 100644 --- a/query/general.go +++ b/query/general.go @@ -52,13 +52,31 @@ func (r *StandardResolver) LookUp(msg *dns.Msg) (util.Response, error) { } } - r.opts.Logger.Debug("Using", dnsClient.Net, "for making the request") + r.opts.Logger.Info("Using", dnsClient.Net, "for making the request") resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, r.opts.Request.Server) if err != nil { return util.Response{}, fmt.Errorf("standard: DNS exchange: %w", err) } + switch dns.RcodeToString[resp.DNS.MsgHdr.Rcode] { + case "BADCOOKIE": + if !r.opts.BadCookie { + fmt.Printf(";; BADCOOKIE, retrying.\n\n") + + msg.Extra = resp.DNS.Extra + + resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, r.opts.Request.Server) + + if err != nil { + return util.Response{}, fmt.Errorf("badcookie: DNS exchange: %w", err) + } + } + + case "NOERR": + break + } + r.opts.Logger.Info("Request successful") if resp.DNS.MsgHdr.Truncated && !r.opts.Truncate { diff --git a/query/general_test.go b/query/general_test.go index fe06c12..396e87b 100644 --- a/query/general_test.go +++ b/query/general_test.go @@ -19,7 +19,7 @@ func TestResolve(t *testing.T) { Logger: util.InitLogger(0), Request: util.Request{ Server: "8.8.4.1", - Port: 53, + Port: 1, Type: dns.TypeA, Name: "example.com.", Timeout: time.Second / 2, diff --git a/query/query.go b/query/query.go index 6bccb34..1bca238 100644 --- a/query/query.go +++ b/query/query.go @@ -35,64 +35,64 @@ func CreateQuery(opts util.Options) (util.Response, error) { // EDNS time :) if opts.EDNS.EnableEDNS { - o := new(dns.OPT) - o.Hdr.Name = "." - o.Hdr.Rrtype = dns.TypeOPT + edns := new(dns.OPT) + edns.Hdr.Name = "." + edns.Hdr.Rrtype = dns.TypeOPT - o.SetVersion(opts.EDNS.Version) + edns.SetVersion(opts.EDNS.Version) if opts.EDNS.Cookie { - e := new(dns.EDNS0_COOKIE) - e.Code = dns.EDNS0COOKIE - e.Cookie = uniuri.NewLenChars(8, []byte("1234567890abcdef")) - o.Option = append(o.Option, e) + cookie := new(dns.EDNS0_COOKIE) + cookie.Code = dns.EDNS0COOKIE + cookie.Cookie = uniuri.NewLenChars(16, []byte("1234567890abcdef")) + edns.Option = append(edns.Option, cookie) - opts.Logger.Info("Setting EDNS cookie to", e.Cookie) + opts.Logger.Info("Setting EDNS cookie to", cookie.Cookie) } if opts.EDNS.Expire { - o.Option = append(o.Option, new(dns.EDNS0_EXPIRE)) + edns.Option = append(edns.Option, new(dns.EDNS0_EXPIRE)) opts.Logger.Info("Setting EDNS Expire option") } if opts.EDNS.KeepOpen { - o.Option = append(o.Option, new(dns.EDNS0_TCP_KEEPALIVE)) + edns.Option = append(edns.Option, new(dns.EDNS0_TCP_KEEPALIVE)) opts.Logger.Info("Setting EDNS TCP Keepalive option") } if opts.EDNS.Nsid { - o.Option = append(o.Option, new(dns.EDNS0_NSID)) + edns.Option = append(edns.Option, new(dns.EDNS0_NSID)) opts.Logger.Info("Setting EDNS NSID option") } if opts.EDNS.Padding { - o.Option = append(o.Option, new(dns.EDNS0_PADDING)) + edns.Option = append(edns.Option, new(dns.EDNS0_PADDING)) opts.Logger.Info("Setting EDNS padding") } - o.SetUDPSize(opts.EDNS.BufSize) + edns.SetUDPSize(opts.EDNS.BufSize) opts.Logger.Info("EDNS UDP buffer set to", opts.EDNS.BufSize) - o.SetZ(opts.EDNS.ZFlag) + edns.SetZ(opts.EDNS.ZFlag) opts.Logger.Info("EDNS Z flag set to", opts.EDNS.ZFlag) if opts.EDNS.DNSSEC { - o.SetDo() + edns.SetDo() opts.Logger.Info("EDNS DNSSEC OK set") } if opts.EDNS.Subnet.Address != nil { - o.Option = append(o.Option, &opts.EDNS.Subnet) + edns.Option = append(edns.Option, &opts.EDNS.Subnet) } - req.Extra = append(req.Extra, o) + req.Extra = append(req.Extra, edns) } else if opts.EDNS.DNSSEC { req.SetEdns0(1232, true) opts.Logger.Warn("DNSSEC implies EDNS, EDNS enabled") diff --git a/template.mk b/template.mk index 0223929..41e441e 100644 --- a/template.mk +++ b/template.mk @@ -28,12 +28,6 @@ all: $(PROG) doc/$(PROG).1 $(PROG): $(SOURCES) $(GO) build -o $(EXE) $(GOFLAGS) . -doc/$(PROG).1: doc/$(PROG).1.scd - $(SCDOC) <$< >$@ - -doc/wiki/$(PROG).1.md: doc/$(PROG).1 - pandoc --from man --to gfm -o $@ $< - ## update_doc: update documentation (requires pandoc) update_doc: doc/wiki/$(PROG).1.md diff --git a/util/options.go b/util/options.go index c406801..1abd2f4 100644 --- a/util/options.go +++ b/util/options.go @@ -27,6 +27,8 @@ type Options struct { Display Display // Ignore Truncation Truncate bool `json:"ignoreTruncate" example:"false"` + // Ignore BADCOOKIE + BadCookie bool `json:"ignoreBadCookie" example:"false"` // Print only the answer Short bool `json:"short" example:"false"` // When Short is true, display where the query came from