diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..10c02d1 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,8 @@ +kind: pipeline +type: docker +name: default +steps: + - name: test + image: golang + commands: + - go test ./... \ No newline at end of file diff --git a/README.md b/README.md index cd9eebe..7e37b9c 100644 --- a/README.md +++ b/README.md @@ -30,5 +30,6 @@ lifting. - Make the CLI less abysmal (migrate to [cobra](https://github.com/spf13/cobra)? or just use stdlib's flags) - Optimize everything -- Make the code less spaghetti -- Documentation, documentation, documentation \ No newline at end of file +- Make the code less spaghetti (partially completed) +- Feature parity with drill + - Making a drop-in replacement for drill? \ No newline at end of file diff --git a/cli.go b/cli.go index 67b1733..76945a4 100644 --- a/cli.go +++ b/cli.go @@ -1,185 +1,194 @@ -package main - -import ( - "fmt" - "runtime" - "strings" - - "git.froth.zone/sam/awl/util" - "github.com/miekg/dns" - "github.com/urfave/cli/v2" - "golang.org/x/net/idna" -) - -// Do all the magic CLI crap -func prepareCLI() *cli.App { - // Custom version string - cli.VersionPrinter = func(c *cli.Context) { - fmt.Printf("%s version %s, built with %s\n", c.App.Name, c.App.Version, runtime.Version()) - } - - cli.VersionFlag = &cli.BoolFlag{ - Name: "v", - Usage: "show version and exit", - } - - cli.HelpFlag = &cli.BoolFlag{ - Name: "h", - Usage: "show this help and exit", - } - - // Hack to get rid of the annoying default on the CLI - oldFlagStringer := cli.FlagStringer - cli.FlagStringer = func(f cli.Flag) string { - return strings.TrimSuffix(oldFlagStringer(f), " (default: false)") - } - - cli.AppHelpTemplate = `{{.Name}} - {{.Usage}} - - Usage: {{.HelpName}} name [@server] [type] - can be a name or an IP address - defaults to A - - arguments can be in any order - {{if .VisibleFlags}} - Options: - {{range .VisibleFlags}}{{.}} - {{end}}{{end}}` - app := &cli.App{ - Name: "awl", - Usage: "drill, writ small", - Version: "v0.2.0", - Flags: []cli.Flag{ - &cli.IntFlag{ - Name: "port", - Aliases: []string{"p"}, - Usage: "`port` to make DNS query", - DefaultText: "53 over plain TCP/UDP, 853 over TLS or QUIC, and 443 over HTTPS", - }, - &cli.BoolFlag{ - Name: "4", - Usage: "force IPv4", - }, - &cli.BoolFlag{ - Name: "6", - Usage: "force IPv6", - }, - &cli.BoolFlag{ - Name: "dnssec", - Aliases: []string{"D"}, - Usage: "enable DNSSEC", - }, - &cli.BoolFlag{ - Name: "json", - Aliases: []string{"j"}, - Usage: "return the result(s) as JSON", - }, - &cli.BoolFlag{ - Name: "short", - Aliases: []string{"s"}, - Usage: "print just the results, equivalent to dig +short", - }, - &cli.BoolFlag{ - Name: "tcp", - Aliases: []string{"t"}, - Usage: "use TCP (default: use UDP)", - }, - &cli.BoolFlag{ - Name: "tls", - Aliases: []string{"T"}, - Usage: "use DNS-over-TLS", - }, - &cli.BoolFlag{ - Name: "https", - Aliases: []string{"H"}, - Usage: "use DNS-over-HTTPS", - }, - &cli.BoolFlag{ - Name: "quic", - Aliases: []string{"Q"}, - Usage: "use DNS-over-QUIC", - }, - &cli.BoolFlag{ - Name: "no-truncate", - Usage: "ignore truncation if a UDP request truncates (default: retry with TCP)", - }, - &cli.BoolFlag{ - Name: "z", - Usage: "set Z (zero) flag (default: not set) [THIS DOESN'T DO ANYTHING]", - }, - &cli.BoolFlag{ - Name: "cd", - Usage: "set CD (CheckingDisabled) flag (default: not set)", - }, - &cli.BoolFlag{ - Name: "no-rd", - Usage: "UNset RD (RecursionDesired) flag (default: set)", - }, - &cli.BoolFlag{ - Name: "no-ra", - Usage: "UNset RA (RecursionAvailable) flag (default: set)", - }, - &cli.BoolFlag{ - Name: "reverse", - Aliases: []string{"x"}, - Usage: "do a reverse lookup", - }, - }, - Action: doQuery, - } - return app -} - -// Parse the wildcard arguments, drill style -func parseArgs(args []string) (util.Answers, error) { - var ( - resp util.Response - ) - for _, arg := range args { - // If it starts with @, it's a DNS server - if strings.HasPrefix(arg, "@") { - resp.Answers.Server = strings.Split(arg, "@")[1] - continue - } - // If there's a dot, it's a name - if strings.Contains(arg, ".") { - resp.Answers.Name, _ = idna.ToUnicode(arg) - continue - } - // If it's a request, it's a request (duh) - if r, ok := dns.StringToType[strings.ToUpper(arg)]; ok { - resp.Answers.Request = r - continue - } - - //else, assume it's a name - var err error - resp.Answers.Name, err = idna.ToUnicode(arg) - if err != nil { - return util.Answers{}, err - } - } - - // If nothing was set, set a default - if resp.Answers.Name == "" { - resp.Answers.Name = "." - if resp.Answers.Request == 0 { - resp.Answers.Request = dns.StringToType["NS"] - } - } else { - if resp.Answers.Request == 0 { - resp.Answers.Request = dns.StringToType["A"] - } - } - if resp.Answers.Server == "" { - resolv, err := dns.ClientConfigFromFile("/etc/resolv.conf") - if err != nil { // Query Google by default, needed for Windows since the DNS library doesn't support Windows - // TODO: Actually find where windows stuffs its dns resolvers - resp.Answers.Server = "8.8.4.4" - } else { - resp.Answers.Server = resolv.Servers[0] - } - } - - return util.Answers{Server: resp.Answers.Server, Request: resp.Answers.Request, Name: resp.Answers.Name}, nil -} +package main + +import ( + "fmt" + "math/rand" + "runtime" + "strings" + + "git.froth.zone/sam/awl/util" + "github.com/miekg/dns" + "github.com/urfave/cli/v2" + "golang.org/x/net/idna" +) + +// Do all the magic CLI crap +func prepareCLI() *cli.App { + // Custom version string + cli.VersionPrinter = func(c *cli.Context) { + fmt.Printf("%s version %s, built with %s\n", c.App.Name, c.App.Version, runtime.Version()) + } + + cli.VersionFlag = &cli.BoolFlag{ + Name: "v", + Usage: "show version and exit", + } + + cli.HelpFlag = &cli.BoolFlag{ + Name: "h", + Usage: "show this help and exit", + } + + // Hack to get rid of the annoying default on the CLI + oldFlagStringer := cli.FlagStringer + cli.FlagStringer = func(f cli.Flag) string { + return strings.TrimSuffix(oldFlagStringer(f), " (default: false)") + } + + cli.AppHelpTemplate = `{{.Name}} - {{.Usage}} + + Usage: {{.HelpName}} name [@server] [record] + can be a name or an IP address + defaults to A + + arguments can be in any order + {{if .VisibleFlags}} + Options: + {{range .VisibleFlags}}{{.}} + {{end}}{{end}}` + app := &cli.App{ + Name: "awl", + Usage: "drill, writ small", + Version: "v0.2.1", + Flags: []cli.Flag{ + &cli.IntFlag{ + Name: "port", + Aliases: []string{"p"}, + Usage: "`` to make DNS query", + DefaultText: "53 over plain TCP/UDP, 853 over TLS or QUIC", + }, + &cli.BoolFlag{ + Name: "4", + Usage: "force IPv4", + }, + &cli.BoolFlag{ + Name: "6", + Usage: "force IPv6", + }, + &cli.BoolFlag{ + Name: "dnssec", + Aliases: []string{"D"}, + Usage: "enable DNSSEC", + }, + &cli.BoolFlag{ + Name: "json", + Aliases: []string{"j"}, + Usage: "return the result(s) as JSON", + }, + &cli.BoolFlag{ + Name: "short", + Aliases: []string{"s"}, + Usage: "print just the results, equivalent to dig +short", + }, + &cli.BoolFlag{ + Name: "tcp", + Aliases: []string{"t"}, + Usage: "use TCP (default: use UDP)", + }, + &cli.BoolFlag{ + Name: "tls", + Aliases: []string{"T"}, + Usage: "use DNS-over-TLS", + }, + &cli.BoolFlag{ + Name: "https", + Aliases: []string{"H"}, + Usage: "use DNS-over-HTTPS", + }, + &cli.BoolFlag{ + Name: "quic", + Aliases: []string{"Q"}, + Usage: "use DNS-over-QUIC", + }, + &cli.BoolFlag{ + Name: "no-truncate", + Usage: "ignore truncation if a UDP request truncates (default: retry with TCP)", + }, + &cli.BoolFlag{ + Name: "aa", + Usage: "set AA (Authoratative Answer) flag (default: not set)", + }, + &cli.BoolFlag{ + Name: "tc", + Usage: "set tc (TrunCated) flag (default: not set)", + }, + &cli.BoolFlag{ + Name: "z", + Usage: "set Z (Zero) flag (default: not set)", + }, + &cli.BoolFlag{ + Name: "cd", + Usage: "set CD (Checking Disabled) flag (default: not set)", + }, + &cli.BoolFlag{ + Name: "no-rd", + Usage: "UNset RD (Recursion Desired) flag (default: set)", + }, + &cli.BoolFlag{ + Name: "no-ra", + Usage: "UNset RA (Recursion Available) flag (default: set)", + }, + &cli.BoolFlag{ + Name: "reverse", + Aliases: []string{"x"}, + Usage: "do a reverse lookup", + }, + }, + Action: doQuery, + } + return app +} + +// Parse the wildcard arguments, drill style +func parseArgs(args []string) (util.Answers, error) { + var ( + resp util.Response + err error + ) + for _, arg := range args { + r, ok := dns.StringToType[strings.ToUpper(arg)] + switch { + // If it starts with @, it's a DNS server + case strings.HasPrefix(arg, "@"): + resp.Answers.Server = strings.Split(arg, "@")[1] + case strings.Contains(arg, "."): + resp.Answers.Name, err = idna.ToUnicode(arg) + if err != nil { + return util.Answers{}, err + } + case ok: + // If it's a DNS request, it's a DNS request (obviously) + resp.Answers.Request = r + default: + //else, assume it's a name + resp.Answers.Name, err = idna.ToUnicode(arg) + if err != nil { + return util.Answers{}, err + } + + } + } + + // If nothing was set, set a default + if resp.Answers.Name == "" { + resp.Answers.Name = "." + if resp.Answers.Request == 0 { + resp.Answers.Request = dns.StringToType["NS"] + } + } else { + if resp.Answers.Request == 0 { + resp.Answers.Request = dns.StringToType["A"] + } + } + if resp.Answers.Server == "" { + resolv, err := dns.ClientConfigFromFile("/etc/resolv.conf") + if err != nil { // Query Google by default, needed for Windows since the DNS library doesn't support Windows + // TODO: Actually find where windows stuffs its dns resolvers + resp.Answers.Server = "8.8.4.4" + } else { + resp.Answers.Server = resolv.Servers[rand.Intn(len(resolv.Servers)-1)] + } + } + + return util.Answers{Server: resp.Answers.Server, Request: resp.Answers.Request, Name: resp.Answers.Name}, nil +} diff --git a/cli_test.go b/cli_test.go new file mode 100644 index 0000000..cf826bb --- /dev/null +++ b/cli_test.go @@ -0,0 +1,41 @@ +package main + +import ( + "testing" + + "git.froth.zone/sam/awl/util" + "github.com/miekg/dns" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestApp(t *testing.T) { + app := prepareCLI() + // What more can even be done lmao + require.NotNil(t, app) +} + +func TestArgParse(t *testing.T) { + tests := []struct { + in []string + want util.Answers + }{ + { + []string{"@::1", "localhost", "AAAA"}, + util.Answers{Server: "::1", Request: dns.TypeAAAA, Name: "localhost"}, + }, + { + []string{"@1.0.0.1", "google.com"}, + util.Answers{Server: "1.0.0.1", Request: dns.TypeA, Name: "google.com"}, + }, + { + []string{"@8.8.4.4"}, + util.Answers{Server: "8.8.4.4", Request: dns.TypeNS, Name: "."}, + }, + } + for _, test := range tests { + act, err := parseArgs(test.in) + assert.Nil(t, err) + assert.Equal(t, test.want, act) + } +} diff --git a/go.mod b/go.mod index c64a234..fa8b0b6 100644 --- a/go.mod +++ b/go.mod @@ -16,22 +16,21 @@ require ( ) require ( - github.com/c-robinson/iplib v1.0.3 github.com/cheekybits/genny v1.0.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/fsnotify/fsnotify v1.4.9 // indirect + github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect github.com/marten-seemann/qtls-go1-17 v0.1.2 // indirect github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect github.com/nxadm/tail v1.4.8 // indirect - github.com/onsi/ginkgo v1.16.4 // indirect + github.com/onsi/ginkgo v1.16.5 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/stretchr/testify v1.7.4 github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect + golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c // indirect + golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.11 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect diff --git a/go.sum b/go.sum index 3f5ecc8..0541b51 100644 --- a/go.sum +++ b/go.sum @@ -8,13 +8,10 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1 dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/c-robinson/iplib v1.0.3 h1:NG0UF0GoEsrC1/vyfX1Lx2Ss7CySWl3KqqXh3q4DdPU= -github.com/c-robinson/iplib v1.0.3/go.mod h1:i3LuuFL1hRT5gFpBRnEydzw8R6yhGkF4szNDIbF8pgo= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -28,8 +25,9 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= 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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= @@ -74,9 +72,11 @@ github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lucas-clemente/quic-go v0.27.2 h1:zsMwwniyybb8B/UDNXRSYee7WpQJVOcjQEGgpw2ikXs= github.com/lucas-clemente/quic-go v0.27.2/go.mod h1:vXgO/11FBSKM+js1NxoaQ/bPtVFYfB7uxhfHXyMhl1A= @@ -104,8 +104,9 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= -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/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= 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= @@ -118,7 +119,6 @@ github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -150,7 +150,6 @@ github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM= @@ -164,7 +163,6 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRT github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= @@ -174,8 +172,8 @@ golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -198,13 +196,9 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220621193019-9d032be2e588 h1:9ubFuySsnAJYGyJrZ3koiEv8FyqofCBdz3G9Mbf2YFc= -golang.org/x/net v0.0.0-20220621193019-9d032be2e588/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220622184535-263ec571b305 h1:dAgbJ2SP4jD6XYfMNLVj0BF21jo2PjChrtGaAvF5M3I= golang.org/x/net v0.0.0-20220622184535-263ec571b305/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -240,12 +234,10 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/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-20220622161953-175b2fd9d664 h1:wEZYwx+kK+KlZ0hpvP2Ls1Xr4+RWnlzGFwPP0aiDjIU= +golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -297,6 +289,7 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= diff --git a/query.go b/query.go index 61d75ce..57753b5 100644 --- a/query.go +++ b/query.go @@ -1,172 +1,178 @@ -package main - -import ( - "encoding/json" - "fmt" - "net" - "strconv" - "strings" - "time" - - "git.froth.zone/sam/awl/query" - "git.froth.zone/sam/awl/util" - "github.com/miekg/dns" - "github.com/urfave/cli/v2" -) - -func doQuery(c *cli.Context) error { - var err error - var resp util.Response - var isHTTPS bool - resp.Answers, err = parseArgs(c.Args().Slice()) - if err != nil { - return nil - } - port := c.Int("port") - - // If port is not set, set it - if port == 0 { - if c.Bool("tls") || c.Bool("quic") { - port = 853 - } else { - port = 53 - } - } - - if c.Bool("https") || strings.HasPrefix(resp.Answers.Server, "https://") { - // add https:// if it doesn't already exist - if !strings.HasPrefix(resp.Answers.Server, "https://") { - resp.Answers.Server = "https://" + resp.Answers.Server - } - isHTTPS = true - } else { - resp.Answers.Server = net.JoinHostPort(resp.Answers.Server, strconv.Itoa(port)) - } - - // Process the IP/Phone number so a PTR/NAPTR can be done - if c.Bool("reverse") { - if dns.TypeToString[resp.Answers.Request] == "A" { - resp.Answers.Request = dns.StringToType["PTR"] - } - resp.Answers.Name, err = util.ReverseDNS(resp.Answers.Name, dns.TypeToString[resp.Answers.Request]) - if err != nil { - return err - } - } - - // if the domain is not canonical, make it canonical - if !strings.HasSuffix(resp.Answers.Name, ".") { - resp.Answers.Name = fmt.Sprintf("%s.", resp.Answers.Name) - } - - msg := new(dns.Msg) - - msg.SetQuestion(resp.Answers.Name, resp.Answers.Request) - - // Set the zero flag if requested (does nothing) - if c.Bool("z") { - msg.Zero = true - } - // Disable DNSSEC validation if enabled - if c.Bool("cd") { - msg.CheckingDisabled = true - } - - if c.Bool("no-rd") { - msg.RecursionDesired = false - } - - if c.Bool("no-ra") { - msg.RecursionAvailable = false - } - - // Set DNSSEC if requested - if c.Bool("dnssec") { - msg.SetEdns0(1232, true) - } - - var in *dns.Msg - - // Make the DNS request - if isHTTPS { - in, resp.Answers.RTT, err = query.ResolveHTTPS(msg, resp.Answers.Server) - } else if c.Bool("quic") { - in, resp.Answers.RTT, err = query.ResolveQUIC(msg, resp.Answers.Server) - } else { - - d := new(dns.Client) - - // Set TCP/UDP, depending on flags - if c.Bool("tcp") { - d.Net = "tcp" - if c.Bool("4") { - d.Net = "tcp4" - } - if c.Bool("6") { - d.Net = "tcp6" - } - } else { - d.Net = "udp" - if c.Bool("4") { - d.Net = "udp4" - } - if c.Bool("6") { - d.Net = "udp6" - } - } - - // This is apparently all it takes to enable DoT - // TODO: Is it really? - if c.Bool("tls") { - d.Net = "tcp-tls" - } - - in, resp.Answers.RTT, err = d.Exchange(msg, resp.Answers.Server) - if err != nil { - return err - } - // If UDP truncates, use TCP instead - if !c.Bool("no-truncate") { - if in.MsgHdr.Truncated { - fmt.Printf(";; Truncated, retrying with TCP\n\n") - d.Net = "tcp" - if c.Bool("4") { - d.Net = "tcp4" - } - if c.Bool("6") { - d.Net = "tcp6" - } - in, resp.Answers.RTT, err = d.Exchange(msg, resp.Answers.Server) - } - } - } - - if err != nil { - return err - } - - if c.Bool("json") { - json, err := json.MarshalIndent(in, "", " ") - if err != nil { - return err - } - fmt.Println(string(json)) - } else { - if !c.Bool("short") { - // Print everything - fmt.Println(in) - fmt.Println(";; Query time:", resp.Answers.RTT) - fmt.Println(";; SERVER:", resp.Answers.Server) - fmt.Println(";; WHEN:", time.Now().Format(time.RFC1123)) - fmt.Println(";; MSG SIZE rcvd:", in.Len()) - } else { - // Print just the responses, nothing else - for _, res := range in.Answer { - temp := strings.Split(res.String(), "\t") - fmt.Println(temp[len(temp)-1]) - } - } - } - - return nil -} +package main + +import ( + "encoding/json" + "fmt" + "net" + "strconv" + "strings" + "time" + + "git.froth.zone/sam/awl/query" + "git.froth.zone/sam/awl/util" + "github.com/miekg/dns" + "github.com/urfave/cli/v2" +) + +func doQuery(c *cli.Context) error { + var ( + err error + resp util.Response + isHTTPS bool + ) + + resp.Answers, err = parseArgs(c.Args().Slice()) + if err != nil { + return err + } + + port := c.Int("port") + + // If port is not set, set it + if port == 0 { + if c.Bool("tls") || c.Bool("quic") { + port = 853 + } else { + port = 53 + } + } + + if c.Bool("https") || strings.HasPrefix(resp.Answers.Server, "https://") { + // add https:// if it doesn't already exist + if !strings.HasPrefix(resp.Answers.Server, "https://") { + resp.Answers.Server = "https://" + resp.Answers.Server + } + isHTTPS = true + } else { + resp.Answers.Server = net.JoinHostPort(resp.Answers.Server, strconv.Itoa(port)) + } + + // Process the IP/Phone number so a PTR/NAPTR can be done + if c.Bool("reverse") { + if dns.TypeToString[resp.Answers.Request] == "A" { + resp.Answers.Request = dns.StringToType["PTR"] + } + resp.Answers.Name, err = util.ReverseDNS(resp.Answers.Name, dns.TypeToString[resp.Answers.Request]) + if err != nil { + return err + } + } + + // if the domain is not canonical, make it canonical + if !strings.HasSuffix(resp.Answers.Name, ".") { + resp.Answers.Name = fmt.Sprintf("%s.", resp.Answers.Name) + } + + msg := new(dns.Msg) + + msg.SetQuestion(resp.Answers.Name, resp.Answers.Request) + + // TODO: maybe not make this a gross chunk of if statements? who knows + + // Make this authoritative (does this do anything?) + if c.Bool("aa") { + msg.Authoritative = true + } + // Set truncated flag (why) + if c.Bool("tc") { + msg.Truncated = true + } + // Set the zero flag if requested (does nothing) + if c.Bool("z") { + msg.Zero = true + } + // Disable DNSSEC validation + if c.Bool("cd") { + msg.CheckingDisabled = true + } + // Disable wanting recursion + if c.Bool("no-rd") { + msg.RecursionDesired = false + } + // Disable recursion being available (I don't think this does anything) + if c.Bool("no-ra") { + msg.RecursionAvailable = false + } + // Set DNSSEC if requested + if c.Bool("dnssec") { + msg.SetEdns0(1232, true) + } + + var in *dns.Msg + + // Make the DNS request + if isHTTPS { + in, resp.Answers.RTT, err = query.ResolveHTTPS(msg, resp.Answers.Server) + } else if c.Bool("quic") { + in, resp.Answers.RTT, err = query.ResolveQUIC(msg, resp.Answers.Server) + } else { + + d := new(dns.Client) + + // Set TCP/UDP, depending on flags + if c.Bool("tcp") || c.Bool("tls") { + d.Net = "tcp" + } else { + d.Net = "udp" + } + + // Set IPv4 or IPv6, depending on flags + switch { + case c.Bool("4"): + d.Net += "4" + case c.Bool("6"): + d.Net += "6" + } + + // Add TLS, if requested + if c.Bool("tls") { + d.Net += "-tls" + } + + in, resp.Answers.RTT, err = d.Exchange(msg, resp.Answers.Server) + if err != nil { + return err + } + // If UDP truncates, use TCP instead (unless truncation is to be ignored) + if in.MsgHdr.Truncated && !c.Bool("no-truncate") { + fmt.Printf(";; Truncated, retrying with TCP\n\n") + d.Net = "tcp" + switch { + case c.Bool("4"): + d.Net += "4" + case c.Bool("6"): + d.Net += "6" + } + in, resp.Answers.RTT, err = d.Exchange(msg, resp.Answers.Server) + } + } + + if err != nil { + return err + } + + if c.Bool("json") { + json, err := json.MarshalIndent(in, "", " ") + if err != nil { + return err + } + fmt.Println(string(json)) + } else { + if !c.Bool("short") { + // Print everything + fmt.Println(in) + fmt.Println(";; Query time:", resp.Answers.RTT) + fmt.Println(";; SERVER:", resp.Answers.Server) + fmt.Println(";; WHEN:", time.Now().Format(time.RFC1123Z)) + fmt.Println(";; MSG SIZE rcvd:", in.Len()) + } else { + // Print just the responses, nothing else + for _, res := range in.Answer { + temp := strings.Split(res.String(), "\t") + fmt.Println(temp[len(temp)-1]) + } + } + } + + return nil +} diff --git a/query/HTTPS.go b/query/HTTPS.go index 6e5c685..8389060 100644 --- a/query/HTTPS.go +++ b/query/HTTPS.go @@ -1,54 +1,54 @@ -package query - -import ( - "bytes" - "fmt" - "io" - "net/http" - "time" - - "github.com/miekg/dns" -) - -// Resolve a DNS-over-HTTPS query -// -// Currently only supports POST requests -func ResolveHTTPS(msg *dns.Msg, server string) (*dns.Msg, time.Duration, error) { - httpR := &http.Client{} - buf, err := msg.Pack() - if err != nil { - return nil, 0, err - } - // query := server + "?dns=" + base64.RawURLEncoding.EncodeToString(buf) - req, err := http.NewRequest("POST", server, bytes.NewBuffer(buf)) - if err != nil { - return nil, 0, fmt.Errorf("DoH: %s", err.Error()) - } - req.Header.Set("Content-Type", "application/dns-message") - req.Header.Set("Accept", "application/dns-message") - - now := time.Now() - res, err := httpR.Do(req) - rtt := time.Since(now) - - if err != nil { - return nil, 0, fmt.Errorf("DoH HTTP request error: %s", err.Error()) - } - defer res.Body.Close() - - if res.StatusCode != http.StatusOK { - return nil, 0, fmt.Errorf("DoH server responded with HTTP %d", res.StatusCode) - } - - fullRes, err := io.ReadAll(res.Body) - if err != nil { - return nil, 0, fmt.Errorf("DoH body read error: %s", err.Error()) - } - response := dns.Msg{} - err = response.Unpack(fullRes) - if err != nil { - return nil, 0, fmt.Errorf("DoH dns message unpack error: %s", err.Error()) - } - - return &response, rtt, nil -} +package query + +import ( + "bytes" + "fmt" + "io" + "net/http" + "time" + + "github.com/miekg/dns" +) + +// Resolve a DNS-over-HTTPS query +// +// Currently only supports POST requests +func ResolveHTTPS(msg *dns.Msg, server string) (*dns.Msg, time.Duration, error) { + httpR := &http.Client{} + buf, err := msg.Pack() + if err != nil { + return nil, 0, err + } + // query := server + "?dns=" + base64.RawURLEncoding.EncodeToString(buf) + req, err := http.NewRequest("POST", server, bytes.NewBuffer(buf)) + if err != nil { + return nil, 0, fmt.Errorf("DoH: %s", err.Error()) + } + req.Header.Set("Content-Type", "application/dns-message") + req.Header.Set("Accept", "application/dns-message") + + now := time.Now() + res, err := httpR.Do(req) + rtt := time.Since(now) + + if err != nil { + return nil, 0, fmt.Errorf("DoH HTTP request error: %s", err.Error()) + } + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return nil, 0, fmt.Errorf("DoH server responded with HTTP %d", res.StatusCode) + } + + fullRes, err := io.ReadAll(res.Body) + if err != nil { + return nil, 0, fmt.Errorf("DoH body read error: %s", err.Error()) + } + response := dns.Msg{} + err = response.Unpack(fullRes) + if err != nil { + return nil, 0, fmt.Errorf("DoH dns message unpack error: %s", err.Error()) + } + + return &response, rtt, nil +} diff --git a/query/QUIC.go b/query/QUIC.go index a45004d..fda5465 100644 --- a/query/QUIC.go +++ b/query/QUIC.go @@ -1,58 +1,61 @@ -package query - -import ( - "crypto/tls" - "io" - "time" - - "github.com/lucas-clemente/quic-go" - "github.com/miekg/dns" -) - -// Resolve DNS over QUIC, the hip new standard (for privacy I think, IDK) -func ResolveQUIC(msg *dns.Msg, server string) (*dns.Msg, time.Duration, error) { - tls := &tls.Config{ - NextProtos: []string{"doq"}, - } - connection, err := quic.DialAddr(server, tls, nil) - if err != nil { - return nil, 0, err - } - - // Close with error: no error - defer connection.CloseWithError(0, "") - - // Compress request to over-the-wire - buf, err := msg.Pack() - if err != nil { - return nil, 0, err - } - t := time.Now() - stream, err := connection.OpenStream() - if err != nil { - return nil, 0, err - } - _, err = stream.Write(buf) - if err != nil { - return nil, 0, err - } - - fullRes, err := io.ReadAll(stream) - if err != nil { - return nil, 0, err - } - rtt := time.Since(t) - - err = stream.Close() - if err != nil { - return nil, 0, err - } - - response := dns.Msg{} - err = response.Unpack(fullRes) - if err != nil { - return nil, 0, err - } - - return &response, rtt, nil -} +package query + +import ( + "crypto/tls" + "io" + "time" + + "github.com/lucas-clemente/quic-go" + "github.com/miekg/dns" +) + +// Resolve DNS over QUIC, the hip new standard (for privacy I think, IDK) +func ResolveQUIC(msg *dns.Msg, server string) (*dns.Msg, time.Duration, error) { + tls := &tls.Config{ + NextProtos: []string{"doq"}, + } + connection, err := quic.DialAddr(server, tls, nil) + if err != nil { + return nil, 0, err + } + + // Compress request to over-the-wire + buf, err := msg.Pack() + if err != nil { + return nil, 0, err + } + t := time.Now() + stream, err := connection.OpenStream() + if err != nil { + return nil, 0, err + } + _, err = stream.Write(buf) + if err != nil { + return nil, 0, err + } + + fullRes, err := io.ReadAll(stream) + if err != nil { + return nil, 0, err + } + rtt := time.Since(t) + + // Close with error: no error + err = connection.CloseWithError(0, "") + if err != nil { + return nil, 0, err + } + + err = stream.Close() + if err != nil { + return nil, 0, err + } + + response := dns.Msg{} + err = response.Unpack(fullRes) + if err != nil { + return nil, 0, err + } + + return &response, rtt, nil +} diff --git a/query/docs.go b/query/docs.go index c1f4aaa..1a4bfa2 100644 --- a/query/docs.go +++ b/query/docs.go @@ -1,2 +1,2 @@ -// Package for the special query types -package query +// Package for the special query types +package query diff --git a/util/docs.go b/util/docs.go index 590c528..7a114a9 100644 --- a/util/docs.go +++ b/util/docs.go @@ -1,2 +1,2 @@ -// Helper functions -package util +// Helper functions +package util diff --git a/util/helpers_test.go b/util/helpers_test.go index 1cb0fd7..0d0b6e5 100644 --- a/util/helpers_test.go +++ b/util/helpers_test.go @@ -1,50 +1,41 @@ -package util - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestIPv4(t *testing.T) { - act, err := ReverseDNS("8.8.4.4", "PTR") - assert.Nil(t, err) - assert.Equal(t, act, "4.4.8.8.in-addr.arpa.", "IPv4 reverse") -} - -// WIP -// func FuzzIPv4(f *testing.F) { -// f.Add("1.1.1.1", "1.1.1.1.in-addr.arpa") -// f.Fuzz(func(t *testing.T, a string, b string) { -// _, err := ReverseDNS(a, b) -// assert.Nil(t, err) -// }) -// } - -func TestIPv6(t *testing.T) { - act, err := ReverseDNS("2606:4700:4700::1111", "PTR") - assert.Nil(t, err) - assert.Equal(t, act, "1.1.1.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.7.4.0.0.7.4.6.0.6.2.ip6.arpa.", "IPv6 reverse") -} - -func TestNAPTR(t *testing.T) { - tests := []struct { - in string - want string - }{ - {"1-800-555-1234", "4.3.2.1.5.5.5.0.0.8.1.e164.arpa."}, - {"+1 800555 1234", "4.3.2.1.5.5.5.0.0.8.1.e164.arpa."}, - {"18005551234", "4.3.2.1.5.5.5.0.0.8.1.e164.arpa."}, - {"+1-770-555-1212", "2.1.2.1.5.5.5.0.7.7.1.e164.arpa."}, - } - for _, test := range tests { - act, err := ReverseDNS(test.in, "NAPTR") - assert.Nil(t, err) - assert.Equal(t, test.want, act) - } -} - -func TestInvalid(t *testing.T) { - _, err := ReverseDNS("AAAAA", "A") - assert.NotNil(t, err) -} +package util + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIPv4(t *testing.T) { + act, err := ReverseDNS("8.8.4.4", "PTR") + assert.Nil(t, err) + assert.Equal(t, act, "4.4.8.8.in-addr.arpa.", "IPv4 reverse") +} + +func TestIPv6(t *testing.T) { + act, err := ReverseDNS("2606:4700:4700::1111", "PTR") + assert.Nil(t, err) + assert.Equal(t, act, "1.1.1.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.7.4.0.0.7.4.6.0.6.2.ip6.arpa.", "IPv6 reverse") +} + +func TestNAPTR(t *testing.T) { + tests := []struct { + in string + want string + }{ + {"1-800-555-1234", "4.3.2.1.5.5.5.0.0.8.1.e164.arpa."}, + {"+1 800555 1234", "4.3.2.1.5.5.5.0.0.8.1.e164.arpa."}, + {"+46766861004", "4.0.0.1.6.8.6.6.7.6.4.e164.arpa."}, + {"17705551212", "2.1.2.1.5.5.5.0.7.7.1.e164.arpa."}, + } + for _, test := range tests { + act, err := ReverseDNS(test.in, "NAPTR") + assert.Nil(t, err) + assert.Equal(t, test.want, act) + } +} + +func TestInvalid(t *testing.T) { + _, err := ReverseDNS("AAAAA", "A") + assert.NotNil(t, err) +}