API work #94
18 changed files with 572 additions and 417 deletions
12
.editorconfig
Normal file
12
.editorconfig
Normal file
|
@ -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
|
39
cli/cli.go
39
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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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":
|
||||
|
|
17
go.mod
17
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
|
||||
|
|
28
go.sum
28
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=
|
||||
|
|
|
@ -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...))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.",
|
||||
},
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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.",
|
||||
|
|
203
query/query.go
203
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 "<nil> 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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
254
query/struct.go
254
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 "<nil> 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")
|
||||
|
|
174
util/options.go
174
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
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue