diff options
Diffstat (limited to 'plugin/hosts/hostsfile.go')
-rw-r--r-- | plugin/hosts/hostsfile.go | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/plugin/hosts/hostsfile.go b/plugin/hosts/hostsfile.go new file mode 100644 index 000000000..91e828099 --- /dev/null +++ b/plugin/hosts/hostsfile.go @@ -0,0 +1,193 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file is a modified version of net/hosts.go from the golang repo + +package hosts + +import ( + "bufio" + "bytes" + "io" + "net" + "os" + "strings" + "sync" + "time" + + "github.com/coredns/coredns/plugin" +) + +const cacheMaxAge = 5 * time.Second + +func parseLiteralIP(addr string) net.IP { + if i := strings.Index(addr, "%"); i >= 0 { + // discard ipv6 zone + addr = addr[0:i] + } + + return net.ParseIP(addr) +} + +func absDomainName(b string) string { + return plugin.Name(b).Normalize() +} + +// Hostsfile contains known host entries. +type Hostsfile struct { + sync.Mutex + + // list of zones we are authoritive for + Origins []string + + // Key for the list of literal IP addresses must be a host + // name. It would be part of DNS labels, a FQDN or an absolute + // FQDN. + // For now the key is converted to lower case for convenience. + byNameV4 map[string][]net.IP + byNameV6 map[string][]net.IP + + // Key for the list of host names must be a literal IP address + // including IPv6 address with zone identifier. + // We don't support old-classful IP address notation. + byAddr map[string][]string + + expire time.Time + path string + mtime time.Time + size int64 +} + +// ReadHosts determines if the cached data needs to be updated based on the size and modification time of the hostsfile. +func (h *Hostsfile) ReadHosts() { + now := time.Now() + + if now.Before(h.expire) && len(h.byAddr) > 0 { + return + } + stat, err := os.Stat(h.path) + if err == nil && h.mtime.Equal(stat.ModTime()) && h.size == stat.Size() { + h.expire = now.Add(cacheMaxAge) + return + } + + var file *os.File + if file, _ = os.Open(h.path); file == nil { + return + } + defer file.Close() + + h.Parse(file) + + // Update the data cache. + h.expire = now.Add(cacheMaxAge) + h.mtime = stat.ModTime() + h.size = stat.Size() +} + +// Parse reads the hostsfile and populates the byName and byAddr maps. +func (h *Hostsfile) Parse(file io.Reader) { + hsv4 := make(map[string][]net.IP) + hsv6 := make(map[string][]net.IP) + is := make(map[string][]string) + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Bytes() + if i := bytes.Index(line, []byte{'#'}); i >= 0 { + // Discard comments. + line = line[0:i] + } + f := bytes.Fields(line) + if len(f) < 2 { + continue + } + addr := parseLiteralIP(string(f[0])) + if addr == nil { + continue + } + ver := ipVersion(string(f[0])) + for i := 1; i < len(f); i++ { + name := absDomainName(string(f[i])) + if plugin.Zones(h.Origins).Matches(name) == "" { + // name is not in Origins + continue + } + switch ver { + case 4: + hsv4[name] = append(hsv4[name], addr) + case 6: + hsv6[name] = append(hsv6[name], addr) + default: + continue + } + is[addr.String()] = append(is[addr.String()], name) + } + } + h.byNameV4 = hsv4 + h.byNameV6 = hsv6 + h.byAddr = is +} + +// ipVersion returns what IP version was used textually +func ipVersion(s string) int { + for i := 0; i < len(s); i++ { + switch s[i] { + case '.': + return 4 + case ':': + return 6 + } + } + return 0 +} + +// LookupStaticHostV4 looks up the IPv4 addresses for the given host from the hosts file. +func (h *Hostsfile) LookupStaticHostV4(host string) []net.IP { + h.Lock() + defer h.Unlock() + h.ReadHosts() + if len(h.byNameV4) != 0 { + if ips, ok := h.byNameV4[absDomainName(host)]; ok { + ipsCp := make([]net.IP, len(ips)) + copy(ipsCp, ips) + return ipsCp + } + } + return nil +} + +// LookupStaticHostV6 looks up the IPv6 addresses for the given host from the hosts file. +func (h *Hostsfile) LookupStaticHostV6(host string) []net.IP { + h.Lock() + defer h.Unlock() + h.ReadHosts() + if len(h.byNameV6) != 0 { + if ips, ok := h.byNameV6[absDomainName(host)]; ok { + ipsCp := make([]net.IP, len(ips)) + copy(ipsCp, ips) + return ipsCp + } + } + return nil +} + +// LookupStaticAddr looks up the hosts for the given address from the hosts file. +func (h *Hostsfile) LookupStaticAddr(addr string) []string { + h.Lock() + defer h.Unlock() + h.ReadHosts() + addr = parseLiteralIP(addr).String() + if addr == "" { + return nil + } + if len(h.byAddr) != 0 { + if hosts, ok := h.byAddr[addr]; ok { + hostsCp := make([]string, len(hosts)) + copy(hostsCp, hosts) + return hostsCp + } + } + return nil +} |