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 }