(feat)EDNS #55
38 changed files with 751 additions and 506 deletions
124
.drone.yml
124
.drone.yml
|
@ -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"
|
||||
}
|
39
cli/cli.go
39
cli/cli.go
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
40
cli/dig.go
40
cli/dig.go
|
@ -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
|
||||
|
|
24
cli/misc.go
24
cli/misc.go
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"testing"
|
||||
|
||||
"git.froth.zone/sam/awl/conf"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
96
doc/awl.1
96
doc/awl.1
|
@ -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
|
5
docs.go
5
docs.go
|
@ -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
2
go.mod
|
@ -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
4
go.sum
|
@ -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=
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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...)
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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{})
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
343
query/print_test.go
Normal 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")
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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{})
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
)
|
||||
|
||||
func TestInitLogger(t *testing.T) {
|
||||
t.Parallel()
|
||||
logger := util.InitLogger(0)
|
||||
assert.Equal(t, logger.Level, logawl.Level(0))
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue