]> Cypherpunks.ru repositories - gostls13.git/blob - src/net/conf.go
cmd/compile/internal/inline: score call sites exposed by inlines
[gostls13.git] / src / net / conf.go
1 // Copyright 2015 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 package net
6
7 import (
8         "errors"
9         "internal/bytealg"
10         "internal/godebug"
11         "io/fs"
12         "os"
13         "runtime"
14         "sync"
15         "syscall"
16 )
17
18 // The net package's name resolution is rather complicated.
19 // There are two main approaches, go and cgo.
20 // The cgo resolver uses C functions like getaddrinfo.
21 // The go resolver reads system files directly and
22 // sends DNS packets directly to servers.
23 //
24 // The netgo build tag prefers the go resolver.
25 // The netcgo build tag prefers the cgo resolver.
26 //
27 // The netgo build tag also prohibits the use of the cgo tool.
28 // However, on Darwin, Plan 9, and Windows the cgo resolver is still available.
29 // On those systems the cgo resolver does not require the cgo tool.
30 // (The term "cgo resolver" was locked in by GODEBUG settings
31 // at a time when the cgo resolver did require the cgo tool.)
32 //
33 // Adding netdns=go to GODEBUG will prefer the go resolver.
34 // Adding netdns=cgo to GODEBUG will prefer the cgo resolver.
35 //
36 // The Resolver struct has a PreferGo field that user code
37 // may set to prefer the go resolver. It is documented as being
38 // equivalent to adding netdns=go to GODEBUG.
39 //
40 // When deciding which resolver to use, we first check the PreferGo field.
41 // If that is not set, we check the GODEBUG setting.
42 // If that is not set, we check the netgo or netcgo build tag.
43 // If none of those are set, we normally prefer the go resolver by default.
44 // However, if the cgo resolver is available,
45 // there is a complex set of conditions for which we prefer the cgo resolver.
46 //
47 // Other files define the netGoBuildTag, netCgoBuildTag, and cgoAvailable
48 // constants.
49
50 // conf is used to determine name resolution configuration.
51 type conf struct {
52         netGo  bool // prefer go approach, based on build tag and GODEBUG
53         netCgo bool // prefer cgo approach, based on build tag and GODEBUG
54
55         dnsDebugLevel int // from GODEBUG
56
57         preferCgo bool // if no explicit preference, use cgo
58
59         goos     string   // copy of runtime.GOOS, used for testing
60         mdnsTest mdnsTest // assume /etc/mdns.allow exists, for testing
61 }
62
63 // mdnsTest is for testing only.
64 type mdnsTest int
65
66 const (
67         mdnsFromSystem mdnsTest = iota
68         mdnsAssumeExists
69         mdnsAssumeDoesNotExist
70 )
71
72 var (
73         confOnce sync.Once // guards init of confVal via initConfVal
74         confVal  = &conf{goos: runtime.GOOS}
75 )
76
77 // systemConf returns the machine's network configuration.
78 func systemConf() *conf {
79         confOnce.Do(initConfVal)
80         return confVal
81 }
82
83 // initConfVal initializes confVal based on the environment
84 // that will not change during program execution.
85 func initConfVal() {
86         dnsMode, debugLevel := goDebugNetDNS()
87         confVal.netGo = netGoBuildTag || dnsMode == "go"
88         confVal.netCgo = netCgoBuildTag || dnsMode == "cgo"
89         confVal.dnsDebugLevel = debugLevel
90
91         if confVal.dnsDebugLevel > 0 {
92                 defer func() {
93                         if confVal.dnsDebugLevel > 1 {
94                                 println("go package net: confVal.netCgo =", confVal.netCgo, " netGo =", confVal.netGo)
95                         }
96                         switch {
97                         case confVal.netGo:
98                                 if netGoBuildTag {
99                                         println("go package net: built with netgo build tag; using Go's DNS resolver")
100                                 } else {
101                                         println("go package net: GODEBUG setting forcing use of Go's resolver")
102                                 }
103                         case !cgoAvailable:
104                                 println("go package net: cgo resolver not supported; using Go's DNS resolver")
105                         case confVal.netCgo || confVal.preferCgo:
106                                 println("go package net: using cgo DNS resolver")
107                         default:
108                                 println("go package net: dynamic selection of DNS resolver")
109                         }
110                 }()
111         }
112
113         // The remainder of this function sets preferCgo based on
114         // conditions that will not change during program execution.
115
116         // By default, prefer the go resolver.
117         confVal.preferCgo = false
118
119         // If the cgo resolver is not available, we can't prefer it.
120         if !cgoAvailable {
121                 return
122         }
123
124         // Some operating systems always prefer the cgo resolver.
125         if goosPrefersCgo() {
126                 confVal.preferCgo = true
127                 return
128         }
129
130         // The remaining checks are specific to Unix systems.
131         switch runtime.GOOS {
132         case "plan9", "windows", "js", "wasip1":
133                 return
134         }
135
136         // If any environment-specified resolver options are specified,
137         // prefer the cgo resolver.
138         // Note that LOCALDOMAIN can change behavior merely by being
139         // specified with the empty string.
140         _, localDomainDefined := syscall.Getenv("LOCALDOMAIN")
141         if localDomainDefined || os.Getenv("RES_OPTIONS") != "" || os.Getenv("HOSTALIASES") != "" {
142                 confVal.preferCgo = true
143                 return
144         }
145
146         // OpenBSD apparently lets you override the location of resolv.conf
147         // with ASR_CONFIG. If we notice that, defer to libc.
148         if runtime.GOOS == "openbsd" && os.Getenv("ASR_CONFIG") != "" {
149                 confVal.preferCgo = true
150                 return
151         }
152 }
153
154 // goosPrefersCgo reports whether the GOOS value passed in prefers
155 // the cgo resolver.
156 func goosPrefersCgo() bool {
157         switch runtime.GOOS {
158         // Historically on Windows and Plan 9 we prefer the
159         // cgo resolver (which doesn't use the cgo tool) rather than
160         // the go resolver. This is because originally these
161         // systems did not support the go resolver.
162         // Keep it this way for better compatibility.
163         // Perhaps we can revisit this some day.
164         case "windows", "plan9":
165                 return true
166
167         // Darwin pops up annoying dialog boxes if programs try to
168         // do their own DNS requests, so prefer cgo.
169         case "darwin", "ios":
170                 return true
171
172         // DNS requests don't work on Android, so prefer the cgo resolver.
173         // Issue #10714.
174         case "android":
175                 return true
176
177         default:
178                 return false
179         }
180 }
181
182 // mustUseGoResolver reports whether a DNS lookup of any sort is
183 // required to use the go resolver. The provided Resolver is optional.
184 // This will report true if the cgo resolver is not available.
185 func (c *conf) mustUseGoResolver(r *Resolver) bool {
186         if !cgoAvailable {
187                 return true
188         }
189
190         if runtime.GOOS == "plan9" {
191                 // TODO(bradfitz): for now we only permit use of the PreferGo
192                 // implementation when there's a non-nil Resolver with a
193                 // non-nil Dialer. This is a sign that they the code is trying
194                 // to use their DNS-speaking net.Conn (such as an in-memory
195                 // DNS cache) and they don't want to actually hit the network.
196                 // Once we add support for looking the default DNS servers
197                 // from plan9, though, then we can relax this.
198                 if r == nil || r.Dial == nil {
199                         return false
200                 }
201         }
202
203         return c.netGo || r.preferGo()
204 }
205
206 // addrLookupOrder determines which strategy to use to resolve addresses.
207 // The provided Resolver is optional. nil means to not consider its options.
208 // It also returns dnsConfig when it was used to determine the lookup order.
209 func (c *conf) addrLookupOrder(r *Resolver, addr string) (ret hostLookupOrder, dnsConf *dnsConfig) {
210         if c.dnsDebugLevel > 1 {
211                 defer func() {
212                         print("go package net: addrLookupOrder(", addr, ") = ", ret.String(), "\n")
213                 }()
214         }
215         return c.lookupOrder(r, "")
216 }
217
218 // hostLookupOrder determines which strategy to use to resolve hostname.
219 // The provided Resolver is optional. nil means to not consider its options.
220 // It also returns dnsConfig when it was used to determine the lookup order.
221 func (c *conf) hostLookupOrder(r *Resolver, hostname string) (ret hostLookupOrder, dnsConf *dnsConfig) {
222         if c.dnsDebugLevel > 1 {
223                 defer func() {
224                         print("go package net: hostLookupOrder(", hostname, ") = ", ret.String(), "\n")
225                 }()
226         }
227         return c.lookupOrder(r, hostname)
228 }
229
230 func (c *conf) lookupOrder(r *Resolver, hostname string) (ret hostLookupOrder, dnsConf *dnsConfig) {
231         // fallbackOrder is the order we return if we can't figure it out.
232         var fallbackOrder hostLookupOrder
233
234         var canUseCgo bool
235         if c.mustUseGoResolver(r) {
236                 // Go resolver was explicitly requested
237                 // or cgo resolver is not available.
238                 // Figure out the order below.
239                 fallbackOrder = hostLookupFilesDNS
240                 canUseCgo = false
241         } else if c.netCgo {
242                 // Cgo resolver was explicitly requested.
243                 return hostLookupCgo, nil
244         } else if c.preferCgo {
245                 // Given a choice, we prefer the cgo resolver.
246                 return hostLookupCgo, nil
247         } else {
248                 // Neither resolver was explicitly requested
249                 // and we have no preference.
250
251                 if bytealg.IndexByteString(hostname, '\\') != -1 || bytealg.IndexByteString(hostname, '%') != -1 {
252                         // Don't deal with special form hostnames
253                         // with backslashes or '%'.
254                         return hostLookupCgo, nil
255                 }
256
257                 // If something is unrecognized, use cgo.
258                 fallbackOrder = hostLookupCgo
259                 canUseCgo = true
260         }
261
262         // On systems that don't use /etc/resolv.conf or /etc/nsswitch.conf, we are done.
263         switch c.goos {
264         case "windows", "plan9", "android", "ios":
265                 return fallbackOrder, nil
266         }
267
268         // Try to figure out the order to use for searches.
269         // If we don't recognize something, use fallbackOrder.
270         // That will use cgo unless the Go resolver was explicitly requested.
271         // If we do figure out the order, return something other
272         // than fallbackOrder to use the Go resolver with that order.
273
274         dnsConf = getSystemDNSConfig()
275
276         if canUseCgo && dnsConf.err != nil && !errors.Is(dnsConf.err, fs.ErrNotExist) && !errors.Is(dnsConf.err, fs.ErrPermission) {
277                 // We can't read the resolv.conf file, so use cgo if we can.
278                 return hostLookupCgo, dnsConf
279         }
280
281         if canUseCgo && dnsConf.unknownOpt {
282                 // We didn't recognize something in resolv.conf,
283                 // so use cgo if we can.
284                 return hostLookupCgo, dnsConf
285         }
286
287         // OpenBSD is unique and doesn't use nsswitch.conf.
288         // It also doesn't support mDNS.
289         if c.goos == "openbsd" {
290                 // OpenBSD's resolv.conf manpage says that a
291                 // non-existent resolv.conf means "lookup" defaults
292                 // to only "files", without DNS lookups.
293                 if errors.Is(dnsConf.err, fs.ErrNotExist) {
294                         return hostLookupFiles, dnsConf
295                 }
296
297                 lookup := dnsConf.lookup
298                 if len(lookup) == 0 {
299                         // https://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/resolv.conf.5
300                         // "If the lookup keyword is not used in the
301                         // system's resolv.conf file then the assumed
302                         // order is 'bind file'"
303                         return hostLookupDNSFiles, dnsConf
304                 }
305                 if len(lookup) < 1 || len(lookup) > 2 {
306                         // We don't recognize this format.
307                         return fallbackOrder, dnsConf
308                 }
309                 switch lookup[0] {
310                 case "bind":
311                         if len(lookup) == 2 {
312                                 if lookup[1] == "file" {
313                                         return hostLookupDNSFiles, dnsConf
314                                 }
315                                 // Unrecognized.
316                                 return fallbackOrder, dnsConf
317                         }
318                         return hostLookupDNS, dnsConf
319                 case "file":
320                         if len(lookup) == 2 {
321                                 if lookup[1] == "bind" {
322                                         return hostLookupFilesDNS, dnsConf
323                                 }
324                                 // Unrecognized.
325                                 return fallbackOrder, dnsConf
326                         }
327                         return hostLookupFiles, dnsConf
328                 default:
329                         // Unrecognized.
330                         return fallbackOrder, dnsConf
331                 }
332
333                 // We always return before this point.
334                 // The code below is for non-OpenBSD.
335         }
336
337         // Canonicalize the hostname by removing any trailing dot.
338         if stringsHasSuffix(hostname, ".") {
339                 hostname = hostname[:len(hostname)-1]
340         }
341         if canUseCgo && stringsHasSuffixFold(hostname, ".local") {
342                 // Per RFC 6762, the ".local" TLD is special. And
343                 // because Go's native resolver doesn't do mDNS or
344                 // similar local resolution mechanisms, assume that
345                 // libc might (via Avahi, etc) and use cgo.
346                 return hostLookupCgo, dnsConf
347         }
348
349         nss := getSystemNSS()
350         srcs := nss.sources["hosts"]
351         // If /etc/nsswitch.conf doesn't exist or doesn't specify any
352         // sources for "hosts", assume Go's DNS will work fine.
353         if errors.Is(nss.err, fs.ErrNotExist) || (nss.err == nil && len(srcs) == 0) {
354                 if canUseCgo && c.goos == "solaris" {
355                         // illumos defaults to
356                         // "nis [NOTFOUND=return] files",
357                         // which the go resolver doesn't support.
358                         return hostLookupCgo, dnsConf
359                 }
360
361                 return hostLookupFilesDNS, dnsConf
362         }
363         if nss.err != nil {
364                 // We failed to parse or open nsswitch.conf, so
365                 // we have nothing to base an order on.
366                 return fallbackOrder, dnsConf
367         }
368
369         var hasDNSSource bool
370         var hasDNSSourceChecked bool
371
372         var filesSource, dnsSource bool
373         var first string
374         for i, src := range srcs {
375                 if src.source == "files" || src.source == "dns" {
376                         if canUseCgo && !src.standardCriteria() {
377                                 // non-standard; let libc deal with it.
378                                 return hostLookupCgo, dnsConf
379                         }
380                         if src.source == "files" {
381                                 filesSource = true
382                         } else {
383                                 hasDNSSource = true
384                                 hasDNSSourceChecked = true
385                                 dnsSource = true
386                         }
387                         if first == "" {
388                                 first = src.source
389                         }
390                         continue
391                 }
392
393                 if canUseCgo {
394                         switch {
395                         case hostname != "" && src.source == "myhostname":
396                                 // Let the cgo resolver handle myhostname
397                                 // if we are looking up the local hostname.
398                                 if isLocalhost(hostname) || isGateway(hostname) || isOutbound(hostname) {
399                                         return hostLookupCgo, dnsConf
400                                 }
401                                 hn, err := getHostname()
402                                 if err != nil || stringsEqualFold(hostname, hn) {
403                                         return hostLookupCgo, dnsConf
404                                 }
405                                 continue
406                         case hostname != "" && stringsHasPrefix(src.source, "mdns"):
407                                 // e.g. "mdns4", "mdns4_minimal"
408                                 // We already returned true before if it was *.local.
409                                 // libc wouldn't have found a hit on this anyway.
410
411                                 // We don't parse mdns.allow files. They're rare. If one
412                                 // exists, it might list other TLDs (besides .local) or even
413                                 // '*', so just let libc deal with it.
414                                 var haveMDNSAllow bool
415                                 switch c.mdnsTest {
416                                 case mdnsFromSystem:
417                                         _, err := os.Stat("/etc/mdns.allow")
418                                         if err != nil && !errors.Is(err, fs.ErrNotExist) {
419                                                 // Let libc figure out what is going on.
420                                                 return hostLookupCgo, dnsConf
421                                         }
422                                         haveMDNSAllow = err == nil
423                                 case mdnsAssumeExists:
424                                         haveMDNSAllow = true
425                                 case mdnsAssumeDoesNotExist:
426                                         haveMDNSAllow = false
427                                 }
428                                 if haveMDNSAllow {
429                                         return hostLookupCgo, dnsConf
430                                 }
431                                 continue
432                         default:
433                                 // Some source we don't know how to deal with.
434                                 return hostLookupCgo, dnsConf
435                         }
436                 }
437
438                 if !hasDNSSourceChecked {
439                         hasDNSSourceChecked = true
440                         for _, v := range srcs[i+1:] {
441                                 if v.source == "dns" {
442                                         hasDNSSource = true
443                                         break
444                                 }
445                         }
446                 }
447
448                 // If we saw a source we don't recognize, which can only
449                 // happen if we can't use the cgo resolver, treat it as DNS,
450                 // but only when there is no dns in all other sources.
451                 if !hasDNSSource {
452                         dnsSource = true
453                         if first == "" {
454                                 first = "dns"
455                         }
456                 }
457         }
458
459         // Cases where Go can handle it without cgo and C thread overhead,
460         // or where the Go resolver has been forced.
461         switch {
462         case filesSource && dnsSource:
463                 if first == "files" {
464                         return hostLookupFilesDNS, dnsConf
465                 } else {
466                         return hostLookupDNSFiles, dnsConf
467                 }
468         case filesSource:
469                 return hostLookupFiles, dnsConf
470         case dnsSource:
471                 return hostLookupDNS, dnsConf
472         }
473
474         // Something weird. Fallback to the default.
475         return fallbackOrder, dnsConf
476 }
477
478 var netdns = godebug.New("netdns")
479
480 // goDebugNetDNS parses the value of the GODEBUG "netdns" value.
481 // The netdns value can be of the form:
482 //
483 //      1       // debug level 1
484 //      2       // debug level 2
485 //      cgo     // use cgo for DNS lookups
486 //      go      // use go for DNS lookups
487 //      cgo+1   // use cgo for DNS lookups + debug level 1
488 //      1+cgo   // same
489 //      cgo+2   // same, but debug level 2
490 //
491 // etc.
492 func goDebugNetDNS() (dnsMode string, debugLevel int) {
493         goDebug := netdns.Value()
494         parsePart := func(s string) {
495                 if s == "" {
496                         return
497                 }
498                 if '0' <= s[0] && s[0] <= '9' {
499                         debugLevel, _, _ = dtoi(s)
500                 } else {
501                         dnsMode = s
502                 }
503         }
504         if i := bytealg.IndexByteString(goDebug, '+'); i != -1 {
505                 parsePart(goDebug[:i])
506                 parsePart(goDebug[i+1:])
507                 return
508         }
509         parsePart(goDebug)
510         return
511 }
512
513 // isLocalhost reports whether h should be considered a "localhost"
514 // name for the myhostname NSS module.
515 func isLocalhost(h string) bool {
516         return stringsEqualFold(h, "localhost") || stringsEqualFold(h, "localhost.localdomain") || stringsHasSuffixFold(h, ".localhost") || stringsHasSuffixFold(h, ".localhost.localdomain")
517 }
518
519 // isGateway reports whether h should be considered a "gateway"
520 // name for the myhostname NSS module.
521 func isGateway(h string) bool {
522         return stringsEqualFold(h, "_gateway")
523 }
524
525 // isOutbound reports whether h should be considered a "outbound"
526 // name for the myhostname NSS module.
527 func isOutbound(h string) bool {
528         return stringsEqualFold(h, "_outbound")
529 }