clientconfig.go raw

   1  package dns
   2  
   3  import (
   4  	"bufio"
   5  	"io"
   6  	"os"
   7  	"strconv"
   8  	"strings"
   9  )
  10  
  11  // ClientConfig wraps the contents of the /etc/resolv.conf file.
  12  type ClientConfig struct {
  13  	Servers  []string // servers to use
  14  	Search   []string // suffixes to append to local name
  15  	Port     string   // what port to use
  16  	Ndots    int      // number of dots in name to trigger absolute lookup
  17  	Timeout  int      // seconds before giving up on packet
  18  	Attempts int      // lost packets before giving up on server, not used in the package dns
  19  }
  20  
  21  // ClientConfigFromFile parses a resolv.conf(5) like file and returns
  22  // a *ClientConfig.
  23  func ClientConfigFromFile(resolvconf string) (*ClientConfig, error) {
  24  	file, err := os.Open(resolvconf)
  25  	if err != nil {
  26  		return nil, err
  27  	}
  28  	defer file.Close()
  29  	return ClientConfigFromReader(file)
  30  }
  31  
  32  // ClientConfigFromReader works like ClientConfigFromFile but takes an io.Reader as argument
  33  func ClientConfigFromReader(resolvconf io.Reader) (*ClientConfig, error) {
  34  	c := new(ClientConfig)
  35  	scanner := bufio.NewScanner(resolvconf)
  36  	c.Servers = make([]string, 0)
  37  	c.Search = make([]string, 0)
  38  	c.Port = "53"
  39  	c.Ndots = 1
  40  	c.Timeout = 5
  41  	c.Attempts = 2
  42  
  43  	for scanner.Scan() {
  44  		if err := scanner.Err(); err != nil {
  45  			return nil, err
  46  		}
  47  		line := scanner.Text()
  48  		f := strings.Fields(line)
  49  		if len(f) < 1 {
  50  			continue
  51  		}
  52  		switch f[0] {
  53  		case "nameserver": // add one name server
  54  			if len(f) > 1 {
  55  				// One more check: make sure server name is
  56  				// just an IP address.  Otherwise we need DNS
  57  				// to look it up.
  58  				name := f[1]
  59  				c.Servers = append(c.Servers, name)
  60  			}
  61  
  62  		case "domain": // set search path to just this domain
  63  			if len(f) > 1 {
  64  				c.Search = make([]string, 1)
  65  				c.Search[0] = f[1]
  66  			} else {
  67  				c.Search = make([]string, 0)
  68  			}
  69  
  70  		case "search": // set search path to given servers
  71  			c.Search = cloneSlice(f[1:])
  72  
  73  		case "options": // magic options
  74  			for _, s := range f[1:] {
  75  				switch {
  76  				case len(s) >= 6 && s[:6] == "ndots:":
  77  					n, _ := strconv.Atoi(s[6:])
  78  					if n < 0 {
  79  						n = 0
  80  					} else if n > 15 {
  81  						n = 15
  82  					}
  83  					c.Ndots = n
  84  				case len(s) >= 8 && s[:8] == "timeout:":
  85  					n, _ := strconv.Atoi(s[8:])
  86  					if n < 1 {
  87  						n = 1
  88  					}
  89  					c.Timeout = n
  90  				case len(s) >= 9 && s[:9] == "attempts:":
  91  					n, _ := strconv.Atoi(s[9:])
  92  					if n < 1 {
  93  						n = 1
  94  					}
  95  					c.Attempts = n
  96  				case s == "rotate":
  97  					/* not imp */
  98  				}
  99  			}
 100  		}
 101  	}
 102  	return c, nil
 103  }
 104  
 105  // NameList returns all of the names that should be queried based on the
 106  // config. It is based off of go's net/dns name building, but it does not
 107  // check the length of the resulting names.
 108  func (c *ClientConfig) NameList(name string) []string {
 109  	// if this domain is already fully qualified, no append needed.
 110  	if IsFqdn(name) {
 111  		return []string{name}
 112  	}
 113  
 114  	// Check to see if the name has more labels than Ndots. Do this before making
 115  	// the domain fully qualified.
 116  	hasNdots := CountLabel(name) > c.Ndots
 117  	// Make the domain fully qualified.
 118  	name = Fqdn(name)
 119  
 120  	// Make a list of names based off search.
 121  	names := []string{}
 122  
 123  	// If name has enough dots, try that first.
 124  	if hasNdots {
 125  		names = append(names, name)
 126  	}
 127  	for _, s := range c.Search {
 128  		names = append(names, Fqdn(name+s))
 129  	}
 130  	// If we didn't have enough dots, try after suffixes.
 131  	if !hasNdots {
 132  		names = append(names, name)
 133  	}
 134  	return names
 135  }
 136