Skip to content

Commit c9164a5

Browse files
committed
net: use C library resolver on FreeBSD, Linux, OS X / amd64, 386
This CL makes it possible to resolve DNS names on OS X without offending the Application-Level Firewall. It also means that cross-compiling from one operating system to another is no longer possible when using package net, because cgo needs to be able to sniff around the local C libraries. We could special-case this one use and check in generated files, but it seems more trouble than it's worth. Cross compiling is dead anyway. It is still possible to use either GOARCH=amd64 or GOARCH=386 on typical Linux and OS X x86 systems. It is also still possible to build GOOS=linux GOARCH=arm on any system, because arm is for now excluded from this change (there is no cgo for arm yet). R=iant, r, mikioh CC=golang-dev https://golang.org/cl/4437053
1 parent 64787e3 commit c9164a5

14 files changed

+279
-34
lines changed

src/pkg/net/Makefile

+18-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ include ../../Make.inc
66

77
TARG=net
88
GOFILES=\
9-
cgo_stub.go\
109
dial.go\
1110
dnsmsg.go\
1211
fd_$(GOOS).go\
@@ -31,13 +30,19 @@ GOFILES_freebsd=\
3130
dnsclient.go\
3231
port.go\
3332

33+
CGOFILES_freebsd=\
34+
cgo_unix.go\
35+
3436
GOFILES_darwin=\
3537
newpollserver.go\
3638
fd.go\
3739
file.go\
3840
dnsconfig.go\
3941
dnsclient.go\
4042
port.go\
43+
44+
CGOFILES_darwin=\
45+
cgo_unix.go\
4146

4247
GOFILES_linux=\
4348
newpollserver.go\
@@ -47,10 +52,22 @@ GOFILES_linux=\
4752
dnsclient.go\
4853
port.go\
4954

55+
ifeq ($(GOARCH),arm)
56+
# ARM has no cgo, so use the stubs.
57+
GOFILES_linux+=cgo_stub.go
58+
else
59+
CGOFILES_linux=\
60+
cgo_unix.go
61+
endif
62+
5063
GOFILES_windows=\
64+
cgo_stub.go\
5165
resolv_windows.go\
5266
file_windows.go\
5367

5468
GOFILES+=$(GOFILES_$(GOOS))
69+
ifneq ($(CGOFILES_$(GOOS)),)
70+
CGOFILES+=$(CGOFILES_$(GOOS))
71+
endif
5572

5673
include ../../Make.pkg

src/pkg/net/cgo_stub.go

