// SPDX-License-Identifier: BSD-3-Clause package util import ( "fmt" "net" "git.froth.zone/sam/awl/logawl" "github.com/miekg/dns" ) // Options is the grand structure for all query options. type Options struct { // The logger Logger *logawl.Logger // Host to verify TLS cert with TLSHost string `json:"tlsHost"` // EDNS Options EDNS // DNS request :) Request Request // Verbosity levels, see [logawl.AllLevels] Verbosity int `json:"-"` // Display options Display Display // Ignore Truncation Truncate bool `json:"ignoreTruncate"` // Print only the answer Short bool `json:"short"` // When Short is true, display where the query came from Identify bool `json:"identify"` // Perform a reverse DNS query when true Reverse bool `json:"reverse"` 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"` // Use DNS-over-TLS to make the query TLS bool `json:"dns_over_tls"` // When using TLS, ignore certificates TLSNoVerify bool `json:"tlsNoVerify"` // Use DNS-over-HTTPS to make the query HTTPS bool `json:"dns_over_https"` // Use DNS-over-QUIC to make the query QUIC bool `json:"dns_over_quic"` // Use DNSCrypt to make the query DNSCrypt bool `json:"dnscrpyt"` // Force IPv4 only IPv4 bool `json:"force_IPv4"` // Force IPv6 only IPv6 bool `json:"force_IPv6"` } // HeaderFlags are the flags that are in DNS headers. type HeaderFlags struct { // Authoritative Answer DNS query flag AA bool `json:"authoritative,"` // Authenticated Data DNS query flag AD bool `json:"authenticatedData,"` // Checking Disabled DNS query flag CD bool `json:"checkingDisabled,"` // QueRy DNS query flag QR bool `json:"query"` // Recursion Desired DNS query flag RD bool `json:"recursionDesired"` // Recursion Available DNS query flag RA bool `json:"recursionAvailable"` // TrunCated DNS query flag TC bool `json:"truncated"` // Zero DNS query flag Z bool `json:"zero"` } // Display contains toggles for what to (and not to) display. type Display struct { /* Section displaying */ // Comments? Comments bool `json:"comments"` // QUESTION SECTION Question bool `json:"question"` // OPT PSEUDOSECTION Opt bool `json:"opt"` // ANSWER SECTION Answer bool `json:"answer"` // AUTHORITY SECTION Authority bool `json:"authority"` // ADDITIONAL SECTION Additional bool `json:"additional"` // Query time, message size, etc. Statistics bool `json:"statistics"` // Display TTL in response TTL bool `json:"ttl"` /* Answer formatting */ // Display Class in response ShowClass bool `json:"showClass"` // Display query before it is sent ShowQuery bool `json:"showQuery"` // Display TTL as human-readable HumanTTL bool `json:"HumanTTL"` // Translate Punycode back to Unicode UcodeTranslate bool `json:"unicode"` } // 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"` // UDP buffer size BufSize uint16 `json:"bufSize"` // Enable/Disable EDNS entirely EnableEDNS bool `json:"edns"` // Sending EDNS cookie Cookie bool `json:"cookie"` // Enabling DNSSEC DNSSEC bool `json:"dnssec"` // Sending EDNS Expire Expire bool `json:"expire"` // Sending EDNS TCP keepopen KeepOpen bool `json:"keepOpen"` // Sending EDNS NSID Nsid bool `json:"nsid"` // Send EDNS Padding Padding bool `json:"padding"` // Set EDNS version (default: 0) Version uint8 `json:"version"` } // 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 }