1
0
mirror of https://github.com/golang/go synced 2024-11-25 08:57:58 -07:00

net: add LookupAddr

R=adg, rsc
CC=golang-dev
https://golang.org/cl/3851041
This commit is contained in:
Kyle Lemons 2011-01-19 15:11:03 -05:00 committed by Russ Cox
parent 73302bc190
commit a4f6ed3574
3 changed files with 114 additions and 8 deletions

View File

@ -15,6 +15,8 @@
package net package net
import ( import (
"bytes"
"fmt"
"os" "os"
"rand" "rand"
"sync" "sync"
@ -357,9 +359,59 @@ func LookupMX(name string) (entries []*MX, err os.Error) {
return return
} }
entries = make([]*MX, len(records)) entries = make([]*MX, len(records))
for i := 0; i < len(records); i++ { for i := range records {
r := records[i].(*dnsRR_MX) r := records[i].(*dnsRR_MX)
entries[i] = &MX{r.Mx, r.Pref} entries[i] = &MX{r.Mx, r.Pref}
} }
return return
} }
// reverseaddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP
// address addr suitable for rDNS (PTR) record lookup or an error if it fails
// to parse the IP address.
func reverseaddr(addr string) (arpa string, err os.Error) {
ip := ParseIP(addr)
if ip == nil {
return "", &DNSError{Error: "unrecognized address", Name: addr}
}
if ip.To4() != nil {
return fmt.Sprintf("%d.%d.%d.%d.in-addr.arpa.", ip[15], ip[14], ip[13], ip[12]), nil
}
// Must be IPv6
var buf bytes.Buffer
// Add it, in reverse, to the buffer
for i := len(ip) - 1; i >= 0; i-- {
s := fmt.Sprintf("%02x", ip[i])
buf.WriteByte(s[1])
buf.WriteByte('.')
buf.WriteByte(s[0])
buf.WriteByte('.')
}
// Append "ip6.arpa." and return (buf already has the final .)
return buf.String() + "ip6.arpa.", nil
}
// LookupAddr performs a reverse lookup for the given address, returning a list
// of names mapping to that address.
func LookupAddr(addr string) (name []string, err os.Error) {
name = lookupStaticAddr(addr)
if len(name) > 0 {
return
}
var arpa string
arpa, err = reverseaddr(addr)
if err != nil {
return
}
var records []dnsRR
_, records, err = lookup(arpa, dnsTypePTR)
if err != nil {
return
}
name = make([]string, len(records))
for i := range records {
r := records[i].(*dnsRR_PTR)
name[i] = r.Ptr
}
return
}

View File

@ -19,7 +19,8 @@ var hostsPath = "/etc/hosts"
// Simple cache. // Simple cache.
var hosts struct { var hosts struct {
sync.Mutex sync.Mutex
data map[string][]string byName map[string][]string
byAddr map[string][]string
time int64 time int64
path string path string
} }
@ -27,8 +28,9 @@ var hosts struct {
func readHosts() { func readHosts() {
now, _, _ := os.Time() now, _, _ := os.Time()
hp := hostsPath hp := hostsPath
if len(hosts.data) == 0 || hosts.time+cacheMaxAge <= now || hosts.path != hp { if len(hosts.byName) == 0 || hosts.time+cacheMaxAge <= now || hosts.path != hp {
hs := make(map[string][]string) hs := 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
@ -45,12 +47,14 @@ func readHosts() {
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], f[0])
is[f[0]] = append(is[f[0]], h)
} }
} }
// Update the data cache. // Update the data cache.
hosts.time, _, _ = os.Time() hosts.time, _, _ = os.Time()
hosts.path = hp hosts.path = hp
hosts.data = hs hosts.byName = hs
hosts.byAddr = is
file.close() file.close()
} }
} }
@ -60,10 +64,23 @@ func lookupStaticHost(host string) []string {
hosts.Lock() hosts.Lock()
defer hosts.Unlock() defer hosts.Unlock()
readHosts() readHosts()
if len(hosts.data) != 0 { if len(hosts.byName) != 0 {
if ips, ok := hosts.data[host]; ok { if ips, ok := hosts.byName[host]; ok {
return ips return ips
} }
} }
return nil return nil
} }
// rlookupStaticHosts looks up the hosts for the given address from /etc/hosts.
func lookupStaticAddr(addr string) []string {
hosts.Lock()
defer hosts.Unlock()
readHosts()
if len(hosts.byAddr) != 0 {
if hosts, ok := hosts.byAddr[addr]; ok {
return hosts
}
}
return nil
}

View File

@ -83,3 +83,40 @@ func TestDialError(t *testing.T) {
} }
} }
} }
var revAddrTests = []struct {
Addr string
Reverse string
ErrPrefix string
}{
{"1.2.3.4", "4.3.2.1.in-addr.arpa.", ""},
{"245.110.36.114", "114.36.110.245.in-addr.arpa.", ""},
{"::ffff:12.34.56.78", "78.56.34.12.in-addr.arpa.", ""},
{"::1", "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.", ""},
{"1::", "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.0.ip6.arpa.", ""},
{"1234:567::89a:bcde", "e.d.c.b.a.9.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.7.6.5.0.4.3.2.1.ip6.arpa.", ""},
{"1234:567:fefe:bcbc:adad:9e4a:89a:bcde", "e.d.c.b.a.9.8.0.a.4.e.9.d.a.d.a.c.b.c.b.e.f.e.f.7.6.5.0.4.3.2.1.ip6.arpa.", ""},
{"1.2.3", "", "unrecognized address"},
{"1.2.3.4.5", "", "unrecognized address"},
{"1234:567:bcbca::89a:bcde", "", "unrecognized address"},
{"1234:567::bcbc:adad::89a:bcde", "", "unrecognized address"},
}
func TestReverseAddress(t *testing.T) {
for i, tt := range revAddrTests {
a, e := reverseaddr(tt.Addr)
if len(tt.ErrPrefix) > 0 && e == nil {
t.Errorf("#%d: expected %q, got <nil> (error)", i, tt.ErrPrefix)
continue
}
if len(tt.ErrPrefix) == 0 && e != nil {
t.Errorf("#%d: expected <nil>, got %q (error)", i, e)
}
if e != nil && e.(*DNSError).Error != tt.ErrPrefix {
t.Errorf("#%d: expected %q, got %q (mismatched error)", i, tt.ErrPrefix, e.(*DNSError).Error)
}
if a != tt.Reverse {
t.Errorf("#%d: expected %q, got %q (reverse address)", i, tt.Reverse, a)
}
}
}