Skip to content

Commit

Permalink
net: fix parsing literal IP addresses in local database
Browse files Browse the repository at this point in the history
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 golang#8243.
Fixes golang#8996.

Change-Id: I48443611cbabed0d69667cc73911ba3de396fd44
Reviewed-on: https://go-review.googlesource.com/10306
Reviewed-by: Brad Fitzpatrick <[email protected]>
  • Loading branch information
cixtor committed Jun 2, 2015
1 parent 1fa0a8c commit 9378493
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 49 deletions.
1 change: 1 addition & 0 deletions src/net/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
)
35 changes: 27 additions & 8 deletions src/net/hosts.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -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 {
Expand All @@ -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)
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand Down
138 changes: 97 additions & 41 deletions src/net/hosts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
12 changes: 12 additions & 0 deletions src/net/testdata/ipv4-hosts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# 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
11 changes: 11 additions & 0 deletions src/net/testdata/ipv6-hosts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# 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
File renamed without changes.

0 comments on commit 9378493

Please sign in to comment.