diff options
Diffstat (limited to 'src/pkg/net')
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} } |