435c0d9fc8
This PR switches the Nomad repository from using govendor to Go modules for managing dependencies. Aspects of the Nomad workflow remain pretty much the same. The usual Makefile targets should continue to work as they always did. The API submodule simply defers to the parent Nomad version on the repository, keeping the semantics of API versioning that currently exists.
136 lines
3.3 KiB
Go
136 lines
3.3 KiB
Go
package dns
|
|
|
|
import (
|
|
"bufio"
|
|
"io"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// ClientConfig wraps the contents of the /etc/resolv.conf file.
|
|
type ClientConfig struct {
|
|
Servers []string // servers to use
|
|
Search []string // suffixes to append to local name
|
|
Port string // what port to use
|
|
Ndots int // number of dots in name to trigger absolute lookup
|
|
Timeout int // seconds before giving up on packet
|
|
Attempts int // lost packets before giving up on server, not used in the package dns
|
|
}
|
|
|
|
// ClientConfigFromFile parses a resolv.conf(5) like file and returns
|
|
// a *ClientConfig.
|
|
func ClientConfigFromFile(resolvconf string) (*ClientConfig, error) {
|
|
file, err := os.Open(resolvconf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer file.Close()
|
|
return ClientConfigFromReader(file)
|
|
}
|
|
|
|
// ClientConfigFromReader works like ClientConfigFromFile but takes an io.Reader as argument
|
|
func ClientConfigFromReader(resolvconf io.Reader) (*ClientConfig, error) {
|
|
c := new(ClientConfig)
|
|
scanner := bufio.NewScanner(resolvconf)
|
|
c.Servers = make([]string, 0)
|
|
c.Search = make([]string, 0)
|
|
c.Port = "53"
|
|
c.Ndots = 1
|
|
c.Timeout = 5
|
|
c.Attempts = 2
|
|
|
|
for scanner.Scan() {
|
|
if err := scanner.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
line := scanner.Text()
|
|
f := strings.Fields(line)
|
|
if len(f) < 1 {
|
|
continue
|
|
}
|
|
switch f[0] {
|
|
case "nameserver": // add one name server
|
|
if len(f) > 1 {
|
|
// One more check: make sure server name is
|
|
// just an IP address. Otherwise we need DNS
|
|
// to look it up.
|
|
name := f[1]
|
|
c.Servers = append(c.Servers, name)
|
|
}
|
|
|
|
case "domain": // set search path to just this domain
|
|
if len(f) > 1 {
|
|
c.Search = make([]string, 1)
|
|
c.Search[0] = f[1]
|
|
} else {
|
|
c.Search = make([]string, 0)
|
|
}
|
|
|
|
case "search": // set search path to given servers
|
|
c.Search = append([]string(nil), f[1:]...)
|
|
|
|
case "options": // magic options
|
|
for _, s := range f[1:] {
|
|
switch {
|
|
case len(s) >= 6 && s[:6] == "ndots:":
|
|
n, _ := strconv.Atoi(s[6:])
|
|
if n < 0 {
|
|
n = 0
|
|
} else if n > 15 {
|
|
n = 15
|
|
}
|
|
c.Ndots = n
|
|
case len(s) >= 8 && s[:8] == "timeout:":
|
|
n, _ := strconv.Atoi(s[8:])
|
|
if n < 1 {
|
|
n = 1
|
|
}
|
|
c.Timeout = n
|
|
case len(s) >= 9 && s[:9] == "attempts:":
|
|
n, _ := strconv.Atoi(s[9:])
|
|
if n < 1 {
|
|
n = 1
|
|
}
|
|
c.Attempts = n
|
|
case s == "rotate":
|
|
/* not imp */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
// NameList returns all of the names that should be queried based on the
|
|
// config. It is based off of go's net/dns name building, but it does not
|
|
// check the length of the resulting names.
|
|
func (c *ClientConfig) NameList(name string) []string {
|
|
// if this domain is already fully qualified, no append needed.
|
|
if IsFqdn(name) {
|
|
return []string{name}
|
|
}
|
|
|
|
// Check to see if the name has more labels than Ndots. Do this before making
|
|
// the domain fully qualified.
|
|
hasNdots := CountLabel(name) > c.Ndots
|
|
// Make the domain fully qualified.
|
|
name = Fqdn(name)
|
|
|
|
// Make a list of names based off search.
|
|
names := []string{}
|
|
|
|
// If name has enough dots, try that first.
|
|
if hasNdots {
|
|
names = append(names, name)
|
|
}
|
|
for _, s := range c.Search {
|
|
names = append(names, Fqdn(name+s))
|
|
}
|
|
// If we didn't have enough dots, try after suffixes.
|
|
if !hasNdots {
|
|
names = append(names, name)
|
|
}
|
|
return names
|
|
}
|