| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											2019-09-19 11:38:15 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/prometheus/client_golang/prometheus" | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | // parseIP calls discards any v6 zone info, before calling net.ParseIP. | 
					
						
							|  |  |  | func parseIP(addr string) net.IP { | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 	if i := strings.Index(addr, "%"); i >= 0 { | 
					
						
							|  |  |  | 		// discard ipv6 zone | 
					
						
							|  |  |  | 		addr = addr[0:i] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return net.ParseIP(addr) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | 		reload:      time.Duration(5 * time.Second), | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | // Map contains the IPv4/IPv6 and reverse mapping. | 
					
						
							|  |  |  | type Map struct { | 
					
						
							|  |  |  | 	// Key for the list of literal IP addresses must be a FQDN lowercased host name. | 
					
						
							|  |  |  | 	name4 map[string][]net.IP | 
					
						
							|  |  |  | 	name6 map[string][]net.IP | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Key for the list of host names must be a literal IP address | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | 	// including IPv6 address without zone identifier. | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 	// We don't support old-classful IP address notation. | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | 	addr map[string][]string | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | func newMap() *Map { | 
					
						
							|  |  |  | 	return &Map{ | 
					
						
							|  |  |  | 		name4: make(map[string][]net.IP), | 
					
						
							|  |  |  | 		name6: make(map[string][]net.IP), | 
					
						
							|  |  |  | 		addr:  make(map[string][]string), | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | // Len returns the total number of addresses in the hostmap, this includes V4/V6 and any reverse addresses. | 
					
						
							|  |  |  | func (h *Map) Len() int { | 
					
						
							| 
									
										
										
										
											2018-07-19 15:16:35 +01:00
										 |  |  | 	l := 0 | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | 	for _, v4 := range h.name4 { | 
					
						
							| 
									
										
										
										
											2018-07-19 15:16:35 +01:00
										 |  |  | 		l += len(v4) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | 	for _, v6 := range h.name6 { | 
					
						
							| 
									
										
										
										
											2018-07-19 15:16:35 +01:00
										 |  |  | 		l += len(v6) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | 	for _, a := range h.addr { | 
					
						
							| 
									
										
										
										
											2018-07-19 15:16:35 +01:00
										 |  |  | 		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 | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | 	hmap *Map | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// inline saves the hosts file that is inlined in a Corefile. | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | 	inline *Map | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// 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() | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-17 19:34:07 +00:00
										 |  |  | 	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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-19 11:38:15 -04:00
										 |  |  | 	hostsEntries.WithLabelValues().Set(float64(h.inline.Len() + h.hmap.Len())) | 
					
						
							|  |  |  | 	hostsReloadTime.Set(float64(stat.ModTime().UnixNano()) / 1e9) | 
					
						
							| 
									
										
										
										
											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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | // Parse reads the hostsfile and populates the byName and addr maps. | 
					
						
							|  |  |  | func (h *Hostsfile) parse(r io.Reader) *Map { | 
					
						
							|  |  |  | 	hmap := newMap() | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | 		addr := parseIP(string(f[0])) | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 		if addr == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		family := 0 | 
					
						
							|  |  |  | 		if addr.To4() != nil { | 
					
						
							|  |  |  | 			family = 1 | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			family = 2 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 		for i := 1; i < len(f); i++ { | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | 			name := plugin.Name(string(f[i])).Normalize() | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | 			switch family { | 
					
						
							|  |  |  | 			case 1: | 
					
						
							|  |  |  | 				hmap.name4[name] = append(hmap.name4[name], addr) | 
					
						
							|  |  |  | 			case 2: | 
					
						
							|  |  |  | 				hmap.name6[name] = append(hmap.name6[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 | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | 			hmap.addr[addr.String()] = append(hmap.addr[addr.String()], name) | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-10-31 01:40:47 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return hmap | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | // lookupStaticHost looks up the IP addresses for the given host from the hosts file. | 
					
						
							|  |  |  | func (h *Hostsfile) lookupStaticHost(m map[string][]net.IP, host string) []net.IP { | 
					
						
							| 
									
										
										
										
											2017-10-24 10:27:07 -06:00
										 |  |  | 	h.RLock() | 
					
						
							|  |  |  | 	defer h.RUnlock() | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | 	if len(m) == 0 { | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | 	ips, ok := m[host] | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | 	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 { | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | 	host = strings.ToLower(host) | 
					
						
							|  |  |  | 	ip1 := h.lookupStaticHost(h.hmap.name4, host) | 
					
						
							|  |  |  | 	ip2 := h.lookupStaticHost(h.inline.name4, host) | 
					
						
							|  |  |  | 	return append(ip1, ip2...) | 
					
						
							| 
									
										
										
										
											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-07-25 18:53:07 +00:00
										 |  |  | 	host = strings.ToLower(host) | 
					
						
							|  |  |  | 	ip1 := h.lookupStaticHost(h.hmap.name6, host) | 
					
						
							|  |  |  | 	ip2 := h.lookupStaticHost(h.inline.name6, host) | 
					
						
							|  |  |  | 	return append(ip1, ip2...) | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | 	addr = parseIP(addr).String() | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 	if addr == "" { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	h.RLock() | 
					
						
							|  |  |  | 	defer h.RUnlock() | 
					
						
							| 
									
										
										
										
											2019-09-25 20:23:43 +08:00
										 |  |  | 	hosts1 := h.hmap.addr[addr] | 
					
						
							|  |  |  | 	hosts2 := h.inline.addr[addr] | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if len(hosts1) == 0 && len(hosts2) == 0 { | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-25 18:53:07 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	hostsCp := make([]string, len(hosts1)+len(hosts2)) | 
					
						
							|  |  |  | 	copy(hostsCp, hosts1) | 
					
						
							|  |  |  | 	copy(hostsCp[len(hosts1):], hosts2) | 
					
						
							| 
									
										
										
										
											2019-02-12 16:09:33 +00:00
										 |  |  | 	return hostsCp | 
					
						
							| 
									
										
										
										
											2017-06-08 13:48:04 -06:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-19 11:38:15 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							|  |  |  | 	hostsEntries = prometheus.NewGaugeVec(prometheus.GaugeOpts{ | 
					
						
							|  |  |  | 		Namespace: plugin.Namespace, | 
					
						
							|  |  |  | 		Subsystem: "hosts", | 
					
						
							|  |  |  | 		Name:      "entries_count", | 
					
						
							|  |  |  | 		Help:      "The combined number of entries in hosts and Corefile.", | 
					
						
							|  |  |  | 	}, []string{}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hostsReloadTime = prometheus.NewGauge(prometheus.GaugeOpts{ | 
					
						
							|  |  |  | 		Namespace: plugin.Namespace, | 
					
						
							|  |  |  | 		Subsystem: "hosts", | 
					
						
							|  |  |  | 		Name:      "reload_timestamp_seconds", | 
					
						
							|  |  |  | 		Help:      "The timestamp of the last reload of hosts file.", | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | ) |