diff --git a/.drone.yml b/.drone.yml index 10c02d1..1ccb75b 100644 --- a/.drone.yml +++ b/.drone.yml @@ -2,7 +2,7 @@ kind: pipeline type: docker name: default steps: - - name: test + - name: Test image: golang commands: - go test ./... \ No newline at end of file diff --git a/cli.go b/cli.go index 6b18497..948991f 100644 --- a/cli.go +++ b/cli.go @@ -1,4 +1,5 @@ // SPDX-License-Identifier: BSD-3-Clause + package main import ( @@ -7,7 +8,9 @@ import ( "runtime" "strings" + "git.froth.zone/sam/awl/conf" "git.froth.zone/sam/awl/util" + "github.com/miekg/dns" "github.com/urfave/cli/v2" "golang.org/x/net/idna" @@ -111,7 +114,7 @@ func prepareCLI() *cli.App { }, &cli.BoolFlag{ Name: "tc", - Usage: "set tc (TrunCated) flag (default: not set)", + Usage: "set TC (TrunCated) flag (default: not set)", }, &cli.BoolFlag{ Name: "z", @@ -182,9 +185,8 @@ func parseArgs(args []string) (util.Answers, error) { } } if resp.Answers.Server == "" { - resolv, err := dns.ClientConfigFromFile("/etc/resolv.conf") - if err != nil { // Query Google by default, needed for Windows since the DNS library doesn't support Windows - // TODO: Actually find where windows stuffs its dns resolvers + resolv, err := conf.GetDNSConfig() + if err != nil { // Query Google by default resp.Answers.Server = "8.8.4.4" } else { resp.Answers.Server = resolv.Servers[rand.Intn(len(resolv.Servers))] diff --git a/cli_test.go b/cli_test.go index dce74a8..b496ce2 100644 --- a/cli_test.go +++ b/cli_test.go @@ -1,4 +1,5 @@ // SPDX-License-Identifier: BSD-3-Clause + package main import ( diff --git a/conf/docs.go b/conf/docs.go new file mode 100644 index 0000000..c57cfb5 --- /dev/null +++ b/conf/docs.go @@ -0,0 +1,6 @@ +/* +Helper functions for getting local nameservers + +Currently supported: Unix, Windows, Plan 9 (tested on 9front) +*/ +package conf diff --git a/conf/notwin.go b/conf/notwin.go new file mode 100644 index 0000000..7f8aa7a --- /dev/null +++ b/conf/notwin.go @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: BSD-3-Clause +//go:build !windows +// +build !windows + +package conf + +import ( + "os" + "runtime" + + "github.com/miekg/dns" +) + +// Get the DNS configuration, either from /etc/resolv.conf or somewhere else +func GetDNSConfig() (*dns.ClientConfig, error) { + if runtime.GOOS == "plan9" { + dat, err := os.ReadFile("/net/ndb") + if err != nil { + return nil, err + } + return getPlan9Config(string(dat)) + } else { + return dns.ClientConfigFromFile("/etc/resolv.conf") + } +} diff --git a/conf/plan9.go b/conf/plan9.go new file mode 100644 index 0000000..cb507fb --- /dev/null +++ b/conf/plan9.go @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: BSD-3-Clause + +package conf + +import ( + "fmt" + "strings" + + "github.com/miekg/dns" +) + +// Plan 9 stores its network data in /net/ndb, which seems to be formatted a specific way +// Yoink it and use it. +// +// See ndb(7). +func getPlan9Config(str string) (*dns.ClientConfig, error) { + str = strings.ReplaceAll(str, "\n", "") + spl := strings.FieldsFunc(str, splitChars) + var servers []string + for _, option := range spl { + if strings.HasPrefix(option, "dns=") { + servers = append(servers, strings.TrimPrefix(option, "dns=")) + } + } + if len(servers) == 0 { + return nil, fmt.Errorf("plan9: no DNS servers found") + } + + // TODO: read more about how customizable Plan 9 is + return &dns.ClientConfig{ + Servers: servers, + Search: []string{}, + Port: "53", + }, nil +} + +// Split the string at either space or tabs +func splitChars(r rune) bool { + return r == ' ' || r == '\t' +} diff --git a/conf/plan9_test.go b/conf/plan9_test.go new file mode 100644 index 0000000..fa81fd2 --- /dev/null +++ b/conf/plan9_test.go @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: BSD-3-Clause + +package conf + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetPlan9Config(t *testing.T) { + ndbs := []struct { + in string + want string + }{ + {`ip=192.168.122.45 ipmask=255.255.255.0 ipgw=192.168.122.1 + sys=chog9 + dns=192.168.122.1`, "192.168.122.1"}, + {`ipnet=murray-hill ip=135.104.0.0 ipmask=255.255.0.0 + dns=135.104.10.1 + ntp=ntp.cs.bell-labs.com + ipnet=plan9 ip=135.104.9.0 ipmask=255.255.255.0 + ntp=oncore.cs.bell-labs.com + smtp=smtp1.cs.bell-labs.com + ip=135.104.9.6 sys=anna dom=anna.cs.bell-labs.com + smtp=smtp2.cs.bell-labs.com`, "135.104.10.1"}, + } + + for _, ndb := range ndbs { + act, err := getPlan9Config(ndb.in) + assert.Nil(t, err) + assert.Equal(t, ndb.want, act.Servers[0]) + } + + invalid := `sys = spindle + dom=spindle.research.bell-labs.com + bootf=/mips/9powerboot + ip=135.104.117.32 ether=080069020677 + proto=il` + + act, err := getPlan9Config(invalid) + assert.ErrorContains(t, err, "no DNS servers found") + assert.Nil(t, act) + +} diff --git a/conf/win.go b/conf/win.go new file mode 100644 index 0000000..2e5d23c --- /dev/null +++ b/conf/win.go @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: BSD-3-Clause +//go:build windows + +package conf + +import ( + "strings" + "unsafe" + + "github.com/miekg/dns" + "golang.org/x/sys/windows" +) + +/* +"Stolen" from +https://gist.github.com/moloch--/9fb1c8497b09b45c840fe93dd23b1e98 +*/ + +// WindowsDnsClientConfig - returns all DNS server addresses using windows fuckery. +func GetDNSConfig() (*dns.ClientConfig, error) { + l := uint32(20000) + b := make([]byte, l) + + // Windows is an utter fucking trash fire of an operating system. + if err := windows.GetAdaptersAddresses(windows.AF_UNSPEC, windows.GAA_FLAG_INCLUDE_PREFIX, 0, (*windows.IpAdapterAddresses)(unsafe.Pointer(&b[0])), &l); err != nil { + return nil, err + } + var addresses []*windows.IpAdapterAddresses + for addr := (*windows.IpAdapterAddresses)(unsafe.Pointer(&b[0])); addr != nil; addr = addr.Next { + addresses = append(addresses, addr) + } + + resolvers := map[string]bool{} + for _, addr := range addresses { + for next := addr.FirstUnicastAddress; next != nil; next = next.Next { + if addr.OperStatus != windows.IfOperStatusUp { + continue + } + if next.Address.IP() != nil { + for dnsServer := addr.FirstDnsServerAddress; dnsServer != nil; dnsServer = dnsServer.Next { + ip := dnsServer.Address.IP() + if ip.IsMulticast() || ip.IsLinkLocalMulticast() || ip.IsLinkLocalUnicast() || ip.IsUnspecified() { + continue + } + if ip.To16() != nil && strings.HasPrefix(ip.To16().String(), "fec0:") { + continue + } + resolvers[ip.String()] = true + } + break + } + } + } + + // Take unique values only + servers := []string{} + for server := range resolvers { + servers = append(servers, server) + } + + // TODO: Make configurable, based on defaults in https://github.com/miekg/dns/blob/master/clientconfig.go + return &dns.ClientConfig{ + Servers: servers, + Search: []string{}, + Port: "53", + Ndots: 1, + Timeout: 5, + Attempts: 1, + }, nil +} diff --git a/go.mod b/go.mod index 1ce1512..ebe8a60 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b // indirect + golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.11 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect