Minor (complete) refactor #38
38 changed files with 757 additions and 294 deletions
10
Makefile
10
Makefile
|
@ -3,8 +3,6 @@ GO := go
|
|||
GOFLAGS := -ldflags '-s -w'
|
||||
PREFIX := /usr/local
|
||||
BINPATH = $(PREFIX)/bin
|
||||
export CGO_ENABLED=1
|
||||
export CC=gcc
|
||||
|
||||
.PHONY: clean doc
|
||||
|
||||
|
@ -18,7 +16,7 @@ doc:
|
|||
scdoc < doc/awl.1.md > doc/awl.1
|
||||
|
||||
test:
|
||||
$(GO) test -race -v ./... -cover
|
||||
$(GO) test -v ./... -cover
|
||||
|
||||
fmt:
|
||||
gofmt -w -s .
|
||||
|
@ -30,6 +28,12 @@ lint: fmt vet
|
|||
|
||||
install: awl
|
||||
install awl $(BINPATH)
|
||||
|
||||
install_doc: doc
|
||||
install doc/awl.1 $(PREFIX)/share/man/man1
|
||||
@echo ""
|
||||
@echo "Make sure to update your manual database"
|
||||
@echo "'sudo mandb' on Debian/Ubuntu"
|
||||
|
||||
clean:
|
||||
$(GO) clean
|
43
cli/cli.go
43
cli/cli.go
|
@ -6,16 +6,18 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"git.froth.zone/sam/awl/internal/structs"
|
||||
"git.froth.zone/sam/awl/internal/helpers"
|
||||
"git.froth.zone/sam/awl/util"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
flag "github.com/stefansundin/go-zflag"
|
||||
)
|
||||
|
||||
// Parse the arguments passed into awl
|
||||
// Parse the arguments passed into awl.
|
||||
func ParseCLI(version string) (Options, error) {
|
||||
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
|
||||
|
||||
flag.Usage = func() {
|
||||
fmt.Println(`awl - drill, writ small
|
||||
|
@ -24,20 +26,19 @@ func ParseCLI(version string) (Options, error) {
|
|||
<name> domain, IP address, phone number
|
||||
<record> defaults to A
|
||||
|
||||
Arguments may be in any order, including flags.
|
||||
Dig-like +[no]commands are also supported, see dig(1) or dig -h
|
||||
Arguments may be in any order, including flags.
|
||||
Dig-like +[no]commands are also supported, see dig(1) or dig -h
|
||||
|
||||
Options:`)
|
||||
flag.PrintDefaults()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// CLI flag
|
||||
var (
|
||||
port = flag.Int("port", 0, "`port` to make DNS query (default: 53 for UDP/TCP, 853 for TLS/QUIC", flag.OptShorthand('p'), flag.OptDisablePrintDefault(true))
|
||||
query = flag.String("query", ".", "domain name to query", flag.OptShorthand('q'))
|
||||
class = flag.String("class", "IN", "DNS class to query", flag.OptShorthand('c'))
|
||||
qType = flag.String("qType", "A", "type to query", flag.OptShorthand('t'))
|
||||
port = flag.Int("port", 0, "`port` to make DNS query (default: 53 for UDP/TCP, 853 for TLS/QUIC)", flag.OptShorthand('p'), flag.OptDisablePrintDefault(true))
|
||||
query = flag.String("query", "", "domain name to `query` (default: .)", flag.OptShorthand('q'))
|
||||
class = flag.String("class", "IN", "DNS `class` to query", flag.OptShorthand('c'))
|
||||
qType = flag.String("qType", "", "`type` to query (default: A)", flag.OptShorthand('t'))
|
||||
|
||||
ipv4 = flag.Bool("4", false, "force IPv4", flag.OptShorthandStr("4"))
|
||||
ipv6 = flag.Bool("6", false, "force IPv6", flag.OptShorthand('6'))
|
||||
|
@ -54,9 +55,9 @@ func ParseCLI(version string) (Options, error) {
|
|||
aa = flag.Bool("aa", false, "set/unset AA (Authoratative Answer) flag (default: not set)")
|
||||
ad = flag.Bool("ad", false, "set/unset AD (Authenticated Data) flag (default: not set)")
|
||||
cd = flag.Bool("cd", false, "set/unset CD (Checking Disabled) flag (default: not set)")
|
||||
qr = flag.Bool("qr", false, "set/unset QR (QueRy) flag (default: set)", flag.OptDisablePrintDefault(true))
|
||||
qr = flag.Bool("qr", false, "set/unset QR (QueRy) flag (default: not set)")
|
||||
rd = flag.Bool("rd", true, "set/unset RD (Recursion Desired) flag (default: set)", flag.OptDisablePrintDefault(true))
|
||||
ra = flag.Bool("ra", false, "set/unset RA (Recursion Available) flag (default: set)", flag.OptDisablePrintDefault(true))
|
||||
ra = flag.Bool("ra", false, "set/unset RA (Recursion Available) flag (default: not set)")
|
||||
tc = flag.Bool("tc", false, "set/unset TC (TrunCated) flag (default: not set)")
|
||||
z = flag.Bool("z", false, "set/unset Z (Zero) flag (default: not set)", flag.OptShorthand('z'))
|
||||
|
||||
|
@ -73,11 +74,13 @@ func ParseCLI(version string) (Options, error) {
|
|||
flag.CommandLine.SortFlags = false
|
||||
|
||||
// Parse the flags
|
||||
flag.Parse()
|
||||
err := flag.CommandLine.Parse(os.Args[1:])
|
||||
if err != nil {
|
||||
return Options{Logger: util.InitLogger(*verbosity)}, err
|
||||
}
|
||||
|
||||
opts := Options{
|
||||
Logger: util.InitLogger(*verbosity),
|
||||
Class: dns.StringToClass[*class],
|
||||
Port: *port,
|
||||
IPv4: *ipv4,
|
||||
IPv6: *ipv6,
|
||||
|
@ -100,22 +103,24 @@ func ParseCLI(version string) (Options, error) {
|
|||
JSON: *json,
|
||||
XML: *xml,
|
||||
YAML: *yaml,
|
||||
Request: structs.Request{
|
||||
Type: dns.StringToType[*qType],
|
||||
Name: *query,
|
||||
Request: helpers.Request{
|
||||
Type: dns.StringToType[strings.ToUpper(*qType)],
|
||||
Class: dns.StringToClass[strings.ToUpper(*class)],
|
||||
Name: *query,
|
||||
},
|
||||
}
|
||||
|
||||
if *versionFlag {
|
||||
fmt.Printf("awl version %s, built with %s\n", version, runtime.Version())
|
||||
os.Exit(0)
|
||||
return opts, ErrNotError
|
||||
}
|
||||
|
||||
// Parse all the arguments that don't start with - or --
|
||||
// This includes the dig-style (+) options
|
||||
err := ParseMiscArgs(flag.Args(), &opts)
|
||||
err = ParseMiscArgs(flag.Args(), &opts)
|
||||
if err != nil {
|
||||
opts.Logger.Fatal(err)
|
||||
opts.Logger.Error(err)
|
||||
return opts, err
|
||||
}
|
||||
|
||||
if opts.Port == 0 {
|
||||
|
|
|
@ -2,44 +2,95 @@
|
|||
|
||||
package cli_test
|
||||
|
||||
// TODO: readd these
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
// import (
|
||||
// "os"
|
||||
// "testing"
|
||||
"git.froth.zone/sam/awl/cli"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
// "git.froth.zone/sam/awl/internal/structs"
|
||||
// "git.froth.zone/sam/awl/query"
|
||||
func TestEmpty(t *testing.T) {
|
||||
old := os.Args
|
||||
os.Args = []string{""}
|
||||
opts, err := cli.ParseCLI("TEST")
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, opts != cli.Options{})
|
||||
os.Args = old
|
||||
}
|
||||
|
||||
// "github.com/miekg/dns"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
// "github.com/stretchr/testify/require"
|
||||
// )
|
||||
func TestInvalidFlag(t *testing.T) {
|
||||
old := os.Args
|
||||
os.Args = []string{"awl", "--treebug"}
|
||||
_, err := cli.ParseCLI("TEST")
|
||||
assert.ErrorContains(t, err, "unknown flag")
|
||||
os.Args = old
|
||||
}
|
||||
|
||||
// func TestApp(t *testing.T) {
|
||||
// t.Parallel()
|
||||
// app := prepareCLI()
|
||||
// // What more can even be done lmao
|
||||
// require.NotNil(t, app)
|
||||
func TestInvalidDig(t *testing.T) {
|
||||
old := os.Args
|
||||
os.Args = []string{"awl", "+a"}
|
||||
_, err := cli.ParseCLI("TEST")
|
||||
assert.ErrorContains(t, err, "dig: unknown flag given")
|
||||
os.Args = old
|
||||
}
|
||||
|
||||
// func TestArgParse(t *testing.T) {
|
||||
// tests := []struct {
|
||||
// in []string
|
||||
// want helpers.Request
|
||||
// }{
|
||||
// {
|
||||
// []string{"@::1", "localhost", "AAAA"},
|
||||
// helpers.Request{Server: "::1", Type: dns.TypeAAAA, Name: "localhost"},
|
||||
// },
|
||||
// {
|
||||
// []string{"@1.0.0.1", "google.com"},
|
||||
// helpers.Request{Server: "1.0.0.1", Type: dns.TypeA, Name: "google.com"},
|
||||
// },
|
||||
// {
|
||||
// []string{"@8.8.4.4"},
|
||||
// helpers.Request{Server: "8.8.4.4", Type: dns.TypeNS, Name: "."},
|
||||
// },
|
||||
// }
|
||||
// for _, test := range tests {
|
||||
// old := os.Args
|
||||
// act, err := cli.ParseCLI(test.in)
|
||||
// assert.Nil(t, err)
|
||||
// assert.Equal(t, test.want, act)
|
||||
// }
|
||||
// }
|
||||
|
||||
func FuzzFlags(f *testing.F) {
|
||||
testcases := []string{"git.froth.zone", "", "!12345", "google.com.edu.org.fr"}
|
||||
for _, tc := range testcases {
|
||||
f.Add(tc)
|
||||
}
|
||||
|
||||
f.Fuzz(func(t *testing.T, orig string) {
|
||||
os.Args = []string{orig}
|
||||
//nolint:errcheck // Only make sure the program does not crash
|
||||
cli.ParseCLI("TEST")
|
||||
})
|
||||
}
|
||||
|
||||
// func TestArgParse(t *testing.T) {
|
||||
// t.Parallel()
|
||||
// tests := []struct {
|
||||
// in []string
|
||||
// want structs.Request
|
||||
// want helpers.Request
|
||||
// }{
|
||||
// {
|
||||
// []string{"@::1", "localhost", "AAAA"},
|
||||
// structs.Request{Server: "::1", Type: dns.TypeAAAA, Name: "localhost"},
|
||||
// helpers.Request{Server: "::1", Type: dns.TypeAAAA, Name: "localhost"},
|
||||
// },
|
||||
// {
|
||||
// []string{"@1.0.0.1", "google.com"},
|
||||
// structs.Request{Server: "1.0.0.1", Type: dns.TypeA, Name: "google.com"},
|
||||
// helpers.Request{Server: "1.0.0.1", Type: dns.TypeA, Name: "google.com"},
|
||||
// },
|
||||
// {
|
||||
// []string{"@8.8.4.4"},
|
||||
// structs.Request{Server: "8.8.4.4", Type: dns.TypeNS, Name: "."},
|
||||
// helpers.Request{Server: "8.8.4.4", Type: dns.TypeNS, Name: "."},
|
||||
// },
|
||||
// }
|
||||
// for _, test := range tests {
|
||||
|
|
23
cli/dig.go
23
cli/dig.go
|
@ -7,27 +7,27 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// Parse dig-like commands and set the options as such
|
||||
// Parse dig-like commands and set the options as such.
|
||||
func ParseDig(arg string, opts *Options) error {
|
||||
// returns true if the flag starts with a no
|
||||
isNo := !strings.HasPrefix(arg, "no")
|
||||
switch arg {
|
||||
// Set DNS query flags
|
||||
case "aaflag", "aaonly", "noaaflag", "noaaonly":
|
||||
case "aa", "aaflag", "aaonly", "noaa", "noaaflag", "noaaonly":
|
||||
opts.AA = isNo
|
||||
case "adflag", "noadflag":
|
||||
case "ad", "adflag", "noad", "noadflag":
|
||||
opts.AD = isNo
|
||||
case "cdflag", "nocdflag":
|
||||
case "cd", "cdflag", "nocd", "nocdflag":
|
||||
opts.CD = isNo
|
||||
case "qrflag", "noqrflag":
|
||||
case "qr", "qrflag", "noqr", "noqrflag":
|
||||
opts.QR = isNo
|
||||
case "raflag", "noraflag":
|
||||
case "ra", "raflag", "nora", "noraflag":
|
||||
opts.RA = isNo
|
||||
case "rdflag", "recurse", "nordflag", "norecurse":
|
||||
case "rd", "rdflag", "recurse", "nord", "nordflag", "norecurse":
|
||||
opts.RD = isNo
|
||||
case "tcflag", "notcflag":
|
||||
case "tc", "tcflag", "notc", "notcflag":
|
||||
opts.TC = isNo
|
||||
case "zflag", "nozflag":
|
||||
case "z", "zflag", "noz", "nozflag":
|
||||
opts.Z = isNo
|
||||
// End DNS query flags
|
||||
|
||||
|
@ -36,8 +36,7 @@ func ParseDig(arg string, opts *Options) error {
|
|||
case "tcp", "vc", "notcp", "novc":
|
||||
opts.TCP = isNo
|
||||
case "ignore", "noignore":
|
||||
// Invert (ignore truncation when true)
|
||||
opts.Truncate = !isNo
|
||||
opts.Truncate = isNo
|
||||
|
||||
// Formatting
|
||||
case "short", "noshort":
|
||||
|
@ -51,7 +50,7 @@ func ParseDig(arg string, opts *Options) error {
|
|||
// End formatting
|
||||
|
||||
default:
|
||||
return fmt.Errorf("dig: Unknown flag given")
|
||||
return fmt.Errorf("dig: unknown flag given")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
43
cli/dig_test.go
Normal file
43
cli/dig_test.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package cli_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.froth.zone/sam/awl/cli"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func FuzzDig(f *testing.F) {
|
||||
f.Log("ParseDig Fuzzing")
|
||||
seeds := []string{
|
||||
"aaflag", "aaonly", "noaaflag", "noaaonly",
|
||||
"adflag", "noadflag",
|
||||
"cdflag", "nocdflag",
|
||||
"qrflag", "noqrflag",
|
||||
"raflag", "noraflag",
|
||||
"rdflag", "recurse", "nordflag", "norecurse",
|
||||
"tcflag", "notcflag",
|
||||
"zflag", "nozflag",
|
||||
"dnssec", "nodnssec",
|
||||
"tcp", "vc", "notcp", "novc",
|
||||
"ignore", "noignore",
|
||||
"short", "noshort",
|
||||
"json", "nojson",
|
||||
"xml", "noxml",
|
||||
"yaml", "noyaml",
|
||||
"invalid",
|
||||
}
|
||||
for _, tc := range seeds {
|
||||
f.Add(tc)
|
||||
}
|
||||
|
||||
f.Fuzz(func(t *testing.T, orig string) {
|
||||
opts := new(cli.Options)
|
||||
err := cli.ParseDig(orig, opts)
|
||||
if err != nil {
|
||||
assert.ErrorContains(t, err, "unknown flag given")
|
||||
}
|
||||
})
|
||||
}
|
45
cli/misc.go
45
cli/misc.go
|
@ -8,14 +8,13 @@ import (
|
|||
"strings"
|
||||
|
||||
"git.froth.zone/sam/awl/conf"
|
||||
"git.froth.zone/sam/awl/internal/structs"
|
||||
"git.froth.zone/sam/awl/util"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
||||
// Parse the wildcard arguments, drill style
|
||||
// Parse the wildcard arguments, drill style.
|
||||
func ParseMiscArgs(args []string, opts *Options) error {
|
||||
var err error
|
||||
|
||||
|
@ -26,13 +25,13 @@ func ParseMiscArgs(args []string, opts *Options) error {
|
|||
case strings.HasPrefix(arg, "@"):
|
||||
opts.Request.Server = arg[1:]
|
||||
case strings.Contains(arg, "."):
|
||||
opts.Query, err = idna.ToASCII(arg)
|
||||
opts.Request.Name, err = idna.ToASCII(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case ok:
|
||||
// If it's a DNS request, it's a DNS request (obviously)
|
||||
opts.Type = r
|
||||
opts.Request.Type = r
|
||||
case strings.HasPrefix(arg, "+"):
|
||||
// Dig-style +queries
|
||||
err = ParseDig(strings.ToLower(arg[1:]), opts)
|
||||
|
@ -40,26 +39,26 @@ func ParseMiscArgs(args []string, opts *Options) error {
|
|||
return err
|
||||
}
|
||||
default:
|
||||
//else, assume it's a name
|
||||
opts.Query, err = idna.ToASCII(arg)
|
||||
|
||||
opts.Request.Name, err = idna.ToASCII(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// If nothing was set, set a default
|
||||
if opts.Query == "" {
|
||||
opts.Query = "."
|
||||
if opts.Type == 0 {
|
||||
opts.Type = dns.StringToType["NS"]
|
||||
if opts.Request.Name == "" {
|
||||
opts.Request.Name = "."
|
||||
if opts.Request.Type == 0 {
|
||||
opts.Request.Type = dns.StringToType["NS"]
|
||||
}
|
||||
} else {
|
||||
if opts.Type == 0 {
|
||||
opts.Type = dns.StringToType["A"]
|
||||
if opts.Request.Type == 0 {
|
||||
opts.Request.Type = dns.StringToType["A"]
|
||||
}
|
||||
}
|
||||
//
|
||||
if opts.Request.Server == "" {
|
||||
switch {
|
||||
case opts.TLS:
|
||||
|
@ -97,29 +96,17 @@ func ParseMiscArgs(args []string, opts *Options) error {
|
|||
// Make reverse adresses proper addresses
|
||||
if opts.Reverse {
|
||||
if dns.TypeToString[opts.Request.Type] == "A" {
|
||||
opts.Type = dns.StringToType["PTR"]
|
||||
opts.Request.Type = dns.StringToType["PTR"]
|
||||
}
|
||||
opts.Query, err = util.ReverseDNS(opts.Query, opts.Request.Type)
|
||||
opts.Request.Name, err = util.ReverseDNS(opts.Request.Name, opts.Request.Type)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if opts.Class == 0 {
|
||||
opts.Class = dns.StringToClass["IN"]
|
||||
}
|
||||
|
||||
// if the domain is not canonical, make it canonical
|
||||
if !strings.HasSuffix(opts.Query, ".") {
|
||||
opts.Query = fmt.Sprintf("%s.", opts.Query)
|
||||
if !strings.HasSuffix(opts.Request.Name, ".") {
|
||||
opts.Request.Name = fmt.Sprintf("%s.", opts.Request.Name)
|
||||
}
|
||||
|
||||
opts.Request = structs.Request{
|
||||
Server: opts.Request.Server,
|
||||
Type: opts.Type,
|
||||
Class: opts.Class,
|
||||
Name: opts.Query,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
111
cli/misc_test.go
Normal file
111
cli/misc_test.go
Normal file
|
@ -0,0 +1,111 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package cli_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.froth.zone/sam/awl/cli"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestParseArgs(t *testing.T) {
|
||||
t.Parallel()
|
||||
args := []string{
|
||||
"go.dev",
|
||||
"AAAA",
|
||||
"@1.1.1.1",
|
||||
"+ignore",
|
||||
}
|
||||
opts := new(cli.Options)
|
||||
err := cli.ParseMiscArgs(args, opts)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, opts.Request.Name, "go.dev.")
|
||||
assert.Equal(t, opts.Request.Type, dns.StringToType["AAAA"])
|
||||
assert.Equal(t, opts.Request.Server, "1.1.1.1")
|
||||
assert.Equal(t, opts.Truncate, true)
|
||||
}
|
||||
|
||||
func TestParseNoInput(t *testing.T) {
|
||||
t.Parallel()
|
||||
args := []string{}
|
||||
opts := new(cli.Options)
|
||||
err := cli.ParseMiscArgs(args, opts)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, opts.Request.Name, ".")
|
||||
assert.Equal(t, opts.Request.Type, dns.StringToType["NS"])
|
||||
}
|
||||
|
||||
func TestParseA(t *testing.T) {
|
||||
t.Parallel()
|
||||
args := []string{
|
||||
"golang.org.",
|
||||
}
|
||||
opts := new(cli.Options)
|
||||
err := cli.ParseMiscArgs(args, opts)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, opts.Request.Name, "golang.org.")
|
||||
assert.Equal(t, opts.Request.Type, dns.StringToType["A"])
|
||||
}
|
||||
|
||||
func TestParsePTR(t *testing.T) {
|
||||
t.Parallel()
|
||||
args := []string{"8.8.8.8"}
|
||||
opts := new(cli.Options)
|
||||
opts.Reverse = true
|
||||
err := cli.ParseMiscArgs(args, opts)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, opts.Request.Type, dns.StringToType["PTR"])
|
||||
}
|
||||
|
||||
func TestDefaultServer(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{"TLS", "dns.google"},
|
||||
{"HTTPS", "https://dns.cloudflare.com/dns-query"},
|
||||
{"QUIC", "dns.adguard.com"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.in, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
args := []string{}
|
||||
opts := new(cli.Options)
|
||||
switch test.in {
|
||||
case "TLS":
|
||||
opts.TLS = true
|
||||
case "HTTPS":
|
||||
opts.HTTPS = true
|
||||
case "QUIC":
|
||||
opts.QUIC = true
|
||||
}
|
||||
err := cli.ParseMiscArgs(args, opts)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, opts.Request.Server, test.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzParseArgs(f *testing.F) {
|
||||
cases := []string{
|
||||
"go.dev",
|
||||
"AAAA",
|
||||
"@1.1.1.1",
|
||||
"+ignore",
|
||||
"e",
|
||||
}
|
||||
for _, tc := range cases {
|
||||
f.Add(tc)
|
||||
}
|
||||
f.Fuzz(func(t *testing.T, arg string) {
|
||||
args := []string{arg}
|
||||
opts := new(cli.Options)
|
||||
//nolint:errcheck // Only make sure the program does not crash
|
||||
cli.ParseMiscArgs(args, opts)
|
||||
})
|
||||
}
|
|
@ -3,17 +3,16 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"git.froth.zone/sam/awl/internal/structs"
|
||||
"errors"
|
||||
|
||||
"git.froth.zone/sam/awl/internal/helpers"
|
||||
"git.froth.zone/sam/awl/logawl"
|
||||
)
|
||||
|
||||
// CLI options structure
|
||||
// CLI options structure.
|
||||
type Options struct {
|
||||
Logger *logawl.Logger // Logger
|
||||
Port int // DNS port
|
||||
Query string // DNS Query
|
||||
Class uint16 // DNS Class
|
||||
Type uint16 // DNS Type
|
||||
IPv4 bool // Force IPv4
|
||||
IPv6 bool // Force IPv6
|
||||
DNSSEC bool // Enable DNSSEC
|
||||
|
@ -32,10 +31,13 @@ type Options struct {
|
|||
Z bool // Set Z (Zero)
|
||||
Reverse bool // Make reverse query
|
||||
Verbosity int // Set logawl verbosity
|
||||
HumanTTL bool // Make TTL human readable
|
||||
Short bool // Short output
|
||||
JSON bool // Outout as JSON
|
||||
XML bool // Output as XML
|
||||
YAML bool // Output at YAML
|
||||
|
||||
Request structs.Request
|
||||
Request helpers.Request
|
||||
}
|
||||
|
||||
var ErrNotError = errors.New("not an error")
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
// Yoink it and use it.
|
||||
//
|
||||
// See ndb(7).
|
||||
func getPlan9Config(str string) (*dns.ClientConfig, error) {
|
||||
func GetPlan9Config(str string) (*dns.ClientConfig, error) {
|
||||
str = strings.ReplaceAll(str, "\n", "")
|
||||
spl := strings.FieldsFunc(str, splitChars)
|
||||
var servers []string
|
||||
|
@ -34,7 +34,7 @@ func getPlan9Config(str string) (*dns.ClientConfig, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
// Split the string at either space or tabs
|
||||
// Split the string at either space or tabs.
|
||||
func splitChars(r rune) bool {
|
||||
return r == ' ' || r == '\t'
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package conf
|
||||
package conf_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"git.froth.zone/sam/awl/conf"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestGetPlan9Config(t *testing.T) {
|
||||
|
@ -28,9 +29,14 @@ func TestGetPlan9Config(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, ndb := range ndbs {
|
||||
act, err := getPlan9Config(ndb.in)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, ndb.want, act.Servers[0])
|
||||
// Go is a little quirky
|
||||
ndb := ndb
|
||||
t.Run(ndb.want, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
act, err := conf.GetPlan9Config(ndb.in)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, ndb.want, act.Servers[0])
|
||||
})
|
||||
}
|
||||
|
||||
invalid := `sys = spindle
|
||||
|
@ -39,8 +45,7 @@ func TestGetPlan9Config(t *testing.T) {
|
|||
ip=135.104.117.32 ether=080069020677
|
||||
proto=il`
|
||||
|
||||
act, err := getPlan9Config(invalid)
|
||||
act, err := conf.GetPlan9Config(invalid)
|
||||
assert.ErrorContains(t, err, "no DNS servers found")
|
||||
assert.Nil(t, act)
|
||||
|
||||
assert.Assert(t, act == nil)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package conf
|
||||
|
||||
|
@ -18,7 +17,7 @@ func GetDNSConfig() (*dns.ClientConfig, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getPlan9Config(string(dat))
|
||||
return GetPlan9Config(string(dat))
|
||||
} else {
|
||||
return dns.ClientConfigFromFile("/etc/resolv.conf")
|
||||
}
|
21
conf/unix_test.go
Normal file
21
conf/unix_test.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build !windows
|
||||
|
||||
package conf_test
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"git.froth.zone/sam/awl/conf"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestNonWinConfig(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Not running Windows, skipping")
|
||||
}
|
||||
conf, err := conf.GetDNSConfig()
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, len(conf.Servers) != 0)
|
||||
}
|
21
conf/win_test.go
Normal file
21
conf/win_test.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build windows
|
||||
|
||||
package conf_test
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"git.froth.zone/sam/awl/conf"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestWinConfig(t *testing.T) {
|
||||
if runtime.GOOS != "windows" {
|
||||
t.Skip("Not running Windows, skipping")
|
||||
}
|
||||
conf, err := conf.GetDNSConfig()
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, len(conf.Servers) != 0)
|
||||
}
|
113
doc/awl.1
113
doc/awl.1
|
@ -5,7 +5,7 @@
|
|||
.nh
|
||||
.ad l
|
||||
.\" Begin generated content:
|
||||
.TH "AWL" "1" "2022-07-20"
|
||||
.TH "awl" "1" "2022-07-21"
|
||||
.PP
|
||||
.SH NAME
|
||||
awl - drill, writ small
|
||||
|
@ -23,23 +23,27 @@ where
|
|||
.PP
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
\fBawl\fR (\fBa\fRwls \fBw\fRant \fBl\fRicorice) is a simple tool designed to make DNS queries, much like the venerable \fIdig\fR(1).\&
|
||||
An awl is a tool used to make small holes, typically used in tannery (leatherworking).\&
|
||||
\fBawl\fR (\fBa\fRwls \fBw\fRant \fBl\fRicorice) is a simple tool designed to make DNS queries, much
|
||||
like the venerable \fIdig\fR(1).\& An awl is a tool used to make small holes, typically
|
||||
used in tannery (leatherworking).\&
|
||||
.PP
|
||||
Written in Go, \fBawl\fR is designed to be a more "modern" version of \fIdrill\fR(1) by including some more RFCs and output options.\&
|
||||
\fBawl\fR is still heavily Work-In-Progress so some features may get added or removed.\&
|
||||
Written in Go, \fBawl\fR is designed to be a more "modern" version of \fIdrill\fR(1) by
|
||||
including some more RFCs and output options.\& \fBawl\fR is still heavily
|
||||
Work-In-Progress so some features may get added or removed.\&
|
||||
.PP
|
||||
.SH OPTIONS
|
||||
.RS 4
|
||||
\fB-D\fR, \fB--dnssec\fR
|
||||
Dig-like queries are supported, see dig(1)
|
||||
.PP
|
||||
\fB-D\fR, \fB--dnssec\fR, \fB+dnssec\fR
|
||||
.br
|
||||
Enable DNSSEC.\& This needs to be manually enabled.\&
|
||||
.PP
|
||||
\fB--debug\fR
|
||||
\fB-v\fR \fIvalue\fR
|
||||
.br
|
||||
Enable debug logging (currently WIP).\&
|
||||
Set verbosity (currently WIP)
|
||||
.PP
|
||||
\fB-v\fR
|
||||
\fB-V\fR
|
||||
.br
|
||||
Print the version and exit.\&
|
||||
.PP
|
||||
|
@ -76,27 +80,35 @@ Written in Go, \fBawl\fR is designed to be a more "modern" version of \fIdrill\f
|
|||
.PD
|
||||
.PP
|
||||
.RE
|
||||
\fB--no-truncate\fR
|
||||
\fB-q\fR, \fB--query\fR \fIdomain\fR
|
||||
.br
|
||||
Domain to query (eg.\& example.\&com)
|
||||
.PP
|
||||
\fB-c\fR, \fB--class\fR \fIclass\fR
|
||||
.br
|
||||
DNS class to query (eg.\& IN, CH)
|
||||
.PP
|
||||
\fB-t\fR, \fB--qType\fR \fItype\fR
|
||||
.br
|
||||
DNS type to query (eg.\& A, NS)
|
||||
.PP
|
||||
\fB--no-truncate\fR, \fB+ignore\fR
|
||||
.br
|
||||
Ignore UDP truncation (by default, awl \fIretries with TCP\fR)
|
||||
.PP
|
||||
\fB-t\fR, \fB--tcp\fR
|
||||
\fB-t\fR, \fB--tcp\fR, \fB+tcp\fR, \fB+vc\fR
|
||||
.br
|
||||
Use TCP for the query (see \fIRFC 7766\fR)
|
||||
.PP
|
||||
\fB-u\fR, \fB--udp\fR
|
||||
.br
|
||||
Use UDP for the query (default)
|
||||
.PP
|
||||
\fB-T\fR, \fB--tls\fR
|
||||
\fB-T\fR, \fB--tls\fR, \fB+tls\fR
|
||||
.br
|
||||
Use DNS-over-TLS, implies \fB-t\fR (see \fIRFC 7858\fR)
|
||||
.PP
|
||||
\fB-H\fR.\& \fB--https\fR
|
||||
\fB-H\fR.\& \fB--https\fR, \fB+https\fR
|
||||
.br
|
||||
Use DNS-over-HTTPS (see \fIRFC 8484\fR)
|
||||
.PP
|
||||
\fB-Q\fR.\& \fB--quic\fR
|
||||
\fB-Q\fR.\& \fB--quic\fR, \fB+quic\fR
|
||||
.br
|
||||
Use DNS-over-QUIC (see \fIRFC 9250\fR)
|
||||
.PP
|
||||
|
@ -110,59 +122,80 @@ Written in Go, \fBawl\fR is designed to be a more "modern" version of \fIdrill\f
|
|||
.SS DNS Flags
|
||||
.PP
|
||||
.RS 4
|
||||
\fB--aa\fR
|
||||
\fB--aa=[false]\fR, \fB+[no]aaflag\fR
|
||||
.br
|
||||
\fISET\fR Authoritative Answer (default: unset)
|
||||
(Set, Unset) AA (Authoritative Answer) flag
|
||||
.PP
|
||||
\fB--ad\fR
|
||||
\fB--ad=[false]\fR, \fB+[no]adflag\fR
|
||||
.br
|
||||
\fISET\fR Authenticated Data (default: unset)
|
||||
(Set, Unset) AD (Authenticated Data) flag
|
||||
.PP
|
||||
\fB--tc\fR
|
||||
\fB--tc=[false]\fR, \fB+[no]tcflag\fR
|
||||
.br
|
||||
\fISET\fR TC (TrunCated) flag (default: not set)
|
||||
(Set, Unset) TC (TrunCated) flag
|
||||
.PP
|
||||
\fB-z\fR
|
||||
\fB-z=[false]\fR, \fB+[no]zflag\fR
|
||||
.br
|
||||
\fISET\fR Z (Zero) flag (default: not set)
|
||||
(Set, Unset) Z (Zero) flag
|
||||
.PP
|
||||
\fB--cd\fR
|
||||
\fB--cd=[false]\fR, \fB+[no]cdflag\fR
|
||||
.br
|
||||
\fISET\fR CD (Checking Disabled) flag (default: not set)
|
||||
(Set, Unset) CD (Checking Disabled) flag
|
||||
.PP
|
||||
\fB--no-qr\fR
|
||||
\fB--qr=[false]\fR, \fB+[no]qrflag\fR
|
||||
.br
|
||||
\fIUNSET\fR QR (QueRy) flag (default: set)
|
||||
(Set, Unset) QR (QueRy) flag
|
||||
.PP
|
||||
\fB--no-rd\fR
|
||||
\fB--rd=[true]\fR, \fB+[no]rdflag\fR
|
||||
.br
|
||||
\fIUNSET\fR RD (Recursion Desired) flag (default: set)
|
||||
(Set, Unset) RD (Recursion Desired) flag
|
||||
.PP
|
||||
\fB--no-ra\fR
|
||||
\fB--ra=[false]\fR, \fB+[no]raflag\fR
|
||||
.br
|
||||
\fIUNSET\fR RA (Recursion Available) flag (default: set)
|
||||
(Set, Unset) RA (Recursion Available) flag
|
||||
.PP
|
||||
.RE
|
||||
.SS Output Options
|
||||
.RS 4
|
||||
\fB-j\fR, \fB--json\fR
|
||||
\fB-j\fR, \fB--json\fR, \fB+json\fR
|
||||
.br
|
||||
Print the query results as JSON.\&
|
||||
.PP
|
||||
\fB-X\fR, \fB--xml\fR
|
||||
\fB-X\fR, \fB--xml\fR, \fB+xml\fR
|
||||
.br
|
||||
Print the query results as XML.\&
|
||||
.PP
|
||||
\fB-y\fR, \fB--yaml\fR
|
||||
\fB-y\fR, \fB--yaml\fR, \fB+yaml\fR
|
||||
.br
|
||||
Print the query results as YAML.\&
|
||||
.PP
|
||||
\fB-s\fR, \fB--short\fR
|
||||
\fB-s\fR, \fB--short\fR, \fB+short\fR
|
||||
.br
|
||||
Print just the results.\&
|
||||
.br
|
||||
Equivalent to \fBdig +short\fR
|
||||
.PP
|
||||
.RE
|
||||
.SH EXAMPLES
|
||||
.nf
|
||||
.RS 4
|
||||
awl grumbulon\&.xyz -j +cd
|
||||
.fi
|
||||
.RE
|
||||
Run a query of your local resolver for the A records of grumbulon.\&xyz, print
|
||||
them as JSON and disable DNSSEC verification.\&
|
||||
.PP
|
||||
.nf
|
||||
.RS 4
|
||||
awl +short example\&.com AAAA @1\&.1\&.1\&.1
|
||||
.fi
|
||||
.RE
|
||||
Query 1.\&1.\&1.\&1 for the AAAA records of example.\&com, print just the answers
|
||||
.PP
|
||||
.nf
|
||||
.RS 4
|
||||
awl -xT PTR 8\&.8\&.4\&.4 @dns\&.google
|
||||
.fi
|
||||
.RE
|
||||
Query dns.\&google over TLS for the PTR record to the IP address 8.\&8.\&4.\&4
|
||||
.PP
|
||||
.SH SEE ALSO
|
||||
\fIdrill\fR(1), \fIdig\fR(1), the many DNS RFCs
|
104
doc/awl.1.md
104
doc/awl.1.md
|
@ -1,4 +1,4 @@
|
|||
AWL(1)
|
||||
awl(1)
|
||||
|
||||
# NAME
|
||||
awl - drill, writ small
|
||||
|
@ -13,22 +13,24 @@ _type_ is the DNS resource type (*example: AAAA*)
|
|||
|
||||
# DESCRIPTION
|
||||
|
||||
*awl* (*a*wls *w*ant *l*icorice) is a simple tool designed to make DNS queries, much like the venerable _dig_(1).
|
||||
An awl is a tool used to make small holes, typically used in tannery (leatherworking).
|
||||
*awl* (*a*wls *w*ant *l*icorice) is a simple tool designed to make DNS queries, much
|
||||
like the venerable _dig_(1). An awl is a tool used to make small holes, typically
|
||||
used in tannery (leatherworking).
|
||||
|
||||
Written in Go, *awl* is designed to be a more "modern" version of _drill_(1) by including some more RFCs and output options.
|
||||
*awl* is still heavily Work-In-Progress so some features may get added or removed.
|
||||
Written in Go, *awl* is designed to be a more "modern" version of _drill_(1) by
|
||||
including some more RFCs and output options. *awl* is still heavily
|
||||
Work-In-Progress so some features may get added or removed.
|
||||
|
||||
# OPTIONS
|
||||
_Dig-like queries are supported, see dig(1)_
|
||||
Dig-like queries are supported, see dig(1)
|
||||
|
||||
*-D*, *--dnssec*++
|
||||
*-D*, *--dnssec*, *+dnssec*++
|
||||
Enable DNSSEC. This needs to be manually enabled.
|
||||
|
||||
*--debug*++
|
||||
Enable debug logging (currently WIP).
|
||||
*-v* _value_++
|
||||
Set verbosity (currently WIP)
|
||||
|
||||
*-v*++
|
||||
*-V*++
|
||||
Print the version and exit.
|
||||
|
||||
*-h*++
|
||||
|
@ -49,22 +51,28 @@ _Default Ports_:
|
|||
- _853_ for *TLS* and *QUIC*
|
||||
- _443_ for *HTTPS*
|
||||
|
||||
*--no-truncate*++
|
||||
*-q*, *--query* _domain_++
|
||||
Domain to query (eg. example.com)
|
||||
|
||||
*-c*, *--class* _class_++
|
||||
DNS class to query (eg. IN, CH)
|
||||
|
||||
*-t*, *--qType* _type_++
|
||||
DNS type to query (eg. A, NS)
|
||||
|
||||
*--no-truncate*, *+ignore*++
|
||||
Ignore UDP truncation (by default, awl _retries with TCP_)
|
||||
|
||||
*-t*, *--tcp*++
|
||||
*-t*, *--tcp*, *+tcp*, *+vc*++
|
||||
Use TCP for the query (see _RFC 7766_)
|
||||
|
||||
*-u*, *--udp*++
|
||||
Use UDP for the query (default)
|
||||
|
||||
*-T*, *--tls*++
|
||||
*-T*, *--tls*, *+tls*++
|
||||
Use DNS-over-TLS, implies *-t* (see _RFC 7858_)
|
||||
|
||||
*-H*. *--https*++
|
||||
*-H*. *--https*, *+https*++
|
||||
Use DNS-over-HTTPS (see _RFC 8484_)
|
||||
|
||||
*-Q*. *--quic*++
|
||||
*-Q*. *--quic*, *+quic*++
|
||||
Use DNS-over-QUIC (see _RFC 9250_)
|
||||
|
||||
*-x*, *--reverse*++
|
||||
|
@ -73,43 +81,59 @@ _Default Ports_:
|
|||
|
||||
## DNS Flags
|
||||
|
||||
*--aa*++
|
||||
_SET_ Authoritative Answer (default: unset)
|
||||
*--aa=[false]*, *+[no]aaflag*++
|
||||
(Set, Unset) AA (Authoritative Answer) flag
|
||||
|
||||
*--ad*++
|
||||
_SET_ Authenticated Data (default: unset)
|
||||
*--ad=[false]*, *+[no]adflag*++
|
||||
(Set, Unset) AD (Authenticated Data) flag
|
||||
|
||||
*--tc*++
|
||||
_SET_ TC (TrunCated) flag (default: not set)
|
||||
*--tc=[false]*, *+[no]tcflag*++
|
||||
(Set, Unset) TC (TrunCated) flag
|
||||
|
||||
*-z*++
|
||||
_SET_ Z (Zero) flag (default: not set)
|
||||
*-z=[false]*, *+[no]zflag*++
|
||||
(Set, Unset) Z (Zero) flag
|
||||
|
||||
*--cd*++
|
||||
_SET_ CD (Checking Disabled) flag (default: not set)
|
||||
*--cd=[false]*, *+[no]cdflag*++
|
||||
(Set, Unset) CD (Checking Disabled) flag
|
||||
|
||||
*--no-qr*++
|
||||
_UNSET_ QR (QueRy) flag (default: set)
|
||||
*--qr=[false]*, *+[no]qrflag*++
|
||||
(Set, Unset) QR (QueRy) flag
|
||||
|
||||
*--no-rd*++
|
||||
_UNSET_ RD (Recursion Desired) flag (default: set)
|
||||
*--rd=[true]*, *+[no]rdflag*++
|
||||
(Set, Unset) RD (Recursion Desired) flag
|
||||
|
||||
*--no-ra*++
|
||||
_UNSET_ RA (Recursion Available) flag (default: set)
|
||||
*--ra=[false]*, *+[no]raflag*++
|
||||
(Set, Unset) RA (Recursion Available) flag
|
||||
|
||||
## Output Options
|
||||
*-j*, *--json*++
|
||||
*-j*, *--json*, *+json*++
|
||||
Print the query results as JSON.
|
||||
|
||||
*-X*, *--xml*++
|
||||
*-X*, *--xml*, *+xml*++
|
||||
Print the query results as XML.
|
||||
|
||||
*-y*, *--yaml*++
|
||||
*-y*, *--yaml*, *+yaml*++
|
||||
Print the query results as YAML.
|
||||
|
||||
*-s*, *--short*++
|
||||
Print just the results.++
|
||||
Equivalent to *dig +short*
|
||||
*-s*, *--short*, *+short*++
|
||||
Print just the results.
|
||||
|
||||
# EXAMPLES
|
||||
```
|
||||
awl grumbulon.xyz -j +cd
|
||||
```
|
||||
Run a query of your local resolver for the A records of grumbulon.xyz, print
|
||||
them as JSON and disable DNSSEC verification.
|
||||
|
||||
```
|
||||
awl +short example.com AAAA @1.1.1.1
|
||||
```
|
||||
Query 1.1.1.1 for the AAAA records of example.com, print just the answers
|
||||
|
||||
```
|
||||
awl -xT PTR 8.8.4.4 @dns.google
|
||||
```
|
||||
Query dns.google over TLS for the PTR record to the IP address 8.8.4.4
|
||||
|
||||
# SEE ALSO
|
||||
_drill_(1), _dig_(1), the many DNS RFCs
|
7
go.mod
7
go.mod
|
@ -8,13 +8,12 @@ require (
|
|||
github.com/stefansundin/go-zflag v1.1.1
|
||||
golang.org/x/net v0.0.0-20220708220712-1185a9018129
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gotest.tools/v3 v3.3.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/google/go-cmp v0.5.5 // indirect
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
|
@ -26,7 +25,7 @@ require (
|
|||
github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/stretchr/testify v1.8.0 // 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-20220715151400-c0bba94af5f8
|
||||
|
|
7
go.sum
7
go.sum
|
@ -55,6 +55,7 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
|
|||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
|
@ -145,6 +146,7 @@ github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYED
|
|||
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/stefansundin/go-zflag v1.1.1 h1:XabhzWS588bVvV1z1UctSa6i8zHkXc5W9otqtnDSHw8=
|
||||
github.com/stefansundin/go-zflag v1.1.1/go.mod h1:HXX5rABl1AoTcZ2jw+CqJ7R8irczaLquGNZlFabZooc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
|
@ -226,6 +228,7 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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=
|
||||
|
@ -254,6 +257,7 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
|
|||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
|
||||
|
@ -261,6 +265,7 @@ golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4
|
|||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
|
@ -303,6 +308,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
|
||||
gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
|
4
internal/helpers/docs.go
Normal file
4
internal/helpers/docs.go
Normal file
|
@ -0,0 +1,4 @@
|
|||
/*
|
||||
Useful structs used everywhere I couldn't find a better place to shove
|
||||
*/
|
||||
package helpers
|
23
internal/helpers/query.go
Normal file
23
internal/helpers/query.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// A structure for a DNS query.
|
||||
type Request struct {
|
||||
Server string `json:"server"` // The server to make the DNS request from
|
||||
Type uint16 `json:"request"` // The type of request
|
||||
Class uint16 `json:"class"` // DNS Class
|
||||
Name string `json:"name"` // The domain name to make a DNS request for
|
||||
}
|
13
internal/helpers/query_test.go
Normal file
13
internal/helpers/query_test.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package helpers_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestNothing(t *testing.T) {
|
||||
assert.Equal(t, 0, 0)
|
||||
}
|
|
@ -20,7 +20,7 @@ because awl is a cli utility it writes directly to std err.
|
|||
//
|
||||
// Logger.SetLevel(3)
|
||||
// This allows you to change the default level (Info) and prevent log messages from being posted at higher verbosity levels
|
||||
//for example if
|
||||
// for example if
|
||||
// Logger.SetLevel(3)
|
||||
// is not called and you call
|
||||
// Logger.Debug()
|
||||
|
|
|
@ -9,32 +9,34 @@ import (
|
|||
"sync/atomic"
|
||||
)
|
||||
|
||||
type Level int32
|
||||
type Logger struct {
|
||||
Mu sync.Mutex
|
||||
Level Level
|
||||
Prefix string
|
||||
Out io.Writer
|
||||
buf []byte
|
||||
isDiscard int32
|
||||
}
|
||||
type (
|
||||
Level int32
|
||||
Logger struct {
|
||||
Mu sync.Mutex
|
||||
Level Level
|
||||
Prefix string
|
||||
Out io.Writer
|
||||
buf []byte
|
||||
isDiscard int32
|
||||
}
|
||||
)
|
||||
|
||||
// Stores whatever input value is in mem address of l.level
|
||||
// Stores whatever input value is in mem address of l.level.
|
||||
func (l *Logger) SetLevel(level Level) {
|
||||
atomic.StoreInt32((*int32)(&l.Level), int32(level))
|
||||
}
|
||||
|
||||
// Mostly nothing
|
||||
// Mostly nothing.
|
||||
func (l *Logger) GetLevel() Level {
|
||||
return l.level()
|
||||
}
|
||||
|
||||
// Retrieves whatever was stored in mem address of l.level
|
||||
// Retrieves whatever was stored in mem address of l.level.
|
||||
func (l *Logger) level() Level {
|
||||
return Level(atomic.LoadInt32((*int32)(&l.Level)))
|
||||
}
|
||||
|
||||
// Unmarshalls the int value of level for writing the header
|
||||
// Unmarshalls the int value of level for writing the header.
|
||||
func (l *Logger) UnMarshalLevel(lv Level) (string, error) {
|
||||
switch lv {
|
||||
case 0:
|
||||
|
@ -61,13 +63,13 @@ var AllLevels = []Level{
|
|||
}
|
||||
|
||||
const (
|
||||
// Fatal logs (will call exit(1))
|
||||
// Fatal logs (will call exit(1)).
|
||||
FatalLevel Level = iota
|
||||
|
||||
// Error logs
|
||||
// Error logs.
|
||||
ErrorLevel
|
||||
|
||||
// What is going on level
|
||||
// What is going on level.
|
||||
InfoLevel
|
||||
// Verbose log level.
|
||||
DebugLevel
|
||||
|
|
|
@ -30,23 +30,27 @@ func (l *Logger) Println(level Level, v ...any) {
|
|||
case 0:
|
||||
err := l.Printer(0, fmt.Sprintln(v...)) //Fatal level
|
||||
if err != nil {
|
||||
// TODO: Make this not a panic
|
||||
panic(err)
|
||||
}
|
||||
os.Exit(1)
|
||||
case 1:
|
||||
err := l.Printer(1, fmt.Sprintln(v...)) //Error level
|
||||
if err != nil {
|
||||
// TODO: Make this not a panic
|
||||
panic(err)
|
||||
}
|
||||
os.Exit(2)
|
||||
case 2:
|
||||
err := l.Printer(2, fmt.Sprintln(v...)) //Info level
|
||||
if err != nil {
|
||||
// TODO: Make this not a panic
|
||||
panic(err)
|
||||
}
|
||||
case 3:
|
||||
err := l.Printer(3, fmt.Sprintln(v...)) //Debug level
|
||||
if err != nil {
|
||||
// TODO: Make this not a panic
|
||||
panic(err)
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
"git.froth.zone/sam/awl/logawl"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
var logger = logawl.New()
|
||||
|
@ -17,35 +17,29 @@ var logger = logawl.New()
|
|||
func TestLogawl(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert.Equal(t, logawl.Level(2), logger.Level) //cast 2 (int) to 2 (level)
|
||||
|
||||
//Validate setting and getting levels from memory works
|
||||
for i := range logawl.AllLevels {
|
||||
logger.SetLevel(logawl.Level(i))
|
||||
assert.Equal(t, logawl.Level(i), logger.GetLevel())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestUnmarshalLevels(t *testing.T) {
|
||||
t.Parallel()
|
||||
m := make(map[int]string)
|
||||
var err error
|
||||
//Fill map with unmarshalled level info
|
||||
|
||||
for i := range logawl.AllLevels {
|
||||
m[i], err = logger.UnMarshalLevel(logawl.Level(i))
|
||||
assert.Nil(t, err)
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
//iterate over map and assert equal
|
||||
for i := range logawl.AllLevels {
|
||||
lv, err := logger.UnMarshalLevel(logawl.Level(i))
|
||||
assert.Nil(t, err)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, m[i], lv)
|
||||
}
|
||||
|
||||
lv, err := logger.UnMarshalLevel(logawl.Level(9001))
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, "", lv)
|
||||
assert.ErrorContains(t, err, "invalid log level choice")
|
||||
}
|
||||
|
@ -79,13 +73,12 @@ func TestLogger(t *testing.T) {
|
|||
fn()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestFmt(t *testing.T) {
|
||||
t.Parallel()
|
||||
ti := time.Now()
|
||||
test := []byte("test")
|
||||
assert.NotNil(t, logger.FormatHeader(&test, ti, 0, logawl.Level(9001))) //make sure error is error
|
||||
assert.ErrorContains(t, logger.FormatHeader(&test, ti, 0, 9001), "invalid log level") //make sure error is error
|
||||
|
||||
}
|
||||
|
|
6
main.go
6
main.go
|
@ -5,6 +5,7 @@ package main
|
|||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
@ -21,6 +22,10 @@ var version = "DEV"
|
|||
func main() {
|
||||
opts, err := cli.ParseCLI(version)
|
||||
if err != nil {
|
||||
// TODO: Make not ew
|
||||
if errors.Is(err, cli.ErrNotError) || strings.Contains(err.Error(), "help requested") {
|
||||
os.Exit(0)
|
||||
}
|
||||
opts.Logger.Fatal(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
@ -67,5 +72,4 @@ func main() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"time"
|
||||
|
||||
"git.froth.zone/sam/awl/cli"
|
||||
"git.froth.zone/sam/awl/internal/structs"
|
||||
"git.froth.zone/sam/awl/internal/helpers"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
|
@ -19,18 +19,18 @@ type HTTPSResolver struct {
|
|||
opts cli.Options
|
||||
}
|
||||
|
||||
func (r *HTTPSResolver) LookUp(msg *dns.Msg) (structs.Response, error) {
|
||||
var resp structs.Response
|
||||
func (r *HTTPSResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
|
||||
var resp helpers.Response
|
||||
httpR := &http.Client{}
|
||||
buf, err := msg.Pack()
|
||||
if err != nil {
|
||||
return structs.Response{}, err
|
||||
return helpers.Response{}, err
|
||||
}
|
||||
r.opts.Logger.Debug("making DoH request")
|
||||
// query := server + "?dns=" + base64.RawURLEncoding.EncodeToString(buf)
|
||||
req, err := http.NewRequest("POST", r.server, bytes.NewBuffer(buf))
|
||||
if err != nil {
|
||||
return structs.Response{}, fmt.Errorf("DoH: %s", err.Error())
|
||||
return helpers.Response{}, fmt.Errorf("DoH: %s", err.Error())
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/dns-message")
|
||||
req.Header.Set("Accept", "application/dns-message")
|
||||
|
@ -40,23 +40,23 @@ func (r *HTTPSResolver) LookUp(msg *dns.Msg) (structs.Response, error) {
|
|||
resp.RTT = time.Since(now)
|
||||
|
||||
if err != nil {
|
||||
return structs.Response{}, fmt.Errorf("DoH HTTP request error: %s", err.Error())
|
||||
return helpers.Response{}, fmt.Errorf("DoH HTTP request error: %s", err.Error())
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return structs.Response{}, fmt.Errorf("DoH server responded with HTTP %d", res.StatusCode)
|
||||
return helpers.Response{}, fmt.Errorf("DoH server responded with HTTP %d", res.StatusCode)
|
||||
}
|
||||
|
||||
fullRes, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return structs.Response{}, fmt.Errorf("DoH body read error: %s", err.Error())
|
||||
return helpers.Response{}, fmt.Errorf("DoH body read error: %s", err.Error())
|
||||
}
|
||||
resp.DNS = &dns.Msg{}
|
||||
r.opts.Logger.Debug("unpacking response")
|
||||
err = resp.DNS.Unpack(fullRes)
|
||||
if err != nil {
|
||||
return structs.Response{}, fmt.Errorf("DoH dns message unpack error: %s", err.Error())
|
||||
return helpers.Response{}, fmt.Errorf("DoH dns message unpack error: %s", err.Error())
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
|
|
|
@ -8,12 +8,12 @@ import (
|
|||
"testing"
|
||||
|
||||
"git.froth.zone/sam/awl/cli"
|
||||
"git.froth.zone/sam/awl/internal/structs"
|
||||
"git.froth.zone/sam/awl/internal/helpers"
|
||||
"git.froth.zone/sam/awl/query"
|
||||
"git.froth.zone/sam/awl/util"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestResolveHTTPS(t *testing.T) {
|
||||
|
@ -23,9 +23,9 @@ func TestResolveHTTPS(t *testing.T) {
|
|||
HTTPS: true,
|
||||
Logger: util.InitLogger(0),
|
||||
}
|
||||
testCase := structs.Request{Server: "dns9.quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone"}
|
||||
testCase := helpers.Request{Server: "dns9.quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone"}
|
||||
resolver, err := query.LoadResolver(testCase.Server, opts)
|
||||
assert.Nil(t, err)
|
||||
assert.NilError(t, err)
|
||||
|
||||
if !strings.HasPrefix(testCase.Server, "https://") {
|
||||
testCase.Server = "https://" + testCase.Server
|
||||
|
@ -37,11 +37,10 @@ func TestResolveHTTPS(t *testing.T) {
|
|||
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(testCase.Name, testCase.Type)
|
||||
msg = msg.SetQuestion(testCase.Name, testCase.Type)
|
||||
// msg = msg.SetQuestion(testCase.Name, testCase.Type)
|
||||
res, err := resolver.LookUp(msg)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, res)
|
||||
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, res != helpers.Response{})
|
||||
}
|
||||
|
||||
func Test2ResolveHTTPS(t *testing.T) {
|
||||
|
@ -51,17 +50,17 @@ func Test2ResolveHTTPS(t *testing.T) {
|
|||
Logger: util.InitLogger(0),
|
||||
}
|
||||
var err error
|
||||
testCase := structs.Request{Server: "dns9.quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone"}
|
||||
testCase := helpers.Request{Server: "dns9.quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone"}
|
||||
resolver, err := query.LoadResolver(testCase.Server, opts)
|
||||
assert.Nil(t, err)
|
||||
assert.NilError(t, err)
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(testCase.Name, testCase.Type)
|
||||
msg = msg.SetQuestion(testCase.Name, testCase.Type)
|
||||
// msg = msg.SetQuestion(testCase.Name, testCase.Type)
|
||||
res, err := resolver.LookUp(msg)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, res, structs.Response{})
|
||||
|
||||
assert.ErrorContains(t, err, "fully qualified")
|
||||
assert.Equal(t, res, helpers.Response{})
|
||||
}
|
||||
|
||||
func Test3ResolveHTTPS(t *testing.T) {
|
||||
t.Parallel()
|
||||
opts := cli.Options{
|
||||
|
@ -69,7 +68,7 @@ func Test3ResolveHTTPS(t *testing.T) {
|
|||
Logger: util.InitLogger(0),
|
||||
}
|
||||
var err error
|
||||
testCase := structs.Request{Server: "dns9..quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone."}
|
||||
testCase := helpers.Request{Server: "dns9..quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone."}
|
||||
if !strings.HasPrefix(testCase.Server, "https://") {
|
||||
testCase.Server = "https://" + testCase.Server
|
||||
}
|
||||
|
@ -78,12 +77,11 @@ func Test3ResolveHTTPS(t *testing.T) {
|
|||
testCase.Name = fmt.Sprintf("%s.", testCase.Name)
|
||||
}
|
||||
resolver, err := query.LoadResolver(testCase.Server, opts)
|
||||
assert.Nil(t, err)
|
||||
assert.NilError(t, err)
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(testCase.Name, testCase.Type)
|
||||
msg = msg.SetQuestion(testCase.Name, testCase.Type)
|
||||
// msg = msg.SetQuestion(testCase.Name, testCase.Type)
|
||||
res, err := resolver.LookUp(msg)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, res, structs.Response{})
|
||||
|
||||
assert.ErrorContains(t, err, "request error")
|
||||
assert.Equal(t, res, helpers.Response{})
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"time"
|
||||
|
||||
"git.froth.zone/sam/awl/cli"
|
||||
"git.froth.zone/sam/awl/internal/structs"
|
||||
"git.froth.zone/sam/awl/internal/helpers"
|
||||
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/miekg/dns"
|
||||
|
@ -19,8 +19,8 @@ type QUICResolver struct {
|
|||
opts cli.Options
|
||||
}
|
||||
|
||||
func (r *QUICResolver) LookUp(msg *dns.Msg) (structs.Response, error) {
|
||||
var resp structs.Response
|
||||
func (r *QUICResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
|
||||
var resp helpers.Response
|
||||
tls := &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
NextProtos: []string{"doq"},
|
||||
|
@ -28,46 +28,46 @@ func (r *QUICResolver) LookUp(msg *dns.Msg) (structs.Response, error) {
|
|||
r.opts.Logger.Debug("making DoQ request")
|
||||
connection, err := quic.DialAddr(r.server, tls, nil)
|
||||
if err != nil {
|
||||
return structs.Response{}, err
|
||||
return helpers.Response{}, err
|
||||
}
|
||||
|
||||
// Compress request to over-the-wire
|
||||
buf, err := msg.Pack()
|
||||
if err != nil {
|
||||
return structs.Response{}, err
|
||||
return helpers.Response{}, err
|
||||
}
|
||||
t := time.Now()
|
||||
stream, err := connection.OpenStream()
|
||||
if err != nil {
|
||||
return structs.Response{}, err
|
||||
return helpers.Response{}, err
|
||||
}
|
||||
_, err = stream.Write(buf)
|
||||
if err != nil {
|
||||
return structs.Response{}, err
|
||||
return helpers.Response{}, err
|
||||
}
|
||||
|
||||
fullRes, err := io.ReadAll(stream)
|
||||
if err != nil {
|
||||
return structs.Response{}, err
|
||||
return helpers.Response{}, err
|
||||
}
|
||||
resp.RTT = time.Since(t)
|
||||
|
||||
// Close with error: no error
|
||||
err = connection.CloseWithError(0, "")
|
||||
if err != nil {
|
||||
return structs.Response{}, err
|
||||
return helpers.Response{}, err
|
||||
}
|
||||
|
||||
err = stream.Close()
|
||||
if err != nil {
|
||||
return structs.Response{}, err
|
||||
return helpers.Response{}, err
|
||||
}
|
||||
|
||||
resp.DNS = &dns.Msg{}
|
||||
r.opts.Logger.Debug("unpacking DoQ response")
|
||||
err = resp.DNS.Unpack(fullRes)
|
||||
if err != nil {
|
||||
return structs.Response{}, err
|
||||
return helpers.Response{}, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
|
|
@ -10,12 +10,12 @@ import (
|
|||
"testing"
|
||||
|
||||
"git.froth.zone/sam/awl/cli"
|
||||
"git.froth.zone/sam/awl/internal/structs"
|
||||
"git.froth.zone/sam/awl/internal/helpers"
|
||||
"git.froth.zone/sam/awl/query"
|
||||
"git.froth.zone/sam/awl/util"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestQuic(t *testing.T) {
|
||||
|
@ -24,18 +24,18 @@ func TestQuic(t *testing.T) {
|
|||
QUIC: true,
|
||||
Logger: util.InitLogger(0),
|
||||
Port: 853,
|
||||
Request: structs.Request{Server: "dns.adguard.com"},
|
||||
Request: helpers.Request{Server: "dns.adguard.com"},
|
||||
}
|
||||
testCase := structs.Request{Server: "dns.//./,,adguard.com", Type: dns.TypeA, Name: "git.froth.zone"}
|
||||
testCase2 := structs.Request{Server: "dns.adguard.com", Type: dns.TypeA, Name: "git.froth.zone"}
|
||||
var testCases []structs.Request
|
||||
testCase := helpers.Request{Server: "dns.//./,,adguard.com", Type: dns.TypeA, Name: "git.froth.zone"}
|
||||
testCase2 := helpers.Request{Server: "dns.adguard.com", Type: dns.TypeA, Name: "git.froth.zone"}
|
||||
var testCases []helpers.Request
|
||||
testCases = append(testCases, testCase)
|
||||
testCases = append(testCases, testCase2)
|
||||
for i := range testCases {
|
||||
switch i {
|
||||
case 0:
|
||||
resolver, err := query.LoadResolver(testCases[i].Server, opts)
|
||||
assert.Nil(t, err)
|
||||
assert.NilError(t, err)
|
||||
// if the domain is not canonical, make it canonical
|
||||
if !strings.HasSuffix(testCase.Name, ".") {
|
||||
testCases[i].Name = fmt.Sprintf("%s.", testCases[i].Name)
|
||||
|
@ -44,11 +44,11 @@ func TestQuic(t *testing.T) {
|
|||
msg.SetQuestion(testCase.Name, testCase.Type)
|
||||
msg = msg.SetQuestion(testCase.Name, testCase.Type)
|
||||
res, err := resolver.LookUp(msg)
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, res, structs.Response{})
|
||||
assert.ErrorContains(t, err, "fully qualified")
|
||||
assert.Equal(t, res, helpers.Response{})
|
||||
case 1:
|
||||
resolver, err := query.LoadResolver(testCase2.Server, opts)
|
||||
assert.Nil(t, err)
|
||||
assert.NilError(t, err)
|
||||
testCase2.Server = net.JoinHostPort(testCase2.Server, strconv.Itoa(opts.Port))
|
||||
// if the domain is not canonical, make it canonical
|
||||
if !strings.HasSuffix(testCase2.Name, ".") {
|
||||
|
@ -58,10 +58,8 @@ func TestQuic(t *testing.T) {
|
|||
msg.SetQuestion(testCase2.Name, testCase2.Type)
|
||||
msg = msg.SetQuestion(testCase2.Name, testCase2.Type)
|
||||
res, err := resolver.LookUp(msg)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, res)
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, res != helpers.Response{})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"git.froth.zone/sam/awl/cli"
|
||||
"git.froth.zone/sam/awl/internal/structs"
|
||||
"git.froth.zone/sam/awl/internal/helpers"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
@ -16,9 +16,9 @@ type StandardResolver struct {
|
|||
opts cli.Options
|
||||
}
|
||||
|
||||
func (r *StandardResolver) LookUp(msg *dns.Msg) (structs.Response, error) {
|
||||
func (r *StandardResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
|
||||
var (
|
||||
resp structs.Response
|
||||
resp helpers.Response
|
||||
err error
|
||||
)
|
||||
dnsClient := new(dns.Client)
|
||||
|
@ -41,7 +41,7 @@ func (r *StandardResolver) LookUp(msg *dns.Msg) (structs.Response, error) {
|
|||
|
||||
resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, r.server)
|
||||
if err != nil {
|
||||
return structs.Response{}, err
|
||||
return helpers.Response{}, err
|
||||
}
|
||||
|
||||
if resp.DNS.MsgHdr.Truncated && !r.opts.Truncate {
|
||||
|
|
54
query/general_test.go
Normal file
54
query/general_test.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package query_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.froth.zone/sam/awl/cli"
|
||||
"git.froth.zone/sam/awl/internal/helpers"
|
||||
"git.froth.zone/sam/awl/query"
|
||||
"git.froth.zone/sam/awl/util"
|
||||
"github.com/miekg/dns"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestResolve(t *testing.T) {
|
||||
t.Parallel()
|
||||
opts := cli.Options{
|
||||
Logger: util.InitLogger(0),
|
||||
Port: 53,
|
||||
Request: helpers.Request{
|
||||
Server: "8.8.4.4",
|
||||
Type: dns.TypeA,
|
||||
Name: "example.com.",
|
||||
},
|
||||
}
|
||||
resolver, err := query.LoadResolver(opts.Request.Server, opts)
|
||||
assert.NilError(t, err)
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(opts.Request.Name, opts.Request.Type)
|
||||
res, err := resolver.LookUp(msg)
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, res != helpers.Response{})
|
||||
}
|
||||
|
||||
func TestTruncate(t *testing.T) {
|
||||
t.Parallel()
|
||||
opts := cli.Options{
|
||||
Logger: util.InitLogger(0),
|
||||
Port: 5301,
|
||||
Request: helpers.Request{
|
||||
Server: "madns.binarystar.systems",
|
||||
Type: dns.TypeTXT,
|
||||
Name: "limit.txt.example.",
|
||||
},
|
||||
}
|
||||
resolver, err := query.LoadResolver(opts.Request.Server, opts)
|
||||
assert.NilError(t, err)
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(opts.Request.Name, opts.Request.Type)
|
||||
res, err := resolver.LookUp(msg)
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, res != helpers.Response{})
|
||||
}
|
|
@ -1,16 +1,19 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package query
|
||||
|
||||
import (
|
||||
"git.froth.zone/sam/awl/cli"
|
||||
"git.froth.zone/sam/awl/internal/structs"
|
||||
"git.froth.zone/sam/awl/internal/helpers"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func CreateQuery(opts cli.Options) (structs.Response, error) {
|
||||
var res structs.Response
|
||||
func CreateQuery(opts cli.Options) (helpers.Response, error) {
|
||||
var res helpers.Response
|
||||
res.DNS = new(dns.Msg)
|
||||
res.DNS.SetQuestion(opts.Query, opts.Type)
|
||||
res.DNS.SetQuestion(opts.Request.Name, opts.Request.Type)
|
||||
res.DNS.Question[0].Qclass = opts.Request.Class
|
||||
|
||||
res.DNS.MsgHdr.Response = opts.QR
|
||||
res.DNS.MsgHdr.Authoritative = opts.AA
|
||||
|
@ -27,7 +30,7 @@ func CreateQuery(opts cli.Options) (structs.Response, error) {
|
|||
|
||||
resolver, err := LoadResolver(opts.Request.Server, opts)
|
||||
if err != nil {
|
||||
return structs.Response{}, err
|
||||
return helpers.Response{}, err
|
||||
}
|
||||
|
||||
return resolver.LookUp(res.DNS)
|
||||
|
|
34
query/query_test.go
Normal file
34
query/query_test.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package query_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.froth.zone/sam/awl/cli"
|
||||
"git.froth.zone/sam/awl/internal/helpers"
|
||||
"git.froth.zone/sam/awl/query"
|
||||
"git.froth.zone/sam/awl/util"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestCreateQ(t *testing.T) {
|
||||
opts := cli.Options{
|
||||
Logger: util.InitLogger(0),
|
||||
Port: 53,
|
||||
QR: false,
|
||||
Z: true,
|
||||
RD: false,
|
||||
DNSSEC: true,
|
||||
Request: helpers.Request{
|
||||
Server: "8.8.4.4",
|
||||
Type: dns.TypeA,
|
||||
Name: "example.com.",
|
||||
},
|
||||
}
|
||||
res, err := query.CreateQuery(opts)
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, res != helpers.Response{})
|
||||
}
|
|
@ -8,12 +8,12 @@ import (
|
|||
"strings"
|
||||
|
||||
"git.froth.zone/sam/awl/cli"
|
||||
"git.froth.zone/sam/awl/internal/structs"
|
||||
"git.froth.zone/sam/awl/internal/helpers"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type Resolver interface {
|
||||
LookUp(*dns.Msg) (structs.Response, error)
|
||||
LookUp(*dns.Msg) (helpers.Response, error)
|
||||
}
|
||||
|
||||
func LoadResolver(server string, opts cli.Options) (Resolver, error) {
|
||||
|
|
|
@ -4,7 +4,7 @@ package util
|
|||
|
||||
import "git.froth.zone/sam/awl/logawl"
|
||||
|
||||
// Initialize the logawl instance
|
||||
// Initialize the logawl instance.
|
||||
func InitLogger(verbosity int) (Logger *logawl.Logger) {
|
||||
Logger = logawl.New()
|
||||
|
||||
|
|
16
util/logger_test.go
Normal file
16
util/logger_test.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package util_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.froth.zone/sam/awl/logawl"
|
||||
"git.froth.zone/sam/awl/util"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestInitLogger(t *testing.T) {
|
||||
logger := util.InitLogger(0)
|
||||
assert.Equal(t, logger.Level, logawl.Level(0))
|
||||
}
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// Given an IP or phone number, return a canonical string to be queried
|
||||
// Given an IP or phone number, return a canonical string to be queried.
|
||||
func ReverseDNS(address string, querInt uint16) (string, error) {
|
||||
query := dns.TypeToString[querInt]
|
||||
if query == "PTR" {
|
||||
|
@ -33,11 +33,10 @@ func ReverseDNS(address string, querInt uint16) (string, error) {
|
|||
return "", errors.New("ReverseDNS: -x flag given but no IP found")
|
||||
}
|
||||
|
||||
// Reverse a string, return the string in reverse
|
||||
// Reverse a string, return the string in reverse.
|
||||
func reverse(s string) string {
|
||||
rns := []rune(s)
|
||||
for i, j := 0, len(rns)-1; i < j; i, j = i+1, j-1 {
|
||||
|
||||
rns[i], rns[j] = rns[j], rns[i]
|
||||
}
|
||||
return string(rns)
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package util
|
||||
package util_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.froth.zone/sam/awl/util"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -16,15 +18,15 @@ var (
|
|||
|
||||
func TestIPv4(t *testing.T) {
|
||||
t.Parallel()
|
||||
act, err := ReverseDNS("8.8.4.4", PTR)
|
||||
assert.Nil(t, err)
|
||||
act, err := util.ReverseDNS("8.8.4.4", PTR)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, act, "4.4.8.8.in-addr.arpa.", "IPv4 reverse")
|
||||
}
|
||||
|
||||
func TestIPv6(t *testing.T) {
|
||||
t.Parallel()
|
||||
act, err := ReverseDNS("2606:4700:4700::1111", PTR)
|
||||
assert.Nil(t, err)
|
||||
act, err := util.ReverseDNS("2606:4700:4700::1111", PTR)
|
||||
assert.NilError(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")
|
||||
}
|
||||
|
||||
|
@ -40,14 +42,19 @@ func TestNAPTR(t *testing.T) {
|
|||
{"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)
|
||||
// Thanks Goroutines, very cool!
|
||||
test := test
|
||||
t.Run(test.in, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
act, err := util.ReverseDNS(test.in, NAPTR)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, test.want, act)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalid(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := ReverseDNS("AAAAA", 1)
|
||||
assert.Error(t, err)
|
||||
_, err := util.ReverseDNS("AAAAA", 1)
|
||||
assert.ErrorContains(t, err, "no IP found")
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue