mirror of
https://github.com/coredns/coredns.git
synced 2026-09-02 10:07:05 -04:00
bufio.Scanner stops at the first line longer than its 64KiB default buffer and reports bufio.ErrTooLong from Err(). parse() never checked Err(), so that line and every entry after it were dropped silently: the hosts file simply looked shorter than it is, with nothing in the log. Raise the scanner's limit to 1MiB (the scanner still grows its buffer lazily, so nothing is preallocated up front) and log an error if the scan does stop early, so the truncation is at least visible. Signed-off-by: Paco Cartones <pacocartones@users.noreply.github.com> Co-authored-by: Paco Cartones <pacocartones@users.noreply.github.com>
309 lines
8.1 KiB
Go
309 lines
8.1 KiB
Go
// 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.
|
|
|
|
package hosts
|
|
|
|
import (
|
|
"net"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/coredns/coredns/plugin"
|
|
)
|
|
|
|
func testHostsfile(file string) *Hostsfile {
|
|
h := &Hostsfile{
|
|
Origins: []string{"."},
|
|
hmap: newMap(),
|
|
inline: newMap(),
|
|
options: newOptions(),
|
|
}
|
|
h.hmap = h.parse(strings.NewReader(file))
|
|
return h
|
|
}
|
|
|
|
type staticHostEntry struct {
|
|
in string
|
|
v4 []string
|
|
v6 []string
|
|
}
|
|
|
|
var (
|
|
hosts = `255.255.255.255 broadcasthost
|
|
127.0.0.2 odin
|
|
127.0.0.3 odin # inline comment
|
|
::2 odin
|
|
127.1.1.1 thor
|
|
# aliases
|
|
127.1.1.2 ullr ullrhost
|
|
fe80::1%lo0 localhost
|
|
# Bogus entries that must be ignored.
|
|
123.123.123 loki
|
|
321.321.321.321`
|
|
singlelinehosts = `127.0.0.2 odin`
|
|
ipv4hosts = `# See https://tools.ietf.org/html/rfc1123.
|
|
#
|
|
|
|
# internet address and host name
|
|
127.0.0.1 localhost # inline comment separated by tab
|
|
127.0.0.2 localhost # inline comment separated by space
|
|
|
|
# internet address, host name and aliases
|
|
127.0.0.3 localhost localhost.localdomain`
|
|
ipv6hosts = `# See https://tools.ietf.org/html/rfc5952, https://tools.ietf.org/html/rfc4007.
|
|
|
|
# internet address and host name
|
|
::1 localhost # inline comment separated by tab
|
|
fe80:0000:0000:0000:0000:0000:0000:0001 localhost # inline comment separated by space
|
|
|
|
# internet address with zone identifier and host name
|
|
fe80:0000:0000:0000:0000:0000:0000:0002%lo0 localhost
|
|
|
|
# internet address, host name and aliases
|
|
fe80::3%lo0 localhost localhost.localdomain`
|
|
casehosts = `127.0.0.1 PreserveMe PreserveMe.local
|
|
::1 PreserveMe PreserveMe.local`
|
|
)
|
|
|
|
var lookupStaticHostTests = []struct {
|
|
file string
|
|
ents []staticHostEntry
|
|
}{
|
|
{
|
|
hosts,
|
|
[]staticHostEntry{
|
|
{"odin.", []string{"127.0.0.2", "127.0.0.3"}, []string{"::2"}},
|
|
{"thor.", []string{"127.1.1.1"}, []string{}},
|
|
{"ullr.", []string{"127.1.1.2"}, []string{}},
|
|
{"ullrhost.", []string{"127.1.1.2"}, []string{}},
|
|
{"localhost.", []string{}, []string{"fe80::1"}},
|
|
},
|
|
},
|
|
{
|
|
singlelinehosts, // see golang.org/issue/6646
|
|
[]staticHostEntry{
|
|
{"odin.", []string{"127.0.0.2"}, []string{}},
|
|
},
|
|
},
|
|
{
|
|
ipv4hosts,
|
|
[]staticHostEntry{
|
|
{"localhost.", []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, []string{}},
|
|
{"localhost.localdomain.", []string{"127.0.0.3"}, []string{}},
|
|
},
|
|
},
|
|
{
|
|
ipv6hosts,
|
|
[]staticHostEntry{
|
|
{"localhost.", []string{}, []string{"::1", "fe80::1", "fe80::2", "fe80::3"}},
|
|
{"localhost.localdomain.", []string{}, []string{"fe80::3"}},
|
|
},
|
|
},
|
|
{
|
|
casehosts,
|
|
[]staticHostEntry{
|
|
{"PreserveMe.", []string{"127.0.0.1"}, []string{"::1"}},
|
|
{"PreserveMe.local.", []string{"127.0.0.1"}, []string{"::1"}},
|
|
},
|
|
},
|
|
}
|
|
|
|
func TestLookupStaticHost(t *testing.T) {
|
|
for _, tt := range lookupStaticHostTests {
|
|
h := testHostsfile(tt.file)
|
|
for _, ent := range tt.ents {
|
|
testStaticHost(t, ent, h)
|
|
}
|
|
}
|
|
}
|
|
|
|
func testStaticHost(t *testing.T, ent staticHostEntry, h *Hostsfile) {
|
|
t.Helper()
|
|
ins := []string{ent.in, plugin.Name(ent.in).Normalize(), strings.ToLower(ent.in), strings.ToUpper(ent.in)}
|
|
for k, in := range ins {
|
|
addrsV4 := h.LookupStaticHostV4(in)
|
|
if len(addrsV4) != len(ent.v4) {
|
|
t.Fatalf("%d, lookupStaticHostV4(%s) = %v; want %v", k, in, addrsV4, ent.v4)
|
|
}
|
|
for i, v4 := range addrsV4 {
|
|
if v4.String() != ent.v4[i] {
|
|
t.Fatalf("%d, lookupStaticHostV4(%s) = %v; want %v", k, in, addrsV4, ent.v4)
|
|
}
|
|
}
|
|
addrsV6 := h.LookupStaticHostV6(in)
|
|
if len(addrsV6) != len(ent.v6) {
|
|
t.Fatalf("%d, lookupStaticHostV6(%s) = %v; want %v", k, in, addrsV6, ent.v6)
|
|
}
|
|
for i, v6 := range addrsV6 {
|
|
if v6.String() != ent.v6[i] {
|
|
t.Fatalf("%d, lookupStaticHostV6(%s) = %v; want %v", k, in, addrsV6, ent.v6)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
type staticIPEntry struct {
|
|
in string
|
|
out []string
|
|
}
|
|
|
|
var lookupStaticAddrTests = []struct {
|
|
file string
|
|
ents []staticIPEntry
|
|
}{
|
|
{
|
|
hosts,
|
|
[]staticIPEntry{
|
|
{"255.255.255.255", []string{"broadcasthost."}},
|
|
{"127.0.0.2", []string{"odin."}},
|
|
{"127.0.0.3", []string{"odin."}},
|
|
{"::2", []string{"odin."}},
|
|
{"127.1.1.1", []string{"thor."}},
|
|
{"127.1.1.2", []string{"ullr.", "ullrhost."}},
|
|
{"fe80::1", []string{"localhost."}},
|
|
},
|
|
},
|
|
{
|
|
singlelinehosts, // see golang.org/issue/6646
|
|
[]staticIPEntry{
|
|
{"127.0.0.2", []string{"odin."}},
|
|
},
|
|
},
|
|
{
|
|
ipv4hosts, // see golang.org/issue/8996
|
|
[]staticIPEntry{
|
|
{"127.0.0.1", []string{"localhost."}},
|
|
{"127.0.0.2", []string{"localhost."}},
|
|
{"127.0.0.3", []string{"localhost.", "localhost.localdomain."}},
|
|
},
|
|
},
|
|
{
|
|
ipv6hosts, // see golang.org/issue/8996
|
|
[]staticIPEntry{
|
|
{"::1", []string{"localhost."}},
|
|
{"fe80::1", []string{"localhost."}},
|
|
{"fe80::2", []string{"localhost."}},
|
|
{"fe80::3", []string{"localhost.", "localhost.localdomain."}},
|
|
},
|
|
},
|
|
{
|
|
casehosts, // see golang.org/issue/12806
|
|
[]staticIPEntry{
|
|
{"127.0.0.1", []string{"PreserveMe.", "PreserveMe.local."}},
|
|
{"::1", []string{"PreserveMe.", "PreserveMe.local."}},
|
|
},
|
|
},
|
|
}
|
|
|
|
func TestLookupStaticAddr(t *testing.T) {
|
|
for _, tt := range lookupStaticAddrTests {
|
|
h := testHostsfile(tt.file)
|
|
for _, ent := range tt.ents {
|
|
testStaticAddr(t, ent, h)
|
|
}
|
|
}
|
|
}
|
|
|
|
func testStaticAddr(t *testing.T, ent staticIPEntry, h *Hostsfile) {
|
|
t.Helper()
|
|
hosts := h.LookupStaticAddr(ent.in)
|
|
for i := range ent.out {
|
|
ent.out[i] = plugin.Name(ent.out[i]).Normalize()
|
|
}
|
|
if !reflect.DeepEqual(hosts, ent.out) {
|
|
t.Errorf("%s, lookupStaticAddr(%s) = %v; want %v", h.path, ent.in, hosts, h)
|
|
}
|
|
}
|
|
|
|
func TestHostCacheModification(t *testing.T) {
|
|
// Ensure that programs can't modify the internals of the host cache.
|
|
// See https://github.com/golang/go/issues/14212.
|
|
|
|
h := testHostsfile(ipv4hosts)
|
|
ent := staticHostEntry{"localhost.", []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, []string{}}
|
|
testStaticHost(t, ent, h)
|
|
// Modify the addresses return by lookupStaticHost.
|
|
addrs := h.LookupStaticHostV6(ent.in)
|
|
for i := range addrs {
|
|
addrs[i] = net.IPv4zero
|
|
}
|
|
testStaticHost(t, ent, h)
|
|
|
|
h = testHostsfile(ipv6hosts)
|
|
entip := staticIPEntry{"::1", []string{"localhost."}}
|
|
testStaticAddr(t, entip, h)
|
|
// Modify the hosts return by lookupStaticAddr.
|
|
hosts := h.LookupStaticAddr(entip.in)
|
|
for i := range hosts {
|
|
hosts[i] += "junk"
|
|
}
|
|
testStaticAddr(t, entip, h)
|
|
}
|
|
|
|
// TestLookupStaticHostReloadRace exercises concurrent A/AAAA lookups against a
|
|
// reload that swaps h.hmap. Before the fix, LookupStaticHostV4/V6 dereferenced
|
|
// h.hmap/h.inline as call arguments (outside the RLock), racing the swap that
|
|
// readHosts performs under h.Lock(). Run with -race.
|
|
func TestLookupStaticHostReloadRace(t *testing.T) {
|
|
f, err := os.CreateTemp(t.TempDir(), "hosts")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := f.WriteString("127.0.0.1 example.org\n::1 example.org\n"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
f.Close()
|
|
|
|
h := &Hostsfile{
|
|
Origins: []string{"."},
|
|
hmap: newMap(),
|
|
inline: newMap(),
|
|
options: newOptions(),
|
|
path: f.Name(),
|
|
}
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
// Reloader: force a re-parse + h.hmap swap on every iteration.
|
|
wg.Go(func() {
|
|
for range 1000 {
|
|
h.Lock()
|
|
h.size = 0
|
|
h.Unlock()
|
|
h.readHosts()
|
|
}
|
|
})
|
|
|
|
// Readers: concurrent A/AAAA lookups.
|
|
for range 4 {
|
|
wg.Go(func() {
|
|
for range 1000 {
|
|
h.LookupStaticHostV4("example.org.")
|
|
h.LookupStaticHostV6("example.org.")
|
|
}
|
|
})
|
|
}
|
|
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestParseLineLongerThanDefaultScanBuffer(t *testing.T) {
|
|
// A line longer than bufio.Scanner's default 64KiB buffer must not stop
|
|
// the scan: the entries that follow it still have to be parsed.
|
|
long := strings.Repeat("a", 70*1024)
|
|
h := testHostsfile("127.0.0.1 before.example.org\n" +
|
|
"127.0.0.2 " + long + ".example.org\n" +
|
|
"127.0.0.3 after.example.org\n")
|
|
|
|
if addrs := h.LookupStaticHostV4("before.example.org."); len(addrs) != 1 || addrs[0].String() != "127.0.0.1" {
|
|
t.Errorf("LookupStaticHostV4(before.example.org.) = %v, want [127.0.0.1]", addrs)
|
|
}
|
|
if addrs := h.LookupStaticHostV4("after.example.org."); len(addrs) != 1 || addrs[0].String() != "127.0.0.3" {
|
|
t.Errorf("LookupStaticHostV4(after.example.org.) = %v, want [127.0.0.3]", addrs)
|
|
}
|
|
}
|