github.com/m10x/go/src@v0.0.0-20220112094212-ba61592315da/net/lookup_unix.go (about) 1 // Copyright 2011 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 aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris 6 7 package net 8 9 import ( 10 "context" 11 "internal/bytealg" 12 "sync" 13 "syscall" 14 15 "golang.org/x/net/dns/dnsmessage" 16 ) 17 18 var onceReadProtocols sync.Once 19 20 // readProtocols loads contents of /etc/protocols into protocols map 21 // for quick access. 22 func readProtocols() { 23 file, err := open("/etc/protocols") 24 if err != nil { 25 return 26 } 27 defer file.close() 28 29 for line, ok := file.readLine(); ok; line, ok = file.readLine() { 30 // tcp 6 TCP # transmission control protocol 31 if i := bytealg.IndexByteString(line, '#'); i >= 0 { 32 line = line[0:i] 33 } 34 f := getFields(line) 35 if len(f) < 2 { 36 continue 37 } 38 if proto, _, ok := dtoi(f[1]); ok { 39 if _, ok := protocols[f[0]]; !ok { 40 protocols[f[0]] = proto 41 } 42 for _, alias := range f[2:] { 43 if _, ok := protocols[alias]; !ok { 44 protocols[alias] = proto 45 } 46 } 47 } 48 } 49 } 50 51 // lookupProtocol looks up IP protocol name in /etc/protocols and 52 // returns correspondent protocol number. 53 func lookupProtocol(_ context.Context, name string) (int, error) { 54 onceReadProtocols.Do(readProtocols) 55 return lookupProtocolMap(name) 56 } 57 58 func (r *Resolver) dial(ctx context.Context, network, server string) (Conn, error) { 59 // Calling Dial here is scary -- we have to be sure not to 60 // dial a name that will require a DNS lookup, or Dial will 61 // call back here to translate it. The DNS config parser has 62 // already checked that all the cfg.servers are IP 63 // addresses, which Dial will use without a DNS lookup. 64 var c Conn 65 var err error 66 if r != nil && r.Dial != nil { 67 c, err = r.Dial(ctx, network, server) 68 } else { 69 var d Dialer 70 c, err = d.DialContext(ctx, network, server) 71 } 72 if err != nil { 73 return nil, mapErr(err) 74 } 75 return c, nil 76 } 77 78 func (r *Resolver) lookupHost(ctx context.Context, host string) (addrs []string, err error) { 79 order := systemConf().hostLookupOrder(r, host) 80 if !r.preferGo() && order == hostLookupCgo { 81 if addrs, err, ok := cgoLookupHost(ctx, host); ok { 82 return addrs, err 83 } 84 // cgo not available (or netgo); fall back to Go's DNS resolver 85 order = hostLookupFilesDNS 86 } 87 return r.goLookupHostOrder(ctx, host, order) 88 } 89 90 func (r *Resolver) lookupIP(ctx context.Context, network, host string) (addrs []IPAddr, err error) { 91 if r.preferGo() { 92 return r.goLookupIP(ctx, network, host) 93 } 94 order := systemConf().hostLookupOrder(r, host) 95 if order == hostLookupCgo { 96 if addrs, err, ok := cgoLookupIP(ctx, network, host); ok { 97 return addrs, err 98 } 99 // cgo not available (or netgo); fall back to Go's DNS resolver 100 order = hostLookupFilesDNS 101 } 102 ips, _, err := r.goLookupIPCNAMEOrder(ctx, network, host, order) 103 return ips, err 104 } 105 106 func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int, error) { 107 if !r.preferGo() && systemConf().canUseCgo() { 108 if port, err, ok := cgoLookupPort(ctx, network, service); ok { 109 if err != nil { 110 // Issue 18213: if cgo fails, first check to see whether we 111 // have the answer baked-in to the net package. 112 if port, err := goLookupPort(network, service); err == nil { 113 return port, nil 114 } 115 } 116 return port, err 117 } 118 } 119 return goLookupPort(network, service) 120 } 121 122 func (r *Resolver) lookupCNAME(ctx context.Context, name string) (string, error) { 123 if !r.preferGo() && systemConf().canUseCgo() { 124 if cname, err, ok := cgoLookupCNAME(ctx, name); ok { 125 return cname, err 126 } 127 } 128 return r.goLookupCNAME(ctx, name) 129 } 130 131 func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) { 132 var target string 133 if service == "" && proto == "" { 134 target = name 135 } else { 136 target = "_" + service + "._" + proto + "." + name 137 } 138 p, server, err := r.lookup(ctx, target, dnsmessage.TypeSRV) 139 if err != nil { 140 return "", nil, err 141 } 142 var srvs []*SRV 143 var cname dnsmessage.Name 144 for { 145 h, err := p.AnswerHeader() 146 if err == dnsmessage.ErrSectionDone { 147 break 148 } 149 if err != nil { 150 return "", nil, &DNSError{ 151 Err: "cannot unmarshal DNS message", 152 Name: name, 153 Server: server, 154 } 155 } 156 if h.Type != dnsmessage.TypeSRV { 157 if err := p.SkipAnswer(); err != nil { 158 return "", nil, &DNSError{ 159 Err: "cannot unmarshal DNS message", 160 Name: name, 161 Server: server, 162 } 163 } 164 continue 165 } 166 if cname.Length == 0 && h.Name.Length != 0 { 167 cname = h.Name 168 } 169 srv, err := p.SRVResource() 170 if err != nil { 171 return "", nil, &DNSError{ 172 Err: "cannot unmarshal DNS message", 173 Name: name, 174 Server: server, 175 } 176 } 177 srvs = append(srvs, &SRV{Target: srv.Target.String(), Port: srv.Port, Priority: srv.Priority, Weight: srv.Weight}) 178 } 179 byPriorityWeight(srvs).sort() 180 return cname.String(), srvs, nil 181 } 182 183 func (r *Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) { 184 p, server, err := r.lookup(ctx, name, dnsmessage.TypeMX) 185 if err != nil { 186 return nil, err 187 } 188 var mxs []*MX 189 for { 190 h, err := p.AnswerHeader() 191 if err == dnsmessage.ErrSectionDone { 192 break 193 } 194 if err != nil { 195 return nil, &DNSError{ 196 Err: "cannot unmarshal DNS message", 197 Name: name, 198 Server: server, 199 } 200 } 201 if h.Type != dnsmessage.TypeMX { 202 if err := p.SkipAnswer(); err != nil { 203 return nil, &DNSError{ 204 Err: "cannot unmarshal DNS message", 205 Name: name, 206 Server: server, 207 } 208 } 209 continue 210 } 211 mx, err := p.MXResource() 212 if err != nil { 213 return nil, &DNSError{ 214 Err: "cannot unmarshal DNS message", 215 Name: name, 216 Server: server, 217 } 218 } 219 mxs = append(mxs, &MX{Host: mx.MX.String(), Pref: mx.Pref}) 220 221 } 222 byPref(mxs).sort() 223 return mxs, nil 224 } 225 226 func (r *Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) { 227 p, server, err := r.lookup(ctx, name, dnsmessage.TypeNS) 228 if err != nil { 229 return nil, err 230 } 231 var nss []*NS 232 for { 233 h, err := p.AnswerHeader() 234 if err == dnsmessage.ErrSectionDone { 235 break 236 } 237 if err != nil { 238 return nil, &DNSError{ 239 Err: "cannot unmarshal DNS message", 240 Name: name, 241 Server: server, 242 } 243 } 244 if h.Type != dnsmessage.TypeNS { 245 if err := p.SkipAnswer(); err != nil { 246 return nil, &DNSError{ 247 Err: "cannot unmarshal DNS message", 248 Name: name, 249 Server: server, 250 } 251 } 252 continue 253 } 254 ns, err := p.NSResource() 255 if err != nil { 256 return nil, &DNSError{ 257 Err: "cannot unmarshal DNS message", 258 Name: name, 259 Server: server, 260 } 261 } 262 nss = append(nss, &NS{Host: ns.NS.String()}) 263 } 264 return nss, nil 265 } 266 267 func (r *Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) { 268 p, server, err := r.lookup(ctx, name, dnsmessage.TypeTXT) 269 if err != nil { 270 return nil, err 271 } 272 var txts []string 273 for { 274 h, err := p.AnswerHeader() 275 if err == dnsmessage.ErrSectionDone { 276 break 277 } 278 if err != nil { 279 return nil, &DNSError{ 280 Err: "cannot unmarshal DNS message", 281 Name: name, 282 Server: server, 283 } 284 } 285 if h.Type != dnsmessage.TypeTXT { 286 if err := p.SkipAnswer(); err != nil { 287 return nil, &DNSError{ 288 Err: "cannot unmarshal DNS message", 289 Name: name, 290 Server: server, 291 } 292 } 293 continue 294 } 295 txt, err := p.TXTResource() 296 if err != nil { 297 return nil, &DNSError{ 298 Err: "cannot unmarshal DNS message", 299 Name: name, 300 Server: server, 301 } 302 } 303 // Multiple strings in one TXT record need to be 304 // concatenated without separator to be consistent 305 // with previous Go resolver. 306 n := 0 307 for _, s := range txt.TXT { 308 n += len(s) 309 } 310 txtJoin := make([]byte, 0, n) 311 for _, s := range txt.TXT { 312 txtJoin = append(txtJoin, s...) 313 } 314 if len(txts) == 0 { 315 txts = make([]string, 0, 1) 316 } 317 txts = append(txts, string(txtJoin)) 318 } 319 return txts, nil 320 } 321 322 func (r *Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error) { 323 if !r.preferGo() && systemConf().canUseCgo() { 324 if ptrs, err, ok := cgoLookupPTR(ctx, addr); ok { 325 return ptrs, err 326 } 327 } 328 return r.goLookupPTR(ctx, addr) 329 } 330 331 // concurrentThreadsLimit returns the number of threads we permit to 332 // run concurrently doing DNS lookups via cgo. A DNS lookup may use a 333 // file descriptor so we limit this to less than the number of 334 // permitted open files. On some systems, notably Darwin, if 335 // getaddrinfo is unable to open a file descriptor it simply returns 336 // EAI_NONAME rather than a useful error. Limiting the number of 337 // concurrent getaddrinfo calls to less than the permitted number of 338 // file descriptors makes that error less likely. We don't bother to 339 // apply the same limit to DNS lookups run directly from Go, because 340 // there we will return a meaningful "too many open files" error. 341 func concurrentThreadsLimit() int { 342 var rlim syscall.Rlimit 343 if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlim); err != nil { 344 return 500 345 } 346 r := int(rlim.Cur) 347 if r > 500 { 348 r = 500 349 } else if r > 30 { 350 r -= 30 351 } 352 return r 353 }