Commit 9378493d authored by Mikio Hara's avatar Mikio Hara

net: fix parsing literal IP addresses in local database

This change fixes incorrect parsing of literal IP addresses in local
database when the addresses contain IPv6 zone identifiers, are in
dotted-decimal notation or in colon-hexadecimal notation with leading
zeros.

https://golang.org/cl/5851 already fixed the code path using getaddrinfo
via cgo. This change fixes the remaining non-cgo code path.

Fixes #8243.
Fixes #8996.

Change-Id: I48443611cbabed0d69667cc73911ba3de396fd44
Reviewed-on: https://go-review.googlesource.com/10306Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
parent 1fa0a8ce
......@@ -5,6 +5,7 @@
package net
var (
testHookHostsPath = "/etc/hosts"
testHookLookupIP = func(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) { return fn(host) }
testHookSetKeepAlive = func() {}
)
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Read static host/IP entries from /etc/hosts.
package net
import (
......@@ -13,8 +11,21 @@ import (
const cacheMaxAge = 5 * time.Minute
// hostsPath points to the file with static IP/address entries.
var hostsPath = "/etc/hosts"
func parseLiteralIP(addr string) string {
var ip IP
var zone string
ip = parseIPv4(addr)
if ip == nil {
ip, zone = parseIPv6(addr, true)
}
if ip == nil {
return ""
}
if zone == "" {
return ip.String()
}
return ip.String() + "%" + zone
}
// Simple cache.
var hosts struct {
......@@ -27,7 +38,7 @@ var hosts struct {
func readHosts() {
now := time.Now()
hp := hostsPath
hp := testHookHostsPath
if len(hosts.byName) == 0 || now.After(hosts.expire) || hosts.path != hp {
hs := make(map[string][]string)
is := make(map[string][]string)
......@@ -41,13 +52,17 @@ func readHosts() {
line = line[0:i]
}
f := getFields(line)
if len(f) < 2 || ParseIP(f[0]) == nil {
if len(f) < 2 {
continue
}
addr := parseLiteralIP(f[0])
if addr == "" {
continue
}
for i := 1; i < len(f); i++ {
h := f[i]
hs[h] = append(hs[h], f[0])
is[f[0]] = append(is[f[0]], h)
hs[h] = append(hs[h], addr)
is[addr] = append(is[addr], h)
}
}
// Update the data cache.
......@@ -77,6 +92,10 @@ func lookupStaticAddr(addr string) []string {
hosts.Lock()
defer hosts.Unlock()
readHosts()
addr = parseLiteralIP(addr)
if addr == "" {
return nil
}
if len(hosts.byAddr) != 0 {
if hosts, ok := hosts.byAddr[addr]; ok {
return hosts
......
......@@ -5,63 +5,119 @@
package net
import (
"reflect"
"sort"
"testing"
)
type hostTest struct {
host string
ips []IP
type staticHostEntry struct {
in string
out []string
}
var hosttests = []hostTest{
{"odin", []IP{
IPv4(127, 0, 0, 2),
IPv4(127, 0, 0, 3),
ParseIP("::2"),
}},
{"thor", []IP{
IPv4(127, 1, 1, 1),
}},
{"loki", []IP{}},
{"ullr", []IP{
IPv4(127, 1, 1, 2),
}},
{"ullrhost", []IP{
IPv4(127, 1, 1, 2),
}},
var lookupStaticHostTests = []struct {
name string
ents []staticHostEntry
}{
{
"testdata/hosts",
[]staticHostEntry{
{"odin", []string{"127.0.0.2", "127.0.0.3", "::2"}},
{"thor", []string{"127.1.1.1"}},
{"ullr", []string{"127.1.1.2"}},
{"ullrhost", []string{"127.1.1.2"}},
{"localhost", []string{"fe80::1%lo0"}},
},
},
{
"testdata/singleline-hosts", // see golang.org/issue/6646
[]staticHostEntry{
{"odin", []string{"127.0.0.2"}},
},
},
{
"testdata/ipv4-hosts", // see golang.org/issue/8996
[]staticHostEntry{
{"localhost", []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}},
{"localhost.localdomain", []string{"127.0.0.3"}},
},
},
{
"testdata/ipv6-hosts", // see golang.org/issue/8996
[]staticHostEntry{
{"localhost", []string{"::1", "fe80::1", "fe80::2%lo0", "fe80::3%lo0"}},
{"localhost.localdomain", []string{"fe80::3%lo0"}},
},
},
}
func TestLookupStaticHost(t *testing.T) {
p := hostsPath
hostsPath = "testdata/hosts"
for i := 0; i < len(hosttests); i++ {
tt := hosttests[i]
ips := lookupStaticHost(tt.host)
if len(ips) != len(tt.ips) {
t.Errorf("# of hosts = %v; want %v", len(ips), len(tt.ips))
continue
}
for k, v := range ips {
if tt.ips[k].String() != v {
t.Errorf("lookupStaticHost(%q) = %v; want %v", tt.host, v, tt.ips[k])
defer func(orig string) { testHookHostsPath = orig }(testHookHostsPath)
for _, tt := range lookupStaticHostTests {
testHookHostsPath = tt.name
for _, ent := range tt.ents {
addrs := lookupStaticHost(ent.in)
if !reflect.DeepEqual(addrs, ent.out) {
t.Errorf("%s, lookupStaticHost(%s) = %v; want %v", tt.name, ent.in, addrs, ent.out)
}
}
}
hostsPath = p
}
// https://golang.org/issue/6646
func TestSingleLineHostsFile(t *testing.T) {
p := hostsPath
hostsPath = "testdata/hosts_singleline"
var lookupStaticAddrTests = []struct {
name string
ents []staticHostEntry
}{
{
"testdata/hosts",
[]staticHostEntry{
{"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%lo0", []string{"localhost"}},
},
},
{
"testdata/singleline-hosts", // see golang.org/issue/6646
[]staticHostEntry{
{"127.0.0.2", []string{"odin"}},
},
},
{
"testdata/ipv4-hosts", // see golang.org/issue/8996
[]staticHostEntry{
{"127.0.0.1", []string{"localhost"}},
{"127.0.0.2", []string{"localhost"}},
{"127.0.0.3", []string{"localhost", "localhost.localdomain"}},
},
},
{
"testdata/ipv6-hosts", // see golang.org/issue/8996
[]staticHostEntry{
{"::1", []string{"localhost"}},
{"fe80::1", []string{"localhost"}},
{"fe80::2%lo0", []string{"localhost"}},
{"fe80::3%lo0", []string{"localhost", "localhost.localdomain"}},
},
},
}
ips := lookupStaticHost("odin")
if len(ips) != 1 || ips[0] != "127.0.0.2" {
t.Errorf("lookupStaticHost = %v, want %v", ips, []string{"127.0.0.2"})
}
func TestLookupStaticAddr(t *testing.T) {
defer func(orig string) { testHookHostsPath = orig }(testHookHostsPath)
hostsPath = p
for _, tt := range lookupStaticAddrTests {
testHookHostsPath = tt.name
for _, ent := range tt.ents {
hosts := lookupStaticAddr(ent.in)
if !reflect.DeepEqual(hosts, ent.out) {
t.Errorf("%s, lookupStaticAddr(%s) = %v; want %v", tt.name, ent.in, hosts, ent.out)
}
}
}
}
func TestLookupHost(t *testing.T) {
......
# See https://tools.ietf.org/html/rfc1123.
#
# The literal IPv4 address parser in the net package is a relaxed
# one. It may accept a literal IPv4 address in dotted-decimal notation
# with leading zeros such as "001.2.003.4".
# internet address and host name
127.0.0.1 localhost # inline comment separated by tab
127.000.000.002 localhost # inline comment separated by space
# internet address, host name and aliases
127.000.000.003 localhost localhost.localdomain
# 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment