Minor (complete) refactor #38
30 changed files with 692 additions and 323 deletions
|
@ -1,3 +1,5 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
local testing(version, arch) = {
|
||||
kind: "pipeline",
|
||||
name: version + "-" + arch ,
|
||||
|
@ -59,7 +61,6 @@ local release() = {
|
|||
]
|
||||
};
|
||||
|
||||
// logawl uses generics so 1.18 is the minimum
|
||||
[
|
||||
testing("1.18", "amd64"),
|
||||
testing("1.18", "arm64"),
|
||||
|
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -15,5 +15,8 @@
|
|||
# vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
dist/
|
||||
go.work
|
||||
dist/
|
||||
|
||||
# Test coverage
|
||||
coverage/
|
62
Makefile
62
Makefile
|
@ -1,36 +1,62 @@
|
|||
# This should only be used for dev environments
|
||||
GO := go
|
||||
GOFLAGS := -ldflags '-s -w'
|
||||
PREFIX := /usr/local
|
||||
BINPATH = $(PREFIX)/bin
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
.PHONY: clean doc
|
||||
HASH := $(shell git describe --always --dirty || echo "UNKNOWN")
|
||||
VER := "git-$(HASH)"
|
||||
|
||||
CGO_ENABLED = 0
|
||||
GO := go
|
||||
GOFLAGS := -ldflags "-s -w -X 'main.version=$(VER)'"
|
||||
SCDOC := scdoc
|
||||
|
||||
PREFIX := /usr/local
|
||||
BINPATH := $(PREFIX)/bin
|
||||
MANPATH := $(PREFIX)/share/man
|
||||
|
||||
PROG := awl
|
||||
ifeq ($(OS),Windows_NT)
|
||||
EXE := $(PROG).exe
|
||||
else
|
||||
EXE := $(PROG)
|
||||
endif
|
||||
|
||||
.PHONY: clean lint
|
||||
|
||||
# hehe
|
||||
all: awl
|
||||
all: $(PROG)
|
||||
|
||||
awl: .
|
||||
$(GO) build -o awl $(GOFLAGS) .
|
||||
$(PROG): lint
|
||||
$(GO) build -o $(EXE) $(GOFLAGS) .
|
||||
|
||||
doc:
|
||||
scdoc < doc/awl.1.md > doc/awl.1
|
||||
doc: doc/awl.1.md
|
||||
$(SCDOC) < doc/$(PROG).1.md > doc/$(PROG).1
|
||||
|
||||
test:
|
||||
$(GO) test -v ./... -cover
|
||||
test: coverage/coverage.out
|
||||
$(GO) test -v -cover -coverprofile=coverage/coverage.out ./...
|
||||
|
||||
fmt:
|
||||
cover: test
|
||||
$(GO) tool cover -func=coverage/coverage.out
|
||||
$(GO) tool cover -html=coverage/coverage.out -o coverage/cover.html
|
||||
|
||||
fmt: *.go
|
||||
gofmt -w -s .
|
||||
|
||||
vet:
|
||||
vet: *.go
|
||||
$(GO) vet ./...
|
||||
|
||||
lint: fmt vet
|
||||
-golangci-lint run
|
||||
|
||||
install: awl
|
||||
install awl $(BINPATH)
|
||||
ifeq ($(OS),Windows_NT)
|
||||
install:
|
||||
$(GO) install $(GOFLAGS) .
|
||||
else
|
||||
install: $(PROG)
|
||||
install $(PROG) $(BINPATH)
|
||||
endif
|
||||
|
||||
|
||||
install_doc: doc
|
||||
install doc/awl.1 $(PREFIX)/share/man/man1
|
||||
install doc/$(PROG).1 $(MANPATH)/man1
|
||||
@echo ""
|
||||
@echo "Make sure to update your manual database"
|
||||
@echo "'sudo mandb' on Debian/Ubuntu"
|
||||
|
|
35
Mkfile
35
Mkfile
|
@ -1,9 +1,34 @@
|
|||
GO=GO
|
||||
awl: awl.go
|
||||
$GO build -o awl
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
GO = go
|
||||
SCDOC = scdoc
|
||||
PROG = awl
|
||||
LDFLAGS = '-s -w'
|
||||
GOFLAGS = -ldflags=$LDFLAGS
|
||||
CGO_ENABLED = 0
|
||||
|
||||
$PROG:
|
||||
$GO build $GOFLAGS -o $PROG .
|
||||
|
||||
doc: doc/$PROG.1.md
|
||||
$SCDOC < doc/$PROG.1.md > doc/$PROG.1
|
||||
|
||||
install:
|
||||
$GO install .
|
||||
$GO install $GOFLAGS .
|
||||
|
||||
install_doc: doc
|
||||
install doc/$PROG.1 /sys/man/1
|
||||
|
||||
test:
|
||||
$GO test ./...
|
||||
$GO test -v ./... -cover
|
||||
|
||||
fmt:
|
||||
gofmt -w -s .
|
||||
|
||||
vet:
|
||||
$GO vet ./...
|
||||
|
||||
lint: fmt vet
|
||||
|
||||
clean:
|
||||
$GO clean
|
||||
|
|
|
@ -14,12 +14,9 @@ lifting.
|
|||
|
||||
## What awl should do
|
||||
|
||||
- Optimize everything
|
||||
- Make the code less spaghetti (partially completed)
|
||||
- Feature parity with drill
|
||||
- Making a drop-in replacement for drill?
|
||||
- What about dig?
|
||||
- Be a drop-in replacement for dig (and maybe drill)
|
||||
- Support DNS RFCs
|
||||
|
||||
## What awl won't do
|
||||
- Print to files or read from files
|
||||
- Colour outputs (unless?)
|
||||
- Colour outputs
|
||||
|
|
45
cli/cli.go
45
cli/cli.go
|
@ -7,6 +7,7 @@ import (
|
|||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.froth.zone/sam/awl/internal/helpers"
|
||||
"git.froth.zone/sam/awl/util"
|
||||
|
@ -44,13 +45,15 @@ func ParseCLI(version string) (Options, error) {
|
|||
ipv6 = flag.Bool("6", false, "force IPv6", flag.OptShorthand('6'))
|
||||
reverse = flag.Bool("reverse", false, "do a reverse lookup", flag.OptShorthand('x'))
|
||||
|
||||
timeout = flag.Float32("timeout", 1, "Timeout, in `seconds`")
|
||||
dnssec = flag.Bool("dnssec", false, "enable DNSSEC", flag.OptShorthand('D'))
|
||||
truncate = flag.Bool("no-truncate", false, "ignore truncation if a UDP request truncates (default= retry with TCP)")
|
||||
|
||||
tcp = flag.Bool("tcp", false, "use TCP")
|
||||
tls = flag.Bool("tls", false, "use DNS-over-TLS", flag.OptShorthand('T'))
|
||||
https = flag.Bool("https", false, "use DNS-over-HTTPS", flag.OptShorthand('H'))
|
||||
quic = flag.Bool("quic", false, "use DNS-over-QUIC", flag.OptShorthand('Q'))
|
||||
tcp = flag.Bool("tcp", false, "use TCP")
|
||||
dnscrypt = flag.Bool("dnscrypt", false, "use DNSCrypt")
|
||||
tls = flag.Bool("tls", false, "use DNS-over-TLS", flag.OptShorthand('T'))
|
||||
https = flag.Bool("https", false, "use DNS-over-HTTPS", flag.OptShorthand('H'))
|
||||
quic = flag.Bool("quic", false, "use DNS-over-QUIC", flag.OptShorthand('Q'))
|
||||
|
||||
aa = flag.Bool("aa", false, "set/unset AA (Authoratative Answer) flag (default: not set)")
|
||||
ad = flag.Bool("ad", false, "set/unset AD (Authenticated Data) flag (default: not set)")
|
||||
|
@ -66,7 +69,13 @@ func ParseCLI(version string) (Options, error) {
|
|||
xml = flag.Bool("xml", false, "print the result(s) as XML", flag.OptShorthand('X'))
|
||||
yaml = flag.Bool("yaml", false, "print the result(s) as yaml", flag.OptShorthand('y'))
|
||||
|
||||
verbosity = flag.Int("verbosity", 0, "sets verbosity", flag.OptShorthand('v'), flag.OptNoOptDefVal("3"))
|
||||
noQ = flag.Bool("no-question", false, "disable printing the question section")
|
||||
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")
|
||||
noStats = flag.Bool("no-statistics", false, "disable printing the statistics section")
|
||||
|
||||
verbosity = flag.Int("verbosity", 0, "sets verbosity `level`", flag.OptShorthand('v'), flag.OptNoOptDefVal("2"))
|
||||
versionFlag = flag.Bool("version", false, "print version information", flag.OptShorthand('V'))
|
||||
)
|
||||
|
||||
|
@ -87,6 +96,7 @@ func ParseCLI(version string) (Options, error) {
|
|||
DNSSEC: *dnssec,
|
||||
Short: *short,
|
||||
TCP: *tcp,
|
||||
DNSCrypt: *dnscrypt,
|
||||
TLS: *tls,
|
||||
HTTPS: *https,
|
||||
QUIC: *quic,
|
||||
|
@ -104,12 +114,23 @@ func ParseCLI(version string) (Options, error) {
|
|||
XML: *xml,
|
||||
YAML: *yaml,
|
||||
Request: helpers.Request{
|
||||
Type: dns.StringToType[strings.ToUpper(*qType)],
|
||||
Class: dns.StringToClass[strings.ToUpper(*class)],
|
||||
Name: *query,
|
||||
Type: dns.StringToType[strings.ToUpper(*qType)],
|
||||
Class: dns.StringToClass[strings.ToUpper(*class)],
|
||||
Name: *query,
|
||||
Timeout: time.Duration(*timeout * float32(time.Second)),
|
||||
},
|
||||
Display: Displays{
|
||||
Question: !*noQ,
|
||||
Answer: !*noAns,
|
||||
Authority: !*noAuth,
|
||||
Additional: !*noAdd,
|
||||
Statistics: !*noStats,
|
||||
},
|
||||
}
|
||||
|
||||
opts.Logger.Info("POSIX flags parsed")
|
||||
opts.Logger.Debug(fmt.Sprintf("%+v", opts))
|
||||
|
||||
if *versionFlag {
|
||||
fmt.Printf("awl version %s, built with %s\n", version, runtime.Version())
|
||||
return opts, ErrNotError
|
||||
|
@ -122,6 +143,8 @@ func ParseCLI(version string) (Options, error) {
|
|||
opts.Logger.Error(err)
|
||||
return opts, err
|
||||
}
|
||||
opts.Logger.Info("Dig/Drill flags parsed")
|
||||
opts.Logger.Debug(fmt.Sprintf("%+v", opts))
|
||||
|
||||
if opts.Port == 0 {
|
||||
if opts.TLS || opts.QUIC {
|
||||
|
@ -130,11 +153,7 @@ func ParseCLI(version string) (Options, error) {
|
|||
opts.Port = 53
|
||||
}
|
||||
}
|
||||
|
||||
// opts.Request = req
|
||||
|
||||
// Set verbosity to full if just -v is specified
|
||||
// flag.Lookup("verbosity").NoOptDefVal = "3"
|
||||
opts.Logger.Info("Port set to", opts.Port)
|
||||
|
||||
return opts, nil
|
||||
}
|
||||
|
|
183
cli/cli_test.go
183
cli/cli_test.go
|
@ -12,10 +12,20 @@ import (
|
|||
|
||||
func TestEmpty(t *testing.T) {
|
||||
old := os.Args
|
||||
os.Args = []string{""}
|
||||
os.Args = []string{"awl", "-4"}
|
||||
opts, err := cli.ParseCLI("TEST")
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, opts != cli.Options{})
|
||||
assert.Assert(t, (opts.Port == 53))
|
||||
assert.Assert(t, opts.IPv4)
|
||||
os.Args = old
|
||||
}
|
||||
|
||||
func TestTLSPort(t *testing.T) {
|
||||
old := os.Args
|
||||
os.Args = []string{"awl", "-T"}
|
||||
opts, err := cli.ParseCLI("TEST")
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, (opts.Port == 853))
|
||||
os.Args = old
|
||||
}
|
||||
|
||||
|
@ -31,35 +41,17 @@ func TestInvalidDig(t *testing.T) {
|
|||
old := os.Args
|
||||
os.Args = []string{"awl", "+a"}
|
||||
_, err := cli.ParseCLI("TEST")
|
||||
assert.ErrorContains(t, err, "dig: unknown flag given")
|
||||
assert.ErrorContains(t, err, "dig: unknown flag")
|
||||
os.Args = old
|
||||
}
|
||||
|
||||
// func TestArgParse(t *testing.T) {
|
||||
// tests := []struct {
|
||||
// in []string
|
||||
// want helpers.Request
|
||||
// }{
|
||||
// {
|
||||
// []string{"@::1", "localhost", "AAAA"},
|
||||
// helpers.Request{Server: "::1", Type: dns.TypeAAAA, Name: "localhost"},
|
||||
// },
|
||||
// {
|
||||
// []string{"@1.0.0.1", "google.com"},
|
||||
// helpers.Request{Server: "1.0.0.1", Type: dns.TypeA, Name: "google.com"},
|
||||
// },
|
||||
// {
|
||||
// []string{"@8.8.4.4"},
|
||||
// helpers.Request{Server: "8.8.4.4", Type: dns.TypeNS, Name: "."},
|
||||
// },
|
||||
// }
|
||||
// for _, test := range tests {
|
||||
// old := os.Args
|
||||
// act, err := cli.ParseCLI(test.in)
|
||||
// assert.Nil(t, err)
|
||||
// assert.Equal(t, test.want, act)
|
||||
// }
|
||||
// }
|
||||
func TestVersion(t *testing.T) {
|
||||
old := os.Args
|
||||
os.Args = []string{"awl", "--version"}
|
||||
_, err := cli.ParseCLI("TEST")
|
||||
assert.ErrorType(t, err, cli.ErrNotError)
|
||||
os.Args = old
|
||||
}
|
||||
|
||||
func FuzzFlags(f *testing.F) {
|
||||
testcases := []string{"git.froth.zone", "", "!12345", "google.com.edu.org.fr"}
|
||||
|
@ -73,138 +65,3 @@ func FuzzFlags(f *testing.F) {
|
|||
cli.ParseCLI("TEST")
|
||||
})
|
||||
}
|
||||
|
||||
// func TestArgParse(t *testing.T) {
|
||||
// t.Parallel()
|
||||
// tests := []struct {
|
||||
// in []string
|
||||
// want helpers.Request
|
||||
// }{
|
||||
// {
|
||||
// []string{"@::1", "localhost", "AAAA"},
|
||||
// helpers.Request{Server: "::1", Type: dns.TypeAAAA, Name: "localhost"},
|
||||
// },
|
||||
// {
|
||||
// []string{"@1.0.0.1", "google.com"},
|
||||
// helpers.Request{Server: "1.0.0.1", Type: dns.TypeA, Name: "google.com"},
|
||||
// },
|
||||
// {
|
||||
// []string{"@8.8.4.4"},
|
||||
// helpers.Request{Server: "8.8.4.4", Type: dns.TypeNS, Name: "."},
|
||||
// },
|
||||
// }
|
||||
// for _, test := range tests {
|
||||
// act, err := parseArgs(test.in, &query.Options{})
|
||||
// assert.Nil(t, err)
|
||||
// assert.Equal(t, test.want, act)
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestQuery(t *testing.T) {
|
||||
// app := prepareCLI()
|
||||
// args := os.Args[0:1]
|
||||
// args = append(args, "--Treebug")
|
||||
// err := app.Run(args)
|
||||
// assert.NotNil(t, err)
|
||||
// }
|
||||
|
||||
// func TestNoArgs(t *testing.T) {
|
||||
// app := prepareCLI()
|
||||
// args := os.Args[0:1]
|
||||
// args = append(args, "--no-truncate")
|
||||
// err := app.Run(args)
|
||||
// assert.Nil(t, err)
|
||||
// }
|
||||
|
||||
// func TestFlags(t *testing.T) {
|
||||
// app := prepareCLI()
|
||||
// args := os.Args[0:1]
|
||||
// args = append(args, "--debug")
|
||||
// args = append(args, "--short")
|
||||
// args = append(args, "-4")
|
||||
// err := app.Run(args)
|
||||
// assert.Nil(t, err)
|
||||
// }
|
||||
|
||||
// func TestHTTPS(t *testing.T) {
|
||||
// app := prepareCLI()
|
||||
// args := os.Args[0:1]
|
||||
// args = append(args, "-H")
|
||||
// // args = append(args, "@https://cloudflare-dns.com/dns-query")
|
||||
// args = append(args, "git.froth.zone")
|
||||
// err := app.Run(args)
|
||||
// assert.Nil(t, err)
|
||||
// }
|
||||
|
||||
// func TestJSON(t *testing.T) {
|
||||
// app := prepareCLI()
|
||||
// args := os.Args[0:1]
|
||||
// args = append(args, "-j")
|
||||
// args = append(args, "git.froth.zone")
|
||||
// err := app.Run(args)
|
||||
// assert.Nil(t, err)
|
||||
// }
|
||||
|
||||
// func TestTLS(t *testing.T) {
|
||||
// app := prepareCLI()
|
||||
// args := os.Args[0:1]
|
||||
// args = append(args, "-T")
|
||||
// args = append(args, "git.froth.zone")
|
||||
// err := app.Run(args)
|
||||
// assert.Nil(t, err)
|
||||
// }
|
||||
|
||||
// func TestXML(t *testing.T) {
|
||||
// app := prepareCLI()
|
||||
// args := os.Args[0:1]
|
||||
// args = append(args, "-X")
|
||||
// args = append(args, "git.froth.zone")
|
||||
// err := app.Run(args)
|
||||
// assert.Nil(t, err)
|
||||
// }
|
||||
|
||||
// func TestYAML(t *testing.T) {
|
||||
// app := prepareCLI()
|
||||
// args := os.Args[0:1]
|
||||
// args = append(args, "-y")
|
||||
// args = append(args, "git.froth.zone")
|
||||
// err := app.Run(args)
|
||||
// assert.Nil(t, err)
|
||||
// }
|
||||
|
||||
// func TestQUIC(t *testing.T) {
|
||||
// app := prepareCLI()
|
||||
// args := os.Args[0:1]
|
||||
// args = append(args, "-Q")
|
||||
// // args = append(args, "@dns.adguard.com")
|
||||
// args = append(args, "git.froth.zone")
|
||||
// err := app.Run(args)
|
||||
// assert.Nil(t, err)
|
||||
// }
|
||||
|
||||
// func TestReverse(t *testing.T) {
|
||||
// app := prepareCLI()
|
||||
// args := os.Args[0:1]
|
||||
// args = append(args, "-x")
|
||||
// args = append(args, "8.8.8.8")
|
||||
// err := app.Run(args)
|
||||
// assert.Nil(t, err)
|
||||
// }
|
||||
|
||||
// func FuzzCli(f *testing.F) {
|
||||
// testcases := []string{"git.froth.zone", "", "!12345", "google.com.edu.org.fr"}
|
||||
// for _, tc := range testcases {
|
||||
// f.Add(tc)
|
||||
// }
|
||||
|
||||
// f.Fuzz(func(t *testing.T, orig string) {
|
||||
// app := prepareCLI()
|
||||
// args := os.Args[0:1]
|
||||
// args = append(args, orig)
|
||||
// err := app.Run(args)
|
||||
// if err != nil {
|
||||
// require.ErrorContains(t, err, "domain must be fully qualified")
|
||||
// }
|
||||
// require.Nil(t, err)
|
||||
// })
|
||||
// }
|
||||
|
|
26
cli/dig.go
26
cli/dig.go
|
@ -11,6 +11,8 @@ import (
|
|||
func ParseDig(arg string, opts *Options) error {
|
||||
// returns true if the flag starts with a no
|
||||
isNo := !strings.HasPrefix(arg, "no")
|
||||
opts.Logger.Info("Setting", arg, "to", isNo)
|
||||
|
||||
switch arg {
|
||||
// Set DNS query flags
|
||||
case "aa", "aaflag", "aaonly", "noaa", "noaaflag", "noaaonly":
|
||||
|
@ -49,8 +51,30 @@ func ParseDig(arg string, opts *Options) error {
|
|||
opts.YAML = isNo
|
||||
// End formatting
|
||||
|
||||
// Output
|
||||
// TODO: get this to work
|
||||
// case "comments", "nocomments":
|
||||
// opts.Display.Comments = isNo
|
||||
case "question", "noquestion":
|
||||
opts.Display.Question = isNo
|
||||
case "answer", "noanswer":
|
||||
opts.Display.Answer = isNo
|
||||
case "authority", "noauthority":
|
||||
opts.Display.Authority = isNo
|
||||
case "additional", "noadditional":
|
||||
opts.Display.Additional = isNo
|
||||
case "stats", "nostats":
|
||||
opts.Display.Statistics = isNo
|
||||
|
||||
case "all", "noall":
|
||||
opts.Display.Question = isNo
|
||||
opts.Display.Answer = isNo
|
||||
opts.Display.Authority = isNo
|
||||
opts.Display.Additional = isNo
|
||||
opts.Display.Statistics = isNo
|
||||
|
||||
default:
|
||||
return fmt.Errorf("dig: unknown flag given")
|
||||
return fmt.Errorf("dig: unknown flag %s given", arg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"git.froth.zone/sam/awl/cli"
|
||||
"git.froth.zone/sam/awl/util"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
|
@ -27,6 +28,12 @@ func FuzzDig(f *testing.F) {
|
|||
"json", "nojson",
|
||||
"xml", "noxml",
|
||||
"yaml", "noyaml",
|
||||
"question", "noquestion",
|
||||
"answer", "noanswer",
|
||||
"authority", "noauthority",
|
||||
"additional", "noadditional",
|
||||
"stats", "nostats",
|
||||
"all", "noall",
|
||||
"invalid",
|
||||
}
|
||||
for _, tc := range seeds {
|
||||
|
@ -35,9 +42,10 @@ func FuzzDig(f *testing.F) {
|
|||
|
||||
f.Fuzz(func(t *testing.T, orig string) {
|
||||
opts := new(cli.Options)
|
||||
opts.Logger = util.InitLogger(0)
|
||||
err := cli.ParseDig(orig, opts)
|
||||
if err != nil {
|
||||
assert.ErrorContains(t, err, "unknown flag given")
|
||||
assert.ErrorContains(t, err, "unknown flag")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
44
cli/misc.go
44
cli/misc.go
|
@ -23,23 +23,48 @@ func ParseMiscArgs(args []string, opts *Options) error {
|
|||
switch {
|
||||
// If it starts with @, it's a DNS server
|
||||
case strings.HasPrefix(arg, "@"):
|
||||
opts.Request.Server = arg[1:]
|
||||
arg = arg[1:]
|
||||
// Automatically set flags based on URI header
|
||||
opts.Logger.Info(arg, "detected as a server")
|
||||
switch {
|
||||
case strings.HasPrefix(arg, "tls://"):
|
||||
opts.TLS = true
|
||||
opts.Request.Server = arg[6:]
|
||||
opts.Logger.Info("DNS-over-TLS implicitly set")
|
||||
case strings.HasPrefix(arg, "https://"):
|
||||
opts.HTTPS = true
|
||||
opts.Request.Server = arg
|
||||
opts.Logger.Info("DNS-over-HTTPS implicitly set")
|
||||
case strings.HasPrefix(arg, "quic://"):
|
||||
opts.QUIC = true
|
||||
opts.Request.Server = arg[7:]
|
||||
opts.Logger.Info("DNS-over-QUIC implicitly set.")
|
||||
case strings.HasPrefix(arg, "sdns://"):
|
||||
opts.DNSCrypt = true
|
||||
opts.Request.Server = arg
|
||||
opts.Logger.Info("DNSCrypt implicitly set")
|
||||
default:
|
||||
opts.Request.Server = arg
|
||||
}
|
||||
case strings.Contains(arg, "."):
|
||||
opts.Logger.Info(arg, "detected as a domain name")
|
||||
opts.Request.Name, err = idna.ToASCII(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case ok:
|
||||
opts.Logger.Info(arg, "detected as a type")
|
||||
// If it's a DNS request, it's a DNS request (obviously)
|
||||
opts.Request.Type = r
|
||||
case strings.HasPrefix(arg, "+"):
|
||||
opts.Logger.Info(arg, "detected as a dig query")
|
||||
// Dig-style +queries
|
||||
err = ParseDig(strings.ToLower(arg[1:]), opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
|
||||
opts.Logger.Info(arg, "is unknown. Assuming domain")
|
||||
opts.Request.Name, err = idna.ToASCII(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -49,18 +74,25 @@ func ParseMiscArgs(args []string, opts *Options) error {
|
|||
|
||||
// If nothing was set, set a default
|
||||
if opts.Request.Name == "" {
|
||||
opts.Logger.Info("Domain not specified, making a default")
|
||||
opts.Request.Name = "."
|
||||
if opts.Request.Type == 0 {
|
||||
opts.Request.Type = dns.StringToType["NS"]
|
||||
}
|
||||
} else {
|
||||
opts.Logger.Info("Query not specified, making an \"A\" query")
|
||||
if opts.Request.Type == 0 {
|
||||
opts.Request.Type = dns.StringToType["A"]
|
||||
}
|
||||
}
|
||||
//
|
||||
if opts.Request.Server == "" {
|
||||
opts.Logger.Info("Server not specified, selecting a default")
|
||||
// Set "defaults" for each if there is no input
|
||||
switch {
|
||||
case opts.DNSCrypt:
|
||||
// This is adguard
|
||||
opts.Request.Server = "sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20"
|
||||
case opts.TLS:
|
||||
opts.Request.Server = "dns.google"
|
||||
case opts.HTTPS:
|
||||
|
@ -70,8 +102,11 @@ func ParseMiscArgs(args []string, opts *Options) error {
|
|||
default:
|
||||
resolv, err := conf.GetDNSConfig()
|
||||
if err != nil {
|
||||
opts.Request.Server = "9.9.9.9"
|
||||
// :^)
|
||||
opts.Logger.Error("Could not query system for server. Using default")
|
||||
opts.Request.Server = "95.216.99.249"
|
||||
} else {
|
||||
// Make sure that if IPv4 or IPv6 is asked for it actually uses it
|
||||
for _, srv := range resolv.Servers {
|
||||
if opts.IPv4 {
|
||||
if strings.Contains(srv, ".") {
|
||||
|
@ -92,9 +127,11 @@ func ParseMiscArgs(args []string, opts *Options) error {
|
|||
}
|
||||
}
|
||||
}
|
||||
opts.Logger.Info("DNS server set to", opts.Request.Server)
|
||||
|
||||
// Make reverse adresses proper addresses
|
||||
if opts.Reverse {
|
||||
opts.Logger.Info("Making reverse DNS query proper *.arpa domain")
|
||||
if dns.TypeToString[opts.Request.Type] == "A" {
|
||||
opts.Request.Type = dns.StringToType["PTR"]
|
||||
}
|
||||
|
@ -107,6 +144,7 @@ func ParseMiscArgs(args []string, opts *Options) error {
|
|||
// if the domain is not canonical, make it canonical
|
||||
if !strings.HasSuffix(opts.Request.Name, ".") {
|
||||
opts.Request.Name = fmt.Sprintf("%s.", opts.Request.Name)
|
||||
opts.Logger.Debug("Domain made canonical")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
package cli_test
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"git.froth.zone/sam/awl/cli"
|
||||
"git.froth.zone/sam/awl/util"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"gotest.tools/v3/assert"
|
||||
|
@ -20,6 +22,7 @@ func TestParseArgs(t *testing.T) {
|
|||
"+ignore",
|
||||
}
|
||||
opts := new(cli.Options)
|
||||
opts.Logger = util.InitLogger(0)
|
||||
err := cli.ParseMiscArgs(args, opts)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, opts.Request.Name, "go.dev.")
|
||||
|
@ -32,6 +35,7 @@ func TestParseNoInput(t *testing.T) {
|
|||
t.Parallel()
|
||||
args := []string{}
|
||||
opts := new(cli.Options)
|
||||
opts.Logger = util.InitLogger(0)
|
||||
err := cli.ParseMiscArgs(args, opts)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, opts.Request.Name, ".")
|
||||
|
@ -44,6 +48,7 @@ func TestParseA(t *testing.T) {
|
|||
"golang.org.",
|
||||
}
|
||||
opts := new(cli.Options)
|
||||
opts.Logger = util.InitLogger(0)
|
||||
err := cli.ParseMiscArgs(args, opts)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, opts.Request.Name, "golang.org.")
|
||||
|
@ -54,18 +59,30 @@ func TestParsePTR(t *testing.T) {
|
|||
t.Parallel()
|
||||
args := []string{"8.8.8.8"}
|
||||
opts := new(cli.Options)
|
||||
opts.Logger = util.InitLogger(0)
|
||||
opts.Reverse = true
|
||||
err := cli.ParseMiscArgs(args, opts)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, opts.Request.Type, dns.StringToType["PTR"])
|
||||
}
|
||||
|
||||
func TestParseInvalidPTR(t *testing.T) {
|
||||
t.Parallel()
|
||||
args := []string{"8.88.8"}
|
||||
opts := new(cli.Options)
|
||||
opts.Logger = util.InitLogger(0)
|
||||
opts.Reverse = true
|
||||
err := cli.ParseMiscArgs(args, opts)
|
||||
assert.ErrorContains(t, err, "unrecognized address")
|
||||
}
|
||||
|
||||
func TestDefaultServer(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{"DNSCRYPT", "sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20"},
|
||||
{"TLS", "dns.google"},
|
||||
{"HTTPS", "https://dns.cloudflare.com/dns-query"},
|
||||
{"QUIC", "dns.adguard.com"},
|
||||
|
@ -76,7 +93,10 @@ func TestDefaultServer(t *testing.T) {
|
|||
t.Parallel()
|
||||
args := []string{}
|
||||
opts := new(cli.Options)
|
||||
opts.Logger = util.InitLogger(0)
|
||||
switch test.in {
|
||||
case "DNSCRYPT":
|
||||
opts.DNSCrypt = true
|
||||
case "TLS":
|
||||
opts.TLS = true
|
||||
case "HTTPS":
|
||||
|
@ -91,6 +111,40 @@ func TestDefaultServer(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestFlagSetting(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
in []string
|
||||
}{
|
||||
{[]string{"@sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20"}},
|
||||
{[]string{"@tls://dns.google"}},
|
||||
{[]string{"@https://dns.cloudflare.com/dns-query"}},
|
||||
{[]string{"@quic://dns.adguard.com"}},
|
||||
}
|
||||
for i, test := range tests {
|
||||
test := test
|
||||
i := i
|
||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||
opts := new(cli.Options)
|
||||
opts.Logger = util.InitLogger(0)
|
||||
t.Parallel()
|
||||
err := cli.ParseMiscArgs(test.in, opts)
|
||||
assert.NilError(t, err)
|
||||
switch i {
|
||||
case 0:
|
||||
assert.Assert(t, opts.DNSCrypt)
|
||||
case 1:
|
||||
assert.Assert(t, opts.TLS)
|
||||
case 2:
|
||||
assert.Assert(t, opts.HTTPS)
|
||||
case 3:
|
||||
assert.Assert(t, opts.QUIC)
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzParseArgs(f *testing.F) {
|
||||
cases := []string{
|
||||
"go.dev",
|
||||
|
@ -105,6 +159,7 @@ func FuzzParseArgs(f *testing.F) {
|
|||
f.Fuzz(func(t *testing.T, arg string) {
|
||||
args := []string{arg}
|
||||
opts := new(cli.Options)
|
||||
opts.Logger = util.InitLogger(0)
|
||||
//nolint:errcheck // Only make sure the program does not crash
|
||||
cli.ParseMiscArgs(args, opts)
|
||||
})
|
||||
|
|
|
@ -17,6 +17,7 @@ type Options struct {
|
|||
IPv6 bool // Force IPv6
|
||||
DNSSEC bool // Enable DNSSEC
|
||||
TCP bool // Query with TCP
|
||||
DNSCrypt bool // Query over DNSCrypt
|
||||
TLS bool // Query over TLS
|
||||
HTTPS bool // Query over HTTPS
|
||||
QUIC bool // Query over QUIC
|
||||
|
@ -31,13 +32,24 @@ type Options struct {
|
|||
Z bool // Set Z (Zero)
|
||||
Reverse bool // Make reverse query
|
||||
Verbosity int // Set logawl verbosity
|
||||
HumanTTL bool // Make TTL human readable
|
||||
Short bool // Short output
|
||||
JSON bool // Outout as JSON
|
||||
XML bool // Output as XML
|
||||
YAML bool // Output at YAML
|
||||
// HumanTTL bool // Make TTL human readable
|
||||
Short bool // Short output
|
||||
JSON bool // Outout as JSON
|
||||
XML bool // Output as XML
|
||||
YAML bool // Output at YAML
|
||||
|
||||
Request helpers.Request
|
||||
Display Displays // Display options
|
||||
Request helpers.Request // DNS reuqest
|
||||
}
|
||||
|
||||
// What to (and not to) display
|
||||
type Displays struct {
|
||||
// Comments bool
|
||||
Question bool // QUESTION SECTION
|
||||
Answer bool // ANSWER SECTION
|
||||
Authority bool // AUTHORITY SECTION
|
||||
Additional bool // ADDITIONAL SECTION
|
||||
Statistics bool // Query time, message size, etc.
|
||||
}
|
||||
|
||||
var ErrNotError = errors.New("not an error")
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"git.froth.zone/sam/awl/conf"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
|
|
51
doc/awl.1
51
doc/awl.1
|
@ -5,10 +5,10 @@
|
|||
.nh
|
||||
.ad l
|
||||
.\" Begin generated content:
|
||||
.TH "awl" "1" "2022-07-21"
|
||||
.TH "awl" "1" "2022-07-22"
|
||||
.PP
|
||||
.SH NAME
|
||||
awl - drill, writ small
|
||||
awl - DNS lookup tool
|
||||
.PP
|
||||
.SH SYNOPSIS
|
||||
\fBawl\fR [ \fIOPTIONS\fR ] \fIname\fR [ \fI@server\fR ] [ \fItype\fR ]
|
||||
|
@ -23,17 +23,17 @@ where
|
|||
.PP
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
\fBawl\fR (\fBa\fRwls \fBw\fRant \fBl\fRicorice) is a simple tool designed to make DNS queries, much
|
||||
like the venerable \fIdig\fR(1).\& An awl is a tool used to make small holes, typically
|
||||
used in tannery (leatherworking).\&
|
||||
\fBawl\fR (\fBa\fRwls \fBw\fRant \fBl\fRicorice) is a simple tool designed to make DNS queries,
|
||||
much like the venerable \fIdig\fR(1).\& An awl is a tool used to make small holes,
|
||||
typically used in tannery (leatherworking).\&
|
||||
.PP
|
||||
Written in Go, \fBawl\fR is designed to be a more "modern" version of \fIdrill\fR(1) by
|
||||
including some more RFCs and output options.\& \fBawl\fR is still heavily
|
||||
\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
|
||||
.SH OPTIONS
|
||||
.RS 4
|
||||
Dig-like queries are supported, see dig(1)
|
||||
Dig-like +[no]flags are supported, see dig(1)
|
||||
.PP
|
||||
\fB-D\fR, \fB--dnssec\fR, \fB+dnssec\fR
|
||||
.br
|
||||
|
@ -100,9 +100,13 @@ Dig-like queries are supported, see dig(1)
|
|||
.br
|
||||
Use TCP for the query (see \fIRFC 7766\fR)
|
||||
.PP
|
||||
\fB--dnscrypt\fR
|
||||
.br
|
||||
Use DNSCrypt
|
||||
.PP
|
||||
\fB-T\fR, \fB--tls\fR, \fB+tls\fR
|
||||
.br
|
||||
Use DNS-over-TLS, implies \fB-t\fR (see \fIRFC 7858\fR)
|
||||
Use DNS-over-TLS, implies \fB--tcp\fR (see \fIRFC 7858\fR)
|
||||
.PP
|
||||
\fB-H\fR.\& \fB--https\fR, \fB+https\fR
|
||||
.br
|
||||
|
@ -155,7 +159,34 @@ Dig-like queries are supported, see dig(1)
|
|||
(Set, Unset) RA (Recursion Available) flag
|
||||
.PP
|
||||
.RE
|
||||
.SS Output Options
|
||||
.SS Output Display
|
||||
.RS 4
|
||||
\fB--no-question\fR, \fB+noquestion\fR
|
||||
.br
|
||||
Do not display the Question section
|
||||
.PP
|
||||
\fB--no-answer\fR, \fB+noanswer\fR
|
||||
.br
|
||||
Do not display the Answer section
|
||||
.PP
|
||||
\fB--no-answer\fR, \fB+noanswer\fR
|
||||
.br
|
||||
Do not display the Answer section
|
||||
.PP
|
||||
\fB--no-authority\fR, \fB+noauthority\fR
|
||||
.br
|
||||
Do not display the Authority section
|
||||
.PP
|
||||
\fB--no-additional\fR, \fB+noadditional\fR
|
||||
.br
|
||||
Do not display the Additional section
|
||||
.PP
|
||||
\fB--no-statistics\fR, \fB+nostatistics\fR
|
||||
.br
|
||||
Do not display the Statistics (additional comments) section
|
||||
.PP
|
||||
.RE
|
||||
.SS Output Formats
|
||||
.RS 4
|
||||
\fB-j\fR, \fB--json\fR, \fB+json\fR
|
||||
.br
|
||||
|
|
47
doc/awl.1.md
47
doc/awl.1.md
|
@ -1,7 +1,7 @@
|
|||
awl(1)
|
||||
|
||||
# NAME
|
||||
awl - drill, writ small
|
||||
awl - DNS lookup tool
|
||||
|
||||
# SYNOPSIS
|
||||
*awl* [ _OPTIONS_ ] _name_ [ _@server_ ] [ _type_ ]++
|
||||
|
@ -13,16 +13,16 @@ _type_ is the DNS resource type (*example: AAAA*)
|
|||
|
||||
# DESCRIPTION
|
||||
|
||||
*awl* (*a*wls *w*ant *l*icorice) is a simple tool designed to make DNS queries, much
|
||||
like the venerable _dig_(1). An awl is a tool used to make small holes, typically
|
||||
used in tannery (leatherworking).
|
||||
*awl* (*a*wls *w*ant *l*icorice) is a simple tool designed to make DNS queries,
|
||||
much like the venerable _dig_(1). An awl is a tool used to make small holes,
|
||||
typically used in tannery (leatherworking).
|
||||
|
||||
Written in Go, *awl* is designed to be a more "modern" version of _drill_(1) by
|
||||
including some more RFCs and output options. *awl* is still heavily
|
||||
*awl* is designed to be a more "modern" version of _drill_(1) by including
|
||||
some more recent RFCs and output options. *awl* is still heavily
|
||||
Work-In-Progress so some features may get added or removed.
|
||||
|
||||
# OPTIONS
|
||||
Dig-like queries are supported, see dig(1)
|
||||
Dig-like +[no]flags are supported, see dig(1)
|
||||
|
||||
*-D*, *--dnssec*, *+dnssec*++
|
||||
Enable DNSSEC. This needs to be manually enabled.
|
||||
|
@ -63,11 +63,14 @@ _Default Ports_:
|
|||
*--no-truncate*, *+ignore*++
|
||||
Ignore UDP truncation (by default, awl _retries with TCP_)
|
||||
|
||||
*-t*, *--tcp*, *+tcp*, *+vc*++
|
||||
*--tcp*, *+tcp*, *+vc*++
|
||||
Use TCP for the query (see _RFC 7766_)
|
||||
|
||||
*--dnscrypt*++
|
||||
Use DNSCrypt
|
||||
|
||||
*-T*, *--tls*, *+tls*++
|
||||
Use DNS-over-TLS, implies *-t* (see _RFC 7858_)
|
||||
Use DNS-over-TLS, implies *--tcp* (see _RFC 7858_)
|
||||
|
||||
*-H*. *--https*, *+https*++
|
||||
Use DNS-over-HTTPS (see _RFC 8484_)
|
||||
|
@ -79,6 +82,9 @@ _Default Ports_:
|
|||
Do a reverse lookup. Sets default _type_ to PTR.++
|
||||
*awl* automatically makes an IP or phone number canonical.
|
||||
|
||||
*--timeout* _seconds_
|
||||
Set the timeout period. Floating point numbers are accepted.
|
||||
|
||||
## DNS Flags
|
||||
|
||||
*--aa=[false]*, *+[no]aaflag*++
|
||||
|
@ -105,7 +111,26 @@ _Default Ports_:
|
|||
*--ra=[false]*, *+[no]raflag*++
|
||||
(Set, Unset) RA (Recursion Available) flag
|
||||
|
||||
## Output Options
|
||||
## Output Display
|
||||
*--no-question*, *+noquestion*++
|
||||
Do not display the Question section
|
||||
|
||||
*--no-answer*, *+noanswer*++
|
||||
Do not display the Answer section
|
||||
|
||||
*--no-answer*, *+noanswer*++
|
||||
Do not display the Answer section
|
||||
|
||||
*--no-authority*, *+noauthority*++
|
||||
Do not display the Authority section
|
||||
|
||||
*--no-additional*, *+noadditional*++
|
||||
Do not display the Additional section
|
||||
|
||||
*--no-statistics*, *+nostats*++
|
||||
Do not display the Statistics (additional comments) section
|
||||
|
||||
## Output Formats
|
||||
*-j*, *--json*, *+json*++
|
||||
Print the query results as JSON.
|
||||
|
||||
|
@ -116,7 +141,7 @@ _Default Ports_:
|
|||
Print the query results as YAML.
|
||||
|
||||
*-s*, *--short*, *+short*++
|
||||
Print just the results.
|
||||
Print just the address of the answer.
|
||||
|
||||
# EXAMPLES
|
||||
```
|
||||
|
|
5
go.mod
5
go.mod
|
@ -3,6 +3,7 @@ module git.froth.zone/sam/awl
|
|||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.3
|
||||
github.com/lucas-clemente/quic-go v0.28.0
|
||||
github.com/miekg/dns v1.1.50
|
||||
github.com/stefansundin/go-zflag v1.1.1
|
||||
|
@ -12,6 +13,10 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
github.com/AdguardTeam/golibs v0.4.2 // indirect
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
|
||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect
|
||||
github.com/ameshkov/dnsstamps v1.0.1 // indirect
|
||||
github.com/google/go-cmp v0.5.5 // indirect
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 // indirect
|
||||
)
|
||||
|
|
21
go.sum
21
go.sum
|
@ -7,7 +7,17 @@ dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBr
|
|||
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/AdguardTeam/golibs v0.4.2 h1:7M28oTZFoFwNmp8eGPb3ImmYbxGaJLyQXeIFVHjME0o=
|
||||
github.com/AdguardTeam/golibs v0.4.2/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
|
||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.3 h1:X9UP5AHtwp46Ji+sGFfF/1Is6OPI/SjxLqhKpx0P5UI=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.3/go.mod h1:xJB9cE1/GF+NB6EEQqRlkoa4bjcV2w7VYn1G+zVq7Bs=
|
||||
github.com/ameshkov/dnsstamps v1.0.1 h1:LhGvgWDzhNJh+kBQd/AfUlq1vfVe109huiXw4JhnPug=
|
||||
github.com/ameshkov/dnsstamps v1.0.1/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
|
@ -68,6 +78,7 @@ github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:Fecb
|
|||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
|
@ -92,6 +103,7 @@ github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 h1:7m/WlWcSROrcK5NxuXaxYD32B
|
|||
github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
@ -153,6 +165,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
|
@ -169,6 +182,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
|
@ -176,6 +190,7 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
|
|||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
|
@ -191,6 +206,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
|
@ -220,10 +237,12 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -256,6 +275,7 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
|
|||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
|
@ -302,6 +322,7 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
|
|||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
|
|
|
@ -16,8 +16,9 @@ type Response struct {
|
|||
|
||||
// A structure for a DNS query.
|
||||
type Request struct {
|
||||
Server string `json:"server"` // The server to make the DNS request from
|
||||
Type uint16 `json:"request"` // The type of request
|
||||
Class uint16 `json:"class"` // DNS Class
|
||||
Name string `json:"name"` // The domain name to make a DNS request for
|
||||
Server string `json:"server"` // The server to make the DNS request from
|
||||
Type uint16 `json:"request"` // The type of request
|
||||
Class uint16 `json:"class"` // DNS Class
|
||||
Name string `json:"name"` // The domain name to make a DNS request for
|
||||
Timeout time.Duration // The maximum timeout
|
||||
}
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package structs
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// The DNS response
|
||||
type Response struct {
|
||||
DNS *dns.Msg // The full DNS response
|
||||
RTT time.Duration `json:"rtt"` // The time it took to make the DNS query
|
||||
}
|
||||
|
||||
// A structure for a DNS query
|
||||
type Request struct {
|
||||
Server string `json:"server"` // The server to make the DNS request from
|
||||
Type uint16 `json:"request"` // The type of request
|
||||
Class uint16 `json:"class"` // DNS Class
|
||||
Name string `json:"name"` // The domain name to make a DNS request for
|
||||
}
|
50
main.go
50
main.go
|
@ -8,6 +8,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -36,6 +37,7 @@ func main() {
|
|||
}
|
||||
switch {
|
||||
case opts.JSON:
|
||||
opts.Logger.Info("Printing as JSON")
|
||||
json, err := json.MarshalIndent(resp.DNS, "", " ")
|
||||
if err != nil {
|
||||
opts.Logger.Fatal(err)
|
||||
|
@ -43,6 +45,7 @@ func main() {
|
|||
}
|
||||
fmt.Println(string(json))
|
||||
case opts.XML:
|
||||
opts.Logger.Info("Printing as XML")
|
||||
xml, err := xml.MarshalIndent(resp.DNS, "", " ")
|
||||
if err != nil {
|
||||
opts.Logger.Fatal(err)
|
||||
|
@ -50,6 +53,7 @@ func main() {
|
|||
}
|
||||
fmt.Println(string(xml))
|
||||
case opts.YAML:
|
||||
opts.Logger.Info("Printing as YAML")
|
||||
yaml, err := yaml.Marshal(resp.DNS)
|
||||
if err != nil {
|
||||
opts.Logger.Fatal(err)
|
||||
|
@ -59,11 +63,49 @@ func main() {
|
|||
default:
|
||||
if !opts.Short {
|
||||
// Print everything
|
||||
|
||||
if !opts.Display.Question {
|
||||
resp.DNS.Question = nil
|
||||
opts.Logger.Info("Disabled question display")
|
||||
}
|
||||
if !opts.Display.Answer {
|
||||
resp.DNS.Answer = nil
|
||||
opts.Logger.Info("Disabled answer display")
|
||||
}
|
||||
if !opts.Display.Authority {
|
||||
resp.DNS.Ns = nil
|
||||
opts.Logger.Info("Disabled authority display")
|
||||
}
|
||||
if !opts.Display.Additional {
|
||||
resp.DNS.Extra = nil
|
||||
opts.Logger.Info("Disabled additional display")
|
||||
}
|
||||
|
||||
fmt.Println(resp.DNS)
|
||||
fmt.Println(";; Query time:", resp.RTT)
|
||||
fmt.Println(";; SERVER:", opts.Request.Server)
|
||||
fmt.Println(";; WHEN:", time.Now().Format(time.RFC1123Z))
|
||||
fmt.Println(";; MSG SIZE rcvd:", resp.DNS.Len())
|
||||
|
||||
if opts.Display.Statistics {
|
||||
fmt.Println(";; Query time:", resp.RTT)
|
||||
|
||||
// Add extra information to server string
|
||||
var extra string
|
||||
switch {
|
||||
case opts.TCP:
|
||||
extra = ":" + strconv.Itoa(opts.Port) + " (TCP)"
|
||||
case opts.TLS:
|
||||
extra = ":" + strconv.Itoa(opts.Port) + " (TLS)"
|
||||
case opts.HTTPS, opts.DNSCrypt:
|
||||
extra = ""
|
||||
case opts.QUIC:
|
||||
extra = ":" + strconv.Itoa(opts.Port) + " (QUIC)"
|
||||
default:
|
||||
extra = ":" + strconv.Itoa(opts.Port) + " (UDP)"
|
||||
}
|
||||
|
||||
fmt.Println(";; SERVER:", opts.Request.Server+extra)
|
||||
fmt.Println(";; WHEN:", time.Now().Format(time.RFC1123Z))
|
||||
fmt.Println(";; MSG SIZE rcvd:", resp.DNS.Len())
|
||||
}
|
||||
|
||||
} else {
|
||||
// Print just the responses, nothing else
|
||||
for _, res := range resp.DNS.Answer {
|
||||
|
|
58
query/DNSCrypt.go
Normal file
58
query/DNSCrypt.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package query
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.froth.zone/sam/awl/cli"
|
||||
"git.froth.zone/sam/awl/internal/helpers"
|
||||
|
||||
"github.com/ameshkov/dnscrypt/v2"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type DNSCryptResolver struct {
|
||||
opts cli.Options
|
||||
}
|
||||
|
||||
func (r *DNSCryptResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
|
||||
|
||||
client := dnscrypt.Client{
|
||||
Timeout: r.opts.Request.Timeout,
|
||||
UDPSize: 1232,
|
||||
}
|
||||
|
||||
if r.opts.TCP || r.opts.TLS {
|
||||
client.Net = "tcp"
|
||||
} else {
|
||||
client.Net = "udp"
|
||||
}
|
||||
|
||||
switch {
|
||||
case r.opts.IPv4:
|
||||
client.Net += "4"
|
||||
case r.opts.IPv6:
|
||||
client.Net += "6"
|
||||
}
|
||||
r.opts.Logger.Debug("Using", client.Net, "for making the request")
|
||||
|
||||
resolverInf, err := client.Dial(r.opts.Request.Server)
|
||||
if err != nil {
|
||||
return helpers.Response{}, err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
res, err := client.Exchange(msg, resolverInf)
|
||||
rtt := time.Since(now)
|
||||
|
||||
if err != nil {
|
||||
return helpers.Response{}, err
|
||||
}
|
||||
r.opts.Logger.Info("Request successful")
|
||||
|
||||
return helpers.Response{
|
||||
DNS: res,
|
||||
RTT: rtt,
|
||||
}, nil
|
||||
}
|
52
query/DNSCrypt_test.go
Normal file
52
query/DNSCrypt_test.go
Normal file
|
@ -0,0 +1,52 @@
|
|||
// 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 TestDNSCrypt(t *testing.T) {
|
||||
tests := []struct {
|
||||
opt cli.Options
|
||||
}{
|
||||
{
|
||||
cli.Options{
|
||||
Logger: util.InitLogger(0),
|
||||
DNSCrypt: true,
|
||||
Request: helpers.Request{
|
||||
Server: "sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20",
|
||||
Type: dns.TypeA,
|
||||
Name: "example.com.",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
cli.Options{
|
||||
Logger: util.InitLogger(0),
|
||||
DNSCrypt: true,
|
||||
TCP: true,
|
||||
IPv4: true,
|
||||
Request: helpers.Request{
|
||||
Server: "sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_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{})
|
||||
}
|
||||
|
||||
}
|
|
@ -15,22 +15,23 @@ import (
|
|||
)
|
||||
|
||||
type HTTPSResolver struct {
|
||||
server string
|
||||
opts cli.Options
|
||||
opts cli.Options
|
||||
}
|
||||
|
||||
func (r *HTTPSResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
|
||||
var resp helpers.Response
|
||||
httpR := &http.Client{}
|
||||
httpR := &http.Client{
|
||||
Timeout: r.opts.Request.Timeout,
|
||||
}
|
||||
buf, err := msg.Pack()
|
||||
if err != nil {
|
||||
return helpers.Response{}, err
|
||||
}
|
||||
r.opts.Logger.Debug("making DoH request")
|
||||
// query := server + "?dns=" + base64.RawURLEncoding.EncodeToString(buf)
|
||||
req, err := http.NewRequest("POST", r.server, bytes.NewBuffer(buf))
|
||||
req, err := http.NewRequest("POST", r.opts.Request.Server, bytes.NewBuffer(buf))
|
||||
if err != nil {
|
||||
return helpers.Response{}, fmt.Errorf("DoH: %s", err.Error())
|
||||
return helpers.Response{}, fmt.Errorf("DoH: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/dns-message")
|
||||
req.Header.Set("Accept", "application/dns-message")
|
||||
|
@ -40,7 +41,7 @@ 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: %s", err.Error())
|
||||
return helpers.Response{}, fmt.Errorf("DoH HTTP request error: %w", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
|
@ -50,13 +51,13 @@ func (r *HTTPSResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
|
|||
|
||||
fullRes, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return helpers.Response{}, fmt.Errorf("DoH body read error: %s", err.Error())
|
||||
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: %s", err.Error())
|
||||
return helpers.Response{}, fmt.Errorf("DoH dns message unpack error: %w", err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
|
|
|
@ -22,21 +22,18 @@ func TestResolveHTTPS(t *testing.T) {
|
|||
opts := cli.Options{
|
||||
HTTPS: true,
|
||||
Logger: util.InitLogger(0),
|
||||
Request: helpers.Request{
|
||||
Server: "https://dns9.quad9.net/dns-query",
|
||||
Type: dns.TypeA,
|
||||
Name: "git.froth.zone.",
|
||||
},
|
||||
}
|
||||
testCase := helpers.Request{Server: "dns9.quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone"}
|
||||
resolver, err := query.LoadResolver(testCase.Server, opts)
|
||||
// testCase := helpers.Request{Server: "https://dns9.quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone."}
|
||||
resolver, err := query.LoadResolver(opts)
|
||||
assert.NilError(t, err)
|
||||
|
||||
if !strings.HasPrefix(testCase.Server, "https://") {
|
||||
testCase.Server = "https://" + testCase.Server
|
||||
}
|
||||
// if the domain is not canonical, make it canonical
|
||||
if !strings.HasSuffix(testCase.Name, ".") {
|
||||
testCase.Name = fmt.Sprintf("%s.", testCase.Name)
|
||||
}
|
||||
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(testCase.Name, testCase.Type)
|
||||
msg.SetQuestion(opts.Request.Name, opts.Request.Type)
|
||||
// msg = msg.SetQuestion(testCase.Name, testCase.Type)
|
||||
res, err := resolver.LookUp(msg)
|
||||
assert.NilError(t, err)
|
||||
|
@ -51,7 +48,7 @@ func Test2ResolveHTTPS(t *testing.T) {
|
|||
}
|
||||
var err error
|
||||
testCase := helpers.Request{Server: "dns9.quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone"}
|
||||
resolver, err := query.LoadResolver(testCase.Server, opts)
|
||||
resolver, err := query.LoadResolver(opts)
|
||||
assert.NilError(t, err)
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(testCase.Name, testCase.Type)
|
||||
|
@ -69,14 +66,11 @@ func Test3ResolveHTTPS(t *testing.T) {
|
|||
}
|
||||
var err error
|
||||
testCase := helpers.Request{Server: "dns9..quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone."}
|
||||
if !strings.HasPrefix(testCase.Server, "https://") {
|
||||
testCase.Server = "https://" + testCase.Server
|
||||
}
|
||||
// if the domain is not canonical, make it canonical
|
||||
if !strings.HasSuffix(testCase.Name, ".") {
|
||||
testCase.Name = fmt.Sprintf("%s.", testCase.Name)
|
||||
}
|
||||
resolver, err := query.LoadResolver(testCase.Server, opts)
|
||||
resolver, err := query.LoadResolver(opts)
|
||||
assert.NilError(t, err)
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(testCase.Name, testCase.Type)
|
||||
|
@ -85,3 +79,27 @@ func Test3ResolveHTTPS(t *testing.T) {
|
|||
assert.ErrorContains(t, err, "request error")
|
||||
assert.Equal(t, res, helpers.Response{})
|
||||
}
|
||||
|
||||
func Test404ResolveHTTPS(t *testing.T) {
|
||||
t.Parallel()
|
||||
var err error
|
||||
opts := cli.Options{
|
||||
HTTPS: true,
|
||||
Logger: util.InitLogger(0),
|
||||
Request: helpers.Request{
|
||||
Server: "https://dns9.quad9.net/dns",
|
||||
Type: dns.TypeA,
|
||||
Name: "git.froth.zone.",
|
||||
},
|
||||
}
|
||||
// testCase := helpers.Request{Server: "https://dns9.quad9.net/dns-query", Type: dns.TypeA, Name: "git.froth.zone."}
|
||||
resolver, err := query.LoadResolver(opts)
|
||||
assert.NilError(t, err)
|
||||
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(opts.Request.Name, opts.Request.Type)
|
||||
// msg = msg.SetQuestion(testCase.Name, testCase.Type)
|
||||
res, err := resolver.LookUp(msg)
|
||||
assert.ErrorContains(t, err, "404")
|
||||
assert.Equal(t, res, helpers.Response{})
|
||||
}
|
||||
|
|
|
@ -15,8 +15,7 @@ import (
|
|||
)
|
||||
|
||||
type QUICResolver struct {
|
||||
server string
|
||||
opts cli.Options
|
||||
opts cli.Options
|
||||
}
|
||||
|
||||
func (r *QUICResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
|
||||
|
@ -25,8 +24,12 @@ func (r *QUICResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
|
|||
MinVersion: tls.VersionTLS12,
|
||||
NextProtos: []string{"doq"},
|
||||
}
|
||||
|
||||
conf := new(quic.Config)
|
||||
conf.HandshakeIdleTimeout = r.opts.Request.Timeout
|
||||
|
||||
r.opts.Logger.Debug("making DoQ request")
|
||||
connection, err := quic.DialAddr(r.server, tls, nil)
|
||||
connection, err := quic.DialAddr(r.opts.Request.Server, tls, conf)
|
||||
if err != nil {
|
||||
return helpers.Response{}, err
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.froth.zone/sam/awl/cli"
|
||||
"git.froth.zone/sam/awl/internal/helpers"
|
||||
|
@ -34,7 +35,7 @@ func TestQuic(t *testing.T) {
|
|||
for i := range testCases {
|
||||
switch i {
|
||||
case 0:
|
||||
resolver, err := query.LoadResolver(testCases[i].Server, opts)
|
||||
resolver, err := query.LoadResolver(opts)
|
||||
assert.NilError(t, err)
|
||||
// if the domain is not canonical, make it canonical
|
||||
if !strings.HasSuffix(testCase.Name, ".") {
|
||||
|
@ -47,7 +48,7 @@ func TestQuic(t *testing.T) {
|
|||
assert.ErrorContains(t, err, "fully qualified")
|
||||
assert.Equal(t, res, helpers.Response{})
|
||||
case 1:
|
||||
resolver, err := query.LoadResolver(testCase2.Server, opts)
|
||||
resolver, err := query.LoadResolver(opts)
|
||||
assert.NilError(t, err)
|
||||
testCase2.Server = net.JoinHostPort(testCase2.Server, strconv.Itoa(opts.Port))
|
||||
// if the domain is not canonical, make it canonical
|
||||
|
@ -63,3 +64,21 @@ func TestQuic(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidQuic(t *testing.T) {
|
||||
t.Parallel()
|
||||
opts := cli.Options{
|
||||
QUIC: true,
|
||||
Logger: util.InitLogger(0),
|
||||
Port: 853,
|
||||
Request: helpers.Request{Server: "example.com", Type: dns.TypeA, Name: "git.froth.zone", Timeout: 10 * time.Millisecond},
|
||||
}
|
||||
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.ErrorContains(t, err, "timeout")
|
||||
assert.Equal(t, res, helpers.Response{})
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ package query
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"git.froth.zone/sam/awl/cli"
|
||||
"git.froth.zone/sam/awl/internal/helpers"
|
||||
|
@ -12,8 +13,7 @@ import (
|
|||
)
|
||||
|
||||
type StandardResolver struct {
|
||||
server string
|
||||
opts cli.Options
|
||||
opts cli.Options
|
||||
}
|
||||
|
||||
func (r *StandardResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
|
||||
|
@ -22,6 +22,9 @@ func (r *StandardResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
|
|||
err error
|
||||
)
|
||||
dnsClient := new(dns.Client)
|
||||
dnsClient.Dialer = &net.Dialer{
|
||||
Timeout: r.opts.Request.Timeout,
|
||||
}
|
||||
if r.opts.TCP || r.opts.TLS {
|
||||
dnsClient.Net = "tcp"
|
||||
} else {
|
||||
|
@ -38,11 +41,13 @@ func (r *StandardResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
|
|||
if r.opts.TLS {
|
||||
dnsClient.Net += "-tls"
|
||||
}
|
||||
r.opts.Logger.Debug("Using", dnsClient.Net, "for making the request")
|
||||
|
||||
resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, r.server)
|
||||
resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, r.opts.Request.Server)
|
||||
if err != nil {
|
||||
return helpers.Response{}, err
|
||||
}
|
||||
r.opts.Logger.Info("Request successful")
|
||||
|
||||
if resp.DNS.MsgHdr.Truncated && !r.opts.Truncate {
|
||||
fmt.Printf(";; Truncated, retrying with TCP\n\n")
|
||||
|
@ -53,7 +58,7 @@ func (r *StandardResolver) LookUp(msg *dns.Msg) (helpers.Response, error) {
|
|||
case r.opts.IPv4:
|
||||
dnsClient.Net += "6"
|
||||
}
|
||||
resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, r.server)
|
||||
resp.DNS, resp.RTT, err = dnsClient.Exchange(msg, r.opts.Request.Server)
|
||||
}
|
||||
|
||||
return resp, err
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
)
|
||||
|
||||
func TestResolve(t *testing.T) {
|
||||
t.Parallel()
|
||||
opts := cli.Options{
|
||||
Logger: util.InitLogger(0),
|
||||
Port: 53,
|
||||
|
@ -24,7 +23,7 @@ func TestResolve(t *testing.T) {
|
|||
Name: "example.com.",
|
||||
},
|
||||
}
|
||||
resolver, err := query.LoadResolver(opts.Request.Server, opts)
|
||||
resolver, err := query.LoadResolver(opts)
|
||||
assert.NilError(t, err)
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(opts.Request.Name, opts.Request.Type)
|
||||
|
@ -34,9 +33,9 @@ func TestResolve(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTruncate(t *testing.T) {
|
||||
t.Parallel()
|
||||
opts := cli.Options{
|
||||
Logger: util.InitLogger(0),
|
||||
IPv4: true,
|
||||
Port: 5301,
|
||||
Request: helpers.Request{
|
||||
Server: "madns.binarystar.systems",
|
||||
|
@ -44,7 +43,7 @@ func TestTruncate(t *testing.T) {
|
|||
Name: "limit.txt.example.",
|
||||
},
|
||||
}
|
||||
resolver, err := query.LoadResolver(opts.Request.Server, opts)
|
||||
resolver, err := query.LoadResolver(opts)
|
||||
assert.NilError(t, err)
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(opts.Request.Name, opts.Request.Type)
|
||||
|
@ -52,3 +51,39 @@ func TestTruncate(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
assert.Assert(t, res != helpers.Response{})
|
||||
}
|
||||
|
||||
func TestResolveAgain(t *testing.T) {
|
||||
tests := []struct {
|
||||
opt cli.Options
|
||||
}{
|
||||
{
|
||||
cli.Options{
|
||||
Logger: util.InitLogger(0),
|
||||
TCP: true,
|
||||
Port: 53,
|
||||
Request: helpers.Request{
|
||||
Server: "8.8.4.4",
|
||||
Type: dns.TypeA,
|
||||
Name: "example.com.",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
cli.Options{
|
||||
Logger: util.InitLogger(0),
|
||||
Port: 53,
|
||||
Request: helpers.Request{
|
||||
Server: "8.8.4.4",
|
||||
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{})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
package query
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.froth.zone/sam/awl/cli"
|
||||
"git.froth.zone/sam/awl/internal/helpers"
|
||||
|
||||
|
@ -28,10 +30,13 @@ func CreateQuery(opts cli.Options) (helpers.Response, error) {
|
|||
res.DNS.SetEdns0(1232, true)
|
||||
}
|
||||
|
||||
resolver, err := LoadResolver(opts.Request.Server, opts)
|
||||
opts.Logger.Debug(fmt.Sprintf("%+v", res))
|
||||
|
||||
resolver, err := LoadResolver(opts)
|
||||
if err != nil {
|
||||
return helpers.Response{}, err
|
||||
}
|
||||
opts.Logger.Info("Query successfully loaded")
|
||||
|
||||
return resolver.LookUp(res.DNS)
|
||||
}
|
||||
|
|
|
@ -16,30 +16,35 @@ type Resolver interface {
|
|||
LookUp(*dns.Msg) (helpers.Response, error)
|
||||
}
|
||||
|
||||
func LoadResolver(server string, opts cli.Options) (Resolver, error) {
|
||||
func LoadResolver(opts cli.Options) (Resolver, error) {
|
||||
switch {
|
||||
case opts.HTTPS:
|
||||
opts.Logger.Debug("loading DoH resolver")
|
||||
if !strings.HasPrefix(server, "https://") {
|
||||
server = "https://" + server
|
||||
opts.Logger.Info("loading DNS-over-HTTPS resolver")
|
||||
if !strings.HasPrefix(opts.Request.Server, "https://") {
|
||||
opts.Request.Server = "https://" + opts.Request.Server
|
||||
}
|
||||
return &HTTPSResolver{
|
||||
server: server,
|
||||
opts: opts,
|
||||
opts: opts,
|
||||
}, nil
|
||||
case opts.QUIC:
|
||||
opts.Logger.Debug("loading DoQ resolver")
|
||||
server = net.JoinHostPort(opts.Request.Server, strconv.Itoa(opts.Port))
|
||||
opts.Logger.Info("loading DNS-over-QUIC resolver")
|
||||
opts.Request.Server = net.JoinHostPort(opts.Request.Server, strconv.Itoa(opts.Port))
|
||||
return &QUICResolver{
|
||||
server: server,
|
||||
opts: opts,
|
||||
opts: opts,
|
||||
}, nil
|
||||
case opts.DNSCrypt:
|
||||
opts.Logger.Info("loading DNSCrypt resolver")
|
||||
if !strings.HasPrefix(opts.Request.Server, "sdns://") {
|
||||
opts.Request.Server = "sdns://" + opts.Request.Server
|
||||
}
|
||||
return &DNSCryptResolver{
|
||||
opts: opts,
|
||||
}, nil
|
||||
default:
|
||||
opts.Logger.Debug("loading standard/DoT resolver")
|
||||
server = net.JoinHostPort(opts.Request.Server, strconv.Itoa(opts.Port))
|
||||
opts.Logger.Info("loading standard/DNS-over-TLS resolver")
|
||||
opts.Request.Server = net.JoinHostPort(opts.Request.Server, strconv.Itoa(opts.Port))
|
||||
return &StandardResolver{
|
||||
server: server,
|
||||
opts: opts,
|
||||
opts: opts,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue