github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/src/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 // +build darwin dragonfly freebsd linux netbsd openbsd solaris 6 7 // Read system DNS config from /etc/resolv.conf 8 9 package net 10 11 import ( 12 "os" 13 "time" 14 ) 15 16 var ( 17 defaultNS = []string{"127.0.0.1:53", "[::1]:53"} 18 getHostname = os.Hostname // variable for testing 19 ) 20 21 type dnsConfig struct { 22 servers []string // server addresses (in host:port form) to use 23 search []string // rooted suffixes to append to local name 24 ndots int // number of dots in name to trigger absolute lookup 25 timeout time.Duration // wait before giving up on a query, including retries 26 attempts int // lost packets before giving up on server 27 rotate bool // round robin among servers 28 unknownOpt bool // anything unknown was encountered 29 lookup []string // OpenBSD top-level database "lookup" order 30 err error // any error that occurs during open of resolv.conf 31 mtime time.Time // time of resolv.conf modification 32 } 33 34 // See resolv.conf(5) on a Linux machine. 35 func dnsReadConfig(filename string) *dnsConfig { 36 conf := &dnsConfig{ 37 ndots: 1, 38 timeout: 5 * time.Second, 39 attempts: 2, 40 } 41 file, err := open(filename) 42 if err != nil { 43 conf.servers = defaultNS 44 conf.search = dnsDefaultSearch() 45 conf.err = err 46 return conf 47 } 48 defer file.close() 49 if fi, err := file.file.Stat(); err == nil { 50 conf.mtime = fi.ModTime() 51 } else { 52 conf.servers = defaultNS 53 conf.search = dnsDefaultSearch() 54 conf.err = err 55 return conf 56 } 57 for line, ok := file.readLine(); ok; line, ok = file.readLine() { 58 if len(line) > 0 && (line[0] == ';' || line[0] == '#') { 59 // comment. 60 continue 61 } 62 f := getFields(line) 63 if len(f) < 1 { 64 continue 65 } 66 switch f[0] { 67 case "nameserver": // add one name server 68 if len(f) > 1 && len(conf.servers) < 3 { // small, but the standard limit 69 // One more check: make sure server name is 70 // just an IP address. Otherwise we need DNS 71 // to look it up. 72 if parseIPv4(f[1]) != nil { 73 conf.servers = append(conf.servers, JoinHostPort(f[1], "53")) 74 } else if ip, _ := parseIPv6(f[1], true); ip != nil { 75 conf.servers = append(conf.servers, JoinHostPort(f[1], "53")) 76 } 77 } 78 79 case "domain": // set search path to just this domain 80 if len(f) > 1 { 81 conf.search = []string{ensureRooted(f[1])} 82 } 83 84 case "search": // set search path to given servers 85 conf.search = make([]string, len(f)-1) 86 for i := 0; i < len(conf.search); i++ { 87 conf.search[i] = ensureRooted(f[i+1]) 88 } 89 90 case "options": // magic options 91 for _, s := range f[1:] { 92 switch { 93 case hasPrefix(s, "ndots:"): 94 n, _, _ := dtoi(s, 6) 95 if n < 1 { 96 n = 1 97 } 98 conf.ndots = n 99 case hasPrefix(s, "timeout:"): 100 n, _, _ := dtoi(s, 8) 101 if n < 1 { 102 n = 1 103 } 104 conf.timeout = time.Duration(n) * time.Second 105 case hasPrefix(s, "attempts:"): 106 n, _, _ := dtoi(s, 9) 107 if n < 1 { 108 n = 1 109 } 110 conf.attempts = n 111 case s == "rotate": 112 conf.rotate = true 113 default: 114 conf.unknownOpt = true 115 } 116 } 117 118 case "lookup": 119 // OpenBSD option: 120 // http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/resolv.conf.5 121 // "the legal space-separated values are: bind, file, yp" 122 conf.lookup = f[1:] 123 124 default: 125 conf.unknownOpt = true 126 } 127 } 128 if len(conf.servers) == 0 { 129 conf.servers = defaultNS 130 } 131 if len(conf.search) == 0 { 132 conf.search = dnsDefaultSearch() 133 } 134 return conf 135 } 136 137 func dnsDefaultSearch() []string { 138 hn, err := getHostname() 139 if err != nil { 140 // best effort 141 return nil 142 } 143 if i := byteIndex(hn, '.'); i >= 0 && i < len(hn)-1 { 144 return []string{ensureRooted(hn[i+1:])} 145 } 146 return nil 147 } 148 149 func hasPrefix(s, prefix string) bool { 150 return len(s) >= len(prefix) && s[:len(prefix)] == prefix 151 } 152 153 func ensureRooted(s string) string { 154 if len(s) > 0 && s[len(s)-1] == '.' { 155 return s 156 } 157 return s + "." 158 }