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 @@ ...@@ -5,6 +5,7 @@
package net package net
var ( var (
testHookHostsPath = "/etc/hosts"
testHookLookupIP = func(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) { return fn(host) } testHookLookupIP = func(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) { return fn(host) }
testHookSetKeepAlive = func() {} testHookSetKeepAlive = func() {}
) )
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Read static host/IP entries from /etc/hosts.
package net package net
import ( import (
...@@ -13,8 +11,21 @@ import ( ...@@ -13,8 +11,21 @@ import (
const cacheMaxAge = 5 * time.Minute const cacheMaxAge = 5 * time.Minute
// hostsPath points to the file with static IP/address entries. func parseLiteralIP(addr string) string {
var hostsPath = "/etc/hosts" 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. // Simple cache.
var hosts struct { var hosts struct {
...@@ -27,7 +38,7 @@ var hosts struct { ...@@ -27,7 +38,7 @@ var hosts struct {
func readHosts() { func readHosts() {
now := time.Now() now := time.Now()
hp := hostsPath hp := testHookHostsPath
if len(hosts.byName) == 0 || now.After(hosts.expire) || hosts.path != hp { if len(hosts.byName) == 0 || now.After(hosts.expire) || hosts.path != hp {
hs := make(map[string][]string) hs := make(map[string][]string)
is := make(map[string][]string) is := make(map[string][]string)
...@@ -41,13 +52,17 @@ func readHosts() { ...@@ -41,13 +52,17 @@ func readHosts() {
line = line[0:i] line = line[0:i]
} }
f := getFields(line) f := getFields(line)
if len(f) < 2 || ParseIP(f[0]) == nil { if len(f) < 2 {
continue
}
addr := parseLiteralIP(f[0])
if addr == "" {
continue continue
} }
for i := 1; i < len(f); i++ { for i := 1; i < len(f); i++ {
h := f[i] h := f[i]
hs[h] = append(hs[h], f[0]) hs[h] = append(hs[h], addr)
is[f[0]] = append(is[f[0]], h) is[addr] = append(is[addr], h)
} }
} }
// Update the data cache. // Update the data cache.
...@@ -77,6 +92,10 @@ func lookupStaticAddr(addr string) []string { ...@@ -77,6 +92,10 @@ func lookupStaticAddr(addr string) []string {
hosts.Lock() hosts.Lock()
defer hosts.Unlock() defer hosts.Unlock()
readHosts() readHosts()
addr = parseLiteralIP(addr)
if addr == "" {
return nil
}
if len(hosts.byAddr) != 0 { if len(hosts.byAddr) != 0 {
if hosts, ok := hosts.byAddr[addr]; ok { if hosts, ok := hosts.byAddr[addr]; ok {
return hosts return hosts
......
...@@ -5,63 +5,119 @@ ...@@ -5,63 +5,119 @@
package net package net
import ( import (
"reflect"
"sort" "sort"
"testing" "testing"
) )
type hostTest struct { type staticHostEntry struct {
host string in string
ips []IP out []string
} }
var hosttests = []hostTest{ var lookupStaticHostTests = []struct {
{"odin", []IP{ name string
IPv4(127, 0, 0, 2), ents []staticHostEntry
IPv4(127, 0, 0, 3), }{
ParseIP("::2"), {
}}, "testdata/hosts",
{"thor", []IP{ []staticHostEntry{
IPv4(127, 1, 1, 1), {"odin", []string{"127.0.0.2", "127.0.0.3", "::2"}},
}}, {"thor", []string{"127.1.1.1"}},
{"loki", []IP{}}, {"ullr", []string{"127.1.1.2"}},
{"ullr", []IP{ {"ullrhost", []string{"127.1.1.2"}},
IPv4(127, 1, 1, 2), {"localhost", []string{"fe80::1%lo0"}},
}}, },
{"ullrhost", []IP{ },
IPv4(127, 1, 1, 2), {
}}, "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) { func TestLookupStaticHost(t *testing.T) {
p := hostsPath defer func(orig string) { testHookHostsPath = orig }(testHookHostsPath)
hostsPath = "testdata/hosts"
for i := 0; i < len(hosttests); i++ { for _, tt := range lookupStaticHostTests {
tt := hosttests[i] testHookHostsPath = tt.name
ips := lookupStaticHost(tt.host) for _, ent := range tt.ents {
if len(ips) != len(tt.ips) { addrs := lookupStaticHost(ent.in)
t.Errorf("# of hosts = %v; want %v", len(ips), len(tt.ips)) if !reflect.DeepEqual(addrs, ent.out) {
continue t.Errorf("%s, lookupStaticHost(%s) = %v; want %v", tt.name, ent.in, addrs, ent.out)
}
for k, v := range ips {
if tt.ips[k].String() != v {
t.Errorf("lookupStaticHost(%q) = %v; want %v", tt.host, v, tt.ips[k])
} }
} }
} }
hostsPath = p
} }
// https://golang.org/issue/6646 var lookupStaticAddrTests = []struct {
func TestSingleLineHostsFile(t *testing.T) { name string
p := hostsPath ents []staticHostEntry
hostsPath = "testdata/hosts_singleline" }{
{
"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") func TestLookupStaticAddr(t *testing.T) {
if len(ips) != 1 || ips[0] != "127.0.0.2" { defer func(orig string) { testHookHostsPath = orig }(testHookHostsPath)
t.Errorf("lookupStaticHost = %v, want %v", ips, []string{"127.0.0.2"})
}
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) { 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