1
0
mirror of https://github.com/golang/go synced 2024-11-16 20:04:52 -07:00

net: add support for /etc/hosts aliases using go resolver

It adds support for /etc/hosts aliases and fixes the difference between the glibc cgo and the go DNS resolver.
Examples: https://pastebin.com/Fv6UcAVr

Fixes #44741

Change-Id: I98c484fced900731fbad800278b296028a45f044
GitHub-Last-Rev: 3d47e44f11
GitHub-Pull-Request: golang/go#51004
Reviewed-on: https://go-review.googlesource.com/c/go/+/382996
Run-TryBot: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
This commit is contained in:
Mateusz Poliwczak 2022-11-10 18:07:57 +00:00 committed by Gopher Robot
parent fd0c0db4a4
commit d931b3b771
5 changed files with 165 additions and 21 deletions

View File

@ -559,7 +559,7 @@ func (r *Resolver) goLookupHost(ctx context.Context, name string) (addrs []strin
func (r *Resolver) goLookupHostOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []string, err error) { func (r *Resolver) goLookupHostOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []string, err error) {
if order == hostLookupFilesDNS || order == hostLookupFiles { if order == hostLookupFilesDNS || order == hostLookupFiles {
// Use entries from /etc/hosts if they match. // Use entries from /etc/hosts if they match.
addrs = lookupStaticHost(name) addrs, _ = lookupStaticHost(name)
if len(addrs) > 0 || order == hostLookupFiles { if len(addrs) > 0 || order == hostLookupFiles {
return return
} }
@ -576,8 +576,9 @@ func (r *Resolver) goLookupHostOrder(ctx context.Context, name string, order hos
} }
// lookup entries from /etc/hosts // lookup entries from /etc/hosts
func goLookupIPFiles(name string) (addrs []IPAddr) { func goLookupIPFiles(name string) (addrs []IPAddr, canonical string) {
for _, haddr := range lookupStaticHost(name) { addr, canonical := lookupStaticHost(name)
for _, haddr := range addr {
haddr, zone := splitHostZone(haddr) haddr, zone := splitHostZone(haddr)
if ip := ParseIP(haddr); ip != nil { if ip := ParseIP(haddr); ip != nil {
addr := IPAddr{IP: ip, Zone: zone} addr := IPAddr{IP: ip, Zone: zone}
@ -585,7 +586,7 @@ func goLookupIPFiles(name string) (addrs []IPAddr) {
} }
} }
sortByRFC6724(addrs) sortByRFC6724(addrs)
return return addrs, canonical
} }
// goLookupIP is the native Go implementation of LookupIP. // goLookupIP is the native Go implementation of LookupIP.
@ -598,11 +599,23 @@ func (r *Resolver) goLookupIP(ctx context.Context, network, host string) (addrs
func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, network, name string, order hostLookupOrder) (addrs []IPAddr, cname dnsmessage.Name, err error) { func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, network, name string, order hostLookupOrder) (addrs []IPAddr, cname dnsmessage.Name, err error) {
if order == hostLookupFilesDNS || order == hostLookupFiles { if order == hostLookupFilesDNS || order == hostLookupFiles {
addrs = goLookupIPFiles(name) var canonical string
if len(addrs) > 0 || order == hostLookupFiles { addrs, canonical = goLookupIPFiles(name)
return addrs, dnsmessage.Name{}, nil
if len(addrs) > 0 {
var err error
cname, err = dnsmessage.NewName(canonical)
if err != nil {
return nil, dnsmessage.Name{}, err
}
return addrs, cname, nil
}
if order == hostLookupFiles {
return nil, dnsmessage.Name{}, &DNSError{Err: errNoSuchHost.Error(), Name: name, IsNotFound: true}
} }
} }
if !isDomainName(name) { if !isDomainName(name) {
// See comment in func lookup above about use of errNoSuchHost. // See comment in func lookup above about use of errNoSuchHost.
return nil, dnsmessage.Name{}, &DNSError{Err: errNoSuchHost.Error(), Name: name, IsNotFound: true} return nil, dnsmessage.Name{}, &DNSError{Err: errNoSuchHost.Error(), Name: name, IsNotFound: true}
@ -776,9 +789,18 @@ func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, network, name strin
sortByRFC6724(addrs) sortByRFC6724(addrs)
if len(addrs) == 0 && !(network == "CNAME" && cname.Length > 0) { if len(addrs) == 0 && !(network == "CNAME" && cname.Length > 0) {
if order == hostLookupDNSFiles { if order == hostLookupDNSFiles {
addrs = goLookupIPFiles(name) var canonical string
addrs, canonical = goLookupIPFiles(name)
if len(addrs) > 0 {
var err error
cname, err = dnsmessage.NewName(canonical)
if err != nil {
return nil, dnsmessage.Name{}, err
}
return addrs, cname, nil
}
} }
if len(addrs) == 0 && lastErr != nil { if lastErr != nil {
return nil, dnsmessage.Name{}, lastErr return nil, dnsmessage.Name{}, lastErr
} }
} }

View File

@ -2170,6 +2170,56 @@ func TestRootNS(t *testing.T) {
} }
} }
func TestGoLookupIPCNAMEOrderHostsAliasesFilesOnlyMode(t *testing.T) {
defer func(orig string) { testHookHostsPath = orig }(testHookHostsPath)
testHookHostsPath = "testdata/aliases"
mode := hostLookupFiles
for _, v := range lookupStaticHostAliasesTest {
testGoLookupIPCNAMEOrderHostsAliases(t, mode, v.lookup, absDomainName(v.res))
}
}
func TestGoLookupIPCNAMEOrderHostsAliasesFilesDNSMode(t *testing.T) {
defer func(orig string) { testHookHostsPath = orig }(testHookHostsPath)
testHookHostsPath = "testdata/aliases"
mode := hostLookupFilesDNS
for _, v := range lookupStaticHostAliasesTest {
testGoLookupIPCNAMEOrderHostsAliases(t, mode, v.lookup, absDomainName(v.res))
}
}
var goLookupIPCNAMEOrderDNSFilesModeTests = []struct {
lookup, res string
}{
// 127.0.1.1
{"invalid.invalid", "invalid.test"},
}
func TestGoLookupIPCNAMEOrderHostsAliasesDNSFilesMode(t *testing.T) {
defer func(orig string) { testHookHostsPath = orig }(testHookHostsPath)
testHookHostsPath = "testdata/aliases"
mode := hostLookupDNSFiles
for _, v := range goLookupIPCNAMEOrderDNSFilesModeTests {
testGoLookupIPCNAMEOrderHostsAliases(t, mode, v.lookup, absDomainName(v.res))
}
}
func testGoLookupIPCNAMEOrderHostsAliases(t *testing.T, mode hostLookupOrder, lookup, lookupRes string) {
ins := []string{lookup, absDomainName(lookup), strings.ToLower(lookup), strings.ToUpper(lookup)}
for _, in := range ins {
_, res, err := goResolver.goLookupIPCNAMEOrder(context.Background(), "ip", in, mode)
if err != nil {
t.Errorf("expected err == nil, but got error: %v", err)
}
if res.String() != lookupRes {
t.Errorf("goLookupIPCNAMEOrder(%v): got %v, want %v", in, res, lookupRes)
}
}
}
// Test that we advertise support for a larger DNS packet size. // Test that we advertise support for a larger DNS packet size.
// This isn't a great test as it just tests the dnsmessage package // This isn't a great test as it just tests the dnsmessage package
// against itself. // against itself.

View File

@ -28,6 +28,11 @@ func parseLiteralIP(addr string) string {
return ip.String() + "%" + zone return ip.String() + "%" + zone
} }
type byName struct {
addrs []string
canonicalName string
}
// hosts contains known host entries. // hosts contains known host entries.
var hosts struct { var hosts struct {
sync.Mutex sync.Mutex
@ -36,7 +41,7 @@ var hosts struct {
// name. It would be part of DNS labels, a FQDN or an absolute // name. It would be part of DNS labels, a FQDN or an absolute
// FQDN. // FQDN.
// For now the key is converted to lower case for convenience. // For now the key is converted to lower case for convenience.
byName map[string][]string byName map[string]byName
// 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 with zone identifier.
@ -62,8 +67,9 @@ func readHosts() {
return return
} }
hs := make(map[string][]string) hs := make(map[string]byName)
is := make(map[string][]string) is := make(map[string][]string)
var file *file var file *file
if file, _ = open(hp); file == nil { if file, _ = open(hp); file == nil {
return return
@ -81,13 +87,32 @@ func readHosts() {
if addr == "" { if addr == "" {
continue continue
} }
var canonical string
for i := 1; i < len(f); i++ { for i := 1; i < len(f); i++ {
name := absDomainName(f[i]) name := absDomainName(f[i])
h := []byte(f[i]) h := []byte(f[i])
lowerASCIIBytes(h) lowerASCIIBytes(h)
key := absDomainName(string(h)) key := absDomainName(string(h))
hs[key] = append(hs[key], addr)
if i == 1 {
canonical = key
}
is[addr] = append(is[addr], name) is[addr] = append(is[addr], name)
if v,ok := hs[key]; ok {
hs[key] = byName{
addrs: append(v.addrs, addr),
canonicalName: v.canonicalName,
}
continue
}
hs[key] = byName{
addrs: []string{addr},
canonicalName: canonical,
}
} }
} }
// Update the data cache. // Update the data cache.
@ -100,8 +125,8 @@ func readHosts() {
file.close() file.close()
} }
// lookupStaticHost looks up the addresses for the given host from /etc/hosts. // lookupStaticHost looks up the addresses and the cannonical name for the given host from /etc/hosts.
func lookupStaticHost(host string) []string { func lookupStaticHost(host string) ([]string, string) {
hosts.Lock() hosts.Lock()
defer hosts.Unlock() defer hosts.Unlock()
readHosts() readHosts()
@ -111,13 +136,13 @@ func lookupStaticHost(host string) []string {
lowerASCIIBytes(lowerHost) lowerASCIIBytes(lowerHost)
host = string(lowerHost) host = string(lowerHost)
} }
if ips, ok := hosts.byName[absDomainName(host)]; ok { if byName, ok := hosts.byName[absDomainName(host)]; ok {
ipsCp := make([]string, len(ips)) ipsCp := make([]string, len(byName.addrs))
copy(ipsCp, ips) copy(ipsCp, byName.addrs)
return ipsCp return ipsCp, byName.canonicalName
} }
} }
return nil return nil, ""
} }
// lookupStaticAddr looks up the hosts for the given address from /etc/hosts. // lookupStaticAddr looks up the hosts for the given address from /etc/hosts.

View File

@ -72,7 +72,7 @@ func TestLookupStaticHost(t *testing.T) {
func testStaticHost(t *testing.T, hostsPath string, ent staticHostEntry) { func testStaticHost(t *testing.T, hostsPath string, ent staticHostEntry) {
ins := []string{ent.in, absDomainName(ent.in), strings.ToLower(ent.in), strings.ToUpper(ent.in)} ins := []string{ent.in, absDomainName(ent.in), strings.ToLower(ent.in), strings.ToUpper(ent.in)}
for _, in := range ins { for _, in := range ins {
addrs := lookupStaticHost(in) addrs, _ := lookupStaticHost(in)
if !reflect.DeepEqual(addrs, ent.out) { if !reflect.DeepEqual(addrs, ent.out) {
t.Errorf("%s, lookupStaticHost(%s) = %v; want %v", hostsPath, in, addrs, ent.out) t.Errorf("%s, lookupStaticHost(%s) = %v; want %v", hostsPath, in, addrs, ent.out)
} }
@ -157,7 +157,7 @@ func TestHostCacheModification(t *testing.T) {
ent := staticHostEntry{"localhost", []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}} ent := staticHostEntry{"localhost", []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}}
testStaticHost(t, testHookHostsPath, ent) testStaticHost(t, testHookHostsPath, ent)
// Modify the addresses return by lookupStaticHost. // Modify the addresses return by lookupStaticHost.
addrs := lookupStaticHost(ent.in) addrs, _ := lookupStaticHost(ent.in)
for i := range addrs { for i := range addrs {
addrs[i] += "junk" addrs[i] += "junk"
} }
@ -173,3 +173,42 @@ func TestHostCacheModification(t *testing.T) {
} }
testStaticAddr(t, testHookHostsPath, ent) testStaticAddr(t, testHookHostsPath, ent)
} }
var lookupStaticHostAliasesTest = []struct {
lookup, res string
}{
// 127.0.0.1
{"test", "test"},
// 127.0.0.2
{"test2.example.com", "test2.example.com"},
{"2.test", "test2.example.com"},
// 127.0.0.3
{"test3.example.com", "3.test"},
{"3.test", "3.test"},
// 127.0.0.4
{"example.com", "example.com"},
// 127.0.0.5
{"test5.example.com", "test4.example.com"},
{"5.test", "test4.example.com"},
{"4.test", "test4.example.com"},
{"test4.example.com", "test4.example.com"},
}
func TestLookupStaticHostAliases(t *testing.T) {
defer func(orig string) { testHookHostsPath = orig }(testHookHostsPath)
testHookHostsPath = "testdata/aliases"
for _, ent := range lookupStaticHostAliasesTest {
testLookupStaticHostAliases(t, ent.lookup, absDomainName(ent.res))
}
}
func testLookupStaticHostAliases(t *testing.T, lookup, lookupRes string) {
ins := []string{lookup, absDomainName(lookup), strings.ToLower(lookup), strings.ToUpper(lookup)}
for _, in := range ins {
_, res := lookupStaticHost(in)
if res != lookupRes {
t.Errorf("lookupStaticHost(%v): got %v, want %v", in, res, lookupRes)
}
}
}

8
src/net/testdata/aliases vendored Normal file
View File

@ -0,0 +1,8 @@
127.0.0.1 test
127.0.0.2 test2.example.com 2.test
127.0.0.3 3.test test3.example.com
127.0.0.4 example.com
127.0.0.5 test4.example.com 4.test 5.test test5.example.com
# must be a non resolvable domain on the internet
127.0.1.1 invalid.test invalid.invalid