plugin/host: don't append the names when reparsing hosts file (#3045)

The host plugin kept on adding entries instead of overwriting. Split the
inline cache off from the /etc/hosts file cache and clear /etc/hosts
file cache and re-parsing.

A bunch of other cleanup as well. Use functions defined in the plugin
package, don't re-parse strings if you don't have to and use To4() to
check the family for IP addresses. Fix all test cases a carried entries
are always fqdn-ed. Various smaller cleanup in unnessacry constants.

Fixes: #3014

Signed-off-by: Miek Gieben <miek@miek.nl>
This commit is contained in:
Miek Gieben
2019-07-25 18:53:07 +00:00
committed by Yong Tang
parent 2a41b9a93b
commit 89fa9bc61e
9 changed files with 147 additions and 171 deletions

1
go.mod
View File

@@ -54,6 +54,7 @@ require (
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 // indirect golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 // indirect
golang.org/x/net v0.0.0-20190628185345-da137c7871d7 // indirect golang.org/x/net v0.0.0-20190628185345-da137c7871d7 // indirect
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 // indirect
google.golang.org/genproto v0.0.0-20190701230453-710ae3a149df // indirect google.golang.org/genproto v0.0.0-20190701230453-710ae3a149df // indirect
google.golang.org/grpc v1.22.0 google.golang.org/grpc v1.22.0
gopkg.in/DataDog/dd-trace-go.v1 v1.16.0 gopkg.in/DataDog/dd-trace-go.v1 v1.16.0

2
go.sum
View File

@@ -325,6 +325,8 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=

View File

@@ -11,14 +11,16 @@ file that exists on disk. It checks the file for changes and updates the zones a
plugin only supports A, AAAA, and PTR records. The hosts plugin can be used with readily plugin only supports A, AAAA, and PTR records. The hosts plugin can be used with readily
available hosts files that block access to advertising servers. available hosts files that block access to advertising servers.
The plugin reloads the content of the hosts file every 5 seconds. Upon reload, CoreDNS will use the new definitions. The plugin reloads the content of the hosts file every 5 seconds. Upon reload, CoreDNS will use the
Should the file be deleted, any inlined content will continue to be served. When the file is restored, it will then again be used. new definitions. Should the file be deleted, any inlined content will continue to be served. When
the file is restored, it will then again be used.
This plugin can only be used once per Server Block. This plugin can only be used once per Server Block.
## The hosts file ## The hosts file
Commonly the entries are of the form `IP_address canonical_hostname [aliases...]` as explained by the hosts(5) man page. Commonly the entries are of the form `IP_address canonical_hostname [aliases...]` as explained by
the hosts(5) man page.
Examples: Examples:
@@ -34,7 +36,8 @@ fdfc:a744:27b5:3b0e::1 example.com example
### PTR records ### PTR records
PTR records for reverse lookups are generated automatically by CoreDNS (based on the hosts file entries) and cannot be created manually. PTR records for reverse lookups are generated automatically by CoreDNS (based on the hosts file
entries) and cannot be created manually.
## Syntax ## Syntax

View File

@@ -31,7 +31,7 @@ func (h Hosts) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (
if zone == "" { if zone == "" {
// PTR zones don't need to be specified in Origins // PTR zones don't need to be specified in Origins
if state.Type() != "PTR" { if state.Type() != "PTR" {
// If this doesn't match we need to fall through regardless of h.Fallthrough // if this doesn't match we need to fall through regardless of h.Fallthrough
return plugin.NextOrFailure(h.Name(), h.Next, ctx, w, r) return plugin.NextOrFailure(h.Name(), h.Next, ctx, w, r)
} }
} }
@@ -89,7 +89,6 @@ func (h Hosts) otherRecordsExist(qtype uint16, qname string) bool {
} }
} }
return false return false
} }
// Name implements the plugin.Handle interface. // Name implements the plugin.Handle interface.
@@ -97,39 +96,36 @@ func (h Hosts) Name() string { return "hosts" }
// a takes a slice of net.IPs and returns a slice of A RRs. // a takes a slice of net.IPs and returns a slice of A RRs.
func a(zone string, ttl uint32, ips []net.IP) []dns.RR { func a(zone string, ttl uint32, ips []net.IP) []dns.RR {
answers := []dns.RR{} answers := make([]dns.RR, len(ips))
for _, ip := range ips { for i, ip := range ips {
r := new(dns.A) r := new(dns.A)
r.Hdr = dns.RR_Header{Name: zone, Rrtype: dns.TypeA, r.Hdr = dns.RR_Header{Name: zone, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: ttl}
Class: dns.ClassINET, Ttl: ttl}
r.A = ip r.A = ip
answers = append(answers, r) answers[i] = r
} }
return answers return answers
} }
// aaaa takes a slice of net.IPs and returns a slice of AAAA RRs. // aaaa takes a slice of net.IPs and returns a slice of AAAA RRs.
func aaaa(zone string, ttl uint32, ips []net.IP) []dns.RR { func aaaa(zone string, ttl uint32, ips []net.IP) []dns.RR {
answers := []dns.RR{} answers := make([]dns.RR, len(ips))
for _, ip := range ips { for i, ip := range ips {
r := new(dns.AAAA) r := new(dns.AAAA)
r.Hdr = dns.RR_Header{Name: zone, Rrtype: dns.TypeAAAA, r.Hdr = dns.RR_Header{Name: zone, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: ttl}
Class: dns.ClassINET, Ttl: ttl}
r.AAAA = ip r.AAAA = ip
answers = append(answers, r) answers[i] = r
} }
return answers return answers
} }
// ptr takes a slice of host names and filters out the ones that aren't in Origins, if specified, and returns a slice of PTR RRs. // ptr takes a slice of host names and filters out the ones that aren't in Origins, if specified, and returns a slice of PTR RRs.
func (h *Hosts) ptr(zone string, ttl uint32, names []string) []dns.RR { func (h *Hosts) ptr(zone string, ttl uint32, names []string) []dns.RR {
answers := []dns.RR{} answers := make([]dns.RR, len(names))
for _, n := range names { for i, n := range names {
r := new(dns.PTR) r := new(dns.PTR)
r.Hdr = dns.RR_Header{Name: zone, Rrtype: dns.TypePTR, r.Hdr = dns.RR_Header{Name: zone, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: ttl}
Class: dns.ClassINET, Ttl: ttl}
r.Ptr = dns.Fqdn(n) r.Ptr = dns.Fqdn(n)
answers = append(answers, r) answers[i] = r
} }
return answers return answers
} }