+4
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,7 @@ func cgoLookupPort(network, service string) (port int, err os.Error, completed b
1919
func cgoLookupIP(name string) (addrs []IP, err os.Error, completed bool) {
2020
return nil, nil, false
2121
}
22+
23+
func cgoLookupCNAME(name string) (cname string, err os.Error, completed bool) {
24+
return "", nil, false
25+
}

src/pkg/net/cgo_unix.go

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// Copyright 2011 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package net
6+
7+
/*
8+
#include <sys/types.h>
9+
#include <sys/socket.h>
10+
#include <netinet/in.h>
11+
#include <netdb.h>
12+
#include <stdlib.h>
13+
#include <unistd.h>
14+
#include <string.h>
15+
*/
16+
import "C"
17+
18+
import (
19+
"os"
20+
"syscall"
21+
"unsafe"
22+
)
23+
24+
func cgoLookupHost(name string) (addrs []string, err os.Error, completed bool) {
25+
ip, err, completed := cgoLookupIP(name)
26+
for _, p := range ip {
27+
addrs = append(addrs, p.String())
28+
}
29+
return
30+
}
31+
32+
func cgoLookupPort(net, service string) (port int, err os.Error, completed bool) {
33+
var res *C.struct_addrinfo
34+
var hints C.struct_addrinfo
35+
36+
switch net {
37+
case "":
38+
// no hints
39+
case "tcp", "tcp4", "tcp6":
40+
hints.ai_socktype = C.SOCK_STREAM
41+
hints.ai_protocol = C.IPPROTO_TCP
42+
case "udp", "udp4", "udp6":
43+
hints.ai_socktype = C.SOCK_DGRAM
44+
hints.ai_protocol = C.IPPROTO_UDP
45+
default:
46+
return 0, UnknownNetworkError(net), true
47+
}
48+
if len(net) >= 4 {
49+
switch net[3] {
50+
case '4':
51+
hints.ai_family = C.AF_INET
52+
case '6':
53+
hints.ai_family = C.AF_INET6
54+
}
55+
}
56+
57+
s := C.CString(service)
58+
defer C.free(unsafe.Pointer(s))
59+
if C.getaddrinfo(nil, s, &hints, &res) == 0 {
60+
defer C.freeaddrinfo(res)
61+
for r := res; r != nil; r = r.ai_next {
62+
switch r.ai_family {
63+
default:
64+
continue
65+
case C.AF_INET:
66+
sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr))
67+
p := (*[2]byte)(unsafe.Pointer(&sa.Port))
68+
return int(p[0])<<8 | int(p[1]), nil, true
69+
case C.AF_INET6:
70+
sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr))
71+
p := (*[2]byte)(unsafe.Pointer(&sa.Port))
72+
return int(p[0])<<8 | int(p[1]), nil, true
73+
}
74+
}
75+
}
76+
return 0, &AddrError{"unknown port", net + "/" + service}, true
77+
}
78+
79+
func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err os.Error, completed bool) {
80+
var res *C.struct_addrinfo
81+
var hints C.struct_addrinfo
82+
83+
// NOTE(rsc): In theory there are approximately balanced
84+
// arguments for and against including AI_ADDRCONFIG
85+
// in the flags (it includes IPv4 results only on IPv4 systems,
86+
// and similarly for IPv6), but in practice setting it causes
87+
// getaddrinfo to return the wrong canonical name on Linux.
88+
// So definitely leave it out.
89+
hints.ai_flags = C.AI_ALL | C.AI_V4MAPPED | C.AI_CANONNAME
90+
91+
h := C.CString(name)
92+
defer C.free(unsafe.Pointer(h))
93+
gerrno, err := C.getaddrinfo(h, nil, &hints, &res)
94+
if gerrno != 0 {
95+
var str string
96+
if gerrno == C.EAI_NONAME {
97+
str = noSuchHost
98+
} else if gerrno == C.EAI_SYSTEM {
99+
str = err.String()
100+
} else {
101+
str = C.GoString(C.gai_strerror(gerrno))
102+
}
103+
return nil, "", &DNSError{Error: str, Name: name}, true
104+
}
105+
defer C.freeaddrinfo(res)
106+
if res != nil {
107+
cname = C.GoString(res.ai_canonname)
108+
if cname == "" {
109+
cname = name
110+
}
111+
if len(cname) > 0 && cname[len(cname)-1] != '.' {
112+
cname += "."
113+
}
114+
}
115+
for r := res; r != nil; r = r.ai_next {
116+
// Everything comes back twice, once for UDP and once for TCP.
117+
if r.ai_socktype != C.SOCK_STREAM {
118+
continue
119+
}
120+
switch r.ai_family {
121+
default:
122+
continue
123+
case C.AF_INET:
124+
sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr))
125+
addrs = append(addrs, copyIP(sa.Addr[:]))
126+
case C.AF_INET6:
127+
sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr))
128+
addrs = append(addrs, copyIP(sa.Addr[:]))
129+
}
130+
}
131+
return addrs, cname, nil, true
132+
}
133+
134+
func cgoLookupIP(name string) (addrs []IP, err os.Error, completed bool) {
135+
addrs, _, err, completed = cgoLookupIPCNAME(name)
136+
return
137+
}
138+
139+
func cgoLookupCNAME(name string) (cname string, err os.Error, completed bool) {
140+
_, cname, err, completed = cgoLookupIPCNAME(name)
141+
return
142+
}
143+
144+
func copyIP(x IP) IP {
145+
y := make(IP, len(x))
146+
copy(y, x)
147+
return y
148+
}

