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/cli/cli.go b/cli/cli.go index 8a593cb..159b6d3 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -116,7 +116,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 +127,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 +157,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 +201,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 +220,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..59353c9 100644 --- a/cli/cli_test.go +++ b/cli/cli_test.go @@ -18,7 +18,7 @@ func TestEmpty(t *testing.T) { opts, err := cli.ParseCLI("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 @@ -31,7 +31,7 @@ func TestTLSPort(t *testing.T) { opts, err := cli.ParseCLI("TEST") assert.NilError(t, err) - assert.Equal(t, opts.Port, 853) + assert.Equal(t, opts.Request.Port, 853) os.Args = args } 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 9f7fcb1..1fa792b 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-20220825204002-c680a09ffe64 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.4.9 // 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 + github.com/onsi/ginkgo v1.16.4 // indirect + golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d // indirect + golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // 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/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..4f95d5f 100644 --- a/go.sum +++ b/go.sum @@ -15,9 +15,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5 h1:RAV05c0xOkJ3dZGS0JFybxFKZ2WMLabgx3uXnd7rpGs= github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= @@ -57,8 +56,8 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak= @@ -77,12 +76,10 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-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/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/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= +golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= 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 +91,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= @@ -118,13 +113,8 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= +golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/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= 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/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..c026343 100644 --- a/query/print.go +++ b/query/print.go @@ -55,8 +55,21 @@ func PrintSpecial(msg *dns.Msg, opts util.Options) (string, error) { func MakePrintable(msg *dns.Msg, opts util.Options) (*Message, error) { var err error + // 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], + }, } for _, question := range msg.Question { @@ -85,8 +98,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 +135,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 +152,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 +175,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)) diff --git a/query/print_test.go b/query/print_test.go index 8dce5d1..4eade9b 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.", diff --git a/query/query.go b/query/query.go index 9b1cda9..1b8dcaf 100644 --- a/query/query.go +++ b/query/query.go @@ -5,10 +5,13 @@ package query import ( "fmt" "strconv" + "strings" + "time" "git.froth.zone/sam/awl/util" "github.com/dchest/uniuri" "github.com/miekg/dns" + "golang.org/x/net/idna" ) const ( @@ -16,6 +19,202 @@ const ( udp = "udp" ) +// 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.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)" + } + + 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 +} + +// 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 +} + // CreateQuery creates a DNS query from the options given. // It sets query flags and EDNS flags from the respective options. func CreateQuery(opts util.Options) (util.Response, error) { @@ -102,7 +301,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 ( @@ -133,7 +332,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..7c76d34 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, 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..a4199e3 100644 --- a/query/struct.go +++ b/query/struct.go @@ -4,48 +4,61 @@ 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 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"` +} + +// Header is the header. +type Header struct { + Opcode string `json:"opcode," xml:"opcode," yaml:"opcode"` + Status string `json:"status," xml:"status," yaml:"status"` + ID uint16 `json:"id," xml:"id," yaml:"id"` + Response bool `json:"response," xml:"response," yaml:"response"` + Authoritative bool `json:"authoritative," xml:"authoritative," yaml:"authoritative"` + Truncated bool `json:"truncated," xml:"truncated," yaml:"truncated"` + RecursionDesired bool `json:"recursionDesired," xml:"recursionDesired," yaml:"recursionDesired"` + RecursionAvailable bool `json:"recursionAvailable," xml:"recursionAvailable," yaml:"recursionAvailable"` + Zero bool `json:"zero," xml:"zero," yaml:"zero"` + AuthenticatedData bool `json:"authenticatedData," xml:"authenticatedData," yaml:"authenticatedData"` + CheckingDisabled bool `json:"checkingDisabled," xml:"checkingDisabled," yaml:"checkingDisabled"` } // 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"` + Class string `json:"class,omitempty" xml:"class,omitempty" yaml:"class,omitempty"` + Type string `json:"type,omitempty" xml:"type,omitempty" yaml:"type,omitempty"` } // 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"` + TTL string `json:"ttl,omitempty" xml:"ttl,omitempty" yaml:"ttl,omitempty"` + Class string `json:"class,omitempty" xml:"class,omitempty" yaml:"class,omitempty"` + Type string `json:"type,omitempty" xml:"type,omitempty" yaml:"type,omitempty"` 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:""` + Value string `json:"value" xml:"value" yaml:"value"` } // Answer is for a DNS Response. @@ -54,199 +67,4 @@ 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 -} - var errNoMessage = errors.New("no message") diff --git a/util/options.go b/util/options.go index 91e5f14..139a558 100644 --- a/util/options.go +++ b/util/options.go @@ -12,67 +12,133 @@ import ( // Options is the grand structure for all query options. type Options struct { - Logger *logawl.Logger - TLSHost string + // The logger + Logger *logawl.Logger + // Host to verify TLS cert with + TLSHost string `json:"tlsHost"` + // 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:"-"` + // Display options + Display Display + // Ignore Truncation + Truncate bool `json:"ignoreTruncate"` + // Print only the answer + Short bool `json:"short"` + // When Short is true, display where the query came from + Identify bool `json:"identify"` + // Perform a reverse DNS query when true + Reverse bool `json:"reverse"` + + 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"` + // Use DNS-over-TLS to make the query + TLS bool `json:"dns_over_tls"` + // When using TLS, ignore certificates + TLSNoVerify bool `json:"tlsNoVerify"` + // Use DNS-over-HTTPS to make the query + HTTPS bool `json:"dns_over_https"` + // Use DNS-over-QUIC to make the query + QUIC bool `json:"dns_over_quic"` + // Use DNSCrypt to make the query + DNSCrypt bool `json:"dnscrpyt"` + + // Force IPv4 only + IPv4 bool `json:"force_IPv4"` + // Force IPv6 only + IPv6 bool `json:"force_IPv6"` } -// 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,"` + // Authenticated Data DNS query flag + AD bool `json:"authenticatedData,"` + // Checking Disabled DNS query flag + CD bool `json:"checkingDisabled,"` + // QueRy DNS query flag + QR bool `json:"query"` + // Recursion Desired DNS query flag + RD bool `json:"recursionDesired"` + // Recursion Available DNS query flag + RA bool `json:"recursionAvailable"` + // TrunCated DNS query flag + TC bool `json:"truncated"` + // Zero DNS query flag + Z bool `json:"zero"` +} + +// Display contains toggles for what to (and not to) display. +type Display struct { + /* Section displaying */ + + // Comments? + Comments bool `json:"comments"` + // QUESTION SECTION + Question bool `json:"question"` + // OPT PSEUDOSECTION + Opt bool `json:"opt"` + // ANSWER SECTION + Answer bool `json:"answer"` + // AUTHORITY SECTION + Authority bool `json:"authority"` + // ADDITIONAL SECTION + Additional bool `json:"additional"` + // Query time, message size, etc. + Statistics bool `json:"statistics"` + // Display TTL in response + TTL bool `json:"ttl"` + + /* Answer formatting */ + + // Display Class in response + ShowClass bool `json:"showClass"` + // Display query before it is sent + ShowQuery bool `json:"showQuery"` + // Display TTL as human-readable + HumanTTL bool `json:"HumanTTL"` + // Translate Punycode back to Unicode + UcodeTranslate bool `json:"unicode"` } // 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"` + // UDP buffer size + BufSize uint16 `json:"bufSize"` + // Enable/Disable EDNS entirely + EnableEDNS bool `json:"edns"` + // Sending EDNS cookie + Cookie bool `json:"cookie"` + // Enabling DNSSEC + DNSSEC bool `json:"dnssec"` + // Sending EDNS Expire + Expire bool `json:"expire"` + // Sending EDNS TCP keepopen + KeepOpen bool `json:"keepOpen"` + // Sending EDNS NSID + Nsid bool `json:"nsid"` + // Send EDNS Padding + Padding bool `json:"padding"` + // Set EDNS version (default: 0) + Version uint8 `json:"version"` } // 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..79f2dbf 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"` } // 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, eg. 8.8.8.8 + Server string `json:"server"` + // Domain to query, eg. example.com + Name string `json:"name"` + // Duration to wait until marking request as failed + Timeout time.Duration `json:"timeout"` + // Port to make DNS request on + Port int `json:"port"` + // Number of failures to make before giving up + Retries int `json:"retries"` + // Request type, eg. A, AAAA, NAPTR + Type uint16 `json:"type"` + // Request class, eg. IN + Class uint16 `json:"class"` }