clientconfig.go (3365B)
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 }