...
Run Format

Source file src/net/dnsconfig_unix.go

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

View as plain text