Sam Therapy
58cee5b3c9
Add the "official" logo. Also run golangci-lint Signed-off-by: Sam Therapy <sam@samtherapy.net>
196 lines
5.3 KiB
Go
196 lines
5.3 KiB
Go
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"strings"
|
|
|
|
"dns.froth.zone/awl/conf"
|
|
"dns.froth.zone/awl/pkg/util"
|
|
"github.com/miekg/dns"
|
|
"golang.org/x/net/idna"
|
|
)
|
|
|
|
// ParseMiscArgs parses the wildcard arguments, dig style.
|
|
// Only one command is supported at a time, so any extra information overrides previous.
|
|
func ParseMiscArgs(args []string, opts *util.Options) error {
|
|
for _, arg := range args {
|
|
r, ok := dns.StringToType[strings.ToUpper(arg)]
|
|
|
|
switch {
|
|
// If it starts with @, it's a DNS server
|
|
case strings.HasPrefix(arg, "@"):
|
|
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 = strings.TrimPrefix(arg, "tls://")
|
|
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")
|
|
|
|
_, endpoint, isSplit := strings.Cut(arg, "/")
|
|
if isSplit {
|
|
opts.HTTPSOptions.Endpoint = "/" + endpoint
|
|
}
|
|
case strings.HasPrefix(arg, "quic://"):
|
|
opts.QUIC = true
|
|
opts.Request.Server = strings.TrimPrefix(arg, "quic://")
|
|
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")
|
|
case strings.HasPrefix(arg, "tcp://"):
|
|
opts.TCP = true
|
|
opts.Request.Server = strings.TrimPrefix(arg, "tcp://")
|
|
opts.Logger.Info("TCP implicitly set")
|
|
case strings.HasPrefix(arg, "udp://"):
|
|
opts.Request.Server = strings.TrimPrefix(arg, "udp://")
|
|
default:
|
|
// Allow HTTPS queries to have a fallback default
|
|
if opts.HTTPS {
|
|
server, endpoint, isSplit := strings.Cut(arg, "/")
|
|
if isSplit {
|
|
opts.HTTPSOptions.Endpoint = "/" + endpoint
|
|
opts.Request.Server = server
|
|
} else {
|
|
opts.Request.Server = server
|
|
}
|
|
} else {
|
|
opts.Request.Server = arg
|
|
}
|
|
}
|
|
|
|
// Dig-style +queries
|
|
case strings.HasPrefix(arg, "+"):
|
|
opts.Logger.Info(arg, "detected as a dig query")
|
|
|
|
if err := ParseDig(strings.ToLower(arg[1:]), opts); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Domain names
|
|
case strings.Contains(arg, "."):
|
|
var err error
|
|
|
|
opts.Logger.Info(arg, "detected as a domain name")
|
|
opts.Request.Name, err = idna.ToASCII(arg)
|
|
if err != nil {
|
|
return fmt.Errorf("unicode to punycode: %w", err)
|
|
}
|
|
|
|
// DNS query type
|
|
case ok:
|
|
opts.Logger.Info(arg, "detected as a type")
|
|
opts.Request.Type = r
|
|
|
|
// Domain?
|
|
default:
|
|
var err error
|
|
|
|
opts.Logger.Info(arg, "is unknown. Assuming domain")
|
|
opts.Request.Name, err = idna.ToASCII(arg)
|
|
if err != nil {
|
|
return fmt.Errorf("unicode to punycode: %w", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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.Logger.Info("Query not specified, making an \"NS\" query")
|
|
opts.Request.Type = dns.StringToType["NS"]
|
|
}
|
|
} else if opts.Request.Type == 0 {
|
|
opts.Logger.Info("Query not specified, making an \"A\" query")
|
|
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:
|
|
opts.Request.Server = "https://dns.cloudflare.com"
|
|
case opts.QUIC:
|
|
opts.Request.Server = "dns.froth.zone"
|
|
default:
|
|
var err error
|
|
resolv, err := conf.GetDNSConfig()
|
|
|
|
if err != nil {
|
|
// :^)
|
|
opts.Logger.Warn("Could not query system for server. Using localhost\n", "Error:", err)
|
|
opts.Request.Server = "127.0.0.1"
|
|
} else {
|
|
// Make sure that if IPv4 or IPv6 is asked for it actually uses it
|
|
harmful:
|
|
for _, srv := range resolv.Servers {
|
|
switch {
|
|
case opts.IPv4:
|
|
if strings.Contains(srv, ".") {
|
|
opts.Request.Server = srv
|
|
|
|
break harmful
|
|
}
|
|
case opts.IPv6:
|
|
if strings.Contains(srv, ":") {
|
|
opts.Request.Server = srv
|
|
|
|
break harmful
|
|
}
|
|
default:
|
|
//#nosec -- This isn't used for anything secure
|
|
opts.Request.Server = resolv.Servers[rand.Intn(len(resolv.Servers))]
|
|
|
|
break harmful
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
opts.Logger.Info("DNS server set to", opts.Request.Server)
|
|
|
|
// Make reverse addresses proper addresses
|
|
if opts.Reverse {
|
|
var err error
|
|
|
|
opts.Logger.Info("Making reverse DNS query proper *.arpa domain")
|
|
|
|
if dns.TypeToString[opts.Request.Type] == "A" {
|
|
opts.Request.Type = dns.StringToType["PTR"]
|
|
}
|
|
|
|
opts.Request.Name, err = util.ReverseDNS(opts.Request.Name, opts.Request.Type)
|
|
if err != nil {
|
|
return fmt.Errorf("reverse DNS: %w", err)
|
|
}
|
|
}
|
|
|
|
// 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.Info("Domain made canonical")
|
|
}
|
|
|
|
return nil
|
|
}
|