Preliminary EDNS
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing

Signed-off-by: Sam Therapy <sam@samtherapy.net>
This commit is contained in:
Sam Therapy 2022-07-30 15:32:03 +02:00
parent abccb5b5cc
commit c5e5573268
Signed by: sam
GPG key ID: 4D8B07C18F31ACBD
26 changed files with 1041 additions and 237 deletions

View file

@ -2,6 +2,7 @@
local testing(version, arch) = {
kind: "pipeline",
type: "docker",
name: version + "-" + arch ,
platform: {
arch: arch
@ -16,19 +17,27 @@ local testing(version, arch) = {
},
{
name: "lint",
image: "rancher/drone-golangci-lint:latest"
image: "rancher/drone-golangci-lint:latest",
depends_on: [
"submodules",
],
},
{
name: "test",
image: "golang:" + version,
commands: [
"go test -race ./... -cover"
]
"go test -v -race ./... -cover"
],
depends_on: [
"submodules",
],
},
],
trigger: {
trigger: {
event: {
exclude: "tag",
exclude: [
"tag"
],
}
},
};
@ -36,14 +45,17 @@ local testing(version, arch) = {
// "Inspired by" https://goreleaser.com/ci/drone/
local release() = {
kind: "pipeline",
type: "docker",
name: "release",
trigger: {
event: "tag"
event: [
"tag"
],
},
steps: [
{
name: "fetch",
image: "docker:git",
image: "alpine/git",
commands : [
"git fetch --tags",
"git submodule update --init --recursive"
@ -67,9 +79,6 @@ local release() = {
commands: [
"goreleaser release"
],
// when: {
// event: "tag"
// }
}
]
};
@ -77,5 +86,7 @@ local release() = {
[
testing("1.18", "amd64"),
testing("1.18", "arm64"),
release()
]

124
.drone.yml Normal file
View file

@ -0,0 +1,124 @@
---
{
"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"
}

28
.github/ISSUE_TEMPLATE/bug.md vendored Normal file
View file

@ -0,0 +1,28 @@
---
name: Bug report
about: Report a bug
---
**Describe the bug**
A clear and concise description of what the bug is.
**Reproduction steps**
Steps to reproduce the behavior:
1. 1
2. 2
3. Bug
**Expected behaviour**
A clear and concise description of what you expected to happen.
**Screenshots / Logs**
Add `-v=4` and add the debug logs to the report here:
**System information (please complete the following information):**
- OS: [e.g. Ubuntu 22.04, OpenBSD, Windows 11]
- Version: [run `awl -V` and print the output]
**Additional context**
Add any other context about the problem here.

18
.github/ISSUE_TEMPLATE/feature.md vendored Normal file
View file

@ -0,0 +1,18 @@
---
name: Feature request
about: Suggest a feature
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is.
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
Links to implementations in dig, drill, etc. should go here.
**Additional context**
Add any other context or screenshots about the feature request here.

9
.github/pull_request_template.md vendored Normal file
View file

@ -0,0 +1,9 @@
<!--
Please check the following:
1. Make sure you are targeting the `master` branch.
2. Make sure that you test and format your contributions: `make test && make lint`
3. Describe what your pull request does and which issue you're targeting (if any)
-->

2
.gitignore vendored
View file

@ -21,3 +21,5 @@ dist/
# Test coverage
coverage/*
!coverage/.gitkeep
awl

View file

@ -13,10 +13,11 @@ $(PROG):
$(GO) build -o $(EXE) $(GOFLAGS) .
## install: installs awl
install: all
ifeq ($(OS),Windows_NT)
install:
$(GO) install $(GOFLAGS) .
else
install: all
install -m755 $(PROG) $(PREFIX)/$(BIN)
install -m644 doc/$(PROG).1 $(MAN)/man1
endif

View file

View file

@ -61,6 +61,6 @@ emails? Send a patch to the
Found a bug or want a new feature? Create an issue
[here](https://git.froth.zone/sam/awl/issues).
### License
### Licence
See [LICENSE](./LICENSE)
See [LICENCE](./LICENCE)

View file

@ -45,10 +45,21 @@ 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`")
retry = flag.Int("retries", 2, "number of `times` to retry")
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)")
timeout = flag.Float32("timeout", 1, "Timeout, in `seconds`")
retry = flag.Int("retries", 2, "number of `times` to retry")
edns = flag.Bool("no-edns", false, "disable EDNS entirely")
ednsVer = flag.Uint8("edns-ver", 0, "set EDNS version")
expire = flag.Bool("expire", false, "set EDNS expire")
dnssec = flag.Bool("dnssec", false, "enable DNSSEC", flag.OptShorthand('D'))
nsid = flag.Bool("nsid", false, "set EDNS NSID", flag.OptShorthand('n'))
cookie = flag.Bool("no-cookie", false, "disable sending EDNS cookie (default: cookie sent)")
tcpKeepAlive = flag.Bool("keep-alive", false, "send EDNS TCP keep-alive")
udpBufSize = flag.Uint16("buffer-size", 1232, "set EDNS UDP buffer size", flag.OptShorthand('b'))
zflag = flag.Uint16("zflag", 0, "set EDNS z-flag")
padding = flag.Bool("pad", false, "set EDNS padding")
truncate = flag.Bool("no-truncate", false, "ignore truncation if a UDP request truncates (default: retry with TCP)")
tcp = flag.Bool("tcp", false, "use TCP")
dnscrypt = flag.Bool("dnscrypt", false, "use DNSCrypt")
@ -65,18 +76,19 @@ func ParseCLI(version string) (Options, error) {
tc = flag.Bool("tc", false, "set/unset TC (TrunCated) flag (default: not set)")
z = flag.Bool("z", false, "set/unset Z (Zero) flag (default: not set)", flag.OptShorthand('z'))
short = flag.Bool("short", false, "print just the results, equivalent to dig +short", flag.OptShorthand('s'))
short = flag.Bool("short", false, "print just the results", flag.OptShorthand('s'))
json = flag.Bool("json", false, "print the result(s) as JSON", flag.OptShorthand('j'))
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'))
noC = flag.Bool("no-comments", false, "disable printing the comments")
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"))
verbosity = flag.Int("verbosity", 1, "sets verbosity `level`", flag.OptShorthand('v'), flag.OptNoOptDefVal("2"))
versionFlag = flag.Bool("version", false, "print version information", flag.OptShorthand('V'))
)
@ -90,30 +102,32 @@ func ParseCLI(version string) (Options, error) {
}
opts := Options{
Logger: util.InitLogger(*verbosity),
Port: *port,
IPv4: *ipv4,
IPv6: *ipv6,
DNSSEC: *dnssec,
Short: *short,
TCP: *tcp,
DNSCrypt: *dnscrypt,
TLS: *tls,
HTTPS: *https,
QUIC: *quic,
Truncate: *truncate,
AA: *aa,
AD: *ad,
TC: *tc,
Z: *z,
CD: *cd,
QR: *qr,
RD: *rd,
RA: *ra,
Reverse: *reverse,
JSON: *json,
XML: *xml,
YAML: *yaml,
Logger: util.InitLogger(*verbosity),
Port: *port,
IPv4: *ipv4,
IPv6: *ipv6,
Short: *short,
TCP: *tcp,
DNSCrypt: *dnscrypt,
TLS: *tls,
HTTPS: *https,
QUIC: *quic,
Truncate: *truncate,
ShowQuery: false,
AA: *aa,
AD: *ad,
TC: *tc,
Z: *z,
CD: *cd,
QR: *qr,
RD: *rd,
RA: *ra,
Reverse: *reverse,
HumanTTL: false,
ShowTTL: true,
JSON: *json,
XML: *xml,
YAML: *yaml,
Request: helpers.Request{
Type: dns.StringToType[strings.ToUpper(*qType)],
Class: dns.StringToClass[strings.ToUpper(*class)],
@ -122,12 +136,25 @@ func ParseCLI(version string) (Options, error) {
Retries: *retry,
},
Display: Displays{
Comments: !*noC,
Question: !*noQ,
Answer: !*noAns,
Authority: !*noAuth,
Additional: !*noAdd,
Statistics: !*noStats,
},
EDNS: EDNS{
EnableEDNS: !*edns,
Cookie: !*cookie,
DNSSEC: *dnssec,
BufSize: *udpBufSize,
Version: *ednsVer,
Expire: *expire,
KeepOpen: *tcpKeepAlive,
Nsid: *nsid,
ZFlag: *zflag,
Padding: *padding,
},
}
opts.Logger.Info("POSIX flags parsed")
@ -142,7 +169,6 @@ func ParseCLI(version string) (Options, error) {
// This includes the dig-style (+) options
err = ParseMiscArgs(flag.Args(), &opts)
if err != nil {
opts.Logger.Warn(err)
return opts, err
}
opts.Logger.Info("Dig/Drill flags parsed")

View file

@ -16,7 +16,7 @@ func TestEmpty(t *testing.T) {
os.Args = []string{"awl", "-4"}
opts, err := cli.ParseCLI("TEST")
assert.NilError(t, err)
assert.Assert(t, (opts.Port == 53))
assert.Equal(t, opts.Port, 53)
assert.Assert(t, opts.IPv4)
os.Args = old
}
@ -26,7 +26,7 @@ func TestTLSPort(t *testing.T) {
os.Args = []string{"awl", "-T"}
opts, err := cli.ParseCLI("TEST")
assert.NilError(t, err)
assert.Assert(t, (opts.Port == 853))
assert.Equal(t, opts.Port, 853)
os.Args = old
}
@ -42,7 +42,7 @@ func TestInvalidDig(t *testing.T) {
old := os.Args
os.Args = []string{"awl", "+a"}
_, err := cli.ParseCLI("TEST")
assert.ErrorContains(t, err, "dig: unknown flag")
assert.ErrorContains(t, err, "digflags: unknown flag")
os.Args = old
}
@ -92,7 +92,7 @@ func FuzzFlags(f *testing.F) {
}
f.Fuzz(func(t *testing.T, orig string) {
os.Args = []string{orig}
os.Args = []string{"awl", orig}
//nolint:errcheck // Only make sure the program does not crash
cli.ParseCLI("TEST")
})

View file

@ -13,86 +13,112 @@ import (
func ParseDig(arg string, opts *Options) error {
// returns true if the flag starts with a no
isNo := !strings.HasPrefix(arg, "no")
if !isNo {
arg = strings.TrimPrefix(arg, "no")
}
opts.Logger.Info("Setting", arg)
switch arg {
// Set DNS query flags
case "aa", "aaflag", "aaonly", "noaa", "noaaflag", "noaaonly":
case "aa", "aaflag", "aaonly":
opts.AA = isNo
case "ad", "adflag", "noad", "noadflag":
case "ad", "adflag":
opts.AD = isNo
case "cd", "cdflag", "nocd", "nocdflag":
case "cd", "cdflag":
opts.CD = isNo
case "qr", "qrflag", "noqr", "noqrflag":
case "qrflag":
opts.QR = isNo
case "ra", "raflag", "nora", "noraflag":
case "ra", "raflag":
opts.RA = isNo
case "rd", "rdflag", "recurse", "nord", "nordflag", "norecurse":
case "rd", "rdflag", "recurse":
opts.RD = isNo
case "tc", "tcflag", "notc", "notcflag":
case "tc", "tcflag":
opts.TC = isNo
case "z", "zflag", "noz", "nozflag":
case "z", "zflag":
opts.Z = isNo
// End DNS query flags
case "qr":
opts.ShowQuery = isNo
case "ttlunits":
opts.HumanTTL = isNo
case "ttlid":
opts.ShowTTL = isNo
// EDNS queries
case "dnssec":
opts.EDNS.DNSSEC = isNo
case "expire":
opts.EDNS.Expire = isNo
case "cookie":
opts.EDNS.Cookie = isNo
case "keepopen", "keepalive":
opts.EDNS.KeepOpen = isNo
case "nsid":
opts.EDNS.Nsid = isNo
case "padding":
opts.EDNS.Padding = isNo
// End EDNS queries
// DNS-over-X
case "dnssec", "nodnssec":
opts.DNSSEC = isNo
case "tcp", "vc", "notcp", "novc":
case "tcp", "vc":
opts.TCP = isNo
case "ignore", "noignore":
case "ignore":
opts.Truncate = isNo
case "tls", "notls":
case "tls":
opts.TLS = isNo
case "dnscrypt", "nodnscrypt":
case "dnscrypt":
opts.DNSCrypt = isNo
case "https", "nohttps":
case "https":
opts.HTTPS = isNo
case "quic", "noquic":
case "quic":
opts.QUIC = isNo
// End DNS-over-X
// Formatting
case "short", "noshort":
case "short":
opts.Short = isNo
case "json", "nojson":
case "identify":
opts.Identify = isNo
case "json":
opts.JSON = isNo
case "xml", "noxml":
case "xml":
opts.XML = isNo
case "yaml", "noyaml":
case "yaml":
opts.YAML = isNo
// End formatting
// Output
// TODO: get this to work
// case "comments", "nocomments":
// opts.Display.Comments = isNo
case "question", "noquestion":
case "comments":
opts.Display.Comments = isNo
case "question":
opts.Display.Question = isNo
case "answer", "noanswer":
case "answer":
opts.Display.Answer = isNo
case "authority", "noauthority":
case "authority":
opts.Display.Authority = isNo
case "additional", "noadditional":
case "additional":
opts.Display.Additional = isNo
case "stats", "nostats":
case "stats":
opts.Display.Statistics = isNo
case "all", "noall":
case "all":
opts.Display.Comments = isNo
opts.Display.Question = isNo
opts.Display.Answer = isNo
opts.Display.Authority = isNo
opts.Display.Additional = isNo
opts.Display.Statistics = isNo
case "idnout":
opts.Display.UcodeTranslate = isNo
default:
// Recursive switch statements WOO
switch {
case strings.HasPrefix(arg, "timeout"):
case strings.HasPrefix(arg, "time"), strings.HasPrefix(arg, "timeout"):
timeout, err := strconv.Atoi(strings.Split(arg, "=")[1])
if err != nil {
return fmt.Errorf("dig: Invalid timeout value")
return fmt.Errorf("digflags: Invalid timeout value")
}
opts.Request.Timeout = time.Duration(timeout)
@ -100,7 +126,7 @@ func ParseDig(arg string, opts *Options) error {
case strings.HasPrefix(arg, "retry"), strings.HasPrefix(arg, "tries"):
tries, err := strconv.Atoi(strings.Split(arg, "=")[1])
if err != nil {
return fmt.Errorf("dig: Invalid retry value")
return fmt.Errorf("digflags: Invalid retry value")
}
if strings.HasPrefix(arg, "tries") {
@ -109,8 +135,43 @@ func ParseDig(arg string, opts *Options) error {
opts.Request.Retries = tries
case strings.HasPrefix(arg, "bufsize"):
size, err := strconv.Atoi(strings.Split(arg, "=")[1])
if err != nil {
return fmt.Errorf("digflags: Invalid UDP buffer size value")
}
opts.EDNS.BufSize = uint16(size)
case strings.HasPrefix(arg, "ednsflags"):
split := strings.Split(arg, "=")
if len(split) > 1 {
ver, err := strconv.ParseInt(split[1], 0, 16)
if err != nil {
return fmt.Errorf("digflags: Invalid EDNS flag")
}
// Ignore setting DO bit
opts.EDNS.ZFlag = uint16(ver & 0x7FFF)
} else {
opts.EDNS.ZFlag = 0
}
case strings.HasPrefix(arg, "edns"):
opts.EDNS.EnableEDNS = isNo
split := strings.Split(arg, "=")
if len(split) > 1 {
ver, err := strconv.Atoi(split[1])
if err != nil {
return fmt.Errorf("digflags: Invalid EDNS version")
}
opts.EDNS.Version = uint8(ver)
} else {
opts.EDNS.Version = 0
}
case strings.HasPrefix(arg, "subnet"):
default:
return fmt.Errorf("dig: unknown flag %s given", arg)
return fmt.Errorf("digflags: unknown flag %s given", arg)
}
}
return nil

View file

@ -21,7 +21,20 @@ func FuzzDig(f *testing.F) {
"rdflag", "recurse", "nordflag", "norecurse",
"tcflag", "notcflag",
"zflag", "nozflag",
"qr", "noqr",
"ttlunits", "nottlunits",
"ttlid", "nottlid",
"dnssec", "nodnssec",
"edns", "edns=a", "edns=0", "noedns",
"expire", "noexpire",
"ednsflags", "ednsflags=\"", "ednsflags=1", "noednsflags",
"cookie", "nocookeie",
"keepopen", "keepalive", "nokeepopen", "nokeepalive",
"nsid", "nonsid",
"padding", "nopadding",
"bufsize=512", "bufsize=a",
"time=5", "timeout=a",
"retry=a", "tries=3",
"tcp", "vc", "notcp", "novc",
"ignore", "noignore",
"tls", "notls",
@ -29,15 +42,18 @@ func FuzzDig(f *testing.F) {
"https", "nohttps",
"quic", "noquic",
"short", "noshort",
"identify", "noidentify",
"json", "nojson",
"xml", "noxml",
"yaml", "noyaml",
"comments", "nocomments",
"question", "noquestion",
"answer", "noanswer",
"authority", "noauthority",
"additional", "noadditional",
"stats", "nostats",
"all", "noall",
"idnout", "noidnout",
"invalid",
}
for _, tc := range seeds {
@ -49,7 +65,7 @@ func FuzzDig(f *testing.F) {
opts.Logger = util.InitLogger(0)
err := cli.ParseDig(orig, opts)
if err != nil {
assert.ErrorContains(t, err, "unknown flag")
assert.ErrorContains(t, err, "digflags:")
}
})
}

View file

@ -15,13 +15,13 @@ type Options struct {
Port int // DNS port
IPv4 bool // Force IPv4
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
Truncate bool // Ignore truncation
ShowQuery bool // Show query before being sent
AA bool // Set Authoratative Answer
AD bool // Set Authenticated Data
CD bool // Set CD
@ -32,24 +32,47 @@ 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
ShowTTL bool //Display TTL
Short bool // Short output
Identify bool // If short, add identity stuffs
JSON bool // Outout as JSON
XML bool // Output as XML
YAML bool // Output at YAML
Display Displays // Display options
Request helpers.Request // DNS reuqest
EDNS // EDNS
}
// 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.
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.
UcodeTranslate bool // Translate Punycode back to Unicode
}
type EDNS struct {
EnableEDNS bool // Enable EDNS
Cookie bool // Enable EDNS cookie
DNSSEC bool // Enable DNSSEC
BufSize uint16 // Set UDP buffer size
Version uint8 // Set EDNS version
Expire bool // Set EDNS expiration
KeepOpen bool // TCP keep alive
Nsid bool // Show EDNS nsid
ZFlag uint16 // EDNS flags
Padding bool // EDNS padding
// Subnet EDNS_SUBNET
}
// Subnets get their own structure because complicated
// type EDNS_SUBNET struct {
// family uint16
// }
var ErrNotError = errors.New("not an error")

View file

@ -5,7 +5,7 @@
.nh
.ad l
.\" Begin generated content:
.TH "awl" "1" "2022-07-25"
.TH "awl" "1" "2022-07-29"
.PP
.SH NAME
awl - DNS lookup tool
@ -23,8 +23,8 @@ 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
@ -39,6 +39,10 @@ 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)
@ -128,7 +132,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
@ -138,61 +142,105 @@ Dig-like +[no]flags are supported, see dig(1)
.SS DNS Flags
.PP
.RS 4
\fB--aa=[false]\fR, \fB+[no]aaflag\fR
\fB--aa\fR[\fI=false\fR], \fB+[no]aaflag\fR
.br
(Set, Unset) AA (Authoritative Answer) flag
.PP
\fB--ad=[false]\fR, \fB+[no]adflag\fR
\fB--ad\fR[\fI=false\fR], \fB+[no]adflag\fR
.br
(Set, Unset) AD (Authenticated Data) flag
.PP
\fB--tc=[false]\fR, \fB+[no]tcflag\fR
\fB--tc\fR[\fI=false\fR], \fB+[no]tcflag\fR
.br
(Set, Unset) TC (TrunCated) flag
.PP
\fB-z=[false]\fR, \fB+[no]zflag\fR
\fB-z\fR[\fI=false\fR], \fB+[no]zflag\fR
.br
(Set, Unset) Z (Zero) flag
.PP
\fB--cd=[false]\fR, \fB+[no]cdflag\fR
\fB--cd\fR[\fI=false\fR], \fB+[no]cdflag\fR
.br
(Set, Unset) CD (Checking Disabled) flag
.PP
\fB--qr=[false]\fR, \fB+[no]qrflag\fR
\fB--qr\fR[\fI=false\fR], \fB+[no]qrflag\fR
.br
(Set, Unset) QR (QueRy) flag
.PP
\fB--rd=[true]\fR, \fB+[no]rdflag\fR
\fB--rd\fR[\fI=true\fR], \fB+[no]rdflag\fR
.br
(Set, Unset) RD (Recursion Desired) flag
.PP
\fB--ra=[false]\fR, \fB+[no]raflag\fR
\fB--ra\fR[\fI=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
.RE
.SS Output Display
.RS 4
\fB--no-question\fR, \fB+noquestion\fR
\fB--no-question\fR, \fB+[no]question\fR
.br
Do not display the Question section
.PP
\fB--no-answer\fR, \fB+noanswer\fR
\fB--no-answer\fR, \fB+[no]answer\fR
.br
Do not display the Answer section
.PP
\fB--no-answer\fR, \fB+noanswer\fR
\fB--no-answer\fR, \fB+[no]answer\fR
.br
Do not display the Answer section
.PP
\fB--no-authority\fR, \fB+noauthority\fR
\fB--no-authority\fR, \fB+[no]authority\fR
.br
Do not display the Authority section
.PP
\fB--no-additional\fR, \fB+noadditional\fR
\fB--no-additional\fR, \fB+[no]additional\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
@ -241,4 +289,10 @@ 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
\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

7
go.mod
View file

@ -4,11 +4,12 @@ go 1.18
require (
github.com/ameshkov/dnscrypt/v2 v2.2.3
github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5
github.com/lucas-clemente/quic-go v0.28.1
github.com/miekg/dns v1.1.50
github.com/stefansundin/go-zflag v1.1.1
golang.org/x/net v0.0.0-20220728211354-c7608f3a8462
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
gotest.tools/v3 v3.3.0
)
@ -33,8 +34,8 @@ 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-20220730100132-1609e554cd39
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.11 // indirect
golang.org/x/tools v0.1.12 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
)

13
go.sum
View file

@ -31,6 +31,8 @@ github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5 h1:RAV05c0xOkJ3dZGS0JFybxFKZ2WMLabgx3uXnd7rpGs=
github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
@ -217,10 +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-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220725212005-46097bf591d3 h1:2yWTtPWWRcISTw3/o+s/Y4UOMnQL71DWyToOANFusCg=
golang.org/x/net v0.0.0-20220725212005-46097bf591d3/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM=
golang.org/x/net v0.0.0-20220726230323-06994584191e h1:wOQNKh1uuDGRnmgF0jDxh7ctgGy/3P4rYWQRVJD4/Yg=
golang.org/x/net v0.0.0-20220726230323-06994584191e/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM=
golang.org/x/net v0.0.0-20220728012108-993b7b1e3a27 h1:Khs7GS6mUxEA1e5DfKm9ojYX4BiI297wdliOwp/CPmw=
@ -246,6 +244,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -271,14 +270,10 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220727055044-e65921a090b8 h1:dyU22nBWzrmTQxtNrr4dzVOvaw35nUYE279vF9UmsI8=
golang.org/x/sys v0.0.0-20220727055044-e65921a090b8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220730100132-1609e554cd39 h1:aNCnH+Fiqs7ZDTFH6oEFjIfbX2HvgQXJ6uQuUbTobjk=
golang.org/x/sys v0.0.0-20220730100132-1609e554cd39/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=
@ -304,6 +299,8 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

93
main.go
View file

@ -3,20 +3,14 @@
package main
import (
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"os"
"strconv"
"strings"
"time"
"git.froth.zone/sam/awl/cli"
"git.froth.zone/sam/awl/internal/helpers"
"git.froth.zone/sam/awl/query"
"gopkg.in/yaml.v2"
)
var version = "DEV"
@ -31,6 +25,7 @@ func main() {
opts.Logger.Error(err)
os.Exit(1)
}
var resp helpers.Response
// Retry queries if a query fails
@ -39,7 +34,7 @@ func main() {
if err == nil {
break
} else {
opts.Logger.Warn("Retrying request, error", err)
opts.Logger.Warn("Retrying request, error:", err)
}
}
@ -48,83 +43,17 @@ func main() {
opts.Logger.Error(err)
os.Exit(9)
}
switch {
case opts.JSON:
opts.Logger.Info("Printing as JSON")
json, err := json.MarshalIndent(resp.DNS, "", " ")
var str string
if opts.JSON || opts.XML || opts.YAML {
str, err = query.PrintSpecial(resp.DNS, opts)
if err != nil {
opts.Logger.Error(err)
opts.Logger.Error("Special print:", err)
os.Exit(10)
}
fmt.Println(string(json))
case opts.XML:
opts.Logger.Info("Printing as XML")
xml, err := xml.MarshalIndent(resp.DNS, "", " ")
if err != nil {
opts.Logger.Error(err)
os.Exit(10)
}
fmt.Println(string(xml))
case opts.YAML:
opts.Logger.Info("Printing as YAML")
yaml, err := yaml.Marshal(resp.DNS)
if err != nil {
opts.Logger.Error(err)
os.Exit(10)
}
fmt.Println(string(yaml))
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)
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 {
temp := strings.Split(res.String(), "\t")
fmt.Println(temp[len(temp)-1])
}
}
} else {
str = query.ToString(resp, opts)
}
fmt.Println(str)
}

14
main_test.go Normal file
View file

@ -0,0 +1,14 @@
// SPDX-License-Identifier: BSD-3-Clause
package main
import (
"os"
"testing"
)
func TestMain(t *testing.T) {
main()
os.Args = []string{"awl", "+yaml", "@dns.froth.zone"}
main()
}

10
mkfile
View file

@ -3,20 +3,18 @@
GO = go
PROG = awl
LDFLAGS = '-s -w'
GOFLAGS = -ldflags=$LDFLAGS
CGO_ENABLED = 0
$PROG:
$GO build $GOFLAGS -o $PROG '-buildvcs=false' .
$GO build -ldflags="-s -w -X=main.version=PLAN9" -o $PROG .
install: $PROG
$GO install $GOFLAGS .
install:
$GO install -ldflags="-s -w -X=main.version=PLAN9" .
cp doc/$PROG.1 /sys/man/1/$PROG
test:
$GO test -v -cover -coverprofile=coverage/coverage.out ./...
$GO test -cover -coverprofile=coverage/coverage.out ./...
fmt:
gofmt -w -s .

175
query/print.go Normal file
View file

@ -0,0 +1,175 @@
// SPDX-License-Identifier: BSD-3-Clause
package query
import (
"encoding/json"
"encoding/xml"
"fmt"
"strconv"
"strings"
"time"
"git.froth.zone/sam/awl/cli"
"golang.org/x/net/idna"
"github.com/miekg/dns"
"gopkg.in/yaml.v3"
)
//
func PrintSpecial(msg *dns.Msg, opts cli.Options) (string, error) {
formatted, err := MakePrintable(msg, opts)
if err != nil {
return "", err
}
switch {
case opts.JSON:
opts.Logger.Info("Printing as JSON")
json, err := json.MarshalIndent(formatted, " ", " ")
return string(json), err
case opts.XML:
opts.Logger.Info("Printing as XML")
xml, err := xml.MarshalIndent(formatted, " ", " ")
return string(xml), err
case opts.YAML:
opts.Logger.Info("Printing as YAML")
yaml, err := yaml.Marshal(formatted)
return string(yaml), err
default:
return "", fmt.Errorf("special: no recognized format given")
}
}
func MakePrintable(msg *dns.Msg, opts cli.Options) (*Message, error) {
var err error
ret := Message{
Header: msg.MsgHdr,
}
for _, question := range msg.Question {
var name string
if opts.Display.UcodeTranslate {
name, err = idna.ToUnicode(question.Name)
if err != nil {
return nil, err
}
} else {
name = question.Name
}
ret.Question = append(ret.Question, Question{
Name: name,
Type: dns.TypeToString[question.Qtype],
Class: dns.ClassToString[question.Qclass],
})
}
for _, answer := range msg.Answer {
temp := strings.Split(answer.String(), "\t")
var (
ttl string
name string
)
if opts.ShowTTL {
if opts.HumanTTL {
ttl = (time.Duration(answer.Header().Ttl) * time.Second).String()
} else {
ttl = strconv.Itoa(int(answer.Header().Ttl))
}
}
if opts.Display.UcodeTranslate {
name, err = idna.ToUnicode(answer.Header().Name)
if err != nil {
return nil, err
}
} else {
name = answer.Header().Name
}
ret.Answer = append(ret.Answer, Answer{
RRHeader: RRHeader{
Name: name,
Type: dns.TypeToString[answer.Header().Rrtype],
Class: dns.ClassToString[answer.Header().Class],
Rdlength: answer.Header().Rdlength,
TTL: ttl,
},
Value: temp[len(temp)-1],
})
}
for _, ns := range msg.Ns {
temp := strings.Split(ns.String(), "\t")
var (
ttl string
name string
)
if opts.ShowTTL {
if opts.HumanTTL {
ttl = (time.Duration(ns.Header().Ttl) * time.Second).String()
} else {
ttl = strconv.Itoa(int(ns.Header().Ttl))
}
}
if opts.Display.UcodeTranslate {
name, err = idna.ToUnicode(ns.Header().Name)
if err != nil {
return nil, err
}
} else {
name = ns.Header().Name
}
ret.Ns = append(ret.Ns, Answer{
RRHeader: RRHeader{
Name: name,
Type: dns.TypeToString[ns.Header().Rrtype],
Class: dns.ClassToString[ns.Header().Class],
Rdlength: ns.Header().Rdlength,
TTL: ttl,
},
Value: temp[len(temp)-1],
})
}
for _, additional := range msg.Extra {
if additional.Header().Rrtype == dns.StringToType["OPT"] {
continue
} else {
temp := strings.Split(additional.String(), "\t")
var (
ttl string
name string
)
if opts.ShowTTL {
if opts.HumanTTL {
ttl = (time.Duration(additional.Header().Ttl) * time.Second).String()
} else {
ttl = strconv.Itoa(int(additional.Header().Ttl))
}
}
if opts.Display.UcodeTranslate {
name, err = idna.ToUnicode(additional.Header().Name)
if err != nil {
return nil, err
}
} else {
name = additional.Header().Name
}
ret.Extra = append(ret.Extra, Answer{
RRHeader: RRHeader{
Name: name,
Type: dns.TypeToString[additional.Header().Rrtype],
Class: dns.ClassToString[additional.Header().Class],
Rdlength: additional.Header().Rdlength,
TTL: ttl,
},
Value: temp[len(temp)-1],
})
}
}
return &ret, nil
}

View file

@ -4,33 +4,114 @@ package query
import (
"fmt"
"strconv"
"git.froth.zone/sam/awl/cli"
"git.froth.zone/sam/awl/internal/helpers"
"github.com/dchest/uniuri"
"github.com/miekg/dns"
)
func CreateQuery(opts cli.Options) (helpers.Response, error) {
var res helpers.Response
res.DNS = new(dns.Msg)
res.DNS.SetQuestion(opts.Request.Name, opts.Request.Type)
res.DNS.Question[0].Qclass = opts.Request.Class
req := new(dns.Msg)
req.SetQuestion(opts.Request.Name, opts.Request.Type)
req.Question[0].Qclass = opts.Request.Class
res.DNS.MsgHdr.Response = opts.QR
res.DNS.MsgHdr.Authoritative = opts.AA
res.DNS.MsgHdr.Truncated = opts.TC
res.DNS.MsgHdr.RecursionDesired = opts.RD
res.DNS.MsgHdr.RecursionAvailable = opts.RA
res.DNS.MsgHdr.Zero = opts.Z
res.DNS.MsgHdr.AuthenticatedData = opts.AD
res.DNS.MsgHdr.CheckingDisabled = opts.CD
// Set standard flags
req.MsgHdr.Response = opts.QR
req.MsgHdr.Authoritative = opts.AA
req.MsgHdr.Truncated = opts.TC
req.MsgHdr.RecursionDesired = opts.RD
req.MsgHdr.RecursionAvailable = opts.RA
req.MsgHdr.Zero = opts.Z
req.MsgHdr.AuthenticatedData = opts.AD
req.MsgHdr.CheckingDisabled = opts.CD
if opts.DNSSEC {
res.DNS.SetEdns0(1232, true)
// EDNS time :)
if opts.EDNS.EnableEDNS {
o := new(dns.OPT)
o.Hdr.Name = "."
o.Hdr.Rrtype = dns.TypeOPT
o.SetVersion(opts.EDNS.Version)
if opts.EDNS.Cookie {
e := new(dns.EDNS0_COOKIE)
e.Code = dns.EDNS0COOKIE
e.Cookie = uniuri.NewLenChars(8, []byte("1234567890abcdef"))
o.Option = append(o.Option, e)
opts.Logger.Info("Setting EDNS cookie to", e.Cookie)
}
if opts.EDNS.Expire {
o.Option = append(o.Option, new(dns.EDNS0_EXPIRE))
opts.Logger.Info("Setting EDNS Expire option")
}
if opts.EDNS.KeepOpen {
o.Option = append(o.Option, new(dns.EDNS0_TCP_KEEPALIVE))
opts.Logger.Info("Setting EDNS TCP Keepalive option")
}
if opts.EDNS.Nsid {
o.Option = append(o.Option, new(dns.EDNS0_NSID))
opts.Logger.Info("Setting EDNS NSID option")
}
if opts.EDNS.Padding {
o.Option = append(o.Option, new(dns.EDNS0_PADDING))
opts.Logger.Info("Setting EDNS padding")
}
o.SetUDPSize(opts.BufSize)
opts.Logger.Info("EDNS UDP buffer set to", opts.BufSize)
o.SetZ(opts.EDNS.ZFlag)
opts.Logger.Info("EDNS Z flag set to", opts.EDNS.ZFlag)
if opts.EDNS.DNSSEC {
o.SetDo()
opts.Logger.Info("EDNS DNSSEC OK set")
}
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")
}
}
opts.Logger.Debug(req)
opts.Logger.Debug(fmt.Sprintf("%+v", res))
if !opts.Short {
if opts.ShowQuery {
opts.Logger.Info("Printing constructed query")
var (
str string
err error
)
if opts.JSON || opts.XML || opts.YAML {
str, err = PrintSpecial(req, opts)
if err != nil {
return helpers.Response{}, err
}
} else {
temp := opts.Display.Statistics
opts.Display.Statistics = false
str = ToString(helpers.Response{
DNS: req,
RTT: 0,
}, opts)
opts.Display.Statistics = temp
str += "\n;; QUERY SIZE: " + strconv.Itoa(req.Len())
}
fmt.Println(str, "\n------")
opts.QR = false
}
}
resolver, err := LoadResolver(opts)
if err != nil {
@ -38,5 +119,5 @@ func CreateQuery(opts cli.Options) (helpers.Response, error) {
}
opts.Logger.Info("Query successfully loaded")
return resolver.LookUp(res.DNS)
return resolver.LookUp(req)
}

View file

@ -21,12 +21,86 @@ func TestCreateQ(t *testing.T) {
QR: false,
Z: true,
RD: false,
DNSSEC: true,
Request: helpers.Request{
Server: "8.8.4.4",
Type: dns.TypeA,
Name: "example.com.",
},
EDNS: cli.EDNS{
EnableEDNS: true,
DNSSEC: true,
Cookie: true,
Expire: true,
KeepOpen: true,
Nsid: true,
},
}
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,
},
}
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)

148
query/struct.go Normal file
View file

@ -0,0 +1,148 @@
// SPDX-License-Identifier: BSD-3-Clause
package query
import (
"strconv"
"strings"
"time"
"git.froth.zone/sam/awl/cli"
"git.froth.zone/sam/awl/internal/helpers"
"github.com/miekg/dns"
)
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"`
}
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"`
}
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"`
Rdlength uint16 `json:"-" xml:"-" yaml:"-"`
}
type Answer struct {
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
//
// 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"
}
var s 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)) + ", "
s += "ANSWER: " + strconv.Itoa(len(res.DNS.Answer)) + ", "
s += "AUTHORITY: " + strconv.Itoa(len(res.DNS.Ns)) + ", "
s += "ADDITIONAL: " + strconv.Itoa(len(res.DNS.Extra)) + "\n"
opt = res.DNS.IsEdns0()
if opt != nil {
// OPT PSEUDOSECTION
s += opt.String() + "\n"
}
}
if opts.Display.Question {
if len(res.DNS.Question) > 0 {
if opts.Display.Comments {
s += "\n;; QUESTION SECTION:\n"
}
for _, r := range res.DNS.Question {
s += r.String() + "\n"
}
}
}
if opts.Display.Answer {
if len(res.DNS.Answer) > 0 {
if opts.Display.Comments {
s += "\n;; ANSWER SECTION:\n"
}
for _, r := range res.DNS.Answer {
if r != nil {
s += r.String() + "\n"
}
}
}
}
if opts.Display.Authority {
if len(res.DNS.Ns) > 0 {
if opts.Display.Comments {
s += "\n;; AUTHORITY SECTION:\n"
}
for _, r := range res.DNS.Ns {
if r != nil {
s += r.String() + "\n"
}
}
}
}
if opts.Display.Additional {
if len(res.DNS.Extra) > 0 && (opt == nil || len(res.DNS.Extra) > 1) {
if opts.Display.Comments {
s += "\n;; ADDITIONAL SECTION:\n"
}
for _, r := range res.DNS.Extra {
if r != nil && r.Header().Rrtype != dns.TypeOPT {
s += r.String() + "\n"
}
}
}
}
if opts.Display.Statistics {
s += "\n;; Query time: " + res.RTT.String()
// 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)"
}
s += "\n;; SERVER: " + opts.Request.Server + extra
s += "\n;; WHEN: " + time.Now().Format(time.RFC1123Z)
s += "\n;; MSG SIZE rcvd: " + strconv.Itoa(res.DNS.Len()) + "\n"
}
} else {
// Print just the responses, nothing else
for _, res := range res.DNS.Answer {
temp := strings.Split(res.String(), "\t")
s += temp[len(temp)-1]
}
if opts.Identify {
s += " from server " + opts.Request.Server + " in " + res.RTT.String()
}
// s += "\n"
}
return s
}

14
query/struct_test.go Normal file
View file

@ -0,0 +1,14 @@
// SPDX-License-Identifier: BSD-3-Clause
package query_test
import (
"testing"
"gotest.tools/v3/assert"
)
// TODO
func TestToString(t *testing.T) {
assert.Assert(t, 1 == 1+0)
}

View file

@ -1,13 +1,13 @@
# SPDX-License-Identifier: BSD-3-Clause
# Template for the BSD/GNU makefiles
HASH ?= $(shell git describe --always --dirty || echo "UNKNOWN")
HASH ?= `git describe --always --dirty || echo "UNKNOWN"`
VER ?= "git-$(HASH)"
CGO_ENABLED ?= 0
GO ?= go
GOFLAGS ?= -ldflags "-s -w -X 'main.version=$(VER)'" -buildvcs=false
COVER ?= $(GO) tool cover
GOFLAGS ?= -ldflags "-s -w -X=main.version=$(VER)"
PREFIX ?= /usr/local
BIN ?= bin
@ -20,10 +20,10 @@ PROG ?= awl
# hehe
all: $(PROG) doc/$(PROG).1
doc/$(PROG).1: doc/wiki/$(PROG).1.md
@cp doc/awl.1 doc/awl.bak
$(SCDOC) <doc/wiki/$(PROG).1.md >doc/$(PROG).1 2>/dev/null && rm doc/awl.bak || mv doc/awl.bak doc/awl.1
$(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:
@ -31,8 +31,8 @@ test:
## cover: generates test coverage, output as HTML
cover: test
$(GO) tool cover -func=coverage/coverage.out
$(GO) tool cover -html=coverage/coverage.out -o coverage/cover.html
$(COVER) -func=coverage/coverage.out
$(COVER) -html=coverage/coverage.out -o coverage/cover.html
fmt:
gofmt -w -s .
@ -51,6 +51,6 @@ clean:
## help: Prints this help message
help:
@echo "Usage: "
@sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /'
@sed -n 's/^##//p' $(MAKEFILE_LIST) | column -t -s ':' | sed -e 's/^/ /'
.PHONY: clean lint test fmt vet help