aboutsummaryrefslogtreecommitdiff
path: root/cmd/prognosis/resolver.go
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/prognosis/resolver.go')
-rw-r--r--cmd/prognosis/resolver.go67
1 files changed, 67 insertions, 0 deletions
diff --git a/cmd/prognosis/resolver.go b/cmd/prognosis/resolver.go
new file mode 100644
index 0000000..095cc20
--- /dev/null
+++ b/cmd/prognosis/resolver.go
@@ -0,0 +1,67 @@
+package main
+
+import (
+ "bufio"
+ "context"
+ "net"
+ "os"
+ "path/filepath"
+ "strings"
+ "time"
+)
+
+// configureResolver teaches Go's resolver where the nameservers are on Android.
+//
+// Android has no /etc/resolv.conf. Go's pure-Go resolver falls back to
+// localhost, so every lookup fails with "dial tcp [::1]:53: connection
+// refused". Termux does ship one, at $PREFIX/etc/resolv.conf, which Go never
+// consults. Reading it here uses whatever nameservers are actually configured
+// rather than hardcoding any.
+//
+// A no-op everywhere /etc/resolv.conf exists, which is every other platform we
+// build for.
+func configureResolver() {
+ if _, err := os.Stat("/etc/resolv.conf"); err == nil {
+ return
+ }
+ prefix := os.Getenv("PREFIX")
+ if prefix == "" {
+ return
+ }
+ servers := nameservers(filepath.Join(prefix, "etc", "resolv.conf"))
+ if len(servers) == 0 {
+ return
+ }
+ net.DefaultResolver = &net.Resolver{
+ PreferGo: true,
+ Dial: func(ctx context.Context, network, _ string) (net.Conn, error) {
+ d := net.Dialer{Timeout: 5 * time.Second}
+ var err error
+ for _, s := range servers {
+ var conn net.Conn
+ conn, err = d.DialContext(ctx, network, net.JoinHostPort(s, "53"))
+ if err == nil {
+ return conn, nil
+ }
+ }
+ return nil, err
+ },
+ }
+}
+
+func nameservers(path string) []string {
+ f, err := os.Open(path)
+ if err != nil {
+ return nil
+ }
+ defer f.Close()
+ var out []string
+ sc := bufio.NewScanner(f)
+ for sc.Scan() {
+ fields := strings.Fields(sc.Text())
+ if len(fields) >= 2 && fields[0] == "nameserver" {
+ out = append(out, fields[1])
+ }
+ }
+ return out
+}