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// +build aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris 6 7package net 8 9import ( 10 "context" 11 "internal/bytealg" 12 "sync" 13 "syscall" 14 15 "golang.org/x/net/dns/dnsmessage" 16) 17 18var onceReadProtocols sync.Once 19 20// readProtocols loads contents of /etc/protocols into protocols map 21// for quick access. 22func 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. 53func lookupProtocol(_ context.Context, name string) (int, error) { 54 onceReadProtocols.Do(readProtocols) 55 return lookupProtocolMap(name) 56} 57 58func (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 78func (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 90func (r *Resolver) lookupIP(ctx context.Context, network, host string) (addrs []IPAddr, err error) { 91 if r.preferGo() { 92 return r.goLookupIP(ctx, 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, host, order) 103 return ips, err 104} 105 106func (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 122func (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 131func (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 183func (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 226func (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 267func (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 322func (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. 341func 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} 354