dnsconfig_unix.mx raw

   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 !windows
   6  
   7  // Read system DNS config from /etc/resolv.conf
   8  
   9  package net
  10  
  11  import (
  12  	"internal/bytealg"
  13  	"internal/stringslite"
  14  	"net/netip"
  15  	"time"
  16  )
  17  
  18  // See resolv.conf(5) on a Linux machine.
  19  func dnsReadConfig(filename string) *dnsConfig {
  20  	conf := &dnsConfig{
  21  		ndots:    1,
  22  		timeout:  5 * time.Second,
  23  		attempts: 2,
  24  	}
  25  	file, err := open(filename)
  26  	if err != nil {
  27  		conf.servers = defaultNS
  28  		conf.search = dnsDefaultSearch()
  29  		conf.err = err
  30  		return conf
  31  	}
  32  	defer file.close()
  33  	if fi, err := file.file.Stat(); err == nil {
  34  		conf.mtime = fi.ModTime()
  35  	} else {
  36  		conf.servers = defaultNS
  37  		conf.search = dnsDefaultSearch()
  38  		conf.err = err
  39  		return conf
  40  	}
  41  	for line, ok := file.readLine(); ok; line, ok = file.readLine() {
  42  		if len(line) > 0 && (line[0] == ';' || line[0] == '#') {
  43  			// comment.
  44  			continue
  45  		}
  46  		f := getFields(line)
  47  		if len(f) < 1 {
  48  			continue
  49  		}
  50  		switch f[0] {
  51  		case "nameserver": // add one name server
  52  			if len(f) > 1 && len(conf.servers) < 3 { // small, but the standard limit
  53  				// One more check: make sure server name is
  54  				// just an IP address. Otherwise we need DNS
  55  				// to look it up.
  56  				if _, err := netip.ParseAddr(f[1]); err == 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 = [][]byte{ensureRooted(f[1])}
  64  			}
  65  
  66  		case "search": // set search path to given servers
  67  			conf.search = [][]byte{: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 stringslite.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 stringslite.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 stringslite.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  				case s == "no-reload":
 122  					conf.noReload = true
 123  				default:
 124  					conf.unknownOpt = true
 125  				}
 126  			}
 127  
 128  		case "lookup":
 129  			// OpenBSD option:
 130  			// https://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/resolv.conf.5
 131  			// "the legal space-separated values are: bind, file, yp"
 132  			conf.lookup = f[1:]
 133  
 134  		default:
 135  			conf.unknownOpt = true
 136  		}
 137  	}
 138  	if len(conf.servers) == 0 {
 139  		conf.servers = defaultNS
 140  	}
 141  	if len(conf.search) == 0 {
 142  		conf.search = dnsDefaultSearch()
 143  	}
 144  	return conf
 145  }
 146  
 147  func dnsDefaultSearch() [][]byte {
 148  	hn, err := getHostname()
 149  	if err != nil {
 150  		// best effort
 151  		return nil
 152  	}
 153  	if i := bytealg.IndexByteString(hn, '.'); i >= 0 && i < len(hn)-1 {
 154  		return [][]byte{ensureRooted(hn[i+1:])}
 155  	}
 156  	return nil
 157  }
 158  
 159  func ensureRooted(s string) string {
 160  	if len(s) > 0 && s[len(s)-1] == '.' {
 161  		return s
 162  	}
 163  	return s + "."
 164  }
 165