another refactor :^)
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing

Signed-off-by: Sam Therapy <sam@samtherapy.net>
This commit is contained in:
Sam Therapy 2022-08-04 01:29:20 +02:00
parent 9e951b84c2
commit 49ea09a251
Signed by: sam
GPG key ID: 4D8B07C18F31ACBD
38 changed files with 751 additions and 506 deletions

View file

@ -1,124 +0,0 @@
---
{
"kind": "pipeline",
"name": "1.18-amd64",
"platform": {
"arch": "amd64"
},
"steps": [
{
"commands": [
"git submodule update --init --recursive"
],
"image": "alpine/git",
"name": "submodules"
},
{
"depends_on": [
"submodules"
],
"image": "rancher/drone-golangci-lint:latest",
"name": "lint"
},
{
"commands": [
"go test -v -race ./... -cover"
],
"depends_on": [
"submodules"
],
"image": "golang:1.18",
"name": "test"
}
],
"trigger": {
"event": {
"exclude": [
"tag"
]
}
},
"type": "docker"
}
---
{
"kind": "pipeline",
"name": "1.18-arm64",
"platform": {
"arch": "arm64"
},
"steps": [
{
"commands": [
"git submodule update --init --recursive"
],
"image": "alpine/git",
"name": "submodules"
},
{
"depends_on": [
"submodules"
],
"image": "rancher/drone-golangci-lint:latest",
"name": "lint"
},
{
"commands": [
"go test -v -race ./... -cover"
],
"depends_on": [
"submodules"
],
"image": "golang:1.18",
"name": "test"
}
],
"trigger": {
"event": {
"exclude": [
"tag"
]
}
},
"type": "docker"
}
---
{
"kind": "pipeline",
"name": "release",
"steps": [
{
"commands": [
"git fetch --tags",
"git submodule update --init --recursive"
],
"image": "alpine/git",
"name": "fetch"
},
{
"commands": [
"go test -race ./... -cover"
],
"image": "golang",
"name": "test"
},
{
"commands": [
"goreleaser release"
],
"environment": {
"GITEA_TOKEN": {
"from_secret": "GITEA_TOKEN"
}
},
"image": "goreleaser/goreleaser",
"name": "release"
}
],
"trigger": {
"event": [
"tag"
]
},
"type": "docker"
}

View file

