Commit 5da14d16 authored by Alex Brainman's avatar Alex Brainman

net: implement windows LookupMX and LookupAddr

Also sort SRV records before returning from LookupSRV.

R=rsc
CC=golang-dev
https://golang.org/cl/4817049
parent ba983326
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"fmt" "fmt"
"os" "os"
"rand" "rand"
"sort"
) )
// DNSError represents a DNS lookup error. // DNSError represents a DNS lookup error.
...@@ -182,9 +183,9 @@ func (s byPriorityWeight) Less(i, j int) bool { ...@@ -182,9 +183,9 @@ func (s byPriorityWeight) Less(i, j int) bool {
(s[i].Priority == s[j].Priority && s[i].Weight < s[j].Weight) (s[i].Priority == s[j].Priority && s[i].Weight < s[j].Weight)
} }
// shuffleSRVByWeight shuffles SRV records by weight using the algorithm // shuffleByWeight shuffles SRV records by weight using the algorithm
// described in RFC 2782. // described in RFC 2782.
func shuffleSRVByWeight(addrs []*SRV) { func (addrs byPriorityWeight) shuffleByWeight() {
sum := 0 sum := 0
for _, addr := range addrs { for _, addr := range addrs {
sum += int(addr.Weight) sum += int(addr.Weight)
...@@ -208,6 +209,19 @@ func shuffleSRVByWeight(addrs []*SRV) { ...@@ -208,6 +209,19 @@ func shuffleSRVByWeight(addrs []*SRV) {
} }
} }
// sort reorders SRV records as specified in RFC 2782.
func (addrs byPriorityWeight) sort() {
sort.Sort(addrs)
i := 0
for j := 1; j < len(addrs); j++ {
if addrs[i].Priority != addrs[j].Priority {
addrs[i:j].shuffleByWeight()
i = j
}
}
addrs[i:].shuffleByWeight()
}
// An MX represents a single DNS MX record. // An MX represents a single DNS MX record.
type MX struct { type MX struct {
Host string Host string
...@@ -222,3 +236,12 @@ func (s byPref) Len() int { return len(s) } ...@@ -222,3 +236,12 @@ func (s byPref) Len() int { return len(s) }
func (s byPref) Less(i, j int) bool { return s[i].Pref < s[j].Pref } func (s byPref) Less(i, j int) bool { return s[i].Pref < s[j].Pref }
func (s byPref) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s byPref) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// sort reorders MX records as specified in RFC 5321.
func (s byPref) sort() {
for i := range s {
j := rand.Intn(i + 1)
s[i], s[j] = s[j], s[i]
}
sort.Sort(s)
}
...@@ -27,3 +27,31 @@ func TestGoogleSRV(t *testing.T) { ...@@ -27,3 +27,31 @@ func TestGoogleSRV(t *testing.T) {
t.Errorf("no results") t.Errorf("no results")
} }
} }
func TestGmailMX(t *testing.T) {
if testing.Short() || avoidMacFirewall {
t.Logf("skipping test to avoid external network")
return
}
mx, err := LookupMX("gmail.com")
if err != nil {
t.Errorf("failed: %s", err)
}
if len(mx) == 0 {
t.Errorf("no results")
}
}
func TestGoogleDNSAddr(t *testing.T) {
if testing.Short() || avoidMacFirewall {
t.Logf("skipping test to avoid external network")
return
}
names, err := LookupAddr("8.8.8.8")
if err != nil {
t.Errorf("failed: %s", err)
}
if len(names) == 0 {
t.Errorf("no results")
}
}
...@@ -6,8 +6,6 @@ package net ...@@ -6,8 +6,6 @@ package net
import ( import (
"os" "os"
"rand"
"sort"
) )
// LookupHost looks up the given host using the local resolver. // LookupHost looks up the given host using the local resolver.
...@@ -68,15 +66,7 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os. ...@@ -68,15 +66,7 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.
r := rr.(*dnsRR_SRV) r := rr.(*dnsRR_SRV)
addrs[i] = &SRV{r.Target, r.Port, r.Priority, r.Weight} addrs[i] = &SRV{r.Target, r.Port, r.Priority, r.Weight}
} }
sort.Sort(byPriorityWeight(addrs)) byPriorityWeight(addrs).sort()
i := 0
for j := 1; j < len(addrs); j++ {
if addrs[i].Priority != addrs[j].Priority {
shuffleSRVByWeight(addrs[i:j])
i = j
}
}
shuffleSRVByWeight(addrs[i:len(addrs)])
return return
} }
...@@ -91,12 +81,7 @@ func LookupMX(name string) (mx []*MX, err os.Error) { ...@@ -91,12 +81,7 @@ func LookupMX(name string) (mx []*MX, err os.Error) {
r := rr[i].(*dnsRR_MX) r := rr[i].(*dnsRR_MX)
mx[i] = &MX{r.Mx, r.Pref} mx[i] = &MX{r.Mx, r.Pref}
} }
// Shuffle the records to match RFC 5321 when sorted byPref(mx).sort()
for i := range mx {
j := rand.Intn(i + 1)
mx[i], mx[j] = mx[j], mx[i]
}
sort.Sort(byPref(mx))
return return
} }
......
...@@ -85,23 +85,46 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os. ...@@ -85,23 +85,46 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.
return "", nil, os.NewSyscallError("LookupSRV", int(e)) return "", nil, os.NewSyscallError("LookupSRV", int(e))
} }
defer syscall.DnsRecordListFree(r, 1) defer syscall.DnsRecordListFree(r, 1)
addrs = make([]*SRV, 100) addrs = make([]*SRV, 0, 10)
i := 0
for p := r; p != nil && p.Type == syscall.DNS_TYPE_SRV; p = p.Next { for p := r; p != nil && p.Type == syscall.DNS_TYPE_SRV; p = p.Next {
v := (*syscall.DNSSRVData)(unsafe.Pointer(&p.Data[0])) v := (*syscall.DNSSRVData)(unsafe.Pointer(&p.Data[0]))
addrs[i] = &SRV{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Target))[:]), v.Port, v.Priority, v.Weight} addrs = append(addrs, &SRV{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Target))[:]), v.Port, v.Priority, v.Weight})
i++
} }
addrs = addrs[0:i] byPriorityWeight(addrs).sort()
return name, addrs, nil return name, addrs, nil
} }
// TODO(brainman): implement LookupMX and LookupAddr.
func LookupMX(name string) (mx []*MX, err os.Error) { func LookupMX(name string) (mx []*MX, err os.Error) {
return nil, os.NewSyscallError("LookupMX", syscall.EWINDOWS) var r *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_MX, 0, nil, &r, nil)
if int(e) != 0 {
return nil, os.NewSyscallError("LookupMX", int(e))
}
defer syscall.DnsRecordListFree(r, 1)
mx = make([]*MX, 0, 10)
for p := r; p != nil && p.Type == syscall.DNS_TYPE_MX; p = p.Next {
v := (*syscall.DNSMXData)(unsafe.Pointer(&p.Data[0]))
mx = append(mx, &MX{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.NameExchange))[:]) + ".", v.Preference})
}
byPref(mx).sort()
return mx, nil
} }
func LookupAddr(addr string) (name []string, err os.Error) { func LookupAddr(addr string) (name []string, err os.Error) {
return nil, os.NewSyscallError("LookupAddr", syscall.EWINDOWS) arpa, err := reverseaddr(addr)
if err != nil {
return nil, err
}
var r *syscall.DNSRecord
e := syscall.DnsQuery(arpa, syscall.DNS_TYPE_PTR, 0, nil, &r, nil)
if int(e) != 0 {
return nil, os.NewSyscallError("LookupAddr", int(e))
}
defer syscall.DnsRecordListFree(r, 1)
name = make([]string, 0, 10)
for p := r; p != nil && p.Type == syscall.DNS_TYPE_PTR; p = p.Next {
v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0]))
name = append(name, syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]))
}
return name, nil
} }
...@@ -478,6 +478,12 @@ type DNSPTRData struct { ...@@ -478,6 +478,12 @@ type DNSPTRData struct {
Host *uint16 Host *uint16
} }
type DNSMXData struct {
NameExchange *uint16
Preference uint16
Pad uint16
}
type DNSRecord struct { type DNSRecord struct {
Next *DNSRecord Next *DNSRecord
Name *uint16 Name *uint16
......
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