feat: Trace #162

Merged
sam merged 8 commits from trace into master 2022-12-10 17:51:50 +00:00
6 changed files with 152 additions and 60 deletions
Showing only changes of commit 1a6819a98f - Show all commits

16
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"args": ["froth.zone", "+trace", "@1.1.1.1"],
"program": "${workspaceFolder}/main.go"
}
]
}

View file

@ -18,6 +18,63 @@ import (
// ParseCLI parses arguments given from the CLI and passes them into an `Options`
// struct.
func ParseCLI(args []string, version string) (*util.Options, error) {
// Parse the standard flags
opts, misc, err := parseFlags(args, version)
if err != nil {
return opts, err
}
// Parse all the arguments that don't start with - or --
// This includes the dig-style (+) options
err = ParseMiscArgs(misc, opts)
if err != nil {
return opts, err
}
opts.Logger.Info("Dig/Drill flags parsed")
opts.Logger.Debug(fmt.Sprintf("%+v", opts))
// Special options and exceptions time
if opts.Request.Port == 0 {
if opts.TLS || opts.QUIC {
opts.Request.Port = 853
} else {
opts.Request.Port = 53
}
}
opts.Logger.Info("Port set to", opts.Request.Port)
// Set timeout to 0.5 seconds if set below 0.5
if opts.Request.Timeout < (time.Second / 2) {
opts.Request.Timeout = (time.Second / 2)
}
if opts.Request.Retries < 0 {
opts.Request.Retries = 0
}
if opts.Trace {
if opts.TLS || opts.HTTPS || opts.QUIC {
opts.Logger.Warn("Every query after the root query will only use UDP.")
}
if opts.Reverse {
opts.Logger.Error("Reverse queries are not currently supported")
}
opts.RD = false
}
opts.Logger.Info("Options fully populated")
opts.Logger.Debug(fmt.Sprintf("%+v", opts))
return opts, nil
}
// Everything that has to do with CLI flags goes here (the posix style, eg. -a and --bbbb).
func parseFlags(args []string, version string) (*util.Options, []string, error) {
flagSet := flag.NewFlagSet(args[0], flag.ContinueOnError)
flagSet.Usage = func() {
@ -46,6 +103,7 @@ func ParseCLI(args []string, version string) (*util.Options, error) {
ipv4 = flagSet.Bool("4", false, "force IPv4", flag.OptShorthand('4'))
ipv6 = flagSet.Bool("6", false, "force IPv6", flag.OptShorthand('6'))
reverse = flagSet.Bool("reverse", false, "do a reverse lookup", flag.OptShorthand('x'))
trace = flagSet.Bool("trace", false, "trace from the root")
timeout = flagSet.Float32("timeout", 5, "Timeout, in `seconds`")
retry = flagSet.Int("retries", 2, "number of `times` to retry")
@ -105,19 +163,20 @@ func ParseCLI(args []string, version string) (*util.Options, error) {
// Parse the flags
if err := flagSet.Parse(args[1:]); err != nil {
return &util.Options{Logger: util.InitLogger(*verbosity)}, fmt.Errorf("flag: %w", err)
return &util.Options{Logger: util.InitLogger(*verbosity)}, nil, fmt.Errorf("flag: %w", err)
}
// TODO: DRY, dumb dumb.
mbz, err := strconv.ParseInt(*mbzflag, 0, 16)
if err != nil {
return &util.Options{Logger: util.InitLogger(*verbosity)}, fmt.Errorf("EDNS MBZ: %w", err)
return &util.Options{Logger: util.InitLogger(*verbosity)}, nil, fmt.Errorf("EDNS MBZ: %w", err)
}
opts := util.Options{
Logger: util.InitLogger(*verbosity),
IPv4: *ipv4,
IPv6: *ipv6,
Trace: *trace,
Short: *short,
TCP: *tcp,
DNSCrypt: *dnscrypt,
@ -185,7 +244,7 @@ func ParseCLI(args []string, version string) (*util.Options, error) {
// TODO: DRY
if *subnet != "" {
if err = util.ParseSubnet(*subnet, &opts); err != nil {
return &opts, fmt.Errorf("%w", err)
return &opts, nil, fmt.Errorf("%w", err)
}
}
@ -195,42 +254,10 @@ func ParseCLI(args []string, version string) (*util.Options, error) {
if *versionFlag {
fmt.Printf("awl version %s, built with %s\n", version, runtime.Version())
return &opts, util.ErrNotError
return &opts, nil, util.ErrNotError
}
// Parse all the arguments that don't start with - or --
// This includes the dig-style (+) options
err = ParseMiscArgs(flagSet.Args(), &opts)
if err != nil {
return &opts, err
}
opts.Logger.Info("Dig/Drill flags parsed")
opts.Logger.Debug(fmt.Sprintf("%+v", opts))
if opts.Request.Port == 0 {
if opts.TLS || opts.QUIC {
opts.Request.Port = 853
} else {
opts.Request.Port = 53
}
}
opts.Logger.Info("Port set to", opts.Request.Port)
// Set timeout to 0.5 seconds if set below 0.5
if opts.Request.Timeout < (time.Second / 2) {
opts.Request.Timeout = (time.Second / 2)
}
if opts.Request.Retries < 0 {
opts.Request.Retries = 0
}
opts.Logger.Info("Options fully populated")
opts.Logger.Debug(fmt.Sprintf("%+v", opts))
return &opts, nil
return &opts, flagSet.Args(), nil
}
var errNoArg = errors.New("no argument given")

View file

@ -25,6 +25,8 @@ func ParseDig(arg string, opts *util.Options) error {
opts.Logger.Info("Setting", arg)
switch arg {
case "trace", "notrace":
opts.Trace = isNo
// Set DNS query flags
case "aa", "aaflag", "aaonly":
opts.AA = isNo

View file

@ -60,6 +60,7 @@ func FuzzDig(f *testing.F) {
"all", "noall",
"idnout", "noidnout",
"class", "noclass",
"trace", "notrace",
"invalid",
}

89
main.go
View file

@ -5,12 +5,15 @@ package main
import (
"errors"
"fmt"
"math/rand"
"os"
"strings"
"time"
cli "git.froth.zone/sam/awl/cmd"
"git.froth.zone/sam/awl/pkg/query"
"git.froth.zone/sam/awl/pkg/util"
"github.com/miekg/dns"
)
var version = "DEV"
@ -28,42 +31,82 @@ func main() {
}
func run(args []string) (opts *util.Options, code int, err error) {
//nolint:gosec //Secure source not needed
r := rand.New(rand.NewSource(time.Now().Unix()))
opts, err = cli.ParseCLI(args, version)
if err != nil {
return opts, 1, fmt.Errorf("parse: %w", err)
}
var resp util.Response
var (
resp util.Response
keepTracing bool
tempDomain string
tempQueryType uint16
)
// Retry queries if a query fails
for i := 0; i <= opts.Request.Retries; i++ {
resp, err = query.CreateQuery(opts)
if err == nil {
break
} else if i != opts.Request.Retries {
opts.Logger.Warn("Retrying request, error:", err)
for ok := true; ok; ok = keepTracing {
if keepTracing {
opts.Request.Name = tempDomain
opts.Request.Type = tempQueryType
} else {
tempDomain = opts.Request.Name
tempQueryType = opts.Request.Type
// Override the query because it needs to be done
opts.Request.Name = "."
opts.Request.Type = dns.TypeNS
}
}
// Retry queries if a query fails
for i := 0; i <= opts.Request.Retries; i++ {
resp, err = query.CreateQuery(opts)
if err == nil {
keepTracing = opts.Trace && (!resp.DNS.Authoritative || opts.Request.Name == ".")
// Query failed, make it fail
if err != nil {
return opts, 9, fmt.Errorf("query: %w", err)
}
break
} else if i != opts.Request.Retries {
opts.Logger.Warn("Retrying request, error:", err)
}
}
var str string
if opts.JSON || opts.XML || opts.YAML {
str, err = query.PrintSpecial(resp, opts)
// Query failed, make it fail
if err != nil {
return opts, 10, fmt.Errorf("format print: %w", err)
return opts, 9, fmt.Errorf("query: %w", err)
}
} else {
str, err = query.ToString(resp, opts)
if err != nil {
return opts, 15, fmt.Errorf("standard print: %w", err)
var str string
if opts.JSON || opts.XML || opts.YAML {
str, err = query.PrintSpecial(resp, opts)
if err != nil {
return opts, 10, fmt.Errorf("format print: %w", err)
}
} else {
str, err = query.ToString(resp, opts)
if err != nil {
return opts, 15, fmt.Errorf("standard print: %w", err)
}
}
fmt.Println(str)
if keepTracing {
// This part is the part that is broken
/*
this needs to be a random answer that comes from the DNS answers.
Since there is no recursion, and the answers are never recursive. The records should all be NS records.
https://pkg.go.dev/github.com/miekg/dns@v1.1.50
*/
_ = resp.DNS.Answer[r.Intn(len(resp.DNS.Answer))]
opts.TLS = false
opts.HTTPS = false
opts.QUIC = false
opts.Request.Port = 53
}
}
fmt.Println(str)
return opts, 0, nil
}

View file

@ -67,6 +67,9 @@ type Options struct {
IPv4 bool `json:"forceIPv4" example:"false"`
// Force IPv6 only
IPv6 bool `json:"forceIPv6" example:"false"`
// Trace from the root
Trace bool `json:"trace" example:"false"`
}
// HTTPSOptions are options exclusively for DNS-over-HTTPS queries.