Minor (complete) refactor #38

Merged
sam merged 20 commits from small-things into master 2022-07-26 00:32:32 +00:00
38 changed files with 757 additions and 294 deletions
Showing only changes of commit 92812c337f - Show all commits

View file

@ -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

View file

@ -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 {

View file

@ -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 {

View file

@ -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
View 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")
}
})
}

View file

@ -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
View 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)
})
}

View file

@ -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")

View file

@ -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'
}

View file

@ -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)
}

View file

@ -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
View 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
View 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
View file

@ -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

View file

@ -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
View file

@ -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
View file

@ -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
View 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
View 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
}

View 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)
}

View file

@ -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()

View file

@ -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

View file

@ -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:

View file

@ -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
}

View file

@ -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() {
}
}
}
}

View file

@ -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

View file

@ -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{})
}

View file

@ -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
}

View file

@ -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{})
}
}
}

View file

@ -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
View 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{})
}

View file

@ -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
View 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{})
}

View file

@ -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) {

View file

@ -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
View 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))
}

View file

@ -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)

View file

@ -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")
}