src/pkg/net/dial.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func Dial(net, addr string) (c Conn, err os.Error) {
3030
switch net {
3131
case "tcp", "tcp4", "tcp6":
3232
var ra *TCPAddr
33-
if ra, err = ResolveTCPAddr(raddr); err != nil {
33+
if ra, err = ResolveTCPAddr(net, raddr); err != nil {
3434
goto Error
3535
}
3636
c, err := DialTCP(net, nil, ra)
@@ -40,7 +40,7 @@ func Dial(net, addr string) (c Conn, err os.Error) {
4040
return c, nil
4141
case "udp", "udp4", "udp6":
4242
var ra *UDPAddr
43-
if ra, err = ResolveUDPAddr(raddr); err != nil {
43+
if ra, err = ResolveUDPAddr(net, raddr); err != nil {
4444
goto Error
4545
}
4646
c, err := DialUDP(net, nil, ra)
@@ -83,7 +83,7 @@ func Listen(net, laddr string) (l Listener, err os.Error) {
8383
case "tcp", "tcp4", "tcp6":
8484
var la *TCPAddr
8585
if laddr != "" {
86-
if la, err = ResolveTCPAddr(laddr); err != nil {
86+
if la, err = ResolveTCPAddr(net, laddr); err != nil {
8787
return nil, err
8888
}
8989
}
@@ -116,7 +116,7 @@ func ListenPacket(net, laddr string) (c PacketConn, err os.Error) {
116116
case "udp", "udp4", "udp6":
117117
var la *UDPAddr
118118
if laddr != "" {
119-
if la, err = ResolveUDPAddr(laddr); err != nil {
119+
if la, err = ResolveUDPAddr(net, laddr); err != nil {
120120
return nil, err
121121
}
122122
}

src/pkg/net/dialgoogle_test.go

+10-5
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,22 @@ func TestDialGoogle(t *testing.T) {
7878
googleaddrs[len(googleaddrs)-1] = ""
7979
}
8080

81-
// Insert an actual IP address for google.com
81+
// Insert an actual IPv4 address for google.com
8282
// into the table.
83-
8483
addrs, err := LookupIP("www.google.com")
8584
if err != nil {
8685
t.Fatalf("lookup www.google.com: %v", err)
8786
}
88-
if len(addrs) == 0 {
89-
t.Fatalf("no addresses for www.google.com")
87+
var ip IP
88+
for _, addr := range addrs {
89+
if x := addr.To4(); x != nil {
90+
ip = x
91+
break
92+
}
93+
}
94+
if ip == nil {
95+
t.Fatalf("no IPv4 addresses for www.google.com")
9096
}
91-
ip := addrs[0].To4()
9297

9398
for i, s := range googleaddrs {
9499
if strings.Contains(s, "%") {

src/pkg/net/dnsclient.go

+17-5
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,11 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err os.Erro
307307
}
308308

309309
// goLookupHost is the native Go implementation of LookupHost.
310+
// Used only if cgoLookupHost refuses to handle the request
311+
// (that is, only if cgoLookupHost is the stub in cgo_stub.go).
312+
// Normally we let cgo use the C library resolver instead of
313+
// depending on our lookup code, so that Go and C get the same
314+
// answers.
310315
func goLookupHost(name string) (addrs []string, err os.Error) {
311316
onceLoadConfig.Do(loadConfig)
312317
if dnserr != nil || cfg == nil {
@@ -330,6 +335,11 @@ func goLookupHost(name string) (addrs []string, err os.Error) {
330335
}
331336

332337
// goLookupIP is the native Go implementation of LookupIP.
338+
// Used only if cgoLookupIP refuses to handle the request
339+
// (that is, only if cgoLookupIP is the stub in cgo_stub.go).
340+
// Normally we let cgo use the C library resolver instead of
341+
// depending on our lookup code, so that Go and C get the same
342+
// answers.
333343
func goLookupIP(name string) (addrs []IP, err os.Error) {
334344
onceLoadConfig.Do(loadConfig)
335345
if dnserr != nil || cfg == nil {
@@ -358,11 +368,13 @@ func goLookupIP(name string) (addrs []IP, err os.Error) {
358368
return
359369
}
360370

361-
// LookupCNAME returns the canonical DNS host for the given name.
362-
// Callers that do not care about the canonical name can call
363-
// LookupHost or LookupIP directly; both take care of resolving
364-
// the canonical name as part of the lookup.
365-
func LookupCNAME(name string) (cname string, err os.Error) {
371+
// goLookupCNAME is the native Go implementation of LookupCNAME.
372+
// Used only if cgoLookupCNAME refuses to handle the request
373+
// (that is, only if cgoLookupCNAME is the stub in cgo_stub.go).
374+
// Normally we let cgo use the C library resolver instead of
375+
// depending on our lookup code, so that Go and C get the same
376+
// answers.
377+
func goLookupCNAME(name string) (cname string, err os.Error) {
366378
onceLoadConfig.Do(loadConfig)
367379
if dnserr != nil || cfg == nil {
368380
err = dnserr

src/pkg/net/hosts_test.go

+15
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package net
66

77
import (
8+
"sort"
89
"testing"
910
)
1011

@@ -51,3 +52,17 @@ func TestLookupStaticHost(t *testing.T) {
5152
}
5253
hostsPath = p
5354
}
55+
56+
func TestLookupHost(t *testing.T) {
57+
// Can't depend on this to return anything in particular,
58+
// but if it does return something, make sure it doesn't
59+
// duplicate addresses (a common bug due to the way
60+
// getaddrinfo works).
61+
addrs, _ := LookupHost("localhost")
62+
sort.SortStrings(addrs)
63+
for i := 0; i+1 < len(addrs); i++ {
64+
if addrs[i] == addrs[i+1] {
65+
t.Fatalf("LookupHost(\"localhost\") = %v, has duplicate addresses", addrs)
66+
}
67+
}
68+
}

src/pkg/net/iprawsock.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ func hostToIP(host string) (ip IP, err os.Error) {
245245
err = err1
246246
goto Error
247247
}
248-
addr = firstSupportedAddr(addrs)
248+
addr = firstSupportedAddr(anyaddr, addrs)
249249
if addr == nil {
250250
// should not happen
251251
err = &AddrError{"LookupHost returned invalid address", addrs[0]}

0 commit comments

Comments
 (0)