| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | // 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"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-14 09:36:06 +01:00
										 |  |  | 	"github.com/coredns/coredns/plugin"
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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 {
 | 
					
						
							| 
									
										
										
										
											2017-09-14 09:36:06 +01:00
										 |  |  | 	return plugin.Name(b).Normalize()
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | type options struct {
 | 
					
						
							|  |  |  | 	// automatically generate IP to Hostname PTR entries
 | 
					
						
							|  |  |  | 	// for host entries we parse
 | 
					
						
							|  |  |  | 	autoReverse bool
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// The TTL of the record we generate
 | 
					
						
							|  |  |  | 	ttl uint32
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// The time between two reload of the configuration
 | 
					
						
							|  |  |  | 	reload time.Duration
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newOptions() *options {
 | 
					
						
							|  |  |  | 	return &options{
 | 
					
						
							|  |  |  | 		autoReverse: true,
 | 
					
						
							|  |  |  | 		ttl:         3600,
 | 
					
						
							|  |  |  | 		reload:      durationOf5s,
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | type hostsMap struct {
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 	// 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
 | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | }
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | const (
 | 
					
						
							|  |  |  | 	durationOf0s = time.Duration(0)
 | 
					
						
							|  |  |  | 	durationOf5s = time.Duration(5 * time.Second)
 | 
					
						
							|  |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | func newHostsMap() *hostsMap {
 | 
					
						
							|  |  |  | 	return &hostsMap{
 | 
					
						
							|  |  |  | 		byNameV4: make(map[string][]net.IP),
 | 
					
						
							|  |  |  | 		byNameV6: make(map[string][]net.IP),
 | 
					
						
							|  |  |  | 		byAddr:   make(map[string][]string),
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-19 15:16:35 +01:00
										 |  |  | // Len returns the total number of addresses in the hostmap, this includes
 | 
					
						
							|  |  |  | // V4/V6 and any reverse addresses.
 | 
					
						
							|  |  |  | func (h *hostsMap) Len() int {
 | 
					
						
							|  |  |  | 	l := 0
 | 
					
						
							|  |  |  | 	for _, v4 := range h.byNameV4 {
 | 
					
						
							|  |  |  | 		l += len(v4)
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	for _, v6 := range h.byNameV6 {
 | 
					
						
							|  |  |  | 		l += len(v6)
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	for _, a := range h.byAddr {
 | 
					
						
							|  |  |  | 		l += len(a)
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return l
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | // Hostsfile contains known host entries.
 | 
					
						
							|  |  |  | type Hostsfile struct {
 | 
					
						
							|  |  |  | 	sync.RWMutex
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-14 17:55:55 +02:00
										 |  |  | 	// list of zones we are authoritative for
 | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | 	Origins []string
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// hosts maps for lookups
 | 
					
						
							|  |  |  | 	hmap *hostsMap
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// inline saves the hosts file that is inlined in a Corefile.
 | 
					
						
							|  |  |  | 	// We need a copy here as we want to use it to initialize the maps for parse.
 | 
					
						
							|  |  |  | 	inline *hostsMap
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// path to the hosts file
 | 
					
						
							|  |  |  | 	path string
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// mtime and size are only read and modified by a single goroutine
 | 
					
						
							|  |  |  | 	mtime time.Time
 | 
					
						
							|  |  |  | 	size  int64
 | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	options *options
 | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // readHosts determines if the cached data needs to be updated based on the size and modification time of the hostsfile.
 | 
					
						
							|  |  |  | func (h *Hostsfile) readHosts() {
 | 
					
						
							|  |  |  | 	file, err := os.Open(h.path)
 | 
					
						
							|  |  |  | 	if err != nil {
 | 
					
						
							|  |  |  | 		// We already log a warning if the file doesn't exist or can't be opened on setup. No need to return the error here.
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 		return
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | 	defer file.Close()
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | 	stat, err := file.Stat()
 | 
					
						
							| 
									
										
										
										
											2019-02-17 19:34:07 +00:00
										 |  |  | 	h.RLock()
 | 
					
						
							|  |  |  | 	size := h.size
 | 
					
						
							|  |  |  | 	h.RUnlock()
 | 
					
						
							|  |  |  | 	if err == nil && h.mtime.Equal(stat.ModTime()) && size == stat.Size() {
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 		return
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | 	newMap := h.parse(file)
 | 
					
						
							| 
									
										
										
										
											2018-11-03 20:00:07 +00:00
										 |  |  | 	log.Debugf("Parsed hosts file into %d entries", newMap.Len())
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-24 13:46:58 -06:00
										 |  |  | 	h.Lock()
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-03 20:00:07 +00:00
										 |  |  | 	h.hmap = newMap
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 	// Update the data cache.
 | 
					
						
							|  |  |  | 	h.mtime = stat.ModTime()
 | 
					
						
							|  |  |  | 	h.size = stat.Size()
 | 
					
						
							| 
									
										
										
										
											2018-11-03 20:00:07 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	h.Unlock()
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | func (h *Hostsfile) initInline(inline []string) {
 | 
					
						
							|  |  |  | 	if len(inline) == 0 {
 | 
					
						
							|  |  |  | 		return
 | 
					
						
							| 
									
										
										
										
											2017-09-21 04:18:13 -07:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | 	h.inline = h.parse(strings.NewReader(strings.Join(inline, "\n")))
 | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | 	*h.hmap = *h.inline
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Parse reads the hostsfile and populates the byName and byAddr maps.
 | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | func (h *Hostsfile) parse(r io.Reader) *hostsMap {
 | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | 	hmap := newHostsMap()
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	scanner := bufio.NewScanner(r)
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 	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]))
 | 
					
						
							| 
									
										
										
										
											2017-09-14 09:36:06 +01:00
										 |  |  | 			if plugin.Zones(h.Origins).Matches(name) == "" {
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 				// name is not in Origins
 | 
					
						
							|  |  |  | 				continue
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 			switch ver {
 | 
					
						
							|  |  |  | 			case 4:
 | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | 				hmap.byNameV4[name] = append(hmap.byNameV4[name], addr)
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 			case 6:
 | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | 				hmap.byNameV6[name] = append(hmap.byNameV6[name], addr)
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 			default:
 | 
					
						
							|  |  |  | 				continue
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | 			if !h.options.autoReverse {
 | 
					
						
							|  |  |  | 				continue
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | 			hmap.byAddr[addr.String()] = append(hmap.byAddr[addr.String()], name)
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | 	for name := range h.hmap.byNameV4 {
 | 
					
						
							|  |  |  | 		hmap.byNameV4[name] = append(hmap.byNameV4[name], h.hmap.byNameV4[name]...)
 | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | 	for name := range h.hmap.byNameV4 {
 | 
					
						
							|  |  |  | 		hmap.byNameV6[name] = append(hmap.byNameV6[name], h.hmap.byNameV6[name]...)
 | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for addr := range h.hmap.byAddr {
 | 
					
						
							|  |  |  | 		hmap.byAddr[addr] = append(hmap.byAddr[addr], h.hmap.byAddr[addr]...)
 | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return hmap
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ipVersion returns what IP version was used textually
 | 
					
						
							| 
									
										
										
										
											2019-01-28 16:36:34 +00:00
										 |  |  | // For why the string is parsed end to start,
 | 
					
						
							|  |  |  | // see IPv4-Compatible IPv6 addresses - RFC 4291 section 2.5.5
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | func ipVersion(s string) int {
 | 
					
						
							| 
									
										
										
										
											2019-01-28 16:36:34 +00:00
										 |  |  | 	for i := len(s) - 1; i >= 0; i-- {
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 		switch s[i] {
 | 
					
						
							|  |  |  | 		case '.':
 | 
					
						
							|  |  |  | 			return 4
 | 
					
						
							|  |  |  | 		case ':':
 | 
					
						
							|  |  |  | 			return 6
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return 0
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | // LookupStaticHost looks up the IP addresses for the given host from the hosts file.
 | 
					
						
							|  |  |  | func (h *Hostsfile) lookupStaticHost(hmapByName map[string][]net.IP, host string) []net.IP {
 | 
					
						
							|  |  |  | 	fqhost := absDomainName(host)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-24 10:27:07 -06:00
										 |  |  | 	h.RLock()
 | 
					
						
							|  |  |  | 	defer h.RUnlock()
 | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if len(hmapByName) == 0 {
 | 
					
						
							|  |  |  | 		return nil
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ips, ok := hmapByName[fqhost]
 | 
					
						
							|  |  |  | 	if !ok {
 | 
					
						
							|  |  |  | 		return nil
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	ipsCp := make([]net.IP, len(ips))
 | 
					
						
							|  |  |  | 	copy(ipsCp, ips)
 | 
					
						
							|  |  |  | 	return ipsCp
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // LookupStaticHostV4 looks up the IPv4 addresses for the given host from the hosts file.
 | 
					
						
							|  |  |  | func (h *Hostsfile) LookupStaticHostV4(host string) []net.IP {
 | 
					
						
							|  |  |  | 	return h.lookupStaticHost(h.hmap.byNameV4, host)
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // LookupStaticHostV6 looks up the IPv6 addresses for the given host from the hosts file.
 | 
					
						
							|  |  |  | func (h *Hostsfile) LookupStaticHostV6(host string) []net.IP {
 | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | 	return h.lookupStaticHost(h.hmap.byNameV6, host)
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // LookupStaticAddr looks up the hosts for the given address from the hosts file.
 | 
					
						
							|  |  |  | func (h *Hostsfile) LookupStaticAddr(addr string) []string {
 | 
					
						
							| 
									
										
										
										
											2017-10-24 10:27:07 -06:00
										 |  |  | 	h.RLock()
 | 
					
						
							|  |  |  | 	defer h.RUnlock()
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 	addr = parseLiteralIP(addr).String()
 | 
					
						
							|  |  |  | 	if addr == "" {
 | 
					
						
							|  |  |  | 		return nil
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | 	if len(h.hmap.byAddr) == 0 {
 | 
					
						
							|  |  |  | 		return nil
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	hosts, ok := h.hmap.byAddr[addr]
 | 
					
						
							|  |  |  | 	if !ok {
 | 
					
						
							|  |  |  | 		return nil
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | 	hostsCp := make([]string, len(hosts))
 | 
					
						
							|  |  |  | 	copy(hostsCp, hosts)
 | 
					
						
							|  |  |  | 	return hostsCp
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | }
 |