github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/net/dnsconfig_unix.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build !js && !windows 6 7 // Read system DNS config from /etc/resolv.conf 8 9 package net 10 11 import ( 12 "internal/bytealg" 13 "time" 14 ) 15 16 // See resolv.conf(5) on a Linux machine. 17 func dnsReadConfig(filename string) *dnsConfig { 18 conf := &dnsConfig{ 19 ndots: 1, 20 timeout: 5 * time.Second, 21 attempts: 2, 22 } 23 file, err := open(filename) 24 if err != nil { 25 conf.servers = defaultNS 26 conf.search = dnsDefaultSearch() 27 conf.err = err 28 return conf 29 } 30 defer file.close() 31 if fi, err := file.file.Stat(); err == nil { 32 conf.mtime = fi.ModTime() 33 } else { 34 conf.servers = defaultNS 35 conf.search = dnsDefaultSearch() 36 conf.err = err 37 return conf 38 } 39 for line, ok := file.readLine(); ok; line, ok = file.readLine() { 40 if len(line) > 0 && (line[0] == ';' || line[0] == '#') { 41 // comment. 42 continue 43 } 44 f := getFields(line) 45 if len(f) < 1 { 46 continue 47 } 48 switch f[0] { 49 case "nameserver": // add one name server 50 if len(f) > 1 && len(conf.servers) < 3 { // small, but the standard limit 51 // One more check: make sure server name is 52 // just an IP address. Otherwise we need DNS 53 // to look it up. 54 if parseIPv4(f[1]) != nil { 55 conf.servers = append(conf.servers, JoinHostPort(f[1], "53")) 56 } else if ip, _ := parseIPv6Zone(f[1]); ip != nil { 57 conf.servers = append(conf.servers, JoinHostPort(f[1], "53")) 58 } 59 } 60 61 case "domain": // set search path to just this domain 62 if len(f) > 1 { 63 conf.search = []string{ensureRooted(f[1])} 64 } 65 66 case "search": // set search path to given servers 67 conf.search = make([]string, 0, len(f)-1) 68 for i := 1; i < len(f); i++ { 69 name := ensureRooted(f[i]) 70 if name == "." { 71 continue 72 } 73 conf.search = append(conf.search, name) 74 } 75 76 case "options": // magic options 77 for _, s := range f[1:] { 78 switch { 79 case hasPrefix(s, "ndots:"): 80 n, _, _ := dtoi(s[6:]) 81 if n < 0 { 82 n = 0 83 } else if n > 15 { 84 n = 15 85 } 86 conf.ndots = n 87 case hasPrefix(s, "timeout:"): 88 n, _, _ := dtoi(s[8:]) 89 if n < 1 { 90 n = 1 91 } 92 conf.timeout = time.Duration(n) * time.Second 93 case hasPrefix(s, "attempts:"): 94 n, _, _ := dtoi(s[9:]) 95 if n < 1 { 96 n = 1 97 } 98 conf.attempts = n 99 case s == "rotate": 100 conf.rotate = true 101 case s == "single-request" || s == "single-request-reopen": 102 // Linux option: 103 // http://man7.org/linux/man-pages/man5/resolv.conf.5.html 104 // "By default, glibc performs IPv4 and IPv6 lookups in parallel [...] 105 // This option disables the behavior and makes glibc 106 // perform the IPv6 and IPv4 requests sequentially." 107 conf.singleRequest = true 108 case s == "use-vc" || s == "usevc" || s == "tcp": 109 // Linux (use-vc), FreeBSD (usevc) and OpenBSD (tcp) option: 110 // http://man7.org/linux/man-pages/man5/resolv.conf.5.html 111 // "Sets RES_USEVC in _res.options. 112 // This option forces the use of TCP for DNS resolutions." 113 // https://www.freebsd.org/cgi/man.cgi?query=resolv.conf&sektion=5&manpath=freebsd-release-ports 114 // https://man.openbsd.org/resolv.conf.5 115 conf.useTCP = true 116 case s == "trust-ad": 117 conf.trustAD = true 118 case s == "edns0": 119 // We use EDNS by default. 120 // Ignore this option. 121 default: 122 conf.unknownOpt = true 123 } 124 } 125 126 case "lookup": 127 // OpenBSD option: 128 // https://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/resolv.conf.5 129 // "the legal space-separated values are: bind, file, yp" 130 conf.lookup = f[1:] 131 132 default: 133 conf.unknownOpt = true 134 } 135 } 136 if len(conf.servers) == 0 { 137 conf.servers = defaultNS 138 } 139 if len(conf.search) == 0 { 140 conf.search = dnsDefaultSearch() 141 } 142 return conf 143 } 144 145 func dnsDefaultSearch() []string { 146 hn, err := getHostname() 147 if err != nil { 148 // best effort 149 return nil 150 } 151 if i := bytealg.IndexByteString(hn, '.'); i >= 0 && i < len(hn)-1 { 152 return []string{ensureRooted(hn[i+1:])} 153 } 154 return nil 155 } 156 157 func hasPrefix(s, prefix string) bool { 158 return len(s) >= len(prefix) && s[:len(prefix)] == prefix 159 } 160 161 func ensureRooted(s string) string { 162 if len(s) > 0 && s[len(s)-1] == '.' { 163 return s 164 } 165 return s + "." 166 }