awl/pkg/util/options.go

201 lines
5.6 KiB
Go

// SPDX-License-Identifier: BSD-3-Clause
package util
import (
"fmt"
"net"
"git.froth.zone/sam/awl/pkg/logawl"
"github.com/miekg/dns"
)
// Options is the grand structure for all query options.
type Options struct {
// The logger
Logger *logawl.Logger `json:"-"`
// Host to verify TLS cert with
TLSHost string `json:"tlsHost" example:""`
// EDNS Options
EDNS
// HTTPS options :)
HTTPSOptions
// DNS request :)
Request
// Verbosity levels, see [logawl.AllLevels]
Verbosity int `json:"-" example:"0"`
// Display options
Display Display
// Ignore Truncation
Truncate bool `json:"ignoreTruncate" example:"false"`
// Ignore BADCOOKIE
BadCookie bool `json:"ignoreBadCookie" example:"false"`
// Print only the answer
Short bool `json:"short" example:"false"`
// When Short is true, display where the query came from
Identify bool `json:"identify" example:"false"`
// Perform a reverse DNS query when true
Reverse bool `json:"reverse" example:"false"`
HeaderFlags
// Display resposne as JSON
JSON bool `json:"-" xml:"-" yaml:"-"`
// Display response as XML
XML bool `json:"-" xml:"-" yaml:"-"`
// Display response as YAML
YAML bool `json:"-" xml:"-" yaml:"-"`
// Use TCP instead of UDP to make the query
TCP bool `json:"tcp" example:"false"`
// Use DNS-over-TLS to make the query
TLS bool `json:"dnsOverTLS" example:"false"`
// When using TLS, ignore certificates
TLSNoVerify bool `json:"tlsNoVerify" example:"false"`
// Use DNS-over-HTTPS to make the query
HTTPS bool `json:"dnsOverHTTPS" example:"false"`
// Use DNS-over-QUIC to make the query
//nolint:tagliatelle // QUIC is an acronym
QUIC bool `json:"dnsOverQUIC" example:"false"`
// Use DNSCrypt to make the query
DNSCrypt bool `json:"dnscrypt" example:"false"`
// Force IPv4 only
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.
type HTTPSOptions struct {
// URL endpoint
Endpoint string `json:"endpoint" example:"/dns-query"`
// True, make GET request.
// False, make POST request.
Get bool `json:"get" example:"false"`
}
// HeaderFlags are the flags that are in DNS headers.
type HeaderFlags struct {
// Authoritative Answer DNS query flag
AA bool `json:"authoritative" example:"false"`
// Authenticated Data DNS query flag
AD bool `json:"authenticatedData" example:"false"`
// Checking Disabled DNS query flag
CD bool `json:"checkingDisabled" example:"false"`
// QueRy DNS query flag
QR bool `json:"query" example:"false"`
// Recursion Desired DNS query flag
RD bool `json:"recursionDesired" example:"true"`
// Recursion Available DNS query flag
RA bool `json:"recursionAvailable" example:"false"`
// TrunCated DNS query flag
TC bool `json:"truncated" example:"false"`
// Zero DNS query flag
Z bool `json:"zero" example:"false"`
}
// Display contains toggles for what to (and not to) display.
type Display struct {
/* Section displaying */
// Comments?
Comments bool `json:"comments" example:"true"`
// QUESTION SECTION
Question bool `json:"question" example:"true"`
// OPT PSEUDOSECTION
Opt bool `json:"opt" example:"true"`
// ANSWER SECTION
Answer bool `json:"answer" example:"true"`
// AUTHORITY SECTION
Authority bool `json:"authority" example:"true"`
// ADDITIONAL SECTION
Additional bool `json:"additional" example:"true"`
// Query time, message size, etc.
Statistics bool `json:"statistics" example:"true"`
// Display TTL in response
TTL bool `json:"ttl" example:"true"`
/* Answer formatting */
// Display Class in response
ShowClass bool `json:"showClass" example:"true"`
// Display query before it is sent
ShowQuery bool `json:"showQuery" example:"false"`
// Display TTL as human-readable
HumanTTL bool `json:"humanTTL" example:"false"`
// Translate Punycode back to Unicode
UcodeTranslate bool `json:"unicode" example:"true"`
}
// EDNS contains toggles for various EDNS options.
type EDNS struct {
// Subnet to originate query from.
Subnet dns.EDNS0_SUBNET `json:"subnet"`
// Must Be Zero flag
ZFlag uint16 `json:"zflag" example:"0"`
// UDP buffer size
BufSize uint16 `json:"bufSize" example:"1232"`
// Enable/Disable EDNS entirely
EnableEDNS bool `json:"edns" example:"false"`
// Sending EDNS cookie
Cookie bool `json:"cookie" example:"true"`
// Enabling DNSSEC
DNSSEC bool `json:"dnssec" example:"false"`
// Sending EDNS Expire
Expire bool `json:"expire" example:"false"`
// Sending EDNS TCP keepopen
KeepOpen bool `json:"keepOpen" example:"false"`
// Sending EDNS NSID
Nsid bool `json:"nsid" example:"false"`
// Send EDNS Padding
Padding bool `json:"padding" example:"false"`
// Set EDNS version (default: 0)
Version uint8 `json:"version" example:"0"`
}
// ParseSubnet takes a subnet argument and makes it into one that the DNS library
// understands.
func ParseSubnet(subnet string, opts *Options) error {
ip, inet, err := net.ParseCIDR(subnet)
if err != nil {
// TODO: make not a default?
if subnet == "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
}
return fmt.Errorf("EDNS subnet parsing: %w", err)
}
sub, _ := inet.Mask.Size()
opts.EDNS.Subnet = dns.EDNS0_SUBNET{}
opts.EDNS.Subnet.Address = ip
opts.EDNS.Subnet.SourceNetmask = uint8(sub)
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
}
return nil
}