View File

@@ -2,7 +2,6 @@ package hosts
import ( import (
"context" "context"
"io"
"strings" "strings"
"testing" "testing"
@@ -12,20 +11,17 @@ import (
"github.com/miekg/dns" "github.com/miekg/dns"
) )
func (h *Hostsfile) parseReader(r io.Reader) {
h.hmap = h.parse(r)
}
func TestLookupA(t *testing.T) { func TestLookupA(t *testing.T) {
h := Hosts{ h := Hosts{
Next: test.ErrorHandler(), Next: test.ErrorHandler(),
Hostsfile: &Hostsfile{ Hostsfile: &Hostsfile{
Origins: []string{"."}, Origins: []string{"."},
hmap: newHostsMap(), hmap: newMap(),
inline: newMap(),
options: newOptions(), options: newOptions(),
}, },
} }
h.parseReader(strings.NewReader(hostsExample)) h.hmap = h.parse(strings.NewReader(hostsExample))
ctx := context.TODO() ctx := context.TODO()

View File

@@ -19,7 +19,8 @@ import (
"github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin"
) )
func parseLiteralIP(addr string) net.IP { // parseIP calls discards any v6 zone info, before calling net.ParseIP.
func parseIP(addr string) net.IP {
if i := strings.Index(addr, "%"); i >= 0 { if i := strings.Index(addr, "%"); i >= 0 {
// discard ipv6 zone // discard ipv6 zone
addr = addr[0:i] addr = addr[0:i]
@@ -28,10 +29,6 @@ func parseLiteralIP(addr string) net.IP {
return net.ParseIP(addr) return net.ParseIP(addr)
} }
func absDomainName(b string) string {
return plugin.Name(b).Normalize()
}
type options struct { type options struct {
// automatically generate IP to Hostname PTR entries // automatically generate IP to Hostname PTR entries
// for host entries we parse // for host entries we parse
@@ -48,48 +45,40 @@ func newOptions() *options {
return &options{ return &options{
autoReverse: true, autoReverse: true,
ttl: 3600, ttl: 3600,
reload: durationOf5s, reload: time.Duration(5 * time.Second),
} }
} }
type hostsMap struct { // Map contains the IPv4/IPv6 and reverse mapping.
// Key for the list of literal IP addresses must be a host type Map struct {
// name. It would be part of DNS labels, a FQDN or an absolute // Key for the list of literal IP addresses must be a FQDN lowercased host name.
// FQDN. name4 map[string][]net.IP
// For now the key is converted to lower case for convenience. name6 map[string][]net.IP
byNameV4 map[string][]net.IP
byNameV6 map[string][]net.IP
// Key for the list of host names must be a literal IP address // Key for the list of host names must be a literal IP address
// including IPv6 address with zone identifier. // including IPv6 address without zone identifier.
// We don't support old-classful IP address notation. // We don't support old-classful IP address notation.
byAddr map[string][]string addr map[string][]string
} }
const ( func newMap() *Map {
durationOf0s = time.Duration(0) return &Map{
durationOf5s = time.Duration(5 * time.Second) name4: make(map[string][]net.IP),
) name6: make(map[string][]net.IP),
addr: make(map[string][]string),
func newHostsMap() *hostsMap {
return &hostsMap{
byNameV4: make(map[string][]net.IP),
byNameV6: make(map[string][]net.IP),
byAddr: make(map[string][]string),
} }
} }
// Len returns the total number of addresses in the hostmap, this includes // Len returns the total number of addresses in the hostmap, this includes V4/V6 and any reverse addresses.
// V4/V6 and any reverse addresses. func (h *Map) Len() int {
func (h *hostsMap) Len() int {
l := 0 l := 0
for _, v4 := range h.byNameV4 { for _, v4 := range h.name4 {
l += len(v4) l += len(v4)
} }
for _, v6 := range h.byNameV6 { for _, v6 := range h.name6 {
l += len(v6) l += len(v6)
} }
for _, a := range h.byAddr { for _, a := range h.addr {
l += len(a) l += len(a)
} }
return l return l
@@ -103,11 +92,10 @@ type Hostsfile struct {
Origins []string Origins []string
// hosts maps for lookups // hosts maps for lookups
hmap *hostsMap hmap *Map
// inline saves the hosts file that is inlined in a Corefile. // 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 *Map
inline *hostsMap
// path to the hosts file // path to the hosts file
path string path string
@@ -132,6 +120,7 @@ func (h *Hostsfile) readHosts() {
h.RLock() h.RLock()
size := h.size size := h.size
h.RUnlock() h.RUnlock()
if err == nil && h.mtime.Equal(stat.ModTime()) && size == stat.Size() { if err == nil && h.mtime.Equal(stat.ModTime()) && size == stat.Size() {
return return
} }
@@ -155,12 +144,11 @@ func (h *Hostsfile) initInline(inline []string) {
} }
h.inline = h.parse(strings.NewReader(strings.Join(inline, "\n"))) h.inline = h.parse(strings.NewReader(strings.Join(inline, "\n")))
*h.hmap = *h.inline
} }
// Parse reads the hostsfile and populates the byName and byAddr maps. // Parse reads the hostsfile and populates the byName and addr maps.
func (h *Hostsfile) parse(r io.Reader) *hostsMap { func (h *Hostsfile) parse(r io.Reader) *Map {
hmap := newHostsMap() hmap := newMap()
scanner := bufio.NewScanner(r) scanner := bufio.NewScanner(r)
for scanner.Scan() { for scanner.Scan() {
@@ -173,73 +161,52 @@ func (h *Hostsfile) parse(r io.Reader) *hostsMap {
if len(f) < 2 { if len(f) < 2 {
continue continue
} }
addr := parseLiteralIP(string(f[0])) addr := parseIP(string(f[0]))
if addr == nil { if addr == nil {
continue continue
} }
ver := ipVersion(string(f[0]))
family := 0
if addr.To4() != nil {
family = 1
} else {
family = 2
}
for i := 1; i < len(f); i++ { for i := 1; i < len(f); i++ {
name := absDomainName(string(f[i])) name := plugin.Name(string(f[i])).Normalize()
if plugin.Zones(h.Origins).Matches(name) == "" { if plugin.Zones(h.Origins).Matches(name) == "" {
// name is not in Origins // name is not in Origins
continue continue
} }
switch ver { switch family {
case 4: case 1:
hmap.byNameV4[name] = append(hmap.byNameV4[name], addr) hmap.name4[name] = append(hmap.name4[name], addr)
case 6: case 2:
hmap.byNameV6[name] = append(hmap.byNameV6[name], addr) hmap.name6[name] = append(hmap.name6[name], addr)
default: default:
continue continue
} }
if !h.options.autoReverse { if !h.options.autoReverse {
continue continue
} }
hmap.byAddr[addr.String()] = append(hmap.byAddr[addr.String()], name) hmap.addr[addr.String()] = append(hmap.addr[addr.String()], name)
} }
} }
for name := range h.hmap.byNameV4 {
hmap.byNameV4[name] = append(hmap.byNameV4[name], h.hmap.byNameV4[name]...)
}
for name := range h.hmap.byNameV4 {
hmap.byNameV6[name] = append(hmap.byNameV6[name], h.hmap.byNameV6[name]...)
}
for addr := range h.hmap.byAddr {
hmap.byAddr[addr] = append(hmap.byAddr[addr], h.hmap.byAddr[addr]...)
}
return hmap return hmap
} }
// ipVersion returns what IP version was used textually // lookupStaticHost looks up the IP addresses for the given host from the hosts file.
// For why the string is parsed end to start, func (h *Hostsfile) lookupStaticHost(m map[string][]net.IP, host string) []net.IP {
// see IPv4-Compatible IPv6 addresses - RFC 4291 section 2.5.5
func ipVersion(s string) int {
for i := len(s) - 1; i >= 0; i-- {
switch s[i] {
case '.':
return 4
case ':':
return 6
}
}
return 0
}
// 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)
h.RLock() h.RLock()
defer h.RUnlock() defer h.RUnlock()
if len(hmapByName) == 0 { if len(m) == 0 {
return nil return nil
} }
ips, ok := hmapByName[fqhost] ips, ok := m[host]
if !ok { if !ok {
return nil return nil
} }
@@ -250,30 +217,38 @@ func (h *Hostsfile) lookupStaticHost(hmapByName map[string][]net.IP, host string
// LookupStaticHostV4 looks up the IPv4 addresses for the given host from the hosts file. // LookupStaticHostV4 looks up the IPv4 addresses for the given host from the hosts file.
func (h *Hostsfile) LookupStaticHostV4(host string) []net.IP { func (h *Hostsfile) LookupStaticHostV4(host string) []net.IP {
return h.lookupStaticHost(h.hmap.byNameV4, host) host = strings.ToLower(host)
ip1 := h.lookupStaticHost(h.hmap.name4, host)
ip2 := h.lookupStaticHost(h.inline.name4, host)
return append(ip1, ip2...)
} }
// LookupStaticHostV6 looks up the IPv6 addresses for the given host from the hosts file. // LookupStaticHostV6 looks up the IPv6 addresses for the given host from the hosts file.
func (h *Hostsfile) LookupStaticHostV6(host string) []net.IP { func (h *Hostsfile) LookupStaticHostV6(host string) []net.IP {
return h.lookupStaticHost(h.hmap.byNameV6, host) host = strings.ToLower(host)
ip1 := h.lookupStaticHost(h.hmap.name6, host)
ip2 := h.lookupStaticHost(h.inline.name6, host)
return append(ip1, ip2...)
} }
// LookupStaticAddr looks up the hosts for the given address from the hosts file. // LookupStaticAddr looks up the hosts for the given address from the hosts file.
func (h *Hostsfile) LookupStaticAddr(addr string) []string { func (h *Hostsfile) LookupStaticAddr(addr string) []string {
h.RLock() addr = parseIP(addr).String()
defer h.RUnlock()
addr = parseLiteralIP(addr).String()
if addr == "" { if addr == "" {
return nil return nil
} }
if len(h.hmap.byAddr) == 0 {
h.RLock()
defer h.RUnlock()
hosts1, _ := h.hmap.addr[addr]
hosts2, _ := h.inline.addr[addr]
if len(hosts1) == 0 && len(hosts2) == 0 {
return nil return nil
} }
hosts, ok := h.hmap.byAddr[addr]
if !ok { hostsCp := make([]string, len(hosts1)+len(hosts2))
return nil copy(hostsCp, hosts1)
} copy(hostsCp[len(hosts1):], hosts2)
hostsCp := make([]string, len(hosts))
copy(hostsCp, hosts)
return hostsCp return hostsCp
} }

View File

@@ -9,15 +9,18 @@ import (
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
"github.com/coredns/coredns/plugin"
) )
func testHostsfile(file string) *Hostsfile { func testHostsfile(file string) *Hostsfile {
h := &Hostsfile{ h := &Hostsfile{
Origins: []string{"."}, Origins: []string{"."},
hmap: newHostsMap(), hmap: newMap(),
inline: newMap(),
options: newOptions(), options: newOptions(),
} }
h.parseReader(strings.NewReader(file)) h.hmap = h.parse(strings.NewReader(file))
return h return h
} }
@@ -74,44 +77,43 @@ var lookupStaticHostTests = []struct {
{ {
hosts, hosts,
[]staticHostEntry{ []staticHostEntry{
{"odin", []string{"127.0.0.2", "127.0.0.3"}, []string{"::2"}}, {"odin.", []string{"127.0.0.2", "127.0.0.3"}, []string{"::2"}},
{"thor", []string{"127.1.1.1"}, []string{}}, {"thor.", []string{"127.1.1.1"}, []string{}},
{"ullr", []string{"127.1.1.2"}, []string{}}, {"ullr.", []string{"127.1.1.2"}, []string{}},
{"ullrhost", []string{"127.1.1.2"}, []string{}}, {"ullrhost.", []string{"127.1.1.2"}, []string{}},
{"localhost", []string{}, []string{"fe80::1"}}, {"localhost.", []string{}, []string{"fe80::1"}},
}, },
}, },
{ {
singlelinehosts, // see golang.org/issue/6646 singlelinehosts, // see golang.org/issue/6646
[]staticHostEntry{ []staticHostEntry{
{"odin", []string{"127.0.0.2"}, []string{}}, {"odin.", []string{"127.0.0.2"}, []string{}},
}, },
}, },
{ {
ipv4hosts, ipv4hosts,
[]staticHostEntry{ []staticHostEntry{
{"localhost", []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, []string{}}, {"localhost.", []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, []string{}},
{"localhost.localdomain", []string{"127.0.0.3"}, []string{}}, {"localhost.localdomain.", []string{"127.0.0.3"}, []string{}},
}, },
}, },
{ {
ipv6hosts, ipv6hosts,
[]staticHostEntry{ []staticHostEntry{
{"localhost", []string{}, []string{"::1", "fe80::1", "fe80::2", "fe80::3"}}, {"localhost.", []string{}, []string{"::1", "fe80::1", "fe80::2", "fe80::3"}},
{"localhost.localdomain", []string{}, []string{"fe80::3"}}, {"localhost.localdomain.", []string{}, []string{"fe80::3"}},
}, },
}, },
{ {
casehosts, casehosts,
[]staticHostEntry{ []staticHostEntry{
{"PreserveMe", []string{"127.0.0.1"}, []string{"::1"}}, {"PreserveMe.", []string{"127.0.0.1"}, []string{"::1"}},
{"PreserveMe.local", []string{"127.0.0.1"}, []string{"::1"}}, {"PreserveMe.local.", []string{"127.0.0.1"}, []string{"::1"}},
}, },
}, },
} }
func TestLookupStaticHost(t *testing.T) { func TestLookupStaticHost(t *testing.T) {
for _, tt := range lookupStaticHostTests { for _, tt := range lookupStaticHostTests {
h := testHostsfile(tt.file) h := testHostsfile(tt.file)
for _, ent := range tt.ents { for _, ent := range tt.ents {
@@ -121,7 +123,7 @@ func TestLookupStaticHost(t *testing.T) {
} }
func testStaticHost(t *testing.T, ent staticHostEntry, h *Hostsfile) { func testStaticHost(t *testing.T, ent staticHostEntry, h *Hostsfile) {
ins := []string{ent.in, absDomainName(ent.in), strings.ToLower(ent.in), strings.ToUpper(ent.in)} ins := []string{ent.in, plugin.Name(ent.in).Normalize(), strings.ToLower(ent.in), strings.ToUpper(ent.in)}
for k, in := range ins { for k, in := range ins {
addrsV4 := h.LookupStaticHostV4(in) addrsV4 := h.LookupStaticHostV4(in)
if len(addrsV4) != len(ent.v4) { if len(addrsV4) != len(ent.v4) {
@@ -156,43 +158,43 @@ var lookupStaticAddrTests = []struct {
{ {
hosts, hosts,
[]staticIPEntry{ []staticIPEntry{
{"255.255.255.255", []string{"broadcasthost"}}, {"255.255.255.255", []string{"broadcasthost."}},
{"127.0.0.2", []string{"odin"}}, {"127.0.0.2", []string{"odin."}},
{"127.0.0.3", []string{"odin"}}, {"127.0.0.3", []string{"odin."}},
{"::2", []string{"odin"}}, {"::2", []string{"odin."}},
{"127.1.1.1", []string{"thor"}}, {"127.1.1.1", []string{"thor."}},
{"127.1.1.2", []string{"ullr", "ullrhost"}}, {"127.1.1.2", []string{"ullr.", "ullrhost."}},
{"fe80::1", []string{"localhost"}}, {"fe80::1", []string{"localhost."}},
}, },
}, },
{ {
singlelinehosts, // see golang.org/issue/6646 singlelinehosts, // see golang.org/issue/6646
[]staticIPEntry{ []staticIPEntry{
{"127.0.0.2", []string{"odin"}}, {"127.0.0.2", []string{"odin."}},
}, },
}, },
{ {
ipv4hosts, // see golang.org/issue/8996 ipv4hosts, // see golang.org/issue/8996
[]staticIPEntry{ []staticIPEntry{
{"127.0.0.1", []string{"localhost"}}, {"127.0.0.1", []string{"localhost."}},
{"127.0.0.2", []string{"localhost"}}, {"127.0.0.2", []string{"localhost."}},
{"127.0.0.3", []string{"localhost", "localhost.localdomain"}}, {"127.0.0.3", []string{"localhost.", "localhost.localdomain."}},
}, },
}, },
{ {
ipv6hosts, // see golang.org/issue/8996 ipv6hosts, // see golang.org/issue/8996
[]staticIPEntry{ []staticIPEntry{
{"::1", []string{"localhost"}}, {"::1", []string{"localhost."}},
{"fe80::1", []string{"localhost"}}, {"fe80::1", []string{"localhost."}},
{"fe80::2", []string{"localhost"}}, {"fe80::2", []string{"localhost."}},
{"fe80::3", []string{"localhost", "localhost.localdomain"}}, {"fe80::3", []string{"localhost.", "localhost.localdomain."}},
}, },
}, },
{ {
casehosts, // see golang.org/issue/12806 casehosts, // see golang.org/issue/12806
[]staticIPEntry{ []staticIPEntry{
{"127.0.0.1", []string{"PreserveMe", "PreserveMe.local"}}, {"127.0.0.1", []string{"PreserveMe.", "PreserveMe.local."}},
{"::1", []string{"PreserveMe", "PreserveMe.local"}}, {"::1", []string{"PreserveMe.", "PreserveMe.local."}},
}, },
}, },
} }
@@ -209,7 +211,7 @@ func TestLookupStaticAddr(t *testing.T) {
func testStaticAddr(t *testing.T, ent staticIPEntry, h *Hostsfile) { func testStaticAddr(t *testing.T, ent staticIPEntry, h *Hostsfile) {
hosts := h.LookupStaticAddr(ent.in) hosts := h.LookupStaticAddr(ent.in)
for i := range ent.out { for i := range ent.out {
ent.out[i] = absDomainName(ent.out[i]) ent.out[i] = plugin.Name(ent.out[i]).Normalize()
} }
if !reflect.DeepEqual(hosts, ent.out) { if !reflect.DeepEqual(hosts, ent.out) {
t.Errorf("%s, lookupStaticAddr(%s) = %v; want %v", h.path, ent.in, hosts, h) t.Errorf("%s, lookupStaticAddr(%s) = %v; want %v", h.path, ent.in, hosts, h)
@@ -221,7 +223,7 @@ func TestHostCacheModification(t *testing.T) {
// See https://github.com/golang/go/issues/14212. // See https://github.com/golang/go/issues/14212.
h := testHostsfile(ipv4hosts) h := testHostsfile(ipv4hosts)
ent := staticHostEntry{"localhost", []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, []string{}} ent := staticHostEntry{"localhost.", []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, []string{}}
testStaticHost(t, ent, h) testStaticHost(t, ent, h)
// Modify the addresses return by lookupStaticHost. // Modify the addresses return by lookupStaticHost.
addrs := h.LookupStaticHostV6(ent.in) addrs := h.LookupStaticHostV6(ent.in)
@@ -231,7 +233,7 @@ func TestHostCacheModification(t *testing.T) {
testStaticHost(t, ent, h) testStaticHost(t, ent, h)
h = testHostsfile(ipv6hosts) h = testHostsfile(ipv6hosts)
entip := staticIPEntry{"::1", []string{"localhost"}} entip := staticIPEntry{"::1", []string{"localhost."}}
testStaticAddr(t, entip, h) testStaticAddr(t, entip, h)
// Modify the hosts return by lookupStaticAddr. // Modify the hosts return by lookupStaticAddr.
hosts := h.LookupStaticAddr(entip.in) hosts := h.LookupStaticAddr(entip.in)

View File

@@ -26,7 +26,7 @@ func init() {
func periodicHostsUpdate(h *Hosts) chan bool { func periodicHostsUpdate(h *Hosts) chan bool {
parseChan := make(chan bool) parseChan := make(chan bool)
if h.options.reload == durationOf0s { if h.options.reload == 0 {
return parseChan return parseChan
} }
@@ -78,7 +78,7 @@ func hostsParse(c *caddy.Controller) (Hosts, error) {
h := Hosts{ h := Hosts{
Hostsfile: &Hostsfile{ Hostsfile: &Hostsfile{
path: "/etc/hosts", path: "/etc/hosts",
hmap: newHostsMap(), hmap: newMap(),
options: options, options: options,
}, },
} }
@@ -152,7 +152,7 @@ func hostsParse(c *caddy.Controller) (Hosts, error) {
if err != nil { if err != nil {
return h, c.Errf("invalid duration for reload '%s'", remaining[0]) return h, c.Errf("invalid duration for reload '%s'", remaining[0])
} }
if reload < durationOf0s { if reload < 0 {
return h, c.Errf("invalid negative duration for reload '%s'", remaining[0]) return h, c.Errf("invalid negative duration for reload '%s'", remaining[0])
} }
options.reload = reload options.reload = reload

View File

@@ -100,7 +100,7 @@ func TestHostsInlineParse(t *testing.T) {
tests := []struct { tests := []struct {
inputFileRules string inputFileRules string
shouldErr bool shouldErr bool
expectedbyAddr map[string][]string expectedaddr map[string][]string
expectedFallthrough fall.F expectedFallthrough fall.F
}{ }{
{ {
@@ -148,19 +148,20 @@ func TestHostsInlineParse(t *testing.T) {
t.Fatalf("Test %d expected no errors, but got '%v'", i, err) t.Fatalf("Test %d expected no errors, but got '%v'", i, err)
} else if !test.shouldErr { } else if !test.shouldErr {
if !h.Fall.Equal(test.expectedFallthrough) { if !h.Fall.Equal(test.expectedFallthrough) {
t.Fatalf("Test %d expected fallthrough of %v, got %v", i, test.expectedFallthrough, h.Fall) t.Errorf("Test %d expected fallthrough of %v, got %v", i, test.expectedFallthrough, h.Fall)
}
for k, expectedVal := range test.expectedaddr {
val, ok := h.inline.addr[k]
if !ok {
t.Errorf("Test %d expected %v, got no entry", i, k)
continue
} }
for k, expectedVal := range test.expectedbyAddr {
if val, ok := h.hmap.byAddr[k]; !ok {
t.Fatalf("Test %d expected %v, got no entry", i, k)
} else {
if len(expectedVal) != len(val) { if len(expectedVal) != len(val) {
t.Fatalf("Test %d expected %v records for %v, got %v", i, len(expectedVal), k, len(val)) t.Errorf("Test %d expected %v records for %v, got %v", i, len(expectedVal), k, len(val))
} }
for j := range expectedVal { for j := range expectedVal {
if expectedVal[j] != val[j] { if expectedVal[j] != val[j] {
t.Fatalf("Test %d expected %v for %v, got %v", i, expectedVal[j], j, val[j]) t.Errorf("Test %d expected %v for %v, got %v", i, expectedVal[j], j, val[j])
}
} }
} }
} }