228 lines
5 KiB
Go
228 lines
5 KiB
Go
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
// Parse dig-like commands and set the options as such.
|
|
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":
|
|
opts.AA = isNo
|
|
case "ad", "adflag":
|
|
opts.AD = isNo
|
|
case "cd", "cdflag":
|
|
opts.CD = isNo
|
|
case "qrflag":
|
|
opts.QR = isNo
|
|
case "ra", "raflag":
|
|
opts.RA = isNo
|
|
case "rd", "rdflag", "recurse":
|
|
opts.RD = isNo
|
|
case "tc", "tcflag":
|
|
opts.TC = isNo
|
|
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 "tcp", "vc":
|
|
opts.TCP = isNo
|
|
case "ignore":
|
|
opts.Truncate = isNo
|
|
case "tls":
|
|
opts.TLS = isNo
|
|
case "dnscrypt":
|
|
opts.DNSCrypt = isNo
|
|
case "https":
|
|
opts.HTTPS = isNo
|
|
case "quic":
|
|
opts.QUIC = isNo
|
|
// End DNS-over-X
|
|
|
|
// Formatting
|
|
case "short":
|
|
opts.Short = isNo
|
|
case "identify":
|
|
opts.Identify = isNo
|
|
case "json":
|
|
opts.JSON = isNo
|
|
case "xml":
|
|
opts.XML = isNo
|
|
case "yaml":
|
|
opts.YAML = isNo
|
|
// End formatting
|
|
|
|
// Output
|
|
case "comments":
|
|
opts.Display.Comments = isNo
|
|
case "question":
|
|
opts.Display.Question = isNo
|
|
case "opt":
|
|
opts.Display.Opt = isNo
|
|
case "answer":
|
|
opts.Display.Answer = isNo
|
|
case "authority":
|
|
opts.Display.Authority = isNo
|
|
case "additional":
|
|
opts.Display.Additional = isNo
|
|
case "stats":
|
|
opts.Display.Statistics = isNo
|
|
case "all":
|
|
opts.Display.Comments = isNo
|
|
opts.Display.Question = isNo
|
|
opts.Display.Opt = 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
|
|
arg := strings.Split(arg, "=")
|
|
switch arg[0] {
|
|
|
|
case "time", "timeout":
|
|
if len(arg) > 1 && arg[1] != "" {
|
|
timeout, err := strconv.Atoi(arg[1])
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("digflags: Invalid timeout value: %w", err)
|
|
}
|
|
|
|
opts.Request.Timeout = time.Duration(timeout)
|
|
} else {
|
|
return fmt.Errorf("digflags: Invalid timeout value: %w", errNoArg)
|
|
}
|
|
|
|
case "retry", "tries":
|
|
if len(arg) > 1 && arg[1] != "" {
|
|
tries, err := strconv.Atoi(arg[1])
|
|
if err != nil {
|
|
return fmt.Errorf("digflags: Invalid retry value: %w", err)
|
|
}
|
|
opts.Request.Retries = tries
|
|
|
|
// TODO: Is there a better way to do this?
|
|
if arg[0] == "tries" {
|
|
opts.Request.Retries++
|
|
}
|
|
} else {
|
|
return fmt.Errorf("digflags: Invalid retry value: %w", errNoArg)
|
|
}
|
|
|
|
case "bufsize":
|
|
if len(arg) > 1 && arg[1] != "" {
|
|
size, err := strconv.Atoi(arg[1])
|
|
if err != nil {
|
|
return fmt.Errorf("digflags: Invalid UDP buffer size value: %w", err)
|
|
}
|
|
opts.EDNS.BufSize = uint16(size)
|
|
} else {
|
|
return fmt.Errorf("digflags: Invalid UDP buffer size value: %w", errNoArg)
|
|
}
|
|
|
|
case "ednsflags":
|
|
if len(arg) > 1 && arg[1] != "" {
|
|
ver, err := strconv.ParseInt(arg[1], 0, 16)
|
|
if err != nil {
|
|
return fmt.Errorf("digflags: Invalid EDNS flag: %w", err)
|
|
}
|
|
// Ignore setting DO bit
|
|
opts.EDNS.ZFlag = uint16(ver & 0x7FFF)
|
|
} else {
|
|
opts.EDNS.ZFlag = 0
|
|
}
|
|
|
|
case "edns":
|
|
opts.EDNS.EnableEDNS = isNo
|
|
if len(arg) > 1 && arg[1] != "" {
|
|
ver, err := strconv.Atoi(arg[1])
|
|
if err != nil {
|
|
return fmt.Errorf("digflags: Invalid EDNS version: %w", err)
|
|
}
|
|
opts.EDNS.Version = uint8(ver)
|
|
} else {
|
|
opts.EDNS.Version = 0
|
|
}
|
|
|
|
case "subnet":
|
|
if len(arg) > 1 && arg[1] != "" {
|
|
ip, inet, err := net.ParseCIDR(arg[1])
|
|
if err != nil {
|
|
// TODO: make not a default?
|
|
if arg[1] == "0" {
|
|
opts.EDNS.Subnet = dns.EDNS0_SUBNET{
|
|
Code: dns.EDNS0SUBNET,
|
|
Family: 1,
|
|
SourceNetmask: 0,
|
|
SourceScope: 0,
|
|
Address: net.IPv4(0, 0, 0, 0),
|
|
}
|
|
return nil
|
|
} else {
|
|
return fmt.Errorf("digflags: Invalid EDNS Subnet: %w", errNoArg)
|
|
}
|
|
}
|
|
subnet, _ := inet.Mask.Size()
|
|
opts.EDNS.Subnet = *new(dns.EDNS0_SUBNET)
|
|
opts.EDNS.Subnet.Address = ip
|
|
opts.EDNS.Subnet.SourceNetmask = uint8(subnet)
|
|
switch ip.To4() {
|
|
case nil:
|
|
// Not a valid IPv4 so assume IPv6
|
|
opts.EDNS.Subnet.Family = 2
|
|
default:
|
|
// Valid IPv4
|
|
opts.EDNS.Subnet.Family = 1
|
|
}
|
|
} else {
|
|
return fmt.Errorf("digflags: Invalid EDNS Subnet: %w", errNoArg)
|
|
}
|
|
|
|
default:
|
|
return fmt.Errorf("digflags: unknown flag %s given", arg)
|
|
}
|
|
}
|
|
return nil
|
|
}
|