mirror of
https://github.com/coredns/coredns.git
synced 2025-10-27 16:24:19 -04:00
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:
1
go.mod
1
go.mod
@@ -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
2
go.sum
@@ -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=
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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])
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user