@ -4,7 +4,6 @@ package cli
import (
"fmt"
"net"
"os"
"runtime"
"strconv"
@ -13,12 +12,12 @@ import (
"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.
// ParseCLI parses arguments given from the CLI and passes them into an `Options`
// struct.
func ParseCLI(version string) (Options, error) {
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
@ -89,7 +88,7 @@ func ParseCLI(version string) (Options, error) {
noOpt = flag.Bool("no-opt", false, "disable printing the OPT pseudosection")
noAns = flag.Bool("no-answer", false, "disable printing the answer section")
noAuth = flag.Bool("no-authority", false, "disable printing the authority section")
noAdd = flag.Bool("no-additional", false, "disable printing the additonal section")
noAdd = flag.Bool("no-additional", false, "disable printing the additional section")
noStats = flag.Bool("no-statistics", false, "disable printing the statistics section")
verbosity = flag.Int("verbosity", 1, "sets verbosity `level`", flag.OptShorthand('v'), flag.OptNoOptDefVal("2"))
@ -102,13 +101,13 @@ func ParseCLI(version string) (Options, error) {
// Parse the flags
err := flag.CommandLine.Parse(os.Args[1:])
if err != nil {
return Options{Logger: util.InitLogger(*verbosity)}, err
return Options{Logger: util.InitLogger(*verbosity)}, fmt.Errorf("flag parse error: %w", err)
}
// TODO: DRY, dumb dumb.
mbz, err := strconv.ParseInt(*zflag, 0, 16)
if err != nil {
return Options{Logger: util.InitLogger(*verbosity)}, err
return Options{Logger: util.InitLogger(*verbosity)}, fmt.Errorf("EDNS MBZ error: %w", err)
}
opts := Options{
@ -170,33 +169,9 @@ func ParseCLI(version string) (Options, error) {
// TODO: DRY
if *subnet != "" {
ip, inet, err := net.ParseCIDR(*subnet)
err := parseSubnet(*subnet, &opts)
if err != nil {
// TODO: make not a default?
if *subnet == "0" {
opts.EDNS.Subnet = dns.EDNS0_SUBNET{
Code: dns.EDNS0SUBNET,
Family: 1,
SourceNetmask: 0,
SourceScope: 0,
Address: net.IPv4(0, 0, 0, 0),
}
} else {
return Options{Logger: util.InitLogger(*verbosity)}, err
}
} else {
sub, _ := inet.Mask.Size()
opts.EDNS.Subnet = *new(dns.EDNS0_SUBNET)
opts.EDNS.Subnet.Address = ip
opts.EDNS.Subnet.SourceNetmask = uint8(sub)
switch ip.To4() {
case nil:
// Not a valid IPv4 so assume IPv6
opts.EDNS.Subnet.Family = 2
default:
// Valid IPv4
opts.EDNS.Subnet.Family = 1
}
return opts, err
}
}

View file

@ -11,6 +11,7 @@ import (
"gotest.tools/v3/assert"
)
// nolint: paralleltest
func TestEmpty(t *testing.T) {
old := os.Args
os.Args = []string{"awl", "-4"}
@ -21,6 +22,7 @@ func TestEmpty(t *testing.T) {
os.Args = old
}
// nolint: paralleltest
func TestTLSPort(t *testing.T) {
old := os.Args
os.Args = []string{"awl", "-T"}
@ -30,6 +32,7 @@ func TestTLSPort(t *testing.T) {
os.Args = old
}
// nolint: paralleltest
func TestSubnet(t *testing.T) {
old := os.Args
os.Args = []string{"awl", "--subnet", "127.0.0.1/32"}
@ -50,6 +53,7 @@ func TestSubnet(t *testing.T) {
os.Args = old
}
// nolint: paralleltest
func TestInvalidFlag(t *testing.T) {
old := os.Args
os.Args = []string{"awl", "--treebug"}
@ -58,14 +62,16 @@ func TestInvalidFlag(t *testing.T) {
os.Args = old
}
// nolint: paralleltest
func TestInvalidDig(t *testing.T) {
old := os.Args
os.Args = []string{"awl", "+a"}
_, err := cli.ParseCLI("TEST")
assert.ErrorContains(t, err, "digflags: unknown flag")
assert.ErrorContains(t, err, "digflags: invalid argument")
os.Args = old
}
// nolint: paralleltest
func TestVersion(t *testing.T) {
old := os.Args
os.Args = []string{"awl", "--version"}
@ -74,6 +80,7 @@ func TestVersion(t *testing.T) {
os.Args = old
}
// nolint: paralleltest
func TestTimeout(t *testing.T) {
args := [][]string{
{"awl", "+timeout=0"},
@ -89,6 +96,7 @@ func TestTimeout(t *testing.T) {
}
}
// nolint: paralleltest
func TestRetries(t *testing.T) {
args := [][]string{
{"awl", "+retry=-2"},
@ -105,6 +113,7 @@ func TestRetries(t *testing.T) {
}
}
// nolint: paralleltest
func FuzzFlags(f *testing.F) {
testcases := []string{"git.froth.zone", "", "!12345", "google.com.edu.org.fr"}
for _, tc := range testcases {

View file

@ -4,15 +4,15 @@ package cli
import (
"fmt"
"net"
"strconv"
"strings"
"time"
"github.com/miekg/dns"
)
// Parse dig-like commands and set the options as such.
// ParseDig parses commands from the popular DNS tool dig.
// All dig commands are taken from https://man.openbsd.org/dig.1 as the source of their functionality.
//
// [no]flags are supported just as flag are and are disabled as such.
func ParseDig(arg string, opts *Options) error {
// returns true if the flag starts with a no
isNo := !strings.HasPrefix(arg, "no")
@ -121,11 +121,9 @@ func ParseDig(arg string, opts *Options) error {
// Recursive switch statements WOO
arg := strings.Split(arg, "=")
switch arg[0] {
case "time", "timeout":
if len(arg) > 1 && arg[1] != "" {
timeout, err := strconv.Atoi(arg[1])
if err != nil {
return fmt.Errorf("digflags: Invalid timeout value: %w", err)
}
@ -188,40 +186,16 @@ func ParseDig(arg string, opts *Options) error {
case "subnet":
if len(arg) > 1 && arg[1] != "" {
ip, inet, err := net.ParseCIDR(arg[1])
err := parseSubnet(arg[1], opts)
if err != nil {
// TODO: make not a default?
if arg[1] == "0" {
opts.EDNS.Subnet = dns.EDNS0_SUBNET{
Code: dns.EDNS0SUBNET,
Family: 1,
SourceNetmask: 0,
SourceScope: 0,
Address: net.IPv4(0, 0, 0, 0),
}
return nil
} else {
return fmt.Errorf("digflags: Invalid EDNS Subnet: %w", errNoArg)
}
}
subnet, _ := inet.Mask.Size()
opts.EDNS.Subnet = *new(dns.EDNS0_SUBNET)
opts.EDNS.Subnet.Address = ip
opts.EDNS.Subnet.SourceNetmask = uint8(subnet)
switch ip.To4() {
case nil:
// Not a valid IPv4 so assume IPv6
opts.EDNS.Subnet.Family = 2
default:
// Valid IPv4
opts.EDNS.Subnet.Family = 1
return fmt.Errorf("digflags: Invalid EDNS Subnet: %w", err)
}
} else {
return fmt.Errorf("digflags: Invalid EDNS Subnet: %w", errNoArg)
}
default:
return fmt.Errorf("digflags: unknown flag %s given", arg)
return &errInvalidArg{arg[0]}
}
}
return nil

View file

@ -9,12 +9,12 @@ import (
"git.froth.zone/sam/awl/conf"
"git.froth.zone/sam/awl/util"
"github.com/miekg/dns"
"golang.org/x/net/idna"
)
// Parse the wildcard arguments, drill style.
// ParseMiscArgs parses the wildcard arguments, drill style.
// Only one command is supported at a time, so any extra information overrides previous.
func ParseMiscArgs(args []string, opts *Options) error {
var err error
@ -60,7 +60,7 @@ func ParseMiscArgs(args []string, opts *Options) error {
opts.Logger.Info(arg, "detected as a domain name")
opts.Request.Name, err = idna.ToASCII(arg)
if err != nil {
return err
return fmt.Errorf("punycode translate error: %w", err)
}
// DNS query type
@ -73,7 +73,7 @@ func ParseMiscArgs(args []string, opts *Options) error {
opts.Logger.Info(arg, "is unknown. Assuming domain")
opts.Request.Name, err = idna.ToASCII(arg)
if err != nil {
return err
return fmt.Errorf("punycode translate error: %w", err)
}
}
}
@ -113,21 +113,23 @@ func ParseMiscArgs(args []string, opts *Options) error {
opts.Request.Server = "95.216.99.249"
} else {
// Make sure that if IPv4 or IPv6 is asked for it actually uses it
harmful:
for _, srv := range resolv.Servers {
if opts.IPv4 {
switch {
case opts.IPv4:
if strings.Contains(srv, ".") {
opts.Request.Server = srv
break
break harmful
}
} else if opts.IPv6 {
case opts.IPv6:
if strings.Contains(srv, ":") {
opts.Request.Server = srv
break
break harmful
}
} else {
default:
//#nosec -- This isn't used for anything secure
opts.Request.Server = resolv.Servers[rand.Intn(len(resolv.Servers))]
break
break harmful
}
}
}
@ -143,7 +145,7 @@ func ParseMiscArgs(args []string, opts *Options) error {
}
opts.Request.Name, err = util.ReverseDNS(opts.Request.Name, opts.Request.Type)
if err != nil {
return err
return fmt.Errorf("reverse DNS error: %w", err)
}
}

View file

@ -8,7 +8,6 @@ import (
"git.froth.zone/sam/awl/cli"
"git.froth.zone/sam/awl/util"
"github.com/miekg/dns"
"gotest.tools/v3/assert"
)
@ -139,7 +138,6 @@ func TestFlagSetting(t *testing.T) {
assert.Assert(t, opts.HTTPS)
case 3:
assert.Assert(t, opts.QUIC)
}
})
}

View file

@ -4,10 +4,11 @@ package cli
import (
"errors"
"fmt"
"net"
"git.froth.zone/sam/awl/internal/helpers"
"git.froth.zone/sam/awl/logawl"
"github.com/miekg/dns"
)
@ -35,7 +36,7 @@ type Options struct {
Reverse bool // Make reverse query
Verbosity int // Set logawl verbosity
HumanTTL bool // Make TTL human readable
ShowTTL bool //Display TTL
ShowTTL bool // Display TTL
Short bool // Short output
Identify bool // If short, add identity stuffs
JSON bool // Outout as JSON
@ -47,7 +48,7 @@ type Options struct {
EDNS // EDNS
}
// What to (and not to) display
// What to (and not to) display.
type Displays struct {
Comments bool
Question bool // QUESTION SECTION
@ -73,6 +74,48 @@ type EDNS struct {
Subnet dns.EDNS0_SUBNET // EDNS Subnet (duh)
}
// parseSubnet takes a subnet argument and makes it into one that the DNS library
// understands.
func parseSubnet(subnet string, opts *Options) error {
ip, inet, err := net.ParseCIDR(subnet)
if err != nil {
// TODO: make not a default?
if subnet == "0" {
opts.EDNS.Subnet = dns.EDNS0_SUBNET{
Code: dns.EDNS0SUBNET,
Family: 1,
SourceNetmask: 0,
SourceScope: 0,
Address: net.IPv4(0, 0, 0, 0),
}
return nil
}
return fmt.Errorf("subnet parsing error %w", err)
}
sub, _ := inet.Mask.Size()
opts.EDNS.Subnet = dns.EDNS0_SUBNET{}
opts.EDNS.Subnet.Address = ip
opts.EDNS.Subnet.SourceNetmask = uint8(sub)
switch ip.To4() {
case nil:
// Not a valid IPv4 so assume IPv6
opts.EDNS.Subnet.Family = 2
default:
// Valid IPv4
opts.EDNS.Subnet.Family = 1
}
return nil
}
var ErrNotError = errors.New("not an error")
var errNoArg = errors.New("no argument given")
type errInvalidArg struct {
arg string
}
func (e *errInvalidArg) Error() string {
return fmt.Sprintf("digflags: invalid argument %s", e.arg)
}

View file

@ -3,7 +3,7 @@
package conf
import (
"fmt"
"errors"
"strings"
"github.com/miekg/dns"
@ -23,7 +23,7 @@ func GetPlan9Config(str string) (*dns.ClientConfig, error) {
}
}
if len(servers) == 0 {
return nil, fmt.Errorf("plan9: no DNS servers found")
return nil, errPlan9
}
// TODO: read more about how customizable Plan 9 is
@ -38,3 +38,5 @@ func GetPlan9Config(str string) (*dns.ClientConfig, error) {
func splitChars(r rune) bool {
return r == ' ' || r == '\t'
}
var errPlan9 = errors.New("plan9Config: no DNS servers found")

View file

@ -6,7 +6,6 @@ import (
"testing"
"git.froth.zone/sam/awl/conf"
"gotest.tools/v3/assert"
)

View file

@ -4,6 +4,7 @@
package conf
import (
"fmt"
"os"
"runtime"
@ -15,7 +16,7 @@ func GetDNSConfig() (*dns.ClientConfig, error) {
if runtime.GOOS == "plan9" {
dat, err := os.ReadFile("/net/ndb")
if err != nil {
return nil, err
return nil, fmt.Errorf("plan9 ndb: %w", err)
}
return GetPlan9Config(string(dat))
} else {

View file

@ -4,6 +4,7 @@
package conf
import (
"fmt"
"strings"
"unsafe"
@ -23,7 +24,7 @@ func GetDNSConfig() (*dns.ClientConfig, error) {
// Windows is an utter fucking trash fire of an operating system.
if err := windows.GetAdaptersAddresses(windows.AF_UNSPEC, windows.GAA_FLAG_INCLUDE_PREFIX, 0, (*windows.IpAdapterAddresses)(unsafe.Pointer(&b[0])), &l); err != nil {
return nil, err
return nil, fmt.Errorf("config, windows: %w", err)
}
var addresses []*windows.IpAdapterAddresses
for addr := (*windows.IpAdapterAddresses)(unsafe.Pointer(&b[0])); addr != nil; addr = addr.Next {

View file

@ -5,7 +5,7 @@
.nh
.ad l
.\" Begin generated content:
.TH "awl" "1" "2022-08-01"
.TH "awl" "1" "2022-08-03"
.PP
.SH NAME
awl - DNS lookup tool
@ -23,11 +23,11 @@ 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,
\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 leatherworking.\&
.PP
\fBawl\fR is designed to be a more "modern" version of \fIdrill\fR(1) by including
\fBawl\fR is designed to be a more "modern" version of \fIdrill\fR(1) by including
some more recent RFCs and output options.\& \fBawl\fR is still heavily
Work-In-Progress so some features may get added or removed.\&
.PP
@ -39,10 +39,6 @@ Dig-like +[no]flags are supported, see dig(1)
.br
Enable DNSSEC.\& This needs to be manually enabled.\&
.PP
\fB--qr\fR, \fB+qr\fR
.br
Print query before sending.\&
.PP
\fB-v\fR \fIvalue\fR
.br
Set verbosity (currently WIP)
@ -132,7 +128,7 @@ Dig-like +[no]flags are supported, see dig(1)
.br
0.\&5 seconds is the minimum.\&
.PP
\fB--retries\fR \fIint\fR, \fB+tries\fR=\fIint\fR, \fB+retry\fR=\fIint\fR
\fB--retries\fR \fIint\fR, \fB+tries\fR=\fIint\fR, \fB+ retry\fR=\fIint\fR
.br
Set the number of retries.\&
.br
@ -142,109 +138,61 @@ Dig-like +[no]flags are supported, see dig(1)
.SS DNS Flags
.PP
.RS 4
\fB--aa\fR[\fI=false\fR], \fB+[no]aaflag\fR
\fB--aa=[false]\fR, \fB+[no]aaflag\fR
.br
(Set, Unset) AA (Authoritative Answer) flag
.PP
\fB--ad\fR[\fI=false\fR], \fB+[no]adflag\fR
\fB--ad=[false]\fR, \fB+[no]adflag\fR
.br
(Set, Unset) AD (Authenticated Data) flag
.PP
\fB--tc\fR[\fI=false\fR], \fB+[no]tcflag\fR
\fB--tc=[false]\fR, \fB+[no]tcflag\fR
.br
(Set, Unset) TC (TrunCated) flag
.PP
\fB-z\fR[\fI=false\fR], \fB+[no]zflag\fR
\fB-z=[false]\fR, \fB+[no]zflag\fR
.br
(Set, Unset) Z (Zero) flag
.PP
\fB--cd\fR[\fI=false\fR], \fB+[no]cdflag\fR
\fB--cd=[false]\fR, \fB+[no]cdflag\fR
.br
(Set, Unset) CD (Checking Disabled) flag
.PP
\fB--qr\fR[\fI=false\fR], \fB+[no]qrflag\fR
\fB--qr=[false]\fR, \fB+[no]qrflag\fR
.br
(Set, Unset) QR (QueRy) flag
.PP
\fB--rd\fR[\fI=true\fR], \fB+[no]rdflag\fR
\fB--rd=[true]\fR, \fB+[no]rdflag\fR
.br
(Set, Unset) RD (Recursion Desired) flag
.PP
\fB--ra\fR[\fI=false\fR], \fB+[no]raflag\fR
\fB--ra=[false]\fR, \fB+[no]raflag\fR
.br
(Set, Unset) RA (Recursion Available) flag
.PP
.RE
.SS EDNS Flags
.RS 4
\fB--edns-ver\fR \fIver\fR, \fB+edns\fR[\fI=ver\fR]
.br
Set EDNS version to \fIver\fR (default 0)
.PP
\fB--no-edns\fR, \fB+noedns\fR
.br
\fB--no-edns\fR and \fB+noedns\fR disable EDNS entirely
.PP
\fB--expire\fR, \fB+[no]expire\fR
.br
Add EDNS expire option to query
.PP
\fB--nsid\fR, \fB+[no]nsid\fR
.br
Add EDNS NSID option to query
.PP
\fB--no-cookie\fR, \fB+[no]cookie\fR
.br
Send an EDNS cookie (sent by default)
.PP
\fB--keep-alive\fR, \fB+[no]keepalive\fR, \fB+[no]keepopen\fR
.br
Send an EDNS TCP keepalive options
.PP
\fB--buffer-size\fR, \fB+bufsize\fR=\fIint\fR
.br
Set UDP buffer size to int (default 1232)
.PP
\fB--zflags\fR \fIflag\fR, \fB+ednsflags\fR[\fI=flag\fR]
.br
Set Z EDNS flag bits.\& Octal, decimal and hex are accepted.\&
Setting the first bit (DO) will be ignored, use \fB-D\fR instead
.PP
\fB--pad\fR, \fB+padding\fR
.br
Set EDNS padding
.PP
\fB+subnet\fR=\fIaddr/prefix\fR
.br
Set EDNS client subnet
.PP
.RE
.SS Output Display
.RS 4
\fB--no-question\fR, \fB+[no]question\fR
\fB--no-question\fR, \fB+noquestion\fR
.br
Do not display the Question section
.PP
\fB--no-answer\fR, \fB+[no]answer\fR
\fB--no-answer\fR, \fB+noanswer\fR
.br
Do not display the Answer section
.PP
\fB--no-answer\fR, \fB+[no]answer\fR
\fB--no-answer\fR, \fB+noanswer\fR
.br
Do not display the Answer section
.PP
\fB--no-authority\fR, \fB+[no]authority\fR
\fB--no-authority\fR, \fB+noauthority\fR
.br
Do not display the Authority section
.PP
\fB--no-additional\fR, \fB+[no]additional\fR
\fB--no-additional\fR, \fB+noadditional\fR
.br
Do not display the Additional section
.PP
\fB--no-comments\fR, \fB+[no]comments\fR
.br
Do not display the query comments
.PP
\fB--no-statistics\fR, \fB+nostats\fR
.br
Do not display the Statistics (additional comments) section
@ -293,10 +241,4 @@ 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
.PP
.SH SEE ALSO
\fIdrill\fR(1), \fIdig\fR(1), the many DNS RFCs
.PP
.SH BUGS
Numerous, report them either to
.br
https://git.\&froth.\&zone/sam/awl/issues or via email
~sammefishe/awl-dev@lists.\&sr.\&ht
\fIdrill\fR(1), \fIdig\fR(1), the many DNS RFCs

View file

@ -1,9 +1,8 @@
/*
awl is a DNS lookup tool written in Go,
similar to (and heavily inspired by) drill.
awl is a DNS lookup tool written in Go, similar to (and heavily inspired by) drill.
It runs and displays similar outputs to drill, without any frills.
Options are given to print with JSON
Options are given to print with JSON, XML and YAML.
Supports results from DNS-over-[UDP, TCP, TLS, HTTPS, QUIC] servers

2
go.mod
View file

@ -34,7 +34,7 @@ require (
github.com/stretchr/testify v1.8.0 // indirect
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704
golang.org/x/sys v0.0.0-20220731174439-a90be440212d
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.12 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect

4
go.sum
View file

@ -219,8 +219,6 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220728211354-c7608f3a8462 h1:UreQrH7DbFXSi9ZFox6FNT3WBooWmdANpU+IfkT1T4I=
golang.org/x/net v0.0.0-20220728211354-c7608f3a8462/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b h1:3ogNYyK4oIQdIKzTu68hQrr4iuVxF3AxKl9Aj/eDrw0=
golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -263,8 +261,6 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d h1:Sv5ogFZatcgIMMtBSTTAgMYsicp25MXBubjXNDKwm80=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704 h1:Y7NOhdqIOU8kYI7BxsgL38d0ot0raxvcW+EMQU2QrT4=
golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View file

@ -3,7 +3,7 @@
package logawl
import (
"fmt"
"errors"
"io"
"sync"
"sync/atomic"
@ -14,10 +14,10 @@ type (
Logger struct {
Mu sync.Mutex
Level Level
isDiscard int32
Prefix string
Out io.Writer
buf []byte
isDiscard int32
}
)
@ -48,7 +48,7 @@ func (l *Logger) UnMarshalLevel(lv Level) (string, error) {
case 3:
return "DEBUG ", nil
}
return "", fmt.Errorf("invalid log level")
return "", errInvalidLevel
}
func (l *Logger) IsLevel(level Level) bool {
@ -74,3 +74,5 @@ const (
// Verbose log level.
DebugLevel
)
var errInvalidLevel = errors.New("invalid log level")

View file

@ -11,41 +11,41 @@ import (
// Calling New instantiates Logger
//
// Level can be changed to one of the other log levels (FatalLevel, ErrorLevel, InfoLevel, DebugLevel)
// Level can be changed to one of the other log levels (ErrorLevel, WarnLevel, InfoLevel, DebugLevel).
func New() *Logger {
return &Logger{
Out: os.Stderr,
Level: InfoLevel, //Default value is InfoLevel
Level: WarnLevel, // Default value is WarnLevel
}
}
// Takes any and prints it out to Logger -> Out (io.Writer (default is std.Err))
// Takes any and prints it out to Logger -> Out (io.Writer (default is std.Err)).
func (l *Logger) Println(level Level, v ...any) {
if atomic.LoadInt32(&l.isDiscard) != 0 {
return
}
//If verbose is not set --debug etc print _nothing_
// If verbose is not set --debug etc print _nothing_
if l.IsLevel(level) {
switch level { //Goes through log levels and does stuff based on them (Fatal os.Exit...etc)
switch level { // Goes through log levels and does stuff based on them (currently nothing)
case 0:
err := l.Printer(0, fmt.Sprintln(v...)) //Fatal level
err := l.Printer(0, fmt.Sprintln(v...)) // Error level
if err != nil {
fmt.Fprintln(os.Stderr, "FATAL: Logger failed: ", err)
fmt.Fprintln(os.Stderr, "Logger failed: ", err)
}
case 1:
err := l.Printer(1, fmt.Sprintln(v...)) //Error level
err := l.Printer(1, fmt.Sprintln(v...)) // Warn level
if err != nil {
fmt.Fprintln(os.Stderr, "FATAL: Logger failed: ", err)
fmt.Fprintln(os.Stderr, "Logger failed: ", err)
}
case 2:
err := l.Printer(2, fmt.Sprintln(v...)) //Info level
err := l.Printer(2, fmt.Sprintln(v...)) // Info level
if err != nil {
fmt.Fprintln(os.Stderr, "FATAL: Logger failed: ", err)
fmt.Fprintln(os.Stderr, "Logger failed: ", err)
}
case 3:
err := l.Printer(3, fmt.Sprintln(v...)) //Debug level
err := l.Printer(3, fmt.Sprintln(v...)) // Debug level
if err != nil {
fmt.Fprintln(os.Stderr, "FATAL: Logger failed: ", err)
fmt.Fprintln(os.Stderr, "Logger failed: ", err)
}
default:
break
@ -53,7 +53,7 @@ func (l *Logger) Println(level Level, v ...any) {
}
}
// Formats the log header as such <LogLevel> YYYY/MM/DD HH:MM:SS (local time) <the message to log>
// Formats the log header as such <LogLevel> YYYY/MM/DD HH:MM:SS (local time) <the message to log>.
func (l *Logger) FormatHeader(buf *[]byte, t time.Time, line int, level Level) error {
if lvl, err := l.UnMarshalLevel(level); err == nil {
// This is ugly but functional
@ -77,12 +77,12 @@ func (l *Logger) FormatHeader(buf *[]byte, t time.Time, line int, level Level) e
*buf = append(*buf, ':')
*buf = append(*buf, ' ')
} else {
return fmt.Errorf("invalid log level choice")
return errInvalidLevel
}
return nil
}
// Printer prints the formatted message directly to stdErr
// Printer prints the formatted message directly to stdErr.
func (l *Logger) Printer(level Level, s string) error {
now := time.Now()
var line int
@ -99,7 +99,10 @@ func (l *Logger) Printer(level Level, s string) error {
l.buf = append(l.buf, '\n')
}
_, err = l.Out.Write(l.buf)
return err
if err != nil {
return fmt.Errorf("logger printing error %w", err)
}
return nil
}
// Some line formatting stuff from Golang log stdlib file
@ -123,22 +126,22 @@ func formatter(buf *[]byte, i int, wid int) {
*buf = append(*buf, b[bp:]...)
}
// Call print directly with Debug level
// Call print directly with Debug level.
func (l *Logger) Debug(v ...any) {
l.Println(DebugLevel, v...)
}
// Call print directly with Info level
// Call print directly with Info level.
func (l *Logger) Info(v ...any) {
l.Println(InfoLevel, v...)
}
// Call print directly with Warn level
// Call print directly with Warn level.
func (l *Logger) Warn(v ...any) {
l.Println(WarnLevel, v...)
}
// Call print directly with Error level
// Call print directly with Error level.
func (l *Logger) Error(v ...any) {
l.Println(ErrLevel, v...)
}

View file

@ -8,7 +8,6 @@ import (
"time"
"git.froth.zone/sam/awl/logawl"
"gotest.tools/v3/assert"
)
@ -48,11 +47,17 @@ func TestLogger(t *testing.T) {
t.Parallel()
for i := range logawl.AllLevels {
// only test non-exiting log levels
switch i {
case 0:
fn := func() {
logger.Error("Test", "E")
}
var buffer bytes.Buffer
logger.Out = &buffer
fn()
case 1:
fn := func() {
logger.Info("")
logger.Warn("Test")
}
var buffer bytes.Buffer
logger.Out = &buffer
@ -67,6 +72,7 @@ func TestLogger(t *testing.T) {
case 3:
fn := func() {
logger.Debug("Test")
logger.Debug("Test 2")
}
var buffer bytes.Buffer
logger.Out = &buffer
@ -79,6 +85,6 @@ func TestFmt(t *testing.T) {
t.Parallel()
ti := time.Now()
test := []byte("test")
assert.ErrorContains(t, logger.FormatHeader(&test, ti, 0, 9001), "invalid log level") //make sure error is error
// make sure error is error
assert.ErrorContains(t, logger.FormatHeader(&test, ti, 0, 9001), "invalid log level")
}

View file

@ -5,9 +5,15 @@ package main
import (
"os"
"testing"
"gotest.tools/v3/assert"
)
// nolint: paralleltest
func TestMain(t *testing.T) {
os.Args = []string{"awl", "+yaml", "@1.1.1.1"}
main()
os.Args = []string{"awl", "+short", "@1.1.1.1"}
main()
assert.Assert(t, 1 == 2-1)
}

View file

@ -3,11 +3,11 @@
package query
import (
"fmt"
"time"
"git.froth.zone/sam/awl/cli"
"git.froth.zone/sam/awl/internal/helpers"
"github.com/ameshkov/dnscrypt/v2"
"github.com/miekg/dns"
)
@ -16,8 +16,8 @@ type DNSCryptResolver struct {
opts cli.Options
}
// LookUp performs a DNS query.
func (r *DNSCryptResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
client := dnscrypt.Client{
Timeout: r.opts.Request.Timeout,
UDPSize: 1232,
@ -39,7 +39,7 @@ func (r *DNSCryptResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
resolverInf, err := client.Dial(r.opts.Request.Server)
if err != nil {
return helpers.Response{}, err
return helpers.Response{}, fmt.Errorf("dnscrypt: dial error: %w", err)
}
now := time.Now()
@ -47,7 +47,7 @@ func (r *DNSCryptResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
rtt := time.Since(now)
if err != nil {
return helpers.Response{}, err
return helpers.Response{}, fmt.Errorf("dnscrypt: exchange error: %w", err)
}
r.opts.Logger.Info("Request successful")

View file

@ -9,12 +9,12 @@ import (
"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 TestDNSCrypt(t *testing.T) {
t.Parallel()
tests := []struct {
opt cli.Options
}{
@ -42,11 +42,30 @@ func TestDNSCrypt(t *testing.T) {
},
},
},
{
cli.Options{
Logger: util.InitLogger(0),
DNSCrypt: true,
TCP: true,
IPv4: true,
Request: helpers.Request{
Server: "QMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20",
Type: dns.TypeAAAA,
Name: "example.com.",
},
},
},
}
for _, test := range tests {
res, err := query.CreateQuery(test.opt)
assert.NilError(t, err)
assert.Assert(t, res != helpers.Response{})
test := test
t.Run("", func(t *testing.T) {
t.Parallel()
res, err := query.CreateQuery(test.opt)
if err == nil {
assert.Assert(t, res != helpers.Response{})
} else {
assert.ErrorContains(t, err, "unsupported stamp")
}
})
}
}

View file

@ -18,6 +18,7 @@ type HTTPSResolver struct {
opts cli.Options
}
// LookUp performs a DNS query.
func (r *HTTPSResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
var resp helpers.Response
httpR := &http.Client{
@ -25,13 +26,13 @@ func (r *HTTPSResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
}
buf, err := msg.Pack()
if err != nil {
return helpers.Response{}, err
return helpers.Response{}, fmt.Errorf("doh: packing error error: %w", err)
}
r.opts.Logger.Debug("making DoH request")
// query := server + "?dns=" + base64.RawURLEncoding.EncodeToString(buf)
//
req, err := http.NewRequest("POST", r.opts.Request.Server, bytes.NewBuffer(buf))
if err != nil {
return helpers.Response{}, fmt.Errorf("DoH: %w", err)
return helpers.Response{}, fmt.Errorf("doh request creation error: %w", err)
}
req.Header.Set("Content-Type", "application/dns-message")
req.Header.Set("Accept", "application/dns-message")
@ -41,24 +42,32 @@ func (r *HTTPSResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
resp.RTT = time.Since(now)
if err != nil {
return helpers.Response{}, fmt.Errorf("DoH HTTP request error: %w", err)
return helpers.Response{}, fmt.Errorf("doh HTTP request error: %w", err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return helpers.Response{}, fmt.Errorf("DoH server responded with HTTP %d", res.StatusCode)
return helpers.Response{}, &errHTTPStatus{res.StatusCode}
}
fullRes, err := io.ReadAll(res.Body)
if err != nil {
return helpers.Response{}, fmt.Errorf("DoH body read error: %w", err)
return helpers.Response{}, fmt.Errorf("doh body read error: %w", err)
}
resp.DNS = &dns.Msg{}
r.opts.Logger.Debug("unpacking response")
err = resp.DNS.Unpack(fullRes)
if err != nil {
return helpers.Response{}, fmt.Errorf("DoH dns message unpack error: %w", err)
return helpers.Response{}, fmt.Errorf("doh dns message unpack error: %w", err)
}
return resp, nil
}
type errHTTPStatus struct {
code int
}
func (e *errHTTPStatus) Error() string {
return fmt.Sprintf("doh server responded with HTTP %d", e.code)
}

View file

@ -11,7 +11,6 @@ import (
"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"
)

View file

@ -4,12 +4,12 @@ package query
import (
"crypto/tls"
"fmt"
"io"
"time"
"git.froth.zone/sam/awl/cli"
"git.froth.zone/sam/awl/internal/helpers"
"github.com/lucas-clemente/quic-go"
"github.com/miekg/dns"
)
@ -18,6 +18,7 @@ type QUICResolver struct {
opts cli.Options
}
// LookUp performs a DNS query.
func (r *QUICResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
var resp helpers.Response
tls := &tls.Config{
@ -31,46 +32,46 @@ func (r *QUICResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
r.opts.Logger.Debug("making DoQ request")
connection, err := quic.DialAddr(r.opts.Request.Server, tls, conf)
if err != nil {
return helpers.Response{}, err
return helpers.Response{}, fmt.Errorf("doq: dial error: %w", err)
}
// Compress request to over-the-wire
buf, err := msg.Pack()
if err != nil {
return helpers.Response{}, err
return helpers.Response{}, fmt.Errorf("doq: pack error: %w", err)
}
t := time.Now()
stream, err := connection.OpenStream()
if err != nil {
return helpers.Response{}, err
return helpers.Response{}, fmt.Errorf("doq: quic stream creation error: %w", err)
}
_, err = stream.Write(buf)
if err != nil {
return helpers.Response{}, err
return helpers.Response{}, fmt.Errorf("doq: quic stream write error: %w", err)
}
fullRes, err := io.ReadAll(stream)
if err != nil {
return helpers.Response{}, err
return helpers.Response{}, fmt.Errorf("doq: quic stream read error: %w", err)
}
resp.RTT = time.Since(t)
// Close with error: no error
err = connection.CloseWithError(0, "")
if err != nil {
return helpers.Response{}, err
return helpers.Response{}, fmt.Errorf("doq: quic connection close error: %w", err)
}
err = stream.Close()
if err != nil {
return helpers.Response{}, err
return helpers.Response{}, fmt.Errorf("doq: quic stream close error: %w", err)
}
resp.DNS = &dns.Msg{}
r.opts.Logger.Debug("unpacking DoQ response")
err = resp.DNS.Unpack(fullRes)
if err != nil {
return helpers.Response{}, err
return helpers.Response{}, fmt.Errorf("doq: upack error: %w", err)
}
return resp, nil
}

View file

@ -14,7 +14,6 @@ import (
"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"
)

View file

@ -8,7 +8,6 @@ import (
"git.froth.zone/sam/awl/cli"
"git.froth.zone/sam/awl/internal/helpers"
"github.com/miekg/dns"
)
@ -16,6 +15,7 @@ type StandardResolver struct {
opts cli.Options
}
// LookUp performs a DNS query
func (r *StandardResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
var (
resp helpers.Response
@ -45,7 +45,7 @@ func (r *StandardResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, r.opts.Request.Server)
if err != nil {
return helpers.Response{}, err
return helpers.Response{}, fmt.Errorf("standard: DNS exchange error: %w", err)
}
r.opts.Logger.Info("Request successful")
@ -55,11 +55,14 @@ func (r *StandardResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
switch {
case r.opts.IPv4:
dnsClient.Net += "4"
case r.opts.IPv4:
case r.opts.IPv6:
dnsClient.Net += "6"
}
resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, r.opts.Request.Server)
}
if err != nil {
return helpers.Response{}, fmt.Errorf("standard: DNS exchange error: %w", err)
}
return resp, err
return resp, nil
}

View file

@ -4,6 +4,7 @@ package query_test
import (
"testing"
"time"
"git.froth.zone/sam/awl/cli"
"git.froth.zone/sam/awl/internal/helpers"
@ -14,25 +15,28 @@ import (
)
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.",
Server: "8.8.4.1",
Type: dns.TypeA,
Name: "example.com.",
Timeout: time.Second / 2,
Retries: 0,
},
}
resolver, err := query.LoadResolver(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{})
_, err = resolver.LookUp(msg)
assert.ErrorContains(t, err, "timeout")
}
func TestTruncate(t *testing.T) {
t.Parallel()
opts := cli.Options{
Logger: util.InitLogger(0),
IPv4: true,
@ -53,6 +57,7 @@ func TestTruncate(t *testing.T) {
}
func TestResolveAgain(t *testing.T) {
t.Parallel()
tests := []struct {
opt cli.Options
}{
@ -79,11 +84,26 @@ func TestResolveAgain(t *testing.T) {
},
},
},
{
cli.Options{
Logger: util.InitLogger(0),
TLS: true,
Port: 853,
Request: helpers.Request{
Server: "dns.google",
Type: dns.TypeAAAA,
Name: "example.com.",
},
},
},
}
for _, test := range tests {
res, err := query.CreateQuery(test.opt)
assert.NilError(t, err)
assert.Assert(t, res != helpers.Response{})
test := test
t.Run("", func(t *testing.T) {
t.Parallel()
res, err := query.CreateQuery(test.opt)
assert.NilError(t, err)
assert.Assert(t, res != helpers.Response{})
})
}
}

View file

@ -5,19 +5,18 @@ package query
import (
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"strconv"
"strings"
"time"
"git.froth.zone/sam/awl/cli"
"golang.org/x/net/idna"
"github.com/miekg/dns"
"golang.org/x/net/idna"
"gopkg.in/yaml.v3"
)
//
func PrintSpecial(msg *dns.Msg, opts cli.Options) (string, error) {
formatted, err := MakePrintable(msg, opts)
if err != nil {
@ -40,10 +39,12 @@ func PrintSpecial(msg *dns.Msg, opts cli.Options) (string, error) {
return string(yaml), err
default:
return "", fmt.Errorf("special: no recognized format given")
return "", errInvalidFormat
}
}
// MakePrintable takes a DNS message and makes it nicer to be printed as JSON,YAML,
// and XML. Little is changed beyond naming
func MakePrintable(msg *dns.Msg, opts cli.Options) (*Message, error) {
var err error
ret := Message{
@ -55,7 +56,7 @@ func MakePrintable(msg *dns.Msg, opts cli.Options) (*Message, error) {
if opts.Display.UcodeTranslate {
name, err = idna.ToUnicode(question.Name)
if err != nil {
return nil, err
return nil, fmt.Errorf("punycode: error translating to unicode: %w", err)
}
} else {
name = question.Name
@ -83,7 +84,7 @@ func MakePrintable(msg *dns.Msg, opts cli.Options) (*Message, error) {
if opts.Display.UcodeTranslate {
name, err = idna.ToUnicode(answer.Header().Name)
if err != nil {
return nil, err
return nil, fmt.Errorf("punycode: error translating to unicode: %w", err)
}
} else {
name = answer.Header().Name
@ -98,7 +99,6 @@ func MakePrintable(msg *dns.Msg, opts cli.Options) (*Message, error) {
},
Value: temp[len(temp)-1],
})
}
for _, ns := range msg.Ns {
@ -117,7 +117,7 @@ func MakePrintable(msg *dns.Msg, opts cli.Options) (*Message, error) {
if opts.Display.UcodeTranslate {
name, err = idna.ToUnicode(ns.Header().Name)
if err != nil {
return nil, err
return nil, fmt.Errorf("punycode: error translating to unicode: %w", err)
}
} else {
name = ns.Header().Name
@ -153,7 +153,7 @@ func MakePrintable(msg *dns.Msg, opts cli.Options) (*Message, error) {
if opts.Display.UcodeTranslate {
name, err = idna.ToUnicode(additional.Header().Name)
if err != nil {
return nil, err
return nil, fmt.Errorf("punycode: error translating to unicode: %w", err)
}
} else {
name = additional.Header().Name
@ -173,3 +173,5 @@ func MakePrintable(msg *dns.Msg, opts cli.Options) (*Message, error) {
return &ret, nil
}
var errInvalidFormat = errors.New("this should never happen")

343
query/print_test.go Normal file
View file

@ -0,0 +1,343 @@
// 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 TestBadFormat(t *testing.T) {
t.Parallel()
_, err := query.PrintSpecial(new(dns.Msg), cli.Options{})
assert.ErrorContains(t, err, "never happen")
}
func TestPrinting(t *testing.T) {
t.Parallel()
opts := cli.Options{
Logger: util.InitLogger(0),
Port: 53,
IPv4: false,
IPv6: false,
TCP: true,
DNSCrypt: false,
TLS: false,
HTTPS: false,
QUIC: false,
Truncate: false,
ShowQuery: true,
AA: false,
AD: false,
CD: false,
QR: false,
RD: true,
RA: false,
TC: false,
Z: false,
Reverse: false,
Verbosity: 0,
HumanTTL: false,
ShowTTL: true,
Short: false,
Identify: false,
JSON: true,
XML: false,
YAML: false,
Display: cli.Displays{
Comments: true,
Question: true,
Answer: true,
Authority: true,
Additional: true,
Statistics: true,
UcodeTranslate: true,
},
Request: helpers.Request{
Server: "a.gtld-servers.net",
Type: dns.StringToType["NS"],
Class: 1,
Name: "google.com.",
Timeout: 0,
Retries: 0,
},
EDNS: cli.EDNS{
EnableEDNS: false,
},
}
resp, err := query.CreateQuery(opts)
assert.NilError(t, err)
str, err := query.PrintSpecial(resp.DNS, opts)
assert.NilError(t, err)
assert.Assert(t, str != "")
}
func TestPrinting2(t *testing.T) {
t.Parallel()
opts := cli.Options{
Logger: util.InitLogger(0),
Port: 53,
IPv4: false,
IPv6: false,
TCP: true,
DNSCrypt: false,
TLS: false,
HTTPS: false,
QUIC: false,
Truncate: false,
ShowQuery: true,
AA: false,
AD: false,
CD: false,
QR: false,
RD: true,
RA: false,
TC: false,
Z: false,
Reverse: false,
Verbosity: 0,
HumanTTL: false,
ShowTTL: true,
Short: true,
Identify: true,
JSON: false,
XML: false,
YAML: true,
Display: cli.Displays{
Comments: true,
Question: true,
Answer: true,
Authority: true,
Additional: true,
Statistics: true,
UcodeTranslate: true,
},
Request: helpers.Request{
Server: "ns1.google.com",
Type: dns.StringToType["NS"],
Class: 1,
Name: "google.com.",
Timeout: 0,
Retries: 0,
},
EDNS: cli.EDNS{
EnableEDNS: false,
},
}
resp, err := query.CreateQuery(opts)
assert.NilError(t, err)
str, err := query.PrintSpecial(resp.DNS, opts)
assert.NilError(t, err)
assert.Assert(t, str != "")
str = query.ToString(resp, opts)
assert.Assert(t, str != "")
}
func TestPrinting3(t *testing.T) {
t.Parallel()
opts := cli.Options{
Logger: util.InitLogger(0),
Port: 53,
IPv4: false,
IPv6: false,
TCP: false,
DNSCrypt: false,
TLS: false,
HTTPS: true,
QUIC: false,
Truncate: false,
ShowQuery: true,
AA: false,
AD: false,
CD: false,
QR: false,
RD: true,
RA: false,
TC: false,
Z: false,
Reverse: false,
Verbosity: 0,
HumanTTL: false,
ShowTTL: true,
Short: false,
Identify: true,
JSON: false,
XML: false,
YAML: true,
Display: cli.Displays{
Comments: true,
Question: true,
Answer: true,
Authority: true,
Additional: true,
Statistics: true,
UcodeTranslate: true,
},
Request: helpers.Request{
Server: "https://dns.froth.zone/dns-query",
Type: dns.StringToType["NS"],
Class: 1,
Name: "freecumextremist.com.",
Timeout: 0,
Retries: 0,
},
EDNS: cli.EDNS{
EnableEDNS: false,
},
}
resp, err := query.CreateQuery(opts)
assert.NilError(t, err)
str, err := query.PrintSpecial(resp.DNS, opts)
assert.NilError(t, err)
assert.Assert(t, str != "")
str = query.ToString(resp, opts)
assert.Assert(t, str != "")
}
func TestPrinting4(t *testing.T) {
t.Parallel()
opts := cli.Options{
Logger: util.InitLogger(0),
Port: 853,
IPv4: false,
IPv6: false,
TCP: false,
DNSCrypt: false,
TLS: true,
HTTPS: false,
QUIC: false,
Truncate: false,
ShowQuery: true,
AA: false,
AD: false,
CD: false,
QR: false,
RD: true,
RA: false,
TC: false,
Z: false,
Reverse: false,
Verbosity: 0,
HumanTTL: false,
ShowTTL: true,
Short: false,
Identify: true,
JSON: false,
XML: false,
YAML: true,
Display: cli.Displays{
Comments: true,
Question: true,
Answer: true,
Authority: true,
Additional: true,
Statistics: true,
UcodeTranslate: true,
},
Request: helpers.Request{
Server: "dns.google",
Type: dns.StringToType["NS"],
Class: 1,
Name: "freecumextremist.com.",
Timeout: 0,
Retries: 0,
},
EDNS: cli.EDNS{
EnableEDNS: false,
},
}
resp, err := query.CreateQuery(opts)
assert.NilError(t, err)
str, err := query.PrintSpecial(resp.DNS, opts)
assert.NilError(t, err)
assert.Assert(t, str != "")
str = query.ToString(resp, opts)
assert.Assert(t, str != "")
}
func TestPrinting5(t *testing.T) {
t.Parallel()
opts := cli.Options{
Logger: util.InitLogger(0),
Port: 53,
IPv4: false,
IPv6: false,
TCP: true,
DNSCrypt: false,
TLS: false,
HTTPS: false,
QUIC: false,
Truncate: false,
ShowQuery: true,
AA: true,
AD: false,
CD: false,
QR: false,
RD: true,
RA: false,
TC: false,
Z: false,
Reverse: false,
Verbosity: 0,
HumanTTL: false,
ShowTTL: true,
Short: false,
Identify: false,
JSON: false,
XML: false,
YAML: true,
Display: cli.Displays{
Comments: true,
Question: true,
Answer: true,
Authority: true,
Additional: true,
Statistics: true,
UcodeTranslate: true,
},
Request: helpers.Request{
Server: "rin.froth.zone",
Type: dns.StringToType["A"],
Class: 1,
Name: "froth.zone.",
Timeout: 0,
Retries: 0,
},
EDNS: cli.EDNS{
EnableEDNS: true,
Cookie: true,
},
}
resp, err := query.CreateQuery(opts)
assert.NilError(t, err)
str, err := query.PrintSpecial(resp.DNS, opts)
assert.NilError(t, err)
assert.Assert(t, str != "")
str = query.ToString(resp, opts)
assert.Assert(t, str != "")
}
func TestToString6(t *testing.T) {
assert.Assert(t, query.ToString(*new(helpers.Response), *new(cli.Options)) == "<nil> MsgHdr")
}

View file

@ -8,7 +8,6 @@ import (
"git.froth.zone/sam/awl/cli"
"git.froth.zone/sam/awl/internal/helpers"
"github.com/dchest/uniuri"
"github.com/miekg/dns"
)
@ -30,7 +29,6 @@ func CreateQuery(opts cli.Options) (helpers.Response, error) {
// EDNS time :)
if opts.EDNS.EnableEDNS {
o := new(dns.OPT)
o.Hdr.Name = "."
o.Hdr.Rrtype = dns.TypeOPT
@ -81,12 +79,10 @@ func CreateQuery(opts cli.Options) (helpers.Response, error) {
}
req.Extra = append(req.Extra, o)
} else {
if opts.EDNS.DNSSEC {
req.SetEdns0(1232, true)
opts.Logger.Warn("DNSSEC implies EDNS, EDNS enabled")
opts.Logger.Info("DNSSEC enabled, UDP buffer set to 1232")
}
} else if opts.EDNS.DNSSEC {
req.SetEdns0(1232, true)
opts.Logger.Warn("DNSSEC implies EDNS, EDNS enabled")
opts.Logger.Info("DNSSEC enabled, UDP buffer set to 1232")
}
opts.Logger.Debug(req)
@ -112,7 +108,7 @@ func CreateQuery(opts cli.Options) (helpers.Response, error) {
opts.Display.Statistics = temp
str += "\n;; QUERY SIZE: " + strconv.Itoa(req.Len())
}
fmt.Println(str, "\n------")
fmt.Println(str)
opts.ShowQuery = false
}
}
@ -123,5 +119,6 @@ func CreateQuery(opts cli.Options) (helpers.Response, error) {
}
opts.Logger.Info("Query successfully loaded")
//nolint:wrapcheck // Error wrapping not needed here
return resolver.LookUp(req)
}

View file

@ -9,100 +9,92 @@ import (
"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,
t.Parallel()
in := []cli.Options{
{
Logger: util.InitLogger(0),
Port: 53,
QR: false,
Z: true,
RD: false,
ShowQuery: true,
YAML: true,
Request: helpers.Request{
Server: "8.8.4.4",
Type: dns.TypeA,
Name: "example.com.",
Request: helpers.Request{
Server: "8.8.4.4",
Type: dns.TypeA,
Name: "example.com.",
},
Display: cli.Displays{
Comments: true,
Question: true,
Opt: true,
Answer: true,
Authority: true,
Additional: true,
Statistics: true,
UcodeTranslate: false,
},
EDNS: cli.EDNS{
EnableEDNS: true,
DNSSEC: true,
Cookie: true,
Expire: true,
KeepOpen: true,
Nsid: true,
},
},
EDNS: cli.EDNS{
EnableEDNS: true,
DNSSEC: true,
Cookie: true,
Expire: true,
KeepOpen: true,
Nsid: true,
{
Logger: util.InitLogger(0),
Port: 53,
QR: false,
Z: true,
RD: false,
ShowQuery: true,
XML: true,
Request: helpers.Request{
Server: "8.8.4.4",
Type: dns.TypeA,
Name: "example.com.",
},
Display: cli.Displays{
Comments: true,
Question: true,
Opt: true,
Answer: true,
Authority: true,
Additional: true,
Statistics: true,
UcodeTranslate: true,
},
EDNS: cli.EDNS{
EnableEDNS: false,
DNSSEC: false,
Cookie: false,
Expire: false,
KeepOpen: false,
Nsid: false,
},
},
}
res, err := query.CreateQuery(opts)
assert.NilError(t, err)
assert.Assert(t, res != helpers.Response{})
}
func TestCreateQr(t *testing.T) {
opts := cli.Options{
Logger: util.InitLogger(0),
Port: 53,
QR: false,
Z: true,
RD: false,
ShowQuery: true,
Request: helpers.Request{
Server: "8.8.4.4",
Type: dns.TypeA,
Name: "example.com.",
},
EDNS: cli.EDNS{
EnableEDNS: false,
DNSSEC: true,
Cookie: false,
Expire: false,
KeepOpen: false,
Nsid: false,
},
Display: cli.Displays{
Comments: true,
Question: true,
Answer: true,
Authority: true,
Additional: true,
Statistics: true,
UcodeTranslate: true,
},
for _, opt := range in {
opt := opt
t.Run("", func(t *testing.T) {
t.Parallel()
res, err := query.CreateQuery(opt)
assert.NilError(t, err)
assert.Assert(t, res != helpers.Response{})
str, err := query.PrintSpecial(res.DNS, opt)
assert.NilError(t, err)
assert.Assert(t, str != "")
str = query.ToString(res, opt)
assert.Assert(t, str != "")
})
}
res, err := query.CreateQuery(opts)
assert.NilError(t, err)
assert.Assert(t, res != helpers.Response{})
}
func TestCreateQr2(t *testing.T) {
opts := cli.Options{
Logger: util.InitLogger(0),
Port: 53,
QR: false,
Z: true,
RD: false,
ShowQuery: true,
XML: true,
Request: helpers.Request{
Server: "8.8.4.4",
Type: dns.TypeA,
Name: "example.com.",
},
EDNS: cli.EDNS{
EnableEDNS: false,
DNSSEC: false,
Cookie: false,
Expire: false,
KeepOpen: false,
Nsid: false,
},
}
res, err := query.CreateQuery(opts)
assert.NilError(t, err)
assert.Assert(t, res != helpers.Response{})
}

View file

@ -12,10 +12,12 @@ import (
"github.com/miekg/dns"
)
// Main resolver interface
type Resolver interface {
LookUp(*dns.Msg) (helpers.Response, error)
}
// LoadResolver loads the respective resolver for performing a DNS query
func LoadResolver(opts cli.Options) (Resolver, error) {
switch {
case opts.HTTPS:

View file

@ -9,42 +9,44 @@ import (
"git.froth.zone/sam/awl/cli"
"git.froth.zone/sam/awl/internal/helpers"
"github.com/miekg/dns"
)
// Overall DNS response message
type Message struct {
Header dns.MsgHdr `json:",omitempty" xml:",omitempty" yaml:",omitempty"`
Question []Question `json:",omitempty" xml:",omitempty" yaml:",omitempty"`
Answer []Answer `json:",omitempty" xml:",omitempty" yaml:",omitempty"`
Ns []Answer `json:",omitempty" xml:",omitempty" yaml:",omitempty"`
Extra []Answer `json:",omitempty" xml:",omitempty" yaml:",omitempty"`
Header dns.MsgHdr `json:"header,omitempty" xml:"header,omitempty" yaml:",omitempty"`
Question []Question `json:"question,omitempty" xml:"question,omitempty" yaml:",omitempty"`
Answer []Answer `json:"answer,omitempty" xml:"answer,omitempty" yaml:",omitempty"`
Ns []Answer `json:"ns,omitempty" xml:"ns,omitempty" yaml:",omitempty"`
Extra []Answer `json:"extra,omitempty" xml:"extra,omitempty" yaml:",omitempty"`
}
// DNS Query
type Question struct {
Name string `json:",omitempty" xml:",omitempty" yaml:",omitempty"`
Type string `json:",omitempty" xml:",omitempty" yaml:",omitempty"`
Class string `json:",omitempty" xml:",omitempty" yaml:",omitempty"`
Name string `json:"name,omitempty" xml:"name,omitempty" yaml:",omitempty"`
Type string `json:"type,omitempty" xml:"type,omitempty" yaml:",omitempty"`
Class string `json:"class,omitempty" xml:"class,omitempty" yaml:",omitempty"`
}
// DNS Resource Headers
type RRHeader struct {
Name string `json:",omitempty" xml:",omitempty" yaml:",omitempty"`
Type string `json:",omitempty" xml:",omitempty" yaml:",omitempty"`
Class string `json:",omitempty" xml:",omitempty" yaml:",omitempty"`
TTL string `json:",omitempty" xml:",omitempty" yaml:",omitempty"`
Name string `json:"name,omitempty" xml:"name,omitempty" yaml:",omitempty"`
Type string `json:"type,omitempty" xml:"type,omitempty" yaml:",omitempty"`
Class string `json:"class,omitempty" xml:"class,omitempty" yaml:",omitempty"`
TTL string `json:"ttl,omitempty" xml:"ttl,omitempty" yaml:",omitempty"`
Rdlength uint16 `json:"-" xml:"-" yaml:"-"`
}
// DNS Response
type Answer struct {
RRHeader `json:"Header,omitempty" xml:"Header,omitempty" yaml:"header,omitempty"`
Value string `json:"Response,omitempty" xml:"Response,omitempty" yaml:"response,omitempty"`
RRHeader `json:"header,omitempty" xml:"header,omitempty" yaml:"header,omitempty"`
Value string `json:"response,omitempty" xml:"response,omitempty" yaml:"response,omitempty"`
}
// Turn the response into dig
// ToString turns the response into something that looks a lot like dig
//
// Much of this is taken from https://github.com/miekg/dns/blob/master/msg.go#L900
func ToString(res helpers.Response, opts cli.Options) string {
if res.DNS == nil {
return "<nil> MsgHdr"
}
@ -52,7 +54,6 @@ func ToString(res helpers.Response, opts cli.Options) string {
var opt *dns.OPT
if !opts.Short {
if opts.Display.Comments {
s += res.DNS.MsgHdr.String() + " "
s += "QUERY: " + strconv.Itoa(len(res.DNS.Question)) + ", "
@ -134,14 +135,18 @@ func ToString(res helpers.Response, opts cli.Options) string {
}
} else {
// Print just the responses, nothing else
for _, res := range res.DNS.Answer {
temp := strings.Split(res.String(), "\t")
for i, resp := range res.DNS.Answer {
temp := strings.Split(resp.String(), "\t")
s += temp[len(temp)-1]
if opts.Identify {
s += " from server " + opts.Request.Server + " in " + res.RTT.String()
}
// Don't print newline on last line
if i != len(res.DNS.Answer)-1 {
s += "\n"
}
}
if opts.Identify {
s += " from server " + opts.Request.Server + " in " + res.RTT.String()
}
// s += "\n"
}
return s

View file

@ -24,15 +24,18 @@ doc/$(PROG).1: doc/wiki/$(PROG).1.md
@cp doc/awl.1 doc/awl.bak
$(SCDOC) <doc/wiki/$(PROG).1.md >doc/$(PROG).1 && rm doc/awl.bak || mv doc/awl.bak doc/awl.1
## test: run go test
test:
$(GO) test -cover -coverprofile=coverage/coverage.out ./...
## cover: generates test coverage, output as HTML
cover: test
coverage/coverage.out: test
$(COVER) -func=coverage/coverage.out
$(COVER) -html=coverage/coverage.out -o coverage/cover.html
## cover: generates test coverage, output as HTML
cover: coverage/coverage.out
fmt:
gofmt -w -s .
@ -41,7 +44,7 @@ vet:
## lint: lint awl, using fmt, vet and golangci-lint
lint: fmt vet
-golangci-lint run
-golangci-lint run --fix
## clean: clean the build files
clean:

View file

@ -5,10 +5,10 @@ package util
import "git.froth.zone/sam/awl/logawl"
// Initialize the logawl instance.
func InitLogger(verbosity int) (Logger *logawl.Logger) {
Logger = logawl.New()
func InitLogger(verbosity int) (logger *logawl.Logger) {
logger = logawl.New()
Logger.SetLevel(logawl.Level(verbosity))
logger.SetLevel(logawl.Level(verbosity))
return
return logger
}

View file

@ -11,6 +11,7 @@ import (
)
func TestInitLogger(t *testing.T) {
t.Parallel()
logger := util.InitLogger(0)
assert.Equal(t, logger.Level, logawl.Level(0))
}

View file

@ -3,18 +3,29 @@
package util
import (
"errors"
"fmt"
"strings"
"github.com/miekg/dns"
)
type errReverseDNS struct {
addr string
}
func (e *errReverseDNS) Error() string {
return fmt.Sprintf("reverseDNS: invalid value %s given", e.addr)
}
// 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" {
return dns.ReverseAddr(address)
str, err := dns.ReverseAddr(address)
if err != nil {
return "", fmt.Errorf("could not reverse: %w", err)
}
return str, nil
} else if query == "NAPTR" {
// get rid of characters not needed
replacer := strings.NewReplacer("+", "", " ", "", "-", "")
@ -30,7 +41,7 @@ func ReverseDNS(address string, querInt uint16) (string, error) {
return arpa.String(), nil
}
return "", errors.New("ReverseDNS: -x flag given but no IP found")
return "", &errReverseDNS{address}
}
// Reverse a string, return the string in reverse.

View file

@ -6,7 +6,6 @@ import (
"testing"
"git.froth.zone/sam/awl/util"
"github.com/miekg/dns"
"gotest.tools/v3/assert"
)
@ -56,5 +55,11 @@ func TestNAPTR(t *testing.T) {
func TestInvalid(t *testing.T) {
t.Parallel()
_, err := util.ReverseDNS("AAAAA", 1)
assert.ErrorContains(t, err, "no IP found")
assert.ErrorContains(t, err, "invalid value AAAAA given")
}
func TestInvalid2(t *testing.T) {
t.Parallel()
_, err := util.ReverseDNS("1.0", PTR)
assert.ErrorContains(t, err, "could not reverse")
}