summaryrefslogtreecommitdiff
path: root/src/pkg/net
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/net')
-rw-r--r--src/pkg/net/Makefile39
-rw-r--r--src/pkg/net/dial.go9
-rw-r--r--src/pkg/net/dialgoogle_test.go102
-rw-r--r--src/pkg/net/dnsclient.go78
-rw-r--r--src/pkg/net/dnsconfig.go1
-rw-r--r--src/pkg/net/dnsmsg.go36
-rw-r--r--src/pkg/net/dnsmsg_test.go107
-rw-r--r--src/pkg/net/fd_linux.go11
-rw-r--r--src/pkg/net/fd_windows.go4
-rw-r--r--src/pkg/net/file_test.go6
-rw-r--r--src/pkg/net/interface.go89
-rw-r--r--src/pkg/net/interface_bsd.go195
-rw-r--r--src/pkg/net/interface_linux.go208
-rw-r--r--src/pkg/net/interface_stub.go51
-rw-r--r--src/pkg/net/interface_test.go90
-rw-r--r--src/pkg/net/ip.go65
-rw-r--r--src/pkg/net/ip_test.go112
-rw-r--r--src/pkg/net/ipraw_test.go11
-rw-r--r--src/pkg/net/iprawsock.go31
-rw-r--r--src/pkg/net/ipsock.go193
-rw-r--r--src/pkg/net/resolv_windows.go4
-rw-r--r--src/pkg/net/sendfile_linux.go84
-rw-r--r--src/pkg/net/sendfile_stub.go14
-rw-r--r--src/pkg/net/server_test.go40
-rw-r--r--src/pkg/net/sock.go31
-rw-r--r--src/pkg/net/sock_bsd.go31
-rw-r--r--src/pkg/net/sock_linux.go25
-rw-r--r--src/pkg/net/sock_windows.go25
-rw-r--r--src/pkg/net/tcpsock.go18
-rw-r--r--src/pkg/net/textproto/reader.go2
-rw-r--r--src/pkg/net/udpsock.go17
31 files changed, 1493 insertions, 236 deletions
diff --git a/src/pkg/net/Makefile b/src/pkg/net/Makefile
index 221871cb1..5472df392 100644
--- a/src/pkg/net/Makefile
+++ b/src/pkg/net/Makefile
@@ -10,6 +10,7 @@ GOFILES=\
dnsmsg.go\
fd_$(GOOS).go\
hosts.go\
+ interface.go\
ip.go\
ipsock.go\
iprawsock.go\
@@ -23,36 +24,49 @@ GOFILES=\
unixsock.go\
GOFILES_freebsd=\
- newpollserver.go\
+ dnsclient.go\
+ dnsconfig.go\
fd.go\
file.go\
- dnsconfig.go\
- dnsclient.go\
+ interface_bsd.go\
+ newpollserver.go\
port.go\
+ sendfile_stub.go\
+ sock_bsd.go\
CGOFILES_freebsd=\
cgo_bsd.go\
cgo_unix.go\
GOFILES_darwin=\
- newpollserver.go\
+ dnsclient.go\
+ dnsconfig.go\
fd.go\
file.go\
- dnsconfig.go\
- dnsclient.go\
+ interface_bsd.go\
+ newpollserver.go\
port.go\
+ sendfile_stub.go\
+ sock_bsd.go\
CGOFILES_darwin=\
cgo_bsd.go\
cgo_unix.go\
-
+
GOFILES_linux=\
- newpollserver.go\
+ dnsclient.go\
+ dnsconfig.go\
fd.go\
file.go\
- dnsconfig.go\
- dnsclient.go\
+ interface_linux.go\
+ newpollserver.go\
port.go\
+ sendfile_linux.go\
+ sock_linux.go\
+
+GOFILES_plan9=\
+ interface_stub.go\
+ sendfile_stub.go\
ifeq ($(GOARCH),arm)
# ARM has no cgo, so use the stubs.
@@ -65,8 +79,11 @@ endif
GOFILES_windows=\
cgo_stub.go\
- resolv_windows.go\
file_windows.go\
+ interface_stub.go\
+ resolv_windows.go\
+ sendfile_stub.go\
+ sock_windows.go\
GOFILES+=$(GOFILES_$(GOOS))
ifneq ($(CGOFILES_$(GOOS)),)
diff --git a/src/pkg/net/dial.go b/src/pkg/net/dial.go
index 16896b426..ead775fe6 100644
--- a/src/pkg/net/dial.go
+++ b/src/pkg/net/dial.go
@@ -60,7 +60,7 @@ func Dial(net, addr string) (c Conn, err os.Error) {
return c, nil
case "ip", "ip4", "ip6":
var ra *IPAddr
- if ra, err = ResolveIPAddr(raddr); err != nil {
+ if ra, err = ResolveIPAddr(net, raddr); err != nil {
goto Error
}
c, err := DialIP(net, nil, ra)
@@ -139,12 +139,13 @@ func ListenPacket(net, laddr string) (c PacketConn, err os.Error) {
return c, nil
}
- if i := last(net, ':'); i > 0 {
- switch net[0:i] {
+ var rawnet string
+ if rawnet, _, err = splitNetProto(net); err != nil {
+ switch rawnet {
case "ip", "ip4", "ip6":
var la *IPAddr
if laddr != "" {
- if la, err = ResolveIPAddr(laddr); err != nil {
+ if la, err = ResolveIPAddr(rawnet, laddr); err != nil {
return nil, err
}
}
diff --git a/src/pkg/net/dialgoogle_test.go b/src/pkg/net/dialgoogle_test.go
index c25089ba4..9ad1770da 100644
--- a/src/pkg/net/dialgoogle_test.go
+++ b/src/pkg/net/dialgoogle_test.go
@@ -41,20 +41,6 @@ func doDial(t *testing.T, network, addr string) {
fd.Close()
}
-var googleaddrs = []string{
- "%d.%d.%d.%d:80",
- "www.google.com:80",
- "%d.%d.%d.%d:http",
- "www.google.com:http",
- "%03d.%03d.%03d.%03d:0080",
- "[::ffff:%d.%d.%d.%d]:80",
- "[::ffff:%02x%02x:%02x%02x]:80",
- "[0:0:0:0:0000:ffff:%d.%d.%d.%d]:80",
- "[0:0:0:0:000000:ffff:%d.%d.%d.%d]:80",
- "[0:0:0:0:0:ffff::%d.%d.%d.%d]:80",
- "[2001:4860:0:2001::68]:80", // ipv6.google.com; removed if ipv6 flag not set
-}
-
func TestLookupCNAME(t *testing.T) {
if testing.Short() {
// Don't use external network.
@@ -67,16 +53,25 @@ func TestLookupCNAME(t *testing.T) {
}
}
-func TestDialGoogle(t *testing.T) {
+var googleaddrsipv4 = []string{
+ "%d.%d.%d.%d:80",
+ "www.google.com:80",
+ "%d.%d.%d.%d:http",
+ "www.google.com:http",
+ "%03d.%03d.%03d.%03d:0080",
+ "[::ffff:%d.%d.%d.%d]:80",
+ "[::ffff:%02x%02x:%02x%02x]:80",
+ "[0:0:0:0:0000:ffff:%d.%d.%d.%d]:80",
+ "[0:0:0:0:000000:ffff:%d.%d.%d.%d]:80",
+ "[0:0:0:0:0:ffff::%d.%d.%d.%d]:80",
+}
+
+func TestDialGoogleIPv4(t *testing.T) {
if testing.Short() {
// Don't use external network.
t.Logf("skipping external network test during -short")
return
}
- // If no ipv6 tunnel, don't try the last address.
- if !*ipv6 {
- googleaddrs[len(googleaddrs)-1] = ""
- }
// Insert an actual IPv4 address for google.com
// into the table.
@@ -95,14 +90,14 @@ func TestDialGoogle(t *testing.T) {
t.Fatalf("no IPv4 addresses for www.google.com")
}
- for i, s := range googleaddrs {
+ for i, s := range googleaddrsipv4 {
if strings.Contains(s, "%") {
- googleaddrs[i] = fmt.Sprintf(s, ip[0], ip[1], ip[2], ip[3])
+ googleaddrsipv4[i] = fmt.Sprintf(s, ip[0], ip[1], ip[2], ip[3])
}
}
- for i := 0; i < len(googleaddrs); i++ {
- addr := googleaddrs[i]
+ for i := 0; i < len(googleaddrsipv4); i++ {
+ addr := googleaddrsipv4[i]
if addr == "" {
continue
}
@@ -110,20 +105,65 @@ func TestDialGoogle(t *testing.T) {
doDial(t, "tcp", addr)
if addr[0] != '[' {
doDial(t, "tcp4", addr)
-
- if !preferIPv4 {
- // make sure preferIPv4 flag works.
- preferIPv4 = true
+ if supportsIPv6 {
+ // make sure syscall.SocketDisableIPv6 flag works.
syscall.SocketDisableIPv6 = true
+ doDial(t, "tcp", addr)
doDial(t, "tcp4", addr)
syscall.SocketDisableIPv6 = false
- preferIPv4 = false
}
}
+ }
+}
+
+var googleaddrsipv6 = []string{
+ "[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:80",
+ "ipv6.google.com:80",
+ "[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:http",
+ "ipv6.google.com:http",
+}
+
+func TestDialGoogleIPv6(t *testing.T) {
+ if testing.Short() {
+ // Don't use external network.
+ t.Logf("skipping external network test during -short")
+ return
+ }
+ // Only run tcp6 if the kernel will take it.
+ if !*ipv6 || !supportsIPv6 {
+ return
+ }
+
+ // Insert an actual IPv6 address for ipv6.google.com
+ // into the table.
+ addrs, err := LookupIP("ipv6.google.com")
+ if err != nil {
+ t.Fatalf("lookup ipv6.google.com: %v", err)
+ }
+ var ip IP
+ for _, addr := range addrs {
+ if x := addr.To16(); x != nil {
+ ip = x
+ break
+ }
+ }
+ if ip == nil {
+ t.Fatalf("no IPv6 addresses for ipv6.google.com")
+ }
+
+ for i, s := range googleaddrsipv6 {
+ if strings.Contains(s, "%") {
+ googleaddrsipv6[i] = fmt.Sprintf(s, ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[8], ip[9], ip[10], ip[11], ip[12], ip[13], ip[14], ip[15])
+ }
+ }
- // Only run tcp6 if the kernel will take it.
- if kernelSupportsIPv6() {
- doDial(t, "tcp6", addr)
+ for i := 0; i < len(googleaddrsipv6); i++ {
+ addr := googleaddrsipv6[i]
+ if addr == "" {
+ continue
}
+ t.Logf("-- %s --", addr)
+ doDial(t, "tcp", addr)
+ doDial(t, "tcp6", addr)
}
}
diff --git a/src/pkg/net/dnsclient.go b/src/pkg/net/dnsclient.go
index 89f2409bf..ae9ca8430 100644
--- a/src/pkg/net/dnsclient.go
+++ b/src/pkg/net/dnsclient.go
@@ -121,15 +121,19 @@ func answer(name, server string, dns *dnsMsg, qtype uint16) (cname string, addrs
Cname:
for cnameloop := 0; cnameloop < 10; cnameloop++ {
addrs = addrs[0:0]
- for i := 0; i < len(dns.answer); i++ {
- rr := dns.answer[i]
+ for _, rr := range dns.answer {
+ if _, justHeader := rr.(*dnsRR_Header); justHeader {
+ // Corrupt record: we only have a
+ // header. That header might say it's
+ // of type qtype, but we don't
+ // actually have it. Skip.
+ continue
+ }
h := rr.Header()
if h.Class == dnsClassINET && h.Name == name {
switch h.Rrtype {
case qtype:
- n := len(addrs)
- addrs = addrs[0 : n+1]
- addrs[n] = rr
+ addrs = append(addrs, rr)
case dnsTypeCNAME:
// redirect to cname
name = rr.(*dnsRR_CNAME).Cname
@@ -181,8 +185,7 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs
func convertRR_A(records []dnsRR) []IP {
addrs := make([]IP, len(records))
- for i := 0; i < len(records); i++ {
- rr := records[i]
+ for i, rr := range records {
a := rr.(*dnsRR_A).A
addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a))
}
@@ -191,8 +194,7 @@ func convertRR_A(records []dnsRR) []IP {
func convertRR_AAAA(records []dnsRR) []IP {
addrs := make([]IP, len(records))
- for i := 0; i < len(records); i++ {
- rr := records[i]
+ for i, rr := range records {
a := make(IP, 16)
copy(a, rr.(*dnsRR_AAAA).AAAA[:])
addrs[i] = a
@@ -384,9 +386,7 @@ func goLookupCNAME(name string) (cname string, err os.Error) {
if err != nil {
return
}
- if len(rr) >= 0 {
- cname = rr[0].(*dnsRR_CNAME).Cname
- }
+ cname = rr[0].(*dnsRR_CNAME).Cname
return
}
@@ -398,10 +398,49 @@ type SRV struct {
Weight uint16
}
+// byPriorityWeight sorts SRV records by ascending priority and weight.
+type byPriorityWeight []*SRV
+
+func (s byPriorityWeight) Len() int { return len(s) }
+
+func (s byPriorityWeight) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+func (s byPriorityWeight) Less(i, j int) bool {
+ return s[i].Priority < s[j].Priority ||
+ (s[i].Priority == s[j].Priority && s[i].Weight < s[j].Weight)
+}
+
+// shuffleSRVByWeight shuffles SRV records by weight using the algorithm
+// described in RFC 2782.
+func shuffleSRVByWeight(addrs []*SRV) {
+ sum := 0
+ for _, addr := range addrs {
+ sum += int(addr.Weight)
+ }
+ for sum > 0 && len(addrs) > 1 {
+ s := 0
+ n := rand.Intn(sum + 1)
+ for i := range addrs {
+ s += int(addrs[i].Weight)
+ if s >= n {
+ if i > 0 {
+ t := addrs[i]
+ copy(addrs[1:i+1], addrs[0:i])
+ addrs[0] = t
+ }
+ break
+ }
+ }
+ sum -= int(addrs[0].Weight)
+ addrs = addrs[1:]
+ }
+}
+
// LookupSRV tries to resolve an SRV query of the given service,
// protocol, and domain name, as specified in RFC 2782. In most cases
// the proto argument can be the same as the corresponding
-// Addr.Network().
+// Addr.Network(). The returned records are sorted by priority
+// and randomized by weight within a priority.
func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.Error) {
target := "_" + service + "._" + proto + "." + name
var records []dnsRR
@@ -410,10 +449,19 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.
return
}
addrs = make([]*SRV, len(records))
- for i := 0; i < len(records); i++ {
- r := records[i].(*dnsRR_SRV)
+ for i, rr := range records {
+ r := rr.(*dnsRR_SRV)
addrs[i] = &SRV{r.Target, r.Port, r.Priority, r.Weight}
}
+ sort.Sort(byPriorityWeight(addrs))
+ 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
}
diff --git a/src/pkg/net/dnsconfig.go b/src/pkg/net/dnsconfig.go
index 26f0e04e9..54e334342 100644
--- a/src/pkg/net/dnsconfig.go
+++ b/src/pkg/net/dnsconfig.go
@@ -30,7 +30,6 @@ func (e *DNSConfigError) String() string {
func (e *DNSConfigError) Timeout() bool { return false }
func (e *DNSConfigError) Temporary() bool { return false }
-
// See resolv.conf(5) on a Linux machine.
// TODO(rsc): Supposed to call uname() and chop the beginning
// of the host name to get the default search domain.
diff --git a/src/pkg/net/dnsmsg.go b/src/pkg/net/dnsmsg.go
index 7b8e5c6d3..0ba69a0ce 100644
--- a/src/pkg/net/dnsmsg.go
+++ b/src/pkg/net/dnsmsg.go
@@ -117,7 +117,6 @@ type dnsRR interface {
Header() *dnsRR_Header
}
-
// Specific DNS RR formats for each query type.
type dnsRR_CNAME struct {
@@ -715,24 +714,35 @@ func (dns *dnsMsg) Unpack(msg []byte) bool {
// Arrays.
dns.question = make([]dnsQuestion, dh.Qdcount)
- dns.answer = make([]dnsRR, dh.Ancount)
- dns.ns = make([]dnsRR, dh.Nscount)
- dns.extra = make([]dnsRR, dh.Arcount)
+ dns.answer = make([]dnsRR, 0, dh.Ancount)
+ dns.ns = make([]dnsRR, 0, dh.Nscount)
+ dns.extra = make([]dnsRR, 0, dh.Arcount)
+
+ var rec dnsRR
for i := 0; i < len(dns.question); i++ {
off, ok = unpackStruct(&dns.question[i], msg, off)
}
- for i := 0; i < len(dns.answer); i++ {
- dns.answer[i], off, ok = unpackRR(msg, off)
- }
- for i := 0; i < len(dns.ns); i++ {
- dns.ns[i], off, ok = unpackRR(msg, off)
+ for i := 0; i < int(dh.Ancount); i++ {
+ rec, off, ok = unpackRR(msg, off)
+ if !ok {
+ return false
+ }
+ dns.answer = append(dns.answer, rec)
}
- for i := 0; i < len(dns.extra); i++ {
- dns.extra[i], off, ok = unpackRR(msg, off)
+ for i := 0; i < int(dh.Nscount); i++ {
+ rec, off, ok = unpackRR(msg, off)
+ if !ok {
+ return false
+ }
+ dns.ns = append(dns.ns, rec)
}
- if !ok {
- return false
+ for i := 0; i < int(dh.Arcount); i++ {
+ rec, off, ok = unpackRR(msg, off)
+ if !ok {
+ return false
+ }
+ dns.extra = append(dns.extra, rec)
}
// if off != len(msg) {
// println("extra bytes in dns packet", off, "<", len(msg));
diff --git a/src/pkg/net/dnsmsg_test.go b/src/pkg/net/dnsmsg_test.go
new file mode 100644
index 000000000..20c9f02b0
--- /dev/null
+++ b/src/pkg/net/dnsmsg_test.go
@@ -0,0 +1,107 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+import (
+ "encoding/hex"
+ "runtime"
+ "testing"
+)
+
+func TestDNSParseSRVReply(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ return
+ }
+ data, err := hex.DecodeString(dnsSRVReply)
+ if err != nil {
+ t.Fatal(err)
+ }
+ msg := new(dnsMsg)
+ ok := msg.Unpack(data)
+ if !ok {
+ t.Fatalf("unpacking packet failed")
+ }
+ if g, e := len(msg.answer), 5; g != e {
+ t.Errorf("len(msg.answer) = %d; want %d", g, e)
+ }
+ for idx, rr := range msg.answer {
+ if g, e := rr.Header().Rrtype, uint16(dnsTypeSRV); g != e {
+ t.Errorf("rr[%d].Header().Rrtype = %d; want %d", idx, g, e)
+ }
+ if _, ok := rr.(*dnsRR_SRV); !ok {
+ t.Errorf("answer[%d] = %T; want *dnsRR_SRV", idx, rr)
+ }
+ }
+ _, addrs, err := answer("_xmpp-server._tcp.google.com.", "foo:53", msg, uint16(dnsTypeSRV))
+ if err != nil {
+ t.Fatalf("answer: %v", err)
+ }
+ if g, e := len(addrs), 5; g != e {
+ t.Errorf("len(addrs) = %d; want %d", g, e)
+ t.Logf("addrs = %#v", addrs)
+ }
+}
+
+func TestDNSParseCorruptSRVReply(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ return
+ }
+ data, err := hex.DecodeString(dnsSRVCorruptReply)
+ if err != nil {
+ t.Fatal(err)
+ }
+ msg := new(dnsMsg)
+ ok := msg.Unpack(data)
+ if !ok {
+ t.Fatalf("unpacking packet failed")
+ }
+ if g, e := len(msg.answer), 5; g != e {
+ t.Errorf("len(msg.answer) = %d; want %d", g, e)
+ }
+ for idx, rr := range msg.answer {
+ if g, e := rr.Header().Rrtype, uint16(dnsTypeSRV); g != e {
+ t.Errorf("rr[%d].Header().Rrtype = %d; want %d", idx, g, e)
+ }
+ if idx == 4 {
+ if _, ok := rr.(*dnsRR_Header); !ok {
+ t.Errorf("answer[%d] = %T; want *dnsRR_Header", idx, rr)
+ }
+ } else {
+ if _, ok := rr.(*dnsRR_SRV); !ok {
+ t.Errorf("answer[%d] = %T; want *dnsRR_SRV", idx, rr)
+ }
+ }
+ }
+ _, addrs, err := answer("_xmpp-server._tcp.google.com.", "foo:53", msg, uint16(dnsTypeSRV))
+ if err != nil {
+ t.Fatalf("answer: %v", err)
+ }
+ if g, e := len(addrs), 4; g != e {
+ t.Errorf("len(addrs) = %d; want %d", g, e)
+ t.Logf("addrs = %#v", addrs)
+ }
+}
+
+// Valid DNS SRV reply
+const dnsSRVReply = "0901818000010005000000000c5f786d70702d736572766572045f74637006676f6f67" +
+ "6c6503636f6d0000210001c00c002100010000012c00210014000014950c786d70702d" +
+ "73657276657234016c06676f6f676c6503636f6d00c00c002100010000012c00210014" +
+ "000014950c786d70702d73657276657232016c06676f6f676c6503636f6d00c00c0021" +
+ "00010000012c00210014000014950c786d70702d73657276657233016c06676f6f676c" +
+ "6503636f6d00c00c002100010000012c00200005000014950b786d70702d7365727665" +
+ "72016c06676f6f676c6503636f6d00c00c002100010000012c00210014000014950c78" +
+ "6d70702d73657276657231016c06676f6f676c6503636f6d00"
+
+// Corrupt DNS SRV reply, with its final RR having a bogus length
+// (perhaps it was truncated, or it's malicious) The mutation is the
+// capital "FF" below, instead of the proper "21".
+const dnsSRVCorruptReply = "0901818000010005000000000c5f786d70702d736572766572045f74637006676f6f67" +
+ "6c6503636f6d0000210001c00c002100010000012c00210014000014950c786d70702d" +
+ "73657276657234016c06676f6f676c6503636f6d00c00c002100010000012c00210014" +
+ "000014950c786d70702d73657276657232016c06676f6f676c6503636f6d00c00c0021" +
+ "00010000012c00210014000014950c786d70702d73657276657233016c06676f6f676c" +
+ "6503636f6d00c00c002100010000012c00200005000014950b786d70702d7365727665" +
+ "72016c06676f6f676c6503636f6d00c00c002100010000012c00FF0014000014950c78" +
+ "6d70702d73657276657231016c06676f6f676c6503636f6d00"
diff --git a/src/pkg/net/fd_linux.go b/src/pkg/net/fd_linux.go
index dcf65c014..70fc344b2 100644
--- a/src/pkg/net/fd_linux.go
+++ b/src/pkg/net/fd_linux.go
@@ -117,6 +117,17 @@ func (p *pollster) DelFD(fd int, mode int) {
} else {
p.StopWaiting(fd, writeFlags)
}
+
+ // Discard any queued up events.
+ i := 0
+ for i < len(p.waitEvents) {
+ if fd == int(p.waitEvents[i].Fd) {
+ copy(p.waitEvents[i:], p.waitEvents[i+1:])
+ p.waitEvents = p.waitEvents[:len(p.waitEvents)-1]
+ } else {
+ i++
+ }
+ }
}
func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err os.Error) {
diff --git a/src/pkg/net/fd_windows.go b/src/pkg/net/fd_windows.go
index c2f736cc1..9ed7801d2 100644
--- a/src/pkg/net/fd_windows.go
+++ b/src/pkg/net/fd_windows.go
@@ -84,7 +84,7 @@ func (o *bufOp) Init(fd *netFD, buf []byte) {
}
}
-// resultSrv will retreive all io completion results from
+// resultSrv will retrieve all io completion results from
// iocp and send them to the correspondent waiting client
// goroutine via channel supplied in the request.
type resultSrv struct {
@@ -513,7 +513,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os.
return nfd, nil
}
-// Not implemeted functions.
+// Unimplemented functions.
func (fd *netFD) dup() (f *os.File, err os.Error) {
// TODO: Implement this
diff --git a/src/pkg/net/file_test.go b/src/pkg/net/file_test.go
index 1ec05fdee..bd1e2c9d7 100644
--- a/src/pkg/net/file_test.go
+++ b/src/pkg/net/file_test.go
@@ -62,7 +62,7 @@ func TestFileListener(t *testing.T) {
}
testFileListener(t, "tcp", "127.0.0.1")
testFileListener(t, "tcp", "127.0.0.1")
- if kernelSupportsIPv6() {
+ if supportsIPv6 && supportsIPv4map {
testFileListener(t, "tcp", "[::ffff:127.0.0.1]")
testFileListener(t, "tcp", "127.0.0.1")
testFileListener(t, "tcp", "[::ffff:127.0.0.1]")
@@ -121,8 +121,10 @@ func TestFilePacketConn(t *testing.T) {
}
testFilePacketConnListen(t, "udp", "127.0.0.1:0")
testFilePacketConnDial(t, "udp", "127.0.0.1:12345")
- if kernelSupportsIPv6() {
+ if supportsIPv6 {
testFilePacketConnListen(t, "udp", "[::1]:0")
+ }
+ if supportsIPv6 && supportsIPv4map {
testFilePacketConnDial(t, "udp", "[::ffff:127.0.0.1]:12345")
}
if syscall.OS == "linux" {
diff --git a/src/pkg/net/interface.go b/src/pkg/net/interface.go
new file mode 100644
index 000000000..f622487ab
--- /dev/null
+++ b/src/pkg/net/interface.go
@@ -0,0 +1,89 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Network interface identification
+
+package net
+
+import (
+ "bytes"
+ "fmt"
+ "os"
+)
+
+// A HardwareAddr represents a physical hardware address.
+type HardwareAddr []byte
+
+func (a HardwareAddr) String() string {
+ var buf bytes.Buffer
+ for i, b := range a {
+ if i > 0 {
+ buf.WriteByte(':')
+ }
+ fmt.Fprintf(&buf, "%02x", b)
+ }
+ return buf.String()
+}
+
+// Interface represents a mapping between network interface name
+// and index. It also represents network interface facility
+// information.
+type Interface struct {
+ Index int // positive integer that starts at one, zero is never used
+ MTU int // maximum transmission unit
+ Name string // e.g., "en0", "lo0", "eth0.100"
+ HardwareAddr HardwareAddr // IEEE MAC-48, EUI-48 and EUI-64 form
+ rawFlags int
+}
+
+// Addrs returns interface addresses for a specific interface.
+func (ifi *Interface) Addrs() ([]Addr, os.Error) {
+ if ifi == nil {
+ return nil, os.NewError("net: invalid interface")
+ }
+ return interfaceAddrTable(ifi.Index)
+}
+
+// Interfaces returns a list of the systems's network interfaces.
+func Interfaces() ([]Interface, os.Error) {
+ return interfaceTable(0)
+}
+
+// InterfaceAddrs returns a list of the system's network interface
+// addresses.
+func InterfaceAddrs() ([]Addr, os.Error) {
+ return interfaceAddrTable(0)
+}
+
+// InterfaceByIndex returns the interface specified by index.
+func InterfaceByIndex(index int) (*Interface, os.Error) {
+ if index <= 0 {
+ return nil, os.NewError("net: invalid interface index")
+ }
+ ift, err := interfaceTable(index)
+ if err != nil {
+ return nil, err
+ }
+ for _, ifi := range ift {
+ return &ifi, nil
+ }
+ return nil, os.NewError("net: no such interface")
+}
+
+// InterfaceByName returns the interface specified by name.
+func InterfaceByName(name string) (*Interface, os.Error) {
+ if name == "" {
+ return nil, os.NewError("net: invalid interface name")
+ }
+ ift, err := interfaceTable(0)
+ if err != nil {
+ return nil, err
+ }
+ for _, ifi := range ift {
+ if name == ifi.Name {
+ return &ifi, nil
+ }
+ }
+ return nil, os.NewError("net: no such interface")
+}
diff --git a/src/pkg/net/interface_bsd.go b/src/pkg/net/interface_bsd.go
new file mode 100644
index 000000000..141b95b38
--- /dev/null
+++ b/src/pkg/net/interface_bsd.go
@@ -0,0 +1,195 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Network interface identification for BSD variants
+
+package net
+
+import (
+ "os"
+ "syscall"
+ "unsafe"
+)
+
+// IsUp returns true if ifi is up.
+func (ifi *Interface) IsUp() bool {
+ if ifi == nil {
+ return false
+ }
+ return ifi.rawFlags&syscall.IFF_UP != 0
+}
+
+// IsLoopback returns true if ifi is a loopback interface.
+func (ifi *Interface) IsLoopback() bool {
+ if ifi == nil {
+ return false
+ }
+ return ifi.rawFlags&syscall.IFF_LOOPBACK != 0
+}
+
+// CanBroadcast returns true if ifi supports a broadcast access
+// capability.
+func (ifi *Interface) CanBroadcast() bool {
+ if ifi == nil {
+ return false
+ }
+ return ifi.rawFlags&syscall.IFF_BROADCAST != 0
+}
+
+// IsPointToPoint returns true if ifi belongs to a point-to-point
+// link.
+func (ifi *Interface) IsPointToPoint() bool {
+ if ifi == nil {
+ return false
+ }
+ return ifi.rawFlags&syscall.IFF_POINTOPOINT != 0
+}
+
+// CanMulticast returns true if ifi supports a multicast access
+// capability.
+func (ifi *Interface) CanMulticast() bool {
+ if ifi == nil {
+ return false
+ }
+ return ifi.rawFlags&syscall.IFF_MULTICAST != 0
+}
+
+// If the ifindex is zero, interfaceTable returns mappings of all
+// network interfaces. Otheriwse it returns a mapping of a specific
+// interface.
+func interfaceTable(ifindex int) ([]Interface, os.Error) {
+ var (
+ tab []byte
+ e int
+ msgs []syscall.RoutingMessage
+ ift []Interface
+ )
+
+ tab, e = syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex)
+ if e != 0 {
+ return nil, os.NewSyscallError("route rib", e)
+ }
+
+ msgs, e = syscall.ParseRoutingMessage(tab)
+ if e != 0 {
+ return nil, os.NewSyscallError("route message", e)
+ }
+
+ for _, m := range msgs {
+ switch v := m.(type) {
+ case *syscall.InterfaceMessage:
+ if ifindex == 0 || ifindex == int(v.Header.Index) {
+ ifi, err := newLink(v)
+ if err != nil {
+ return nil, err
+ }
+ ift = append(ift, ifi...)
+ }
+ }
+ }
+
+ return ift, nil
+}
+
+func newLink(m *syscall.InterfaceMessage) ([]Interface, os.Error) {
+ var ift []Interface
+
+ sas, e := syscall.ParseRoutingSockaddr(m)
+ if e != 0 {
+ return nil, os.NewSyscallError("route sockaddr", e)
+ }
+
+ for _, s := range sas {
+ switch v := s.(type) {
+ case *syscall.SockaddrDatalink:
+ // NOTE: SockaddrDatalink.Data is minimum work area,
+ // can be larger.
+ m.Data = m.Data[unsafe.Offsetof(v.Data):]
+ ifi := Interface{Index: int(m.Header.Index), rawFlags: int(m.Header.Flags)}
+ var name [syscall.IFNAMSIZ]byte
+ for i := 0; i < int(v.Nlen); i++ {
+ name[i] = byte(m.Data[i])
+ }
+ ifi.Name = string(name[:v.Nlen])
+ ifi.MTU = int(m.Header.Data.Mtu)
+ addr := make([]byte, v.Alen)
+ for i := 0; i < int(v.Alen); i++ {
+ addr[i] = byte(m.Data[int(v.Nlen)+i])
+ }
+ ifi.HardwareAddr = addr[:v.Alen]
+ ift = append(ift, ifi)
+ }
+ }
+
+ return ift, nil
+}
+
+// If the ifindex is zero, interfaceAddrTable returns addresses
+// for all network interfaces. Otherwise it returns addresses
+// for a specific interface.
+func interfaceAddrTable(ifindex int) ([]Addr, os.Error) {
+ var (
+ tab []byte
+ e int
+ msgs []syscall.RoutingMessage
+ ifat []Addr
+ )
+
+ tab, e = syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex)
+ if e != 0 {
+ return nil, os.NewSyscallError("route rib", e)
+ }
+
+ msgs, e = syscall.ParseRoutingMessage(tab)
+ if e != 0 {
+ return nil, os.NewSyscallError("route message", e)
+ }
+
+ for _, m := range msgs {
+ switch v := m.(type) {
+ case *syscall.InterfaceAddrMessage:
+ if ifindex == 0 || ifindex == int(v.Header.Index) {
+ ifa, err := newAddr(v)
+ if err != nil {
+ return nil, err
+ }
+ ifat = append(ifat, ifa...)
+ }
+ }
+ }
+
+ return ifat, nil
+}
+
+func newAddr(m *syscall.InterfaceAddrMessage) ([]Addr, os.Error) {
+ var ifat []Addr
+
+ sas, e := syscall.ParseRoutingSockaddr(m)
+ if e != 0 {
+ return nil, os.NewSyscallError("route sockaddr", e)
+ }
+
+ for _, s := range sas {
+ var ifa IPAddr
+ switch v := s.(type) {
+ case *syscall.SockaddrInet4:
+ ifa.IP = IPv4(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3])
+ case *syscall.SockaddrInet6:
+ ifa.IP = make(IP, IPv6len)
+ copy(ifa.IP, v.Addr[:])
+ // NOTE: KAME based IPv6 protcol stack usually embeds
+ // the interface index in the interface-local or link-
+ // local address as the kernel-internal form.
+ if ifa.IP.IsLinkLocalUnicast() ||
+ ifa.IP.IsInterfaceLocalMulticast() ||
+ ifa.IP.IsLinkLocalMulticast() {
+ // remove embedded scope zone ID
+ ifa.IP[2], ifa.IP[3] = 0, 0
+ }
+ }
+ ifat = append(ifat, ifa.toAddr())
+ }
+
+ return ifat, nil
+}
diff --git a/src/pkg/net/interface_linux.go b/src/pkg/net/interface_linux.go
new file mode 100644
index 000000000..5c9657834
--- /dev/null
+++ b/src/pkg/net/interface_linux.go
@@ -0,0 +1,208 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Network interface identification for Linux
+
+package net
+
+import (
+ "os"
+ "syscall"
+ "unsafe"
+)
+
+// IsUp returns true if ifi is up.
+func (ifi *Interface) IsUp() bool {
+ if ifi == nil {
+ return false
+ }
+ return ifi.rawFlags&syscall.IFF_UP != 0
+}
+
+// IsLoopback returns true if ifi is a loopback interface.
+func (ifi *Interface) IsLoopback() bool {
+ if ifi == nil {
+ return false
+ }
+ return ifi.rawFlags&syscall.IFF_LOOPBACK != 0
+}
+
+// CanBroadcast returns true if ifi supports a broadcast access
+// capability.
+func (ifi *Interface) CanBroadcast() bool {
+ if ifi == nil {
+ return false
+ }
+ return ifi.rawFlags&syscall.IFF_BROADCAST != 0
+}
+
+// IsPointToPoint returns true if ifi belongs to a point-to-point
+// link.
+func (ifi *Interface) IsPointToPoint() bool {
+ if ifi == nil {
+ return false
+ }
+ return ifi.rawFlags&syscall.IFF_POINTOPOINT != 0
+}
+
+// CanMulticast returns true if ifi supports a multicast access
+// capability.
+func (ifi *Interface) CanMulticast() bool {
+ if ifi == nil {
+ return false
+ }
+ return ifi.rawFlags&syscall.IFF_MULTICAST != 0
+}
+
+// If the ifindex is zero, interfaceTable returns mappings of all
+// network interfaces. Otheriwse it returns a mapping of a specific
+// interface.
+func interfaceTable(ifindex int) ([]Interface, os.Error) {
+ var (
+ ift []Interface
+ tab []byte
+ msgs []syscall.NetlinkMessage
+ e int
+ )
+
+ tab, e = syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC)
+ if e != 0 {
+ return nil, os.NewSyscallError("netlink rib", e)
+ }
+
+ msgs, e = syscall.ParseNetlinkMessage(tab)
+ if e != 0 {
+ return nil, os.NewSyscallError("netlink message", e)
+ }
+
+ for _, m := range msgs {
+ switch m.Header.Type {
+ case syscall.NLMSG_DONE:
+ goto done
+ case syscall.RTM_NEWLINK:
+ ifim := (*syscall.IfInfomsg)(unsafe.Pointer(&m.Data[0]))
+ if ifindex == 0 || ifindex == int(ifim.Index) {
+ attrs, e := syscall.ParseNetlinkRouteAttr(&m)
+ if e != 0 {
+ return nil, os.NewSyscallError("netlink routeattr", e)
+ }
+ ifi := newLink(attrs, ifim)
+ ift = append(ift, ifi)
+ }
+ }
+ }
+
+done:
+ return ift, nil
+}
+
+func newLink(attrs []syscall.NetlinkRouteAttr, ifim *syscall.IfInfomsg) Interface {
+ ifi := Interface{Index: int(ifim.Index), rawFlags: int(ifim.Flags)}
+ for _, a := range attrs {
+ switch a.Attr.Type {
+ case syscall.IFLA_ADDRESS:
+ var nonzero bool
+ for _, b := range a.Value {
+ if b != 0 {
+ nonzero = true
+ }
+ }
+ if nonzero {
+ ifi.HardwareAddr = a.Value[:]
+ }
+ case syscall.IFLA_IFNAME:
+ ifi.Name = string(a.Value[:])
+ case syscall.IFLA_MTU:
+ ifi.MTU = int(uint32(a.Value[3])<<24 | uint32(a.Value[2])<<16 | uint32(a.Value[1])<<8 | uint32(a.Value[0]))
+ }
+ }
+ return ifi
+}
+
+// If the ifindex is zero, interfaceAddrTable returns addresses
+// for all network interfaces. Otherwise it returns addresses
+// for a specific interface.
+func interfaceAddrTable(ifindex int) ([]Addr, os.Error) {
+ var (
+ ifat4 []Addr
+ ifat6 []Addr
+ tab []byte
+ msgs4 []syscall.NetlinkMessage
+ msgs6 []syscall.NetlinkMessage
+ e int
+ err os.Error
+ )
+
+ tab, e = syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_INET)
+ if e != 0 {
+ return nil, os.NewSyscallError("netlink rib", e)
+ }
+ msgs4, e = syscall.ParseNetlinkMessage(tab)
+ if e != 0 {
+ return nil, os.NewSyscallError("netlink message", e)
+ }
+ ifat4, err = addrTable(msgs4, ifindex)
+ if err != nil {
+ return nil, err
+ }
+
+ tab, e = syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_INET6)
+ if e != 0 {
+ return nil, os.NewSyscallError("netlink rib", e)
+ }
+ msgs6, e = syscall.ParseNetlinkMessage(tab)
+ if e != 0 {
+ return nil, os.NewSyscallError("netlink message", e)
+ }
+ ifat6, err = addrTable(msgs6, ifindex)
+ if err != nil {
+ return nil, err
+ }
+
+ return append(ifat4, ifat6...), nil
+}
+
+func addrTable(msgs []syscall.NetlinkMessage, ifindex int) ([]Addr, os.Error) {
+ var ifat []Addr
+
+ for _, m := range msgs {
+ switch m.Header.Type {
+ case syscall.NLMSG_DONE:
+ goto done
+ case syscall.RTM_NEWADDR:
+ ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0]))
+ if ifindex == 0 || ifindex == int(ifam.Index) {
+ attrs, e := syscall.ParseNetlinkRouteAttr(&m)
+ if e != 0 {
+ return nil, os.NewSyscallError("netlink routeattr", e)
+ }
+ ifat = append(ifat, newAddr(attrs, int(ifam.Family))...)
+ }
+ }
+ }
+
+done:
+ return ifat, nil
+}
+
+func newAddr(attrs []syscall.NetlinkRouteAttr, family int) []Addr {
+ var ifat []Addr
+
+ for _, a := range attrs {
+ switch a.Attr.Type {
+ case syscall.IFA_ADDRESS:
+ ifa := IPAddr{}
+ switch family {
+ case syscall.AF_INET:
+ ifa.IP = IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3])
+ case syscall.AF_INET6:
+ ifa.IP = make(IP, IPv6len)
+ copy(ifa.IP, a.Value[:])
+ }
+ ifat = append(ifat, ifa.toAddr())
+ }
+ }
+
+ return ifat
+}
diff --git a/src/pkg/net/interface_stub.go b/src/pkg/net/interface_stub.go
new file mode 100644
index 000000000..feb871bb5
--- /dev/null
+++ b/src/pkg/net/interface_stub.go
@@ -0,0 +1,51 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Network interface identification
+
+package net
+
+import "os"
+
+// IsUp returns true if ifi is up.
+func (ifi *Interface) IsUp() bool {
+ return false
+}
+
+// IsLoopback returns true if ifi is a loopback interface.
+func (ifi *Interface) IsLoopback() bool {
+ return false
+}
+
+// CanBroadcast returns true if ifi supports a broadcast access
+// capability.
+func (ifi *Interface) CanBroadcast() bool {
+ return false
+}
+
+// IsPointToPoint returns true if ifi belongs to a point-to-point
+// link.
+func (ifi *Interface) IsPointToPoint() bool {
+ return false
+}
+
+// CanMulticast returns true if ifi supports a multicast access
+// capability.
+func (ifi *Interface) CanMulticast() bool {
+ return false
+}
+
+// If the ifindex is zero, interfaceTable returns mappings of all
+// network interfaces. Otheriwse it returns a mapping of a specific
+// interface.
+func interfaceTable(ifindex int) ([]Interface, os.Error) {
+ return nil, nil
+}
+
+// If the ifindex is zero, interfaceAddrTable returns addresses
+// for all network interfaces. Otherwise it returns addresses
+// for a specific interface.
+func interfaceAddrTable(ifindex int) ([]Addr, os.Error) {
+ return nil, nil
+}
diff --git a/src/pkg/net/interface_test.go b/src/pkg/net/interface_test.go
new file mode 100644
index 000000000..938434623
--- /dev/null
+++ b/src/pkg/net/interface_test.go
@@ -0,0 +1,90 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+import (
+ "bytes"
+ "testing"
+)
+
+func sameInterface(i, j *Interface) bool {
+ if i == nil || j == nil {
+ return false
+ }
+ if i.Index == j.Index && i.Name == j.Name && bytes.Equal(i.HardwareAddr, j.HardwareAddr) {
+ return true
+ }
+ return false
+}
+
+func interfaceFlagsString(ifi *Interface) string {
+ fs := "<"
+ if ifi.IsUp() {
+ fs += "UP,"
+ }
+ if ifi.CanBroadcast() {
+ fs += "BROADCAST,"
+ }
+ if ifi.IsLoopback() {
+ fs += "LOOPBACK,"
+ }
+ if ifi.IsPointToPoint() {
+ fs += "POINTOPOINT,"
+ }
+ if ifi.CanMulticast() {
+ fs += "MULTICAST,"
+ }
+ if len(fs) > 1 {
+ fs = fs[:len(fs)-1]
+ }
+ fs += ">"
+ return fs
+}
+
+func TestInterfaces(t *testing.T) {
+ ift, err := Interfaces()
+ if err != nil {
+ t.Fatalf("Interfaces() failed: %v", err)
+ }
+ t.Logf("table: len/cap = %v/%v\n", len(ift), cap(ift))
+
+ for _, ifi := range ift {
+ ifxi, err := InterfaceByIndex(ifi.Index)
+ if err != nil {
+ t.Fatalf("InterfaceByIndex(%#q) failed: %v", ifi.Index, err)
+ }
+ if !sameInterface(ifxi, &ifi) {
+ t.Fatalf("InterfaceByIndex(%#q) = %v, want %v", ifi.Index, *ifxi, ifi)
+ }
+ ifxn, err := InterfaceByName(ifi.Name)
+ if err != nil {
+ t.Fatalf("InterfaceByName(%#q) failed: %v", ifi.Name, err)
+ }
+ if !sameInterface(ifxn, &ifi) {
+ t.Fatalf("InterfaceByName(%#q) = %v, want %v", ifi.Name, *ifxn, ifi)
+ }
+ ifat, err := ifi.Addrs()
+ if err != nil {
+ t.Fatalf("Interface.Addrs() failed: %v", err)
+ }
+ t.Logf("%s: flags %s, ifindex %v, mtu %v\n", ifi.Name, interfaceFlagsString(&ifi), ifi.Index, ifi.MTU)
+ for _, ifa := range ifat {
+ t.Logf("\tinterface address %s\n", ifa.String())
+ }
+ t.Logf("\thardware address %v", ifi.HardwareAddr.String())
+ }
+}
+
+func TestInterfaceAddrs(t *testing.T) {
+ ifat, err := InterfaceAddrs()
+ if err != nil {
+ t.Fatalf("InterfaceAddrs() failed: %v", err)
+ }
+ t.Logf("table: len/cap = %v/%v\n", len(ifat), cap(ifat))
+
+ for _, ifa := range ifat {
+ t.Logf("interface address %s\n", ifa.String())
+ }
+}
diff --git a/src/pkg/net/ip.go b/src/pkg/net/ip.go
index 61b2c687e..b0e2c4205 100644
--- a/src/pkg/net/ip.go
+++ b/src/pkg/net/ip.go
@@ -75,10 +75,71 @@ var (
// Well-known IPv6 addresses
var (
- IPzero = make(IP, IPv6len) // all zeros
- IPv6loopback = IP([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1})
+ IPv6zero = IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+ IPv6unspecified = IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+ IPv6loopback = IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
+ IPv6interfacelocalallnodes = IP{0xff, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}
+ IPv6linklocalallnodes = IP{0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}
+ IPv6linklocalallrouters = IP{0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02}
)
+// IsUnspecified returns true if ip is an unspecified address.
+func (ip IP) IsUnspecified() bool {
+ if ip.Equal(IPv4zero) || ip.Equal(IPv6unspecified) {
+ return true
+ }
+ return false
+}
+
+// IsLoopback returns true if ip is a loopback address.
+func (ip IP) IsLoopback() bool {
+ if ip4 := ip.To4(); ip4 != nil && ip4[0] == 127 {
+ return true
+ }
+ return ip.Equal(IPv6loopback)
+}
+
+// IsMulticast returns true if ip is a multicast address.
+func (ip IP) IsMulticast() bool {
+ if ip4 := ip.To4(); ip4 != nil && ip4[0]&0xf0 == 0xe0 {
+ return true
+ }
+ return ip[0] == 0xff
+}
+
+// IsInterfaceLinkLocalMulticast returns true if ip is
+// an interface-local multicast address.
+func (ip IP) IsInterfaceLocalMulticast() bool {
+ return len(ip) == IPv6len && ip[0] == 0xff && ip[1]&0x0f == 0x01
+}
+
+// IsLinkLocalMulticast returns true if ip is a link-local
+// multicast address.
+func (ip IP) IsLinkLocalMulticast() bool {
+ if ip4 := ip.To4(); ip4 != nil && ip4[0] == 224 && ip4[1] == 0 && ip4[2] == 0 {
+ return true
+ }
+ return ip[0] == 0xff && ip[1]&0x0f == 0x02
+}
+
+// IsLinkLocalUnicast returns true if ip is a link-local
+// unicast address.
+func (ip IP) IsLinkLocalUnicast() bool {
+ if ip4 := ip.To4(); ip4 != nil && ip4[0] == 169 && ip4[1] == 254 {
+ return true
+ }
+ return ip[0] == 0xfe && ip[1]&0xc0 == 0x80
+}
+
+// IsGlobalUnicast returns true if ip is a global unicast
+// address.
+func (ip IP) IsGlobalUnicast() bool {
+ return !ip.IsUnspecified() &&
+ !ip.IsLoopback() &&
+ !ip.IsMulticast() &&
+ !ip.IsLinkLocalUnicast()
+}
+
// Is p all zeros?
func isZeros(p IP) bool {
for i := 0; i < len(p); i++ {
diff --git a/src/pkg/net/ip_test.go b/src/pkg/net/ip_test.go
index 2008953ef..b189b10c4 100644
--- a/src/pkg/net/ip_test.go
+++ b/src/pkg/net/ip_test.go
@@ -9,6 +9,7 @@ import (
"reflect"
"testing"
"os"
+ "runtime"
)
func isEqual(a, b []byte) bool {
@@ -31,11 +32,7 @@ var parseiptests = []struct {
{"abc", nil},
{"123:", nil},
{"::ffff:127.0.0.1", IPv4(127, 0, 0, 1)},
- {"2001:4860:0:2001::68",
- IP{0x20, 0x01, 0x48, 0x60, 0, 0, 0x20, 0x01,
- 0, 0, 0, 0, 0, 0, 0x00, 0x68,
- },
- },
+ {"2001:4860:0:2001::68", IP{0x20, 0x01, 0x48, 0x60, 0, 0, 0x20, 0x01, 0, 0, 0, 0, 0, 0, 0x00, 0x68}},
{"::ffff:4a7d:1363", IPv4(74, 125, 19, 99)},
}
@@ -52,29 +49,21 @@ var ipstringtests = []struct {
out string
}{
// cf. RFC 5952 (A Recommendation for IPv6 Address Text Representation)
- {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0,
- 0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1},
+ {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1},
"2001:db8::123:12:1"},
- {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0x1},
+ {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1},
"2001:db8::1"},
- {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0x1,
- 0, 0, 0, 0x1, 0, 0, 0, 0x1},
+ {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1},
"2001:db8:0:1:0:1:0:1"},
- {IP{0x20, 0x1, 0xd, 0xb8, 0, 0x1, 0, 0,
- 0, 0x1, 0, 0, 0, 0x1, 0, 0},
+ {IP{0x20, 0x1, 0xd, 0xb8, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0},
"2001:db8:1:0:1:0:1:0"},
- {IP{0x20, 0x1, 0, 0, 0, 0, 0, 0,
- 0, 0x1, 0, 0, 0, 0, 0, 0x1},
+ {IP{0x20, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1},
"2001::1:0:0:1"},
- {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0,
- 0, 0x1, 0, 0, 0, 0, 0, 0},
+ {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0},
"2001:db8:0:0:1::"},
- {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0,
- 0, 0x1, 0, 0, 0, 0, 0, 0x1},
+ {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1},
"2001:db8::1:0:0:1"},
- {IP{0x20, 0x1, 0xD, 0xB8, 0, 0, 0, 0,
- 0, 0xA, 0, 0xB, 0, 0xC, 0, 0xD},
+ {IP{0x20, 0x1, 0xD, 0xB8, 0, 0, 0, 0, 0, 0xA, 0, 0xB, 0, 0xC, 0, 0xD},
"2001:db8::a:b:c:d"},
}
@@ -143,3 +132,84 @@ func TestJoinHostPort(t *testing.T) {
}
}
}
+
+var ipaftests = []struct {
+ in IP
+ af4 bool
+ af6 bool
+}{
+ {IPv4bcast, true, false},
+ {IPv4allsys, true, false},
+ {IPv4allrouter, true, false},
+ {IPv4zero, true, false},
+ {IPv4(224, 0, 0, 1), true, false},
+ {IPv4(127, 0, 0, 1), true, false},
+ {IPv4(240, 0, 0, 1), true, false},
+ {IPv6unspecified, false, true},
+ {IPv6loopback, false, true},
+ {IPv6interfacelocalallnodes, false, true},
+ {IPv6linklocalallnodes, false, true},
+ {IPv6linklocalallrouters, false, true},
+ {ParseIP("ff05::a:b:c:d"), false, true},
+ {ParseIP("fe80::1:2:3:4"), false, true},
+ {ParseIP("2001:db8::123:12:1"), false, true},
+}
+
+func TestIPAddrFamily(t *testing.T) {
+ for _, tt := range ipaftests {
+ if af := tt.in.To4() != nil; af != tt.af4 {
+ t.Errorf("verifying IPv4 address family for %#q = %v, want %v", tt.in, af, tt.af4)
+ }
+ if af := len(tt.in) == IPv6len && tt.in.To4() == nil; af != tt.af6 {
+ t.Errorf("verifying IPv6 address family for %#q = %v, want %v", tt.in, af, tt.af6)
+ }
+ }
+}
+
+var ipscopetests = []struct {
+ scope func(IP) bool
+ in IP
+ ok bool
+}{
+ {IP.IsUnspecified, IPv4zero, true},
+ {IP.IsUnspecified, IPv4(127, 0, 0, 1), false},
+ {IP.IsUnspecified, IPv6unspecified, true},
+ {IP.IsUnspecified, IPv6interfacelocalallnodes, false},
+ {IP.IsLoopback, IPv4(127, 0, 0, 1), true},
+ {IP.IsLoopback, IPv4(127, 255, 255, 254), true},
+ {IP.IsLoopback, IPv4(128, 1, 2, 3), false},
+ {IP.IsLoopback, IPv6loopback, true},
+ {IP.IsLoopback, IPv6linklocalallrouters, false},
+ {IP.IsMulticast, IPv4(224, 0, 0, 0), true},
+ {IP.IsMulticast, IPv4(239, 0, 0, 0), true},
+ {IP.IsMulticast, IPv4(240, 0, 0, 0), false},
+ {IP.IsMulticast, IPv6linklocalallnodes, true},
+ {IP.IsMulticast, IP{0xff, 0x05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, true},
+ {IP.IsMulticast, IP{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false},
+ {IP.IsLinkLocalMulticast, IPv4(224, 0, 0, 0), true},
+ {IP.IsLinkLocalMulticast, IPv4(239, 0, 0, 0), false},
+ {IP.IsLinkLocalMulticast, IPv6linklocalallrouters, true},
+ {IP.IsLinkLocalMulticast, IP{0xff, 0x05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false},
+ {IP.IsLinkLocalUnicast, IPv4(169, 254, 0, 0), true},
+ {IP.IsLinkLocalUnicast, IPv4(169, 255, 0, 0), false},
+ {IP.IsLinkLocalUnicast, IP{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, true},
+ {IP.IsLinkLocalUnicast, IP{0xfe, 0xc0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false},
+ {IP.IsGlobalUnicast, IPv4(240, 0, 0, 0), true},
+ {IP.IsGlobalUnicast, IPv4(232, 0, 0, 0), false},
+ {IP.IsGlobalUnicast, IPv4(169, 254, 0, 0), false},
+ {IP.IsGlobalUnicast, IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1}, true},
+ {IP.IsGlobalUnicast, IP{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false},
+ {IP.IsGlobalUnicast, IP{0xff, 0x05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false},
+}
+
+func name(f interface{}) string {
+ return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
+}
+
+func TestIPAddrScope(t *testing.T) {
+ for _, tt := range ipscopetests {
+ if ok := tt.scope(tt.in); ok != tt.ok {
+ t.Errorf("%s(%#q) = %v, want %v", name(tt.scope), tt.in, ok, tt.ok)
+ }
+ }
+}
diff --git a/src/pkg/net/ipraw_test.go b/src/pkg/net/ipraw_test.go
index ee8c71fc1..7cc9604b5 100644
--- a/src/pkg/net/ipraw_test.go
+++ b/src/pkg/net/ipraw_test.go
@@ -60,7 +60,8 @@ func parsePingReply(p []byte) (id, seq int) {
}
var srchost = flag.String("srchost", "", "Source of the ICMP ECHO request")
-var dsthost = flag.String("dsthost", "localhost", "Destination for the ICMP ECHO request")
+// 127.0.0.1 because this is an IPv4-specific test.
+var dsthost = flag.String("dsthost", "127.0.0.1", "Destination for the ICMP ECHO request")
// test (raw) IP socket using ICMP
func TestICMP(t *testing.T) {
@@ -74,15 +75,15 @@ func TestICMP(t *testing.T) {
err os.Error
)
if *srchost != "" {
- laddr, err = ResolveIPAddr(*srchost)
+ laddr, err = ResolveIPAddr("ip4", *srchost)
if err != nil {
- t.Fatalf(`net.ResolveIPAddr("%v") = %v, %v`, *srchost, laddr, err)
+ t.Fatalf(`net.ResolveIPAddr("ip4", %v") = %v, %v`, *srchost, laddr, err)
}
}
- raddr, err := ResolveIPAddr(*dsthost)
+ raddr, err := ResolveIPAddr("ip4", *dsthost)
if err != nil {
- t.Fatalf(`net.ResolveIPAddr("%v") = %v, %v`, *dsthost, raddr, err)
+ t.Fatalf(`net.ResolveIPAddr("ip4", %v") = %v, %v`, *dsthost, raddr, err)
}
c, err := ListenIP("ip4:icmp", laddr)
diff --git a/src/pkg/net/iprawsock.go b/src/pkg/net/iprawsock.go
index 5be6fe4e0..a811027b1 100644
--- a/src/pkg/net/iprawsock.go
+++ b/src/pkg/net/iprawsock.go
@@ -43,7 +43,7 @@ func (a *IPAddr) family() int {
if a == nil || len(a.IP) <= 4 {
return syscall.AF_INET
}
- if ip := a.IP.To4(); ip != nil {
+ if a.IP.To4() != nil {
return syscall.AF_INET
}
return syscall.AF_INET6
@@ -61,10 +61,11 @@ func (a *IPAddr) toAddr() sockaddr {
}
// ResolveIPAddr parses addr as a IP address and resolves domain
-// names to numeric addresses. A literal IPv6 host address must be
+// names to numeric addresses on the network net, which must be
+// "ip", "ip4" or "ip6". A literal IPv6 host address must be
// enclosed in square brackets, as in "[::]".
-func ResolveIPAddr(addr string) (*IPAddr, os.Error) {
- ip, err := hostToIP(addr)
+func ResolveIPAddr(net, addr string) (*IPAddr, os.Error) {
+ ip, err := hostToIP(net, addr)
if err != nil {
return nil, err
}
@@ -234,32 +235,36 @@ func (c *IPConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) {
}
// Convert "host" into IP address.
-func hostToIP(host string) (ip IP, err os.Error) {
+func hostToIP(net, host string) (ip IP, err os.Error) {
var addr IP
// Try as an IP address.
addr = ParseIP(host)
if addr == nil {
+ filter := anyaddr
+ if net != "" && net[len(net)-1] == '4' {
+ filter = ipv4only
+ }
+ if net != "" && net[len(net)-1] == '6' {
+ filter = ipv6only
+ }
// Not an IP address. Try as a DNS name.
addrs, err1 := LookupHost(host)
if err1 != nil {
err = err1
goto Error
}
- addr = firstSupportedAddr(anyaddr, addrs)
+ addr = firstFavoriteAddr(filter, addrs)
if addr == nil {
// should not happen
- err = &AddrError{"LookupHost returned invalid address", addrs[0]}
+ err = &AddrError{"LookupHost returned no suitable address", addrs[0]}
goto Error
}
}
-
return addr, nil
-
Error:
return nil, err
}
-
var protocols map[string]int
func readProtocols() {
@@ -285,7 +290,7 @@ func readProtocols() {
}
}
-func netProtoSplit(netProto string) (net string, proto int, err os.Error) {
+func splitNetProto(netProto string) (net string, proto int, err os.Error) {
onceReadProtocols.Do(readProtocols)
i := last(netProto, ':')
if i < 0 { // no colon
@@ -307,7 +312,7 @@ func netProtoSplit(netProto string) (net string, proto int, err os.Error) {
// DialIP connects to the remote address raddr on the network net,
// which must be "ip", "ip4", or "ip6".
func DialIP(netProto string, laddr, raddr *IPAddr) (c *IPConn, err os.Error) {
- net, proto, err := netProtoSplit(netProto)
+ net, proto, err := splitNetProto(netProto)
if err != nil {
return
}
@@ -331,7 +336,7 @@ func DialIP(netProto string, laddr, raddr *IPAddr) (c *IPConn, err os.Error) {
// and WriteTo methods can be used to receive and send IP
// packets with per-packet addressing.
func ListenIP(netProto string, laddr *IPAddr) (c *IPConn, err os.Error) {
- net, proto, err := netProtoSplit(netProto)
+ net, proto, err := splitNetProto(netProto)
if err != nil {
return
}
diff --git a/src/pkg/net/ipsock.go b/src/pkg/net/ipsock.go
index e8bcac646..0b8c388f1 100644
--- a/src/pkg/net/ipsock.go
+++ b/src/pkg/net/ipsock.go
@@ -15,25 +15,105 @@ import (
// only dealing with IPv4 sockets? As long as the host system
// understands IPv6, it's okay to pass IPv4 addresses to the IPv6
// interface. That simplifies our code and is most general.
-// Unfortunately, we need to run on kernels built without IPv6 support too.
-// So probe the kernel to figure it out.
-func kernelSupportsIPv6() bool {
- s, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
- if err != 0 {
- return false
+// Unfortunately, we need to run on kernels built without IPv6
+// support too. So probe the kernel to figure it out.
+//
+// probeIPv6Stack probes both basic IPv6 capability and IPv6 IPv4-
+// mapping capability which is controlled by IPV6_V6ONLY socket
+// option and/or kernel state "net.inet6.ip6.v6only".
+// It returns two boolean values. If the first boolean value is
+// true, kernel supports basic IPv6 functionality. If the second
+// boolean value is true, kernel supports IPv6 IPv4-mapping.
+func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
+ var probes = []struct {
+ s int
+ la TCPAddr
+ ok bool
+ }{
+ // IPv6 communication capability
+ {-1, TCPAddr{IP: ParseIP("::1")}, false},
+ // IPv6 IPv4-mapped address communication capability
+ {-1, TCPAddr{IP: IPv4(127, 0, 0, 1)}, false},
}
- defer closesocket(s)
+ var errno int
- la := &TCPAddr{IP: IPv4(127, 0, 0, 1)}
- sa, oserr := la.toAddr().sockaddr(syscall.AF_INET6)
- if oserr != nil {
- return false
+ for i := range probes {
+ probes[i].s, errno = syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
+ if errno != 0 {
+ continue
+ }
+ defer closesocket(probes[i].s)
+ sa, err := probes[i].la.toAddr().sockaddr(syscall.AF_INET6)
+ if err != nil {
+ continue
+ }
+ errno = syscall.Bind(probes[i].s, sa)
+ if errno != 0 {
+ continue
+ }
+ probes[i].ok = true
}
- return syscall.Bind(s, sa) == 0
+ return probes[0].ok, probes[1].ok
}
-var preferIPv4 = !kernelSupportsIPv6()
+var supportsIPv6, supportsIPv4map = probeIPv6Stack()
+
+// favoriteAddrFamily returns the appropriate address family to
+// the given net, raddr, laddr and mode. At first it figures
+// address family out from the net. If mode indicates "listen"
+// and laddr.(type).IP is nil, it assumes that the user wants to
+// make a passive connection with wildcard address family, both
+// INET and INET6, and wildcard address. Otherwise guess: if the
+// addresses are IPv4 then returns INET, or else returns INET6.
+func favoriteAddrFamily(net string, raddr, laddr sockaddr, mode string) int {
+ switch net[len(net)-1] {
+ case '4':
+ return syscall.AF_INET
+ case '6':
+ return syscall.AF_INET6
+ }
+
+ if mode == "listen" {
+ switch a := laddr.(type) {
+ case *TCPAddr:
+ if a.IP == nil && supportsIPv6 {
+ return syscall.AF_INET6
+ }
+ case *UDPAddr:
+ if a.IP == nil && supportsIPv6 {
+ return syscall.AF_INET6
+ }
+ case *IPAddr:
+ if a.IP == nil && supportsIPv6 {
+ return syscall.AF_INET6
+ }
+ }
+ }
+
+ if (laddr == nil || laddr.family() == syscall.AF_INET) &&
+ (raddr == nil || raddr.family() == syscall.AF_INET) {
+ return syscall.AF_INET
+ }
+ return syscall.AF_INET6
+}
+
+func firstFavoriteAddr(filter func(IP) IP, addrs []string) (addr IP) {
+ if filter == anyaddr {
+ // We'll take any IP address, but since the dialing code
+ // does not yet try multiple addresses, prefer to use
+ // an IPv4 address if possible. This is especially relevant
+ // if localhost resolves to [ipv6-localhost, ipv4-localhost].
+ // Too much code assumes localhost == ipv4-localhost.
+ addr = firstSupportedAddr(ipv4only, addrs)
+ if addr == nil {
+ addr = firstSupportedAddr(anyaddr, addrs)
+ }
+ } else {
+ addr = firstSupportedAddr(filter, addrs)
+ }
+ return
+}
func firstSupportedAddr(filter func(IP) IP, addrs []string) IP {
for _, s := range addrs {
@@ -44,22 +124,28 @@ func firstSupportedAddr(filter func(IP) IP, addrs []string) IP {
return nil
}
-func anyaddr(x IP) IP { return x }
+func anyaddr(x IP) IP {
+ if x4 := x.To4(); x4 != nil {
+ return x4
+ }
+ if supportsIPv6 {
+ return x
+ }
+ return nil
+}
+
func ipv4only(x IP) IP { return x.To4() }
func ipv6only(x IP) IP {
// Only return addresses that we can use
// with the kernel's IPv6 addressing modes.
- // If preferIPv4 is set, it means the IPv6 stack
- // cannot take IPv4 addresses directly (we prefer
- // to use the IPv4 stack) so reject IPv4 addresses.
- if x.To4() != nil && preferIPv4 {
- return nil
+ if len(x) == IPv6len && x.To4() == nil && supportsIPv6 {
+ return x
}
- return x
+ return nil
}
-// TODO(rsc): if syscall.OS == "linux", we're supposd to read
+// TODO(rsc): if syscall.OS == "linux", we're supposed to read
// /proc/sys/net/core/somaxconn,
// to take advantage of kernels that have raised the limit.
func listenBacklog() int { return syscall.SOMAXCONN }
@@ -75,26 +161,9 @@ type sockaddr interface {
}
func internetSocket(net string, laddr, raddr sockaddr, socktype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err os.Error) {
- // Figure out IP version.
- // If network has a suffix like "tcp4", obey it.
var oserr os.Error
- family := syscall.AF_INET6
- switch net[len(net)-1] {
- case '4':
- family = syscall.AF_INET
- case '6':
- // nothing to do
- default:
- // Otherwise, guess.
- // If the addresses are IPv4 and we prefer IPv4, use 4; else 6.
- if preferIPv4 &&
- (laddr == nil || laddr.family() == syscall.AF_INET) &&
- (raddr == nil || raddr.family() == syscall.AF_INET) {
- family = syscall.AF_INET
- }
- }
-
var la, ra syscall.Sockaddr
+ family := favoriteAddrFamily(net, raddr, laddr, mode)
if laddr != nil {
if la, oserr = laddr.sockaddr(family); oserr != nil {
goto Error
@@ -119,25 +188,6 @@ Error:
return nil, &OpError{mode, net, addr, oserr}
}
-func getip(fd int, remote bool) (ip []byte, port int, ok bool) {
- // No attempt at error reporting because
- // there are no possible errors, and the
- // caller won't report them anyway.
- var sa syscall.Sockaddr
- if remote {
- sa, _ = syscall.Getpeername(fd)
- } else {
- sa, _ = syscall.Getsockname(fd)
- }
- switch sa := sa.(type) {
- case *syscall.SockaddrInet4:
- return sa.Addr[0:], sa.Port, true
- case *syscall.SockaddrInet6:
- return sa.Addr[0:], sa.Port, true
- }
- return
-}
-
type InvalidAddrError string
func (e InvalidAddrError) String() string { return string(e) }
@@ -161,13 +211,13 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) {
return s, nil
case syscall.AF_INET6:
if len(ip) == 0 {
- ip = IPzero
+ ip = IPv6zero
}
// IPv4 callers use 0.0.0.0 to mean "announce on any available address".
// In IPv6 mode, Linux treats that as meaning "announce on 0.0.0.0",
// which it refuses to do. Rewrite to the IPv6 all zeros.
- if p4 := ip.To4(); p4 != nil && p4[0] == 0 && p4[1] == 0 && p4[2] == 0 && p4[3] == 0 {
- ip = IPzero
+ if ip.Equal(IPv4zero) {
+ ip = IPv6zero
}
if ip = ip.To16(); ip == nil {
return nil, InvalidAddrError("non-IPv6 address")
@@ -231,9 +281,10 @@ func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) {
addr = ParseIP(host)
if addr == nil {
filter := anyaddr
- if len(net) >= 4 && net[3] == '4' {
+ if net != "" && net[len(net)-1] == '4' {
filter = ipv4only
- } else if len(net) >= 4 && net[3] == '6' {
+ }
+ if net != "" && net[len(net)-1] == '6' {
filter = ipv6only
}
// Not an IP address. Try as a DNS name.
@@ -242,22 +293,10 @@ func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) {
err = err1
goto Error
}
- if filter == anyaddr {
- // We'll take any IP address, but since the dialing code
- // does not yet try multiple addresses, prefer to use
- // an IPv4 address if possible. This is especially relevant
- // if localhost resolves to [ipv6-localhost, ipv4-localhost].
- // Too much code assumes localhost == ipv4-localhost.
- addr = firstSupportedAddr(ipv4only, addrs)
- if addr == nil {
- addr = firstSupportedAddr(anyaddr, addrs)
- }
- } else {
- addr = firstSupportedAddr(filter, addrs)
- }
+ addr = firstFavoriteAddr(filter, addrs)
if addr == nil {
// should not happen
- err = &AddrError{"LookupHost returned invalid address", addrs[0]}
+ err = &AddrError{"LookupHost returned no suitable address", addrs[0]}
goto Error
}
}
diff --git a/src/pkg/net/resolv_windows.go b/src/pkg/net/resolv_windows.go
index 3506ea177..f7c3f51be 100644
--- a/src/pkg/net/resolv_windows.go
+++ b/src/pkg/net/resolv_windows.go
@@ -113,6 +113,10 @@ func reverseaddr(addr string) (arpa string, err os.Error) {
panic("unimplemented")
}
+func answer(name, server string, dns *dnsMsg, qtype uint16) (cname string, addrs []dnsRR, err os.Error) {
+ panic("unimplemented")
+}
+
// DNSError represents a DNS lookup error.
type DNSError struct {
Error string // description of the error
diff --git a/src/pkg/net/sendfile_linux.go b/src/pkg/net/sendfile_linux.go
new file mode 100644
index 000000000..6a5a06c8c
--- /dev/null
+++ b/src/pkg/net/sendfile_linux.go
@@ -0,0 +1,84 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+import (
+ "io"
+ "os"
+ "syscall"
+)
+
+// maxSendfileSize is the largest chunk size we ask the kernel to copy
+// at a time.
+const maxSendfileSize int = 4 << 20
+
+// sendFile copies the contents of r to c using the sendfile
+// system call to minimize copies.
+//
+// if handled == true, sendFile returns the number of bytes copied and any
+// non-EOF error.
+//
+// if handled == false, sendFile performed no work.
+func sendFile(c *netFD, r io.Reader) (written int64, err os.Error, handled bool) {
+ var remain int64 = 1 << 62 // by default, copy until EOF
+
+ lr, ok := r.(*io.LimitedReader)
+ if ok {
+ remain, r = lr.N, lr.R
+ if remain <= 0 {
+ return 0, nil, true
+ }
+ }
+ f, ok := r.(*os.File)
+ if !ok {
+ return 0, nil, false
+ }
+
+ c.wio.Lock()
+ defer c.wio.Unlock()
+ c.incref()
+ defer c.decref()
+ if c.wdeadline_delta > 0 {
+ // This is a little odd that we're setting the timeout
+ // for the entire file but Write has the same issue
+ // (if one slurps the whole file into memory and
+ // do one large Write). At least they're consistent.
+ c.wdeadline = pollserver.Now() + c.wdeadline_delta
+ } else {
+ c.wdeadline = 0
+ }
+
+ dst := c.sysfd
+ src := f.Fd()
+ for remain > 0 {
+ n := maxSendfileSize
+ if int64(n) > remain {
+ n = int(remain)
+ }
+ n, errno := syscall.Sendfile(dst, src, nil, n)
+ if n > 0 {
+ written += int64(n)
+ remain -= int64(n)
+ }
+ if n == 0 && errno == 0 {
+ break
+ }
+ if errno == syscall.EAGAIN && c.wdeadline >= 0 {
+ pollserver.WaitWrite(c)
+ continue
+ }
+ if errno != 0 {
+ // This includes syscall.ENOSYS (no kernel
+ // support) and syscall.EINVAL (fd types which
+ // don't implement sendfile together)
+ err = &OpError{"sendfile", c.net, c.raddr, os.Errno(errno)}
+ break
+ }
+ }
+ if lr != nil {
+ lr.N = remain
+ }
+ return written, err, written > 0
+}
diff --git a/src/pkg/net/sendfile_stub.go b/src/pkg/net/sendfile_stub.go
new file mode 100644
index 000000000..43e8104e9
--- /dev/null
+++ b/src/pkg/net/sendfile_stub.go
@@ -0,0 +1,14 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+import (
+ "io"
+ "os"
+)
+
+func sendFile(c *netFD, r io.Reader) (n int64, err os.Error, handled bool) {
+ return 0, nil, false
+}
diff --git a/src/pkg/net/server_test.go b/src/pkg/net/server_test.go
index 075748b83..36780d789 100644
--- a/src/pkg/net/server_test.go
+++ b/src/pkg/net/server_test.go
@@ -92,15 +92,22 @@ func connect(t *testing.T, network, addr string, isEmpty bool) {
}
func doTest(t *testing.T, network, listenaddr, dialaddr string) {
- t.Logf("Test %s %s %s\n", network, listenaddr, dialaddr)
+ t.Logf("Test %q %q %q\n", network, listenaddr, dialaddr)
+ switch listenaddr {
+ case "", "0.0.0.0", "[::]", "[::ffff:0.0.0.0]":
+ if testing.Short() || avoidMacFirewall {
+ t.Logf("skip wildcard listen during short test")
+ return
+ }
+ }
listening := make(chan string)
done := make(chan int)
- if network == "tcp" {
+ if network == "tcp" || network == "tcp4" || network == "tcp6" {
listenaddr += ":0" // any available port
}
go runServe(t, network, listenaddr, listening, done)
addr := <-listening // wait for server to start
- if network == "tcp" {
+ if network == "tcp" || network == "tcp4" || network == "tcp6" {
dialaddr += addr[strings.LastIndex(addr, ":"):]
}
connect(t, network, dialaddr, false)
@@ -108,10 +115,33 @@ func doTest(t *testing.T, network, listenaddr, dialaddr string) {
}
func TestTCPServer(t *testing.T) {
+ doTest(t, "tcp", "", "127.0.0.1")
+ doTest(t, "tcp", "0.0.0.0", "127.0.0.1")
doTest(t, "tcp", "127.0.0.1", "127.0.0.1")
- if kernelSupportsIPv6() {
+ doTest(t, "tcp4", "", "127.0.0.1")
+ doTest(t, "tcp4", "0.0.0.0", "127.0.0.1")
+ doTest(t, "tcp4", "127.0.0.1", "127.0.0.1")
+ if supportsIPv6 {
+ doTest(t, "tcp", "", "[::1]")
+ doTest(t, "tcp", "[::]", "[::1]")
doTest(t, "tcp", "[::1]", "[::1]")
+ doTest(t, "tcp6", "", "[::1]")
+ doTest(t, "tcp6", "[::]", "[::1]")
+ doTest(t, "tcp6", "[::1]", "[::1]")
+ }
+ if supportsIPv6 && supportsIPv4map {
+ doTest(t, "tcp", "[::ffff:0.0.0.0]", "127.0.0.1")
+ doTest(t, "tcp", "[::]", "127.0.0.1")
+ doTest(t, "tcp4", "[::ffff:0.0.0.0]", "127.0.0.1")
+ doTest(t, "tcp6", "", "127.0.0.1")
+ doTest(t, "tcp6", "[::ffff:0.0.0.0]", "127.0.0.1")
+ doTest(t, "tcp6", "[::]", "127.0.0.1")
doTest(t, "tcp", "127.0.0.1", "[::ffff:127.0.0.1]")
+ doTest(t, "tcp", "[::ffff:127.0.0.1]", "127.0.0.1")
+ doTest(t, "tcp4", "127.0.0.1", "[::ffff:127.0.0.1]")
+ doTest(t, "tcp4", "[::ffff:127.0.0.1]", "127.0.0.1")
+ doTest(t, "tcp6", "127.0.0.1", "[::ffff:127.0.0.1]")
+ doTest(t, "tcp6", "[::ffff:127.0.0.1]", "127.0.0.1")
}
}
@@ -186,7 +216,7 @@ func TestUDPServer(t *testing.T) {
for _, isEmpty := range []bool{false, true} {
doTestPacket(t, "udp", "0.0.0.0", "127.0.0.1", isEmpty)
doTestPacket(t, "udp", "", "127.0.0.1", isEmpty)
- if kernelSupportsIPv6() {
+ if supportsIPv6 && supportsIPv4map {
doTestPacket(t, "udp", "[::]", "[::ffff:127.0.0.1]", isEmpty)
doTestPacket(t, "udp", "[::]", "127.0.0.1", isEmpty)
doTestPacket(t, "udp", "0.0.0.0", "[::ffff:127.0.0.1]", isEmpty)
diff --git a/src/pkg/net/sock.go b/src/pkg/net/sock.go
index bd88f7ece..eae7f3711 100644
--- a/src/pkg/net/sock.go
+++ b/src/pkg/net/sock.go
@@ -7,6 +7,7 @@
package net
import (
+ "io"
"os"
"reflect"
"syscall"
@@ -32,17 +33,7 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal
syscall.CloseOnExec(s)
syscall.ForkLock.RUnlock()
- // Allow reuse of recently-used addresses.
- syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
-
- // Allow broadcast.
- syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)
-
- if f == syscall.AF_INET6 {
- // using ip, tcp, udp, etc.
- // allow both protocols even if the OS default is otherwise.
- syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
- }
+ setKernelSpecificSockopt(s, f)
if la != nil {
e = syscall.Bind(s, la)
@@ -164,15 +155,13 @@ func (e *UnknownSocketError) String() string {
return "unknown socket address type " + reflect.TypeOf(e.sa).String()
}
-func sockaddrToString(sa syscall.Sockaddr) (name string, err os.Error) {
- switch a := sa.(type) {
- case *syscall.SockaddrInet4:
- return JoinHostPort(IP(a.Addr[0:]).String(), itoa(a.Port)), nil
- case *syscall.SockaddrInet6:
- return JoinHostPort(IP(a.Addr[0:]).String(), itoa(a.Port)), nil
- case *syscall.SockaddrUnix:
- return a.Name, nil
- }
+type writerOnly struct {
+ io.Writer
+}
- return "", &UnknownSocketError{sa}
+// Fallback implementation of io.ReaderFrom's ReadFrom, when sendfile isn't
+// applicable.
+func genericReadFrom(w io.Writer, r io.Reader) (n int64, err os.Error) {
+ // Use wrapper to hide existing r.ReadFrom from io.Copy.
+ return io.Copy(writerOnly{w}, r)
}
diff --git a/src/pkg/net/sock_bsd.go b/src/pkg/net/sock_bsd.go
new file mode 100644
index 000000000..5fd52074a
--- /dev/null
+++ b/src/pkg/net/sock_bsd.go
@@ -0,0 +1,31 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Sockets for BSD variants
+
+package net
+
+import (
+ "syscall"
+)
+
+func setKernelSpecificSockopt(s, f int) {
+ // Allow reuse of recently-used addresses.
+ syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
+
+ // Allow reuse of recently-used ports.
+ // This option is supported only in descendants of 4.4BSD,
+ // to make an effective multicast application and an application
+ // that requires quick draw possible.
+ syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1)
+
+ // Allow broadcast.
+ syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)
+
+ if f == syscall.AF_INET6 {
+ // using ip, tcp, udp, etc.
+ // allow both protocols even if the OS default is otherwise.
+ syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
+ }
+}
diff --git a/src/pkg/net/sock_linux.go b/src/pkg/net/sock_linux.go
new file mode 100644
index 000000000..ec31e803b
--- /dev/null
+++ b/src/pkg/net/sock_linux.go
@@ -0,0 +1,25 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Sockets for Linux
+
+package net
+
+import (
+ "syscall"
+)
+
+func setKernelSpecificSockopt(s, f int) {
+ // Allow reuse of recently-used addresses.
+ syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
+
+ // Allow broadcast.
+ syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)
+
+ if f == syscall.AF_INET6 {
+ // using ip, tcp, udp, etc.
+ // allow both protocols even if the OS default is otherwise.
+ syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
+ }
+}
diff --git a/src/pkg/net/sock_windows.go b/src/pkg/net/sock_windows.go
new file mode 100644
index 000000000..e17c60b98
--- /dev/null
+++ b/src/pkg/net/sock_windows.go
@@ -0,0 +1,25 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Sockets for Windows
+
+package net
+
+import (
+ "syscall"
+)
+
+func setKernelSpecificSockopt(s, f int) {
+ // Allow reuse of recently-used addresses and ports.
+ syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
+
+ // Allow broadcast.
+ syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)
+
+ if f == syscall.AF_INET6 {
+ // using ip, tcp, udp, etc.
+ // allow both protocols even if the OS default is otherwise.
+ syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
+ }
+}
diff --git a/src/pkg/net/tcpsock.go b/src/pkg/net/tcpsock.go
index d9aa7cf19..9ee6c14f7 100644
--- a/src/pkg/net/tcpsock.go
+++ b/src/pkg/net/tcpsock.go
@@ -7,6 +7,7 @@
package net
import (
+ "io"
"os"
"syscall"
)
@@ -41,7 +42,7 @@ func (a *TCPAddr) family() int {
if a == nil || len(a.IP) <= 4 {
return syscall.AF_INET
}
- if ip := a.IP.To4(); ip != nil {
+ if a.IP.To4() != nil {
return syscall.AF_INET
}
return syscall.AF_INET6
@@ -60,10 +61,11 @@ func (a *TCPAddr) toAddr() sockaddr {
// ResolveTCPAddr parses addr as a TCP address of the form
// host:port and resolves domain names or port names to
-// numeric addresses. A literal IPv6 host address must be
+// numeric addresses on the network net, which must be "tcp",
+// "tcp4" or "tcp6". A literal IPv6 host address must be
// enclosed in square brackets, as in "[::]:80".
-func ResolveTCPAddr(network, addr string) (*TCPAddr, os.Error) {
- ip, port, err := hostPortToIP(network, addr)
+func ResolveTCPAddr(net, addr string) (*TCPAddr, os.Error) {
+ ip, port, err := hostPortToIP(net, addr)
if err != nil {
return nil, err
}
@@ -94,6 +96,14 @@ func (c *TCPConn) Read(b []byte) (n int, err os.Error) {
return c.fd.Read(b)
}
+// ReadFrom implements the io.ReaderFrom ReadFrom method.
+func (c *TCPConn) ReadFrom(r io.Reader) (int64, os.Error) {
+ if n, err, handled := sendFile(c.fd, r); handled {
+ return n, err
+ }
+ return genericReadFrom(c, r)
+}
+
// Write implements the net.Conn Write method.
func (c *TCPConn) Write(b []byte) (n int, err os.Error) {
if !c.ok() {
diff --git a/src/pkg/net/textproto/reader.go b/src/pkg/net/textproto/reader.go
index ac1278689..e65374903 100644
--- a/src/pkg/net/textproto/reader.go
+++ b/src/pkg/net/textproto/reader.go
@@ -237,7 +237,7 @@ func (r *Reader) ReadResponse(expectCode int) (code int, message string, err os.
// to a method on r.
//
// Dot encoding is a common framing used for data blocks
-// in text protcols like SMTP. The data consists of a sequence
+// in text protocols such as SMTP. The data consists of a sequence
// of lines, each of which ends in "\r\n". The sequence itself
// ends at a line containing just a dot: ".\r\n". Lines beginning
// with a dot are escaped with an additional dot to avoid
diff --git a/src/pkg/net/udpsock.go b/src/pkg/net/udpsock.go
index 67684471b..5469acffa 100644
--- a/src/pkg/net/udpsock.go
+++ b/src/pkg/net/udpsock.go
@@ -41,7 +41,7 @@ func (a *UDPAddr) family() int {
if a == nil || len(a.IP) <= 4 {
return syscall.AF_INET
}
- if ip := a.IP.To4(); ip != nil {
+ if a.IP.To4() != nil {
return syscall.AF_INET
}
return syscall.AF_INET6
@@ -60,10 +60,11 @@ func (a *UDPAddr) toAddr() sockaddr {
// ResolveUDPAddr parses addr as a UDP address of the form
// host:port and resolves domain names or port names to
-// numeric addresses. A literal IPv6 host address must be
+// numeric addresses on the network net, which must be "udp",
+// "udp4" or "udp6". A literal IPv6 host address must be
// enclosed in square brackets, as in "[::]:80".
-func ResolveUDPAddr(network, addr string) (*UDPAddr, os.Error) {
- ip, port, err := hostPortToIP(network, addr)
+func ResolveUDPAddr(net, addr string) (*UDPAddr, os.Error) {
+ ip, port, err := hostPortToIP(net, addr)
if err != nil {
return nil, err
}
@@ -292,10 +293,10 @@ func (c *UDPConn) JoinGroup(addr IP) os.Error {
if ip == nil {
return &OpError{"joingroup", "udp", &IPAddr{ip}, errInvalidMulticast}
}
- mreq := &syscall.IpMreq{
+ mreq := &syscall.IPMreq{
Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]},
}
- err := os.NewSyscallError("setsockopt", syscall.SetsockoptIpMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq))
+ err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq))
if err != nil {
return &OpError{"joingroup", "udp", &IPAddr{ip}, err}
}
@@ -311,10 +312,10 @@ func (c *UDPConn) LeaveGroup(addr IP) os.Error {
if ip == nil {
return &OpError{"leavegroup", "udp", &IPAddr{ip}, errInvalidMulticast}
}
- mreq := &syscall.IpMreq{
+ mreq := &syscall.IPMreq{
Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]},
}
- err := os.NewSyscallError("setsockopt", syscall.SetsockoptIpMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreq))
+ err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreq))
if err != nil {
return &OpError{"leavegroup", "udp", &IPAddr{ip}, err}
}