aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/net
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/net')
-rw-r--r--libgo/go/net/cgo_bsd.go2
-rw-r--r--libgo/go/net/cgo_stub.go2
-rw-r--r--libgo/go/net/cgo_unix.go2
-rw-r--r--libgo/go/net/dial.go4
-rw-r--r--libgo/go/net/dnsclient_unix.go16
-rw-r--r--libgo/go/net/dnsconfig.go2
-rw-r--r--libgo/go/net/fd.go25
-rw-r--r--libgo/go/net/fd_linux.go2
-rw-r--r--libgo/go/net/fd_windows.go71
-rw-r--r--libgo/go/net/file.go8
-rw-r--r--libgo/go/net/file_test.go11
-rw-r--r--libgo/go/net/interface.go57
-rw-r--r--libgo/go/net/interface_bsd.go3
-rw-r--r--libgo/go/net/interface_linux.go33
-rw-r--r--libgo/go/net/interface_stub.go2
-rw-r--r--libgo/go/net/interface_test.go46
-rw-r--r--libgo/go/net/ip.go252
-rw-r--r--libgo/go/net/ip_test.go240
-rw-r--r--libgo/go/net/iprawsock_posix.go49
-rw-r--r--libgo/go/net/ipsock_posix.go2
-rw-r--r--libgo/go/net/lookup_plan9.go24
-rw-r--r--libgo/go/net/lookup_test.go27
-rw-r--r--libgo/go/net/lookup_unix.go84
-rw-r--r--libgo/go/net/lookup_windows.go38
-rw-r--r--libgo/go/net/multicast_test.go116
-rw-r--r--libgo/go/net/net_test.go50
-rw-r--r--libgo/go/net/newpollserver.go2
-rw-r--r--libgo/go/net/parse.go43
-rw-r--r--libgo/go/net/port.go2
-rw-r--r--libgo/go/net/sendfile_stub.go2
-rw-r--r--libgo/go/net/sendfile_windows.go2
-rw-r--r--libgo/go/net/server_test.go4
-rw-r--r--libgo/go/net/sock.go3
-rw-r--r--libgo/go/net/sock_bsd.go2
-rw-r--r--libgo/go/net/tcpsock_posix.go27
-rw-r--r--libgo/go/net/textproto/header.go2
-rw-r--r--libgo/go/net/textproto/pipeline.go2
-rw-r--r--libgo/go/net/textproto/reader.go33
-rw-r--r--libgo/go/net/textproto/reader_test.go53
-rw-r--r--libgo/go/net/udpsock_plan9.go11
-rw-r--r--libgo/go/net/udpsock_posix.go100
-rw-r--r--libgo/go/net/unixsock_posix.go2
42 files changed, 1110 insertions, 348 deletions
diff --git a/libgo/go/net/cgo_bsd.go b/libgo/go/net/cgo_bsd.go
index 3951d84..b8c6ef9 100644
--- a/libgo/go/net/cgo_bsd.go
+++ b/libgo/go/net/cgo_bsd.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build darwin freebsd
+
package net
/*
diff --git a/libgo/go/net/cgo_stub.go b/libgo/go/net/cgo_stub.go
index c6277cb..565cbe7 100644
--- a/libgo/go/net/cgo_stub.go
+++ b/libgo/go/net/cgo_stub.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build openbsd
+
// Stub cgo routines for systems that do not use cgo to do network lookups.
package net
diff --git a/libgo/go/net/cgo_unix.go b/libgo/go/net/cgo_unix.go
index d088eab..bfb3dfd 100644
--- a/libgo/go/net/cgo_unix.go
+++ b/libgo/go/net/cgo_unix.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build darwin freebsd linux
+
package net
/*
diff --git a/libgo/go/net/dial.go b/libgo/go/net/dial.go
index 10c67dc..85d54b3 100644
--- a/libgo/go/net/dial.go
+++ b/libgo/go/net/dial.go
@@ -59,10 +59,10 @@ func Dial(net, addr string) (c Conn, err os.Error) {
case *IPAddr:
c, err = DialIP(net, nil, ra)
default:
- err = UnknownNetworkError(net)
+ err = &OpError{"dial", net + " " + addr, nil, UnknownNetworkError(net)}
}
if err != nil {
- return nil, &OpError{"dial", net + " " + addr, nil, err}
+ return nil, err
}
return
}
diff --git a/libgo/go/net/dnsclient_unix.go b/libgo/go/net/dnsclient_unix.go
index f407b17..eb7db5e 100644
--- a/libgo/go/net/dnsclient_unix.go
+++ b/libgo/go/net/dnsclient_unix.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build darwin freebsd linux openbsd
+
// DNS client: see RFC 1035.
// Has to be linked into package net for Dial.
@@ -113,7 +115,7 @@ func convertRR_A(records []dnsRR) []IP {
func convertRR_AAAA(records []dnsRR) []IP {
addrs := make([]IP, len(records))
for i, rr := range records {
- a := make(IP, 16)
+ a := make(IP, IPv6len)
copy(a, rr.(*dnsRR_AAAA).AAAA[:])
addrs[i] = a
}
@@ -213,6 +215,18 @@ func goLookupHost(name string) (addrs []string, err os.Error) {
// depending on our lookup code, so that Go and C get the same
// answers.
func goLookupIP(name string) (addrs []IP, err os.Error) {
+ // Use entries from /etc/hosts if possible.
+ haddrs := lookupStaticHost(name)
+ if len(haddrs) > 0 {
+ for _, haddr := range haddrs {
+ if ip := ParseIP(haddr); ip != nil {
+ addrs = append(addrs, ip)
+ }
+ }
+ if len(addrs) > 0 {
+ return
+ }
+ }
onceLoadConfig.Do(loadConfig)
if dnserr != nil || cfg == nil {
err = dnserr
diff --git a/libgo/go/net/dnsconfig.go b/libgo/go/net/dnsconfig.go
index 54e3343..afc0599 100644
--- a/libgo/go/net/dnsconfig.go
+++ b/libgo/go/net/dnsconfig.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build darwin freebsd linux openbsd
+
// Read system DNS config from /etc/resolv.conf
package net
diff --git a/libgo/go/net/fd.go b/libgo/go/net/fd.go
index 707dcca..80d40af 100644
--- a/libgo/go/net/fd.go
+++ b/libgo/go/net/fd.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build darwin freebsd linux openbsd
+
package net
import (
@@ -150,7 +152,7 @@ func (s *pollServer) LookupFD(fd int, mode int) *netFD {
if !ok {
return nil
}
- s.pending[key] = nil, false
+ delete(s.pending, key)
return netfd
}
@@ -193,7 +195,7 @@ func (s *pollServer) CheckDeadlines() {
}
if t > 0 {
if t <= now {
- s.pending[key] = nil, false
+ delete(s.pending, key)
if mode == 'r' {
s.poll.DelFD(fd.sysfd, mode)
fd.rdeadline = -1
@@ -356,6 +358,25 @@ func (fd *netFD) Close() os.Error {
return nil
}
+func (fd *netFD) shutdown(how int) os.Error {
+ if fd == nil || fd.sysfile == nil {
+ return os.EINVAL
+ }
+ errno := syscall.Shutdown(fd.sysfd, how)
+ if errno != 0 {
+ return &OpError{"shutdown", fd.net, fd.laddr, os.Errno(errno)}
+ }
+ return nil
+}
+
+func (fd *netFD) CloseRead() os.Error {
+ return fd.shutdown(syscall.SHUT_RD)
+}
+
+func (fd *netFD) CloseWrite() os.Error {
+ return fd.shutdown(syscall.SHUT_WR)
+}
+
func (fd *netFD) Read(p []byte) (n int, err os.Error) {
if fd == nil {
return 0, os.EINVAL
diff --git a/libgo/go/net/fd_linux.go b/libgo/go/net/fd_linux.go
index 70fc344b..c860c84 100644
--- a/libgo/go/net/fd_linux.go
+++ b/libgo/go/net/fd_linux.go
@@ -105,7 +105,7 @@ func (p *pollster) StopWaiting(fd int, bits uint) {
if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_DEL, fd, nil); e != 0 {
print("Epoll delete fd=", fd, ": ", os.Errno(e).String(), "\n")
}
- p.events[fd] = 0, false
+ delete(p.events, fd)
}
}
diff --git a/libgo/go/net/fd_windows.go b/libgo/go/net/fd_windows.go
index 3757e14..8e8b3b7 100644
--- a/libgo/go/net/fd_windows.go
+++ b/libgo/go/net/fd_windows.go
@@ -23,7 +23,7 @@ var initErr os.Error
func init() {
var d syscall.WSAData
- e := syscall.WSAStartup(uint32(0x101), &d)
+ e := syscall.WSAStartup(uint32(0x202), &d)
if e != 0 {
initErr = os.NewSyscallError("WSAStartup", e)
}
@@ -52,15 +52,27 @@ type anOp struct {
// of the struct, as our code rely on it.
o syscall.Overlapped
- resultc chan ioResult // io completion results
- errnoc chan int // io submit / cancel operation errors
+ resultc chan ioResult
+ errnoc chan int
fd *netFD
}
-func (o *anOp) Init(fd *netFD) {
+func (o *anOp) Init(fd *netFD, mode int) {
o.fd = fd
- o.resultc = make(chan ioResult, 1)
- o.errnoc = make(chan int)
+ var i int
+ if mode == 'r' {
+ i = 0
+ } else {
+ i = 1
+ }
+ if fd.resultc[i] == nil {
+ fd.resultc[i] = make(chan ioResult, 1)
+ }
+ o.resultc = fd.resultc[i]
+ if fd.errnoc[i] == nil {
+ fd.errnoc[i] = make(chan int)
+ }
+ o.errnoc = fd.errnoc[i]
}
func (o *anOp) Op() *anOp {
@@ -74,8 +86,8 @@ type bufOp struct {
buf syscall.WSABuf
}
-func (o *bufOp) Init(fd *netFD, buf []byte) {
- o.anOp.Init(fd)
+func (o *bufOp) Init(fd *netFD, buf []byte, mode int) {
+ o.anOp.Init(fd, mode)
o.buf.Len = uint32(len(buf))
if len(buf) == 0 {
o.buf.Buf = nil
@@ -208,12 +220,14 @@ type netFD struct {
closing bool
// immutable until Close
- sysfd syscall.Handle
- family int
- proto int
- net string
- laddr Addr
- raddr Addr
+ sysfd syscall.Handle
+ family int
+ proto int
+ net string
+ laddr Addr
+ raddr Addr
+ resultc [2]chan ioResult // read/write completion results
+ errnoc [2]chan int // read/write submit or cancel operation errors
// owned by client
rdeadline_delta int64
@@ -298,6 +312,25 @@ func (fd *netFD) Close() os.Error {
return nil
}
+func (fd *netFD) shutdown(how int) os.Error {
+ if fd == nil || fd.sysfd == syscall.InvalidHandle {
+ return os.EINVAL
+ }
+ errno := syscall.Shutdown(fd.sysfd, how)
+ if errno != 0 {
+ return &OpError{"shutdown", fd.net, fd.laddr, os.Errno(errno)}
+ }
+ return nil
+}
+
+func (fd *netFD) CloseRead() os.Error {
+ return fd.shutdown(syscall.SHUT_RD)
+}
+
+func (fd *netFD) CloseWrite() os.Error {
+ return fd.shutdown(syscall.SHUT_WR)
+}
+
// Read from network.
type readOp struct {
@@ -325,7 +358,7 @@ func (fd *netFD) Read(buf []byte) (n int, err os.Error) {
return 0, os.EINVAL
}
var o readOp
- o.Init(fd, buf)
+ o.Init(fd, buf, 'r')
n, err = iosrv.ExecIO(&o, fd.rdeadline_delta)
if err == nil && n == 0 {
err = os.EOF
@@ -365,7 +398,7 @@ func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err os.Error)
return 0, nil, os.EINVAL
}
var o readFromOp
- o.Init(fd, buf)
+ o.Init(fd, buf, 'r')
o.rsan = int32(unsafe.Sizeof(o.rsa))
n, err = iosrv.ExecIO(&o, fd.rdeadline_delta)
if err != nil {
@@ -402,7 +435,7 @@ func (fd *netFD) Write(buf []byte) (n int, err os.Error) {
return 0, os.EINVAL
}
var o writeOp
- o.Init(fd, buf)
+ o.Init(fd, buf, 'w')
return iosrv.ExecIO(&o, fd.wdeadline_delta)
}
@@ -437,7 +470,7 @@ func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (n int, err os.Error)
return 0, os.EINVAL
}
var o writeToOp
- o.Init(fd, buf)
+ o.Init(fd, buf, 'w')
o.sa = sa
return iosrv.ExecIO(&o, fd.wdeadline_delta)
}
@@ -487,7 +520,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os.
// Submit accept request.
var o acceptOp
- o.Init(fd)
+ o.Init(fd, 'r')
o.newsock = s
_, err = iosrv.ExecIO(&o, 0)
if err != nil {
diff --git a/libgo/go/net/file.go b/libgo/go/net/file.go
index 0e411a1..ed2559d 100644
--- a/libgo/go/net/file.go
+++ b/libgo/go/net/file.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build darwin freebsd linux openbsd
+
package net
import (
@@ -20,6 +22,7 @@ func newFileFD(f *os.File) (nfd *netFD, err os.Error) {
return nil, os.NewSyscallError("getsockopt", errno)
}
+ family := syscall.AF_UNSPEC
toAddr := sockaddrToTCP
sa, _ := syscall.Getsockname(fd)
switch sa.(type) {
@@ -27,18 +30,21 @@ func newFileFD(f *os.File) (nfd *netFD, err os.Error) {
closesocket(fd)
return nil, os.EINVAL
case *syscall.SockaddrInet4:
+ family = syscall.AF_INET
if proto == syscall.SOCK_DGRAM {
toAddr = sockaddrToUDP
} else if proto == syscall.SOCK_RAW {
toAddr = sockaddrToIP
}
case *syscall.SockaddrInet6:
+ family = syscall.AF_INET6
if proto == syscall.SOCK_DGRAM {
toAddr = sockaddrToUDP
} else if proto == syscall.SOCK_RAW {
toAddr = sockaddrToIP
}
case *syscall.SockaddrUnix:
+ family = syscall.AF_UNIX
toAddr = sockaddrToUnix
if proto == syscall.SOCK_DGRAM {
toAddr = sockaddrToUnixgram
@@ -50,7 +56,7 @@ func newFileFD(f *os.File) (nfd *netFD, err os.Error) {
sa, _ = syscall.Getpeername(fd)
raddr := toAddr(sa)
- if nfd, err = newFD(fd, 0, proto, laddr.Network()); err != nil {
+ if nfd, err = newFD(fd, family, proto, laddr.Network()); err != nil {
return nil, err
}
nfd.setAddr(laddr, raddr)
diff --git a/libgo/go/net/file_test.go b/libgo/go/net/file_test.go
index 9a8c2dc..0fa6740 100644
--- a/libgo/go/net/file_test.go
+++ b/libgo/go/net/file_test.go
@@ -73,7 +73,7 @@ func TestFileListener(t *testing.T) {
}
}
-func testFilePacketConn(t *testing.T, pcf packetConnFile) {
+func testFilePacketConn(t *testing.T, pcf packetConnFile, listen bool) {
f, err := pcf.File()
if err != nil {
t.Fatalf("File failed: %v", err)
@@ -85,6 +85,11 @@ func testFilePacketConn(t *testing.T, pcf packetConnFile) {
if !reflect.DeepEqual(pcf.LocalAddr(), c.LocalAddr()) {
t.Fatalf("LocalAddrs not equal: %#v != %#v", pcf.LocalAddr(), c.LocalAddr())
}
+ if listen {
+ if _, err := c.WriteTo([]byte{}, c.LocalAddr()); err != nil {
+ t.Fatalf("WriteTo failed: %v", err)
+ }
+ }
if err := c.Close(); err != nil {
t.Fatalf("Close failed: %v", err)
}
@@ -98,7 +103,7 @@ func testFilePacketConnListen(t *testing.T, net, laddr string) {
if err != nil {
t.Fatalf("Listen failed: %v", err)
}
- testFilePacketConn(t, l.(packetConnFile))
+ testFilePacketConn(t, l.(packetConnFile), true)
if err := l.Close(); err != nil {
t.Fatalf("Close failed: %v", err)
}
@@ -109,7 +114,7 @@ func testFilePacketConnDial(t *testing.T, net, raddr string) {
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
- testFilePacketConn(t, c.(packetConnFile))
+ testFilePacketConn(t, c.(packetConnFile), false)
if err := c.Close(); err != nil {
t.Fatalf("Close failed: %v", err)
}
diff --git a/libgo/go/net/interface.go b/libgo/go/net/interface.go
index 8a14cb2..2696b7f 100644
--- a/libgo/go/net/interface.go
+++ b/libgo/go/net/interface.go
@@ -26,6 +26,63 @@ func (a HardwareAddr) String() string {
return buf.String()
}
+// ParseMAC parses s as an IEEE 802 MAC-48, EUI-48, or EUI-64 using one of the
+// following formats:
+// 01:23:45:67:89:ab
+// 01:23:45:67:89:ab:cd:ef
+// 01-23-45-67-89-ab
+// 01-23-45-67-89-ab-cd-ef
+// 0123.4567.89ab
+// 0123.4567.89ab.cdef
+func ParseMAC(s string) (hw HardwareAddr, err os.Error) {
+ if len(s) < 14 {
+ goto error
+ }
+
+ if s[2] == ':' || s[2] == '-' {
+ if (len(s)+1)%3 != 0 {
+ goto error
+ }
+ n := (len(s) + 1) / 3
+ if n != 6 && n != 8 {
+ goto error
+ }
+ hw = make(HardwareAddr, n)
+ for x, i := 0, 0; i < n; i++ {
+ var ok bool
+ if hw[i], ok = xtoi2(s[x:], s[2]); !ok {
+ goto error
+ }
+ x += 3
+ }
+ } else if s[4] == '.' {
+ if (len(s)+1)%5 != 0 {
+ goto error
+ }
+ n := 2 * (len(s) + 1) / 5
+ if n != 6 && n != 8 {
+ goto error
+ }
+ hw = make(HardwareAddr, n)
+ for x, i := 0, 0; i < n; i += 2 {
+ var ok bool
+ if hw[i], ok = xtoi2(s[x:x+2], 0); !ok {
+ goto error
+ }
+ if hw[i+1], ok = xtoi2(s[x+2:], s[4]); !ok {
+ goto error
+ }
+ x += 5
+ }
+ } else {
+ goto error
+ }
+ return hw, nil
+
+error:
+ return nil, os.NewError("invalid MAC address: " + s)
+}
+
// Interface represents a mapping between network interface name
// and index. It also represents network interface facility
// information.
diff --git a/libgo/go/net/interface_bsd.go b/libgo/go/net/interface_bsd.go
index 2675f94..54fa5dd 100644
--- a/libgo/go/net/interface_bsd.go
+++ b/libgo/go/net/interface_bsd.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build darwin freebsd openbsd
+
// Network interface identification for BSD variants
package net
@@ -148,7 +150,6 @@ func newAddr(m *syscall.InterfaceAddrMessage) ([]Addr, os.Error) {
}
for _, s := range sas {
-
switch v := s.(type) {
case *syscall.SockaddrInet4:
ifa := &IPAddr{IP: IPv4(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3])}
diff --git a/libgo/go/net/interface_linux.go b/libgo/go/net/interface_linux.go
index 3d2a0bb..36ae04ff 100644
--- a/libgo/go/net/interface_linux.go
+++ b/libgo/go/net/interface_linux.go
@@ -103,42 +103,29 @@ func linkFlags(rawFlags uint32) Flags {
// for a specific interface.
func interfaceAddrTable(ifindex int) ([]Addr, os.Error) {
var (
- tab []byte
- e int
- err os.Error
- ifat4 []Addr
- ifat6 []Addr
- msgs4 []syscall.NetlinkMessage
- msgs6 []syscall.NetlinkMessage
+ tab []byte
+ e int
+ err os.Error
+ ifat []Addr
+ msgs []syscall.NetlinkMessage
)
- tab, e = syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_INET)
+ tab, e = syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
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)
+ msgs, e = syscall.ParseNetlinkMessage(tab)
if e != 0 {
return nil, os.NewSyscallError("netlink message", e)
}
- ifat6, err = addrTable(msgs6, ifindex)
+
+ ifat, err = addrTable(msgs, ifindex)
if err != nil {
return nil, err
}
- return append(ifat4, ifat6...), nil
+ return ifat, nil
}
func addrTable(msgs []syscall.NetlinkMessage, ifindex int) ([]Addr, os.Error) {
diff --git a/libgo/go/net/interface_stub.go b/libgo/go/net/interface_stub.go
index 950de6c..282b38b 100644
--- a/libgo/go/net/interface_stub.go
+++ b/libgo/go/net/interface_stub.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build plan9
+
// Network interface identification
package net
diff --git a/libgo/go/net/interface_test.go b/libgo/go/net/interface_test.go
index 0e4089a..c918f24 100644
--- a/libgo/go/net/interface_test.go
+++ b/libgo/go/net/interface_test.go
@@ -6,6 +6,9 @@ package net
import (
"bytes"
+ "os"
+ "reflect"
+ "strings"
"testing"
)
@@ -71,3 +74,46 @@ func TestInterfaceAddrs(t *testing.T) {
t.Logf("interface address %q\n", ifa.String())
}
}
+
+var mactests = []struct {
+ in string
+ out HardwareAddr
+ err string
+}{
+ {"01:23:45:67:89:AB", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab}, ""},
+ {"01-23-45-67-89-AB", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab}, ""},
+ {"0123.4567.89AB", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab}, ""},
+ {"ab:cd:ef:AB:CD:EF", HardwareAddr{0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef}, ""},
+ {"01.02.03.04.05.06", nil, "invalid MAC address"},
+ {"01:02:03:04:05:06:", nil, "invalid MAC address"},
+ {"x1:02:03:04:05:06", nil, "invalid MAC address"},
+ {"01002:03:04:05:06", nil, "invalid MAC address"},
+ {"01:02003:04:05:06", nil, "invalid MAC address"},
+ {"01:02:03004:05:06", nil, "invalid MAC address"},
+ {"01:02:03:04005:06", nil, "invalid MAC address"},
+ {"01:02:03:04:05006", nil, "invalid MAC address"},
+ {"01-02:03:04:05:06", nil, "invalid MAC address"},
+ {"01:02-03-04-05-06", nil, "invalid MAC address"},
+ {"0123:4567:89AF", nil, "invalid MAC address"},
+ {"0123-4567-89AF", nil, "invalid MAC address"},
+ {"01:23:45:67:89:AB:CD:EF", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, ""},
+ {"01-23-45-67-89-AB-CD-EF", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, ""},
+ {"0123.4567.89AB.CDEF", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, ""},
+}
+
+func match(err os.Error, s string) bool {
+ if s == "" {
+ return err == nil
+ }
+ return err != nil && strings.Contains(err.String(), s)
+}
+
+func TestParseMAC(t *testing.T) {
+ for _, tt := range mactests {
+ out, err := ParseMAC(tt.in)
+ if !reflect.DeepEqual(out, tt.out) || !match(err, tt.err) {
+ t.Errorf("ParseMAC(%q) = %v, %v, want %v, %v", tt.in, out, err, tt.out,
+ tt.err)
+ }
+ }
+}
diff --git a/libgo/go/net/ip.go b/libgo/go/net/ip.go
index b0e2c42..61dc3be9 100644
--- a/libgo/go/net/ip.go
+++ b/libgo/go/net/ip.go
@@ -21,11 +21,8 @@ const (
)
// An IP is a single IP address, an array of bytes.
-// Functions in this package accept either 4-byte (IP v4)
-// or 16-byte (IP v6) arrays as input. Unless otherwise
-// specified, functions in this package always return
-// IP addresses in 16-byte form using the canonical
-// embedding.
+// Functions in this package accept either 4-byte (IPv4)
+// or 16-byte (IPv6) arrays as input.
//
// Note that in this documentation, referring to an
// IP address as an IPv4 address or an IPv6 address
@@ -37,6 +34,12 @@ type IP []byte
// An IP mask is an IP address.
type IPMask []byte
+// An IPNet represents an IP network.
+type IPNet struct {
+ IP IP // network number
+ Mask IPMask // network mask
+}
+
// IPv4 returns the IP address (in 16-byte form) of the
// IPv4 address a.b.c.d.
func IPv4(a, b, c, d byte) IP {
@@ -51,20 +54,42 @@ func IPv4(a, b, c, d byte) IP {
var v4InV6Prefix = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}
-// IPv4Mask returns the IP mask (in 16-byte form) of the
+// IPv4Mask returns the IP mask (in 4-byte form) of the
// IPv4 mask a.b.c.d.
func IPv4Mask(a, b, c, d byte) IPMask {
- p := make(IPMask, IPv6len)
- for i := 0; i < 12; i++ {
- p[i] = 0xff
- }
- p[12] = a
- p[13] = b
- p[14] = c
- p[15] = d
+ p := make(IPMask, IPv4len)
+ p[0] = a
+ p[1] = b
+ p[2] = c
+ p[3] = d
return p
}
+// CIDRMask returns an IPMask consisting of `ones' 1 bits
+// followed by 0s up to a total length of `bits' bits.
+// For a mask of this form, CIDRMask is the inverse of IPMask.Size.
+func CIDRMask(ones, bits int) IPMask {
+ if bits != 8*IPv4len && bits != 8*IPv6len {
+ return nil
+ }
+ if ones < 0 || ones > bits {
+ return nil
+ }
+ l := bits / 8
+ m := make(IPMask, l)
+ n := uint(ones)
+ for i := 0; i < l; i++ {
+ if n >= 8 {
+ m[i] = 0xff
+ n -= 8
+ continue
+ }
+ m[i] = ^byte(0xff >> n)
+ n = 0
+ }
+ return m
+}
+
// Well-known IPv4 addresses
var (
IPv4bcast = IPv4(255, 255, 255, 255) // broadcast
@@ -213,13 +238,13 @@ func allFF(b []byte) bool {
// Mask returns the result of masking the IP address ip with mask.
func (ip IP) Mask(mask IPMask) IP {
- n := len(ip)
- if len(mask) == 16 && len(ip) == 4 && allFF(mask[:12]) {
+ if len(mask) == IPv6len && len(ip) == IPv4len && allFF(mask[:12]) {
mask = mask[12:]
}
- if len(mask) == 4 && len(ip) == 16 && bytesEqual(ip[:12], v4InV6Prefix) {
+ if len(mask) == IPv4len && len(ip) == IPv6len && bytesEqual(ip[:12], v4InV6Prefix) {
ip = ip[12:]
}
+ n := len(ip)
if n != len(mask) {
return nil
}
@@ -230,40 +255,6 @@ func (ip IP) Mask(mask IPMask) IP {
return out
}
-// Convert i to decimal string.
-func itod(i uint) string {
- if i == 0 {
- return "0"
- }
-
- // Assemble decimal in reverse order.
- var b [32]byte
- bp := len(b)
- for ; i > 0; i /= 10 {
- bp--
- b[bp] = byte(i%10) + '0'
- }
-
- return string(b[bp:])
-}
-
-// Convert i to hexadecimal string.
-func itox(i uint) string {
- if i == 0 {
- return "0"
- }
-
- // Assemble hexadecimal in reverse order.
- var b [32]byte
- bp := len(b)
- for ; i > 0; i /= 16 {
- bp--
- b[bp] = "0123456789abcdef"[byte(i%16)]
- }
-
- return string(b[bp:])
-}
-
// String returns the string form of the IP address ip.
// If the address is an IPv4 address, the string representation
// is dotted decimal ("74.125.19.99"). Otherwise the representation
@@ -272,11 +263,11 @@ func (ip IP) String() string {
p := ip
if len(ip) == 0 {
- return ""
+ return "<nil>"
}
// If IPv4, use dotted notation.
- if p4 := p.To4(); len(p4) == 4 {
+ if p4 := p.To4(); len(p4) == IPv4len {
return itod(uint(p4[0])) + "." +
itod(uint(p4[1])) + "." +
itod(uint(p4[2])) + "." +
@@ -289,9 +280,9 @@ func (ip IP) String() string {
// Find longest run of zeros.
e0 := -1
e1 := -1
- for i := 0; i < 16; i += 2 {
+ for i := 0; i < IPv6len; i += 2 {
j := i
- for j < 16 && p[j] == 0 && p[j+1] == 0 {
+ for j < IPv6len && p[j] == 0 && p[j+1] == 0 {
j += 2
}
if j > i && j-i > e1-e0 {
@@ -307,17 +298,17 @@ func (ip IP) String() string {
// Print with possible :: in place of run of zeros
var s string
- for i := 0; i < 16; i += 2 {
+ for i := 0; i < IPv6len; i += 2 {
if i == e0 {
s += "::"
i = e1
- if i >= 16 {
+ if i >= IPv6len {
break
}
} else if i > 0 {
s += ":"
}
- s += itox((uint(p[i]) << 8) | uint(p[i+1]))
+ s += itox((uint(p[i])<<8)|uint(p[i+1]), 1)
}
return s
}
@@ -329,10 +320,10 @@ func (ip IP) Equal(x IP) bool {
if len(ip) == len(x) {
return bytesEqual(ip, x)
}
- if len(ip) == 4 && len(x) == 16 {
+ if len(ip) == IPv4len && len(x) == IPv6len {
return bytesEqual(x[0:12], v4InV6Prefix) && bytesEqual(ip, x[12:])
}
- if len(ip) == 16 && len(x) == 4 {
+ if len(ip) == IPv6len && len(x) == IPv4len {
return bytesEqual(ip[0:12], v4InV6Prefix) && bytesEqual(ip[12:], x)
}
return false
@@ -379,25 +370,86 @@ func simpleMaskLength(mask IPMask) int {
return n
}
-// String returns the string representation of mask.
-// If the mask is in the canonical form--ones followed by zeros--the
-// string representation is just the decimal number of ones.
-// If the mask is in a non-canonical form, it is formatted
-// as an IP address.
-func (mask IPMask) String() string {
- switch len(mask) {
- case 4:
- n := simpleMaskLength(mask)
- if n >= 0 {
- return itod(uint(n + (IPv6len-IPv4len)*8))
+// Size returns the number of leading ones and total bits in the mask.
+// If the mask is not in the canonical form--ones followed by zeros--then
+// Size returns 0, 0.
+func (m IPMask) Size() (ones, bits int) {
+ ones, bits = simpleMaskLength(m), len(m)*8
+ if ones == -1 {
+ return 0, 0
+ }
+ return
+}
+
+// String returns the hexadecimal form of m, with no punctuation.
+func (m IPMask) String() string {
+ s := ""
+ for _, b := range m {
+ s += itox(uint(b), 2)
+ }
+ if len(s) == 0 {
+ return "<nil>"
+ }
+ return s
+}
+
+func networkNumberAndMask(n *IPNet) (ip IP, m IPMask) {
+ if ip = n.IP.To4(); ip == nil {
+ ip = n.IP
+ if len(ip) != IPv6len {
+ return nil, nil
}
- case 16:
- n := simpleMaskLength(mask)
- if n >= 12*8 {
- return itod(uint(n - 12*8))
+ }
+ m = n.Mask
+ switch len(m) {
+ case IPv4len:
+ if len(ip) != IPv4len {
+ return nil, nil
+ }
+ case IPv6len:
+ if len(ip) == IPv4len {
+ m = m[12:]
+ }
+ default:
+ return nil, nil
+ }
+ return
+}
+
+// Contains reports whether the network includes ip.
+func (n *IPNet) Contains(ip IP) bool {
+ nn, m := networkNumberAndMask(n)
+ if x := ip.To4(); x != nil {
+ ip = x
+ }
+ l := len(ip)
+ if l != len(nn) {
+ return false
+ }
+ for i := 0; i < l; i++ {
+ if nn[i]&m[i] != ip[i]&m[i] {
+ return false
}
}
- return IP(mask).String()
+ return true
+}
+
+// String returns the CIDR notation of n like "192.168.100.1/24"
+// or "2001:DB8::/48" as defined in RFC 4632 and RFC 4291.
+// If the mask is not in the canonical form, it returns the
+// string which consists of an IP address, followed by a slash
+// character and a mask expressed as hexadecimal form with no
+// punctuation like "192.168.100.1/c000ff00".
+func (n *IPNet) String() string {
+ nn, m := networkNumberAndMask(n)
+ if nn == nil || m == nil {
+ return "<nil>"
+ }
+ l := simpleMaskLength(m)
+ if l == -1 {
+ return nn.String() + "/" + m.String()
+ }
+ return nn.String() + "/" + itod(uint(l))
}
// Parse IPv4 address (d.d.d.d).
@@ -440,7 +492,7 @@ func parseIPv4(s string) IP {
// * The last 32 bits can be in IPv4 form.
// Thus, ::ffff:1.2.3.4 is the IPv4 address 1.2.3.4.
func parseIPv6(s string) IP {
- p := make(IP, 16)
+ p := make(IP, IPv6len)
ellipsis := -1 // position of ellipsis in p
i := 0 // index in string s
@@ -482,7 +534,7 @@ func parseIPv6(s string) IP {
p[j+2] = p4[14]
p[j+3] = p4[15]
i = len(s)
- j += 4
+ j += IPv4len
break
}
@@ -569,46 +621,28 @@ func ParseIP(s string) IP {
}
// ParseCIDR parses s as a CIDR notation IP address and mask,
-// like "192.168.100.1/24", "2001:DB8::/48", as defined in
+// like "192.168.100.1/24" or "2001:DB8::/48", as defined in
// RFC 4632 and RFC 4291.
-func ParseCIDR(s string) (ip IP, mask IPMask, err os.Error) {
+//
+// It returns the IP address and the network implied by the IP
+// and mask. For example, ParseCIDR("192.168.100.1/16") returns
+// the IP address 192.168.100.1 and the network 192.168.0.0/16.
+func ParseCIDR(s string) (IP, *IPNet, os.Error) {
i := byteIndex(s, '/')
if i < 0 {
return nil, nil, &ParseError{"CIDR address", s}
}
ipstr, maskstr := s[:i], s[i+1:]
- iplen := 4
- ip = parseIPv4(ipstr)
+ iplen := IPv4len
+ ip := parseIPv4(ipstr)
if ip == nil {
- iplen = 16
+ iplen = IPv6len
ip = parseIPv6(ipstr)
}
- nn, i, ok := dtoi(maskstr, 0)
- if ip == nil || !ok || i != len(maskstr) || nn < 0 || nn > 8*iplen {
+ n, i, ok := dtoi(maskstr, 0)
+ if ip == nil || !ok || i != len(maskstr) || n < 0 || n > 8*iplen {
return nil, nil, &ParseError{"CIDR address", s}
}
- n := uint(nn)
- if iplen == 4 {
- v4mask := ^uint32(0xffffffff >> n)
- mask = IPv4Mask(byte(v4mask>>24), byte(v4mask>>16), byte(v4mask>>8), byte(v4mask))
- } else {
- mask = make(IPMask, 16)
- for i := 0; i < 16; i++ {
- if n >= 8 {
- mask[i] = 0xff
- n -= 8
- continue
- }
- mask[i] = ^byte(0xff >> n)
- n = 0
-
- }
- }
- // address must not have any bits not in mask
- for i := range ip {
- if ip[i]&^mask[i] != 0 {
- return nil, nil, &ParseError{"CIDR address", s}
- }
- }
- return ip, mask, nil
+ m := CIDRMask(n, 8*iplen)
+ return ip, &IPNet{ip.Mask(m), m}, nil
}
diff --git a/libgo/go/net/ip_test.go b/libgo/go/net/ip_test.go
index b189b10..07e627a 100644
--- a/libgo/go/net/ip_test.go
+++ b/libgo/go/net/ip_test.go
@@ -34,12 +34,13 @@ var parseiptests = []struct {
{"::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}},
{"::ffff:4a7d:1363", IPv4(74, 125, 19, 99)},
+ {"", nil},
}
func TestParseIP(t *testing.T) {
for _, tt := range parseiptests {
if out := ParseIP(tt.in); !isEqual(out, tt.out) {
- t.Errorf("ParseIP(%#q) = %v, want %v", tt.in, out, tt.out)
+ t.Errorf("ParseIP(%q) = %v, want %v", tt.in, out, tt.out)
}
}
}
@@ -49,60 +50,213 @@ 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},
- "2001:db8::123:12:1"},
- {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},
- "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},
- "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},
- "2001::1:0:0:1"},
- {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},
- "2001:db8::1:0:0:1"},
- {IP{0x20, 0x1, 0xD, 0xB8, 0, 0, 0, 0, 0, 0xA, 0, 0xB, 0, 0xC, 0, 0xD},
- "2001:db8::a:b:c:d"},
+ {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}, "2001:db8::1"},
+ {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}, "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}, "2001::1:0:0:1"},
+ {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}, "2001:db8::1:0:0:1"},
+ {IP{0x20, 0x1, 0xD, 0xB8, 0, 0, 0, 0, 0, 0xA, 0, 0xB, 0, 0xC, 0, 0xD}, "2001:db8::a:b:c:d"},
+ {nil, "<nil>"},
}
func TestIPString(t *testing.T) {
for _, tt := range ipstringtests {
if out := tt.in.String(); out != tt.out {
- t.Errorf("IP.String(%v) = %#q, want %#q", tt.in, out, tt.out)
+ t.Errorf("IP.String(%v) = %q, want %q", tt.in, out, tt.out)
}
}
}
-var parsecidrtests = []struct {
- in string
- ip IP
+var ipmasktests = []struct {
+ in IP
mask IPMask
- err os.Error
+ out IP
+}{
+ {IPv4(192, 168, 1, 127), IPv4Mask(255, 255, 255, 128), IPv4(192, 168, 1, 0)},
+ {IPv4(192, 168, 1, 127), IPMask(ParseIP("255.255.255.192")), IPv4(192, 168, 1, 64)},
+ {IPv4(192, 168, 1, 127), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffe0")), IPv4(192, 168, 1, 96)},
+ {IPv4(192, 168, 1, 127), IPv4Mask(255, 0, 255, 0), IPv4(192, 0, 1, 0)},
+ {ParseIP("2001:db8::1"), IPMask(ParseIP("ffff:ff80::")), ParseIP("2001:d80::")},
+ {ParseIP("2001:db8::1"), IPMask(ParseIP("f0f0:0f0f::")), ParseIP("2000:d08::")},
+}
+
+func TestIPMask(t *testing.T) {
+ for _, tt := range ipmasktests {
+ if out := tt.in.Mask(tt.mask); out == nil || !tt.out.Equal(out) {
+ t.Errorf("IP(%v).Mask(%v) = %v, want %v", tt.in, tt.mask, out, tt.out)
+ }
+ }
+}
+
+var ipmaskstringtests = []struct {
+ in IPMask
+ out string
+}{
+ {IPv4Mask(255, 255, 255, 240), "fffffff0"},
+ {IPv4Mask(255, 0, 128, 0), "ff008000"},
+ {IPMask(ParseIP("ffff:ff80::")), "ffffff80000000000000000000000000"},
+ {IPMask(ParseIP("ef00:ff80::cafe:0")), "ef00ff800000000000000000cafe0000"},
+ {nil, "<nil>"},
+}
+
+func TestIPMaskString(t *testing.T) {
+ for _, tt := range ipmaskstringtests {
+ if out := tt.in.String(); out != tt.out {
+ t.Errorf("IPMask.String(%v) = %q, want %q", tt.in, out, tt.out)
+ }
+ }
+}
+
+var parsecidrtests = []struct {
+ in string
+ ip IP
+ net *IPNet
+ err os.Error
}{
- {"135.104.0.0/32", IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 255), nil},
- {"0.0.0.0/24", IPv4(0, 0, 0, 0), IPv4Mask(255, 255, 255, 0), nil},
- {"135.104.0.0/24", IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 0), nil},
- {"135.104.0.1/32", IPv4(135, 104, 0, 1), IPv4Mask(255, 255, 255, 255), nil},
- {"135.104.0.1/24", nil, nil, &ParseError{"CIDR address", "135.104.0.1/24"}},
- {"::1/128", ParseIP("::1"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")), nil},
- {"abcd:2345::/127", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe")), nil},
- {"abcd:2345::/65", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:8000::")), nil},
- {"abcd:2345::/64", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff::")), nil},
- {"abcd:2345::/63", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:fffe::")), nil},
- {"abcd:2345::/33", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:8000::")), nil},
- {"abcd:2345::/32", ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff::")), nil},
- {"abcd:2344::/31", ParseIP("abcd:2344::"), IPMask(ParseIP("ffff:fffe::")), nil},
- {"abcd:2300::/24", ParseIP("abcd:2300::"), IPMask(ParseIP("ffff:ff00::")), nil},
- {"abcd:2345::/24", nil, nil, &ParseError{"CIDR address", "abcd:2345::/24"}},
- {"2001:DB8::/48", ParseIP("2001:DB8::"), IPMask(ParseIP("ffff:ffff:ffff::")), nil},
+ {"135.104.0.0/32", IPv4(135, 104, 0, 0), &IPNet{IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 255)}, nil},
+ {"0.0.0.0/24", IPv4(0, 0, 0, 0), &IPNet{IPv4(0, 0, 0, 0), IPv4Mask(255, 255, 255, 0)}, nil},
+ {"135.104.0.0/24", IPv4(135, 104, 0, 0), &IPNet{IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 0)}, nil},
+ {"135.104.0.1/32", IPv4(135, 104, 0, 1), &IPNet{IPv4(135, 104, 0, 1), IPv4Mask(255, 255, 255, 255)}, nil},
+ {"135.104.0.1/24", IPv4(135, 104, 0, 1), &IPNet{IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 0)}, nil},
+ {"::1/128", ParseIP("::1"), &IPNet{ParseIP("::1"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))}, nil},
+ {"abcd:2345::/127", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe"))}, nil},
+ {"abcd:2345::/65", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:8000::"))}, nil},
+ {"abcd:2345::/64", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff::"))}, nil},
+ {"abcd:2345::/63", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:fffe::"))}, nil},
+ {"abcd:2345::/33", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:8000::"))}, nil},
+ {"abcd:2345::/32", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff::"))}, nil},
+ {"abcd:2344::/31", ParseIP("abcd:2344::"), &IPNet{ParseIP("abcd:2344::"), IPMask(ParseIP("ffff:fffe::"))}, nil},
+ {"abcd:2300::/24", ParseIP("abcd:2300::"), &IPNet{ParseIP("abcd:2300::"), IPMask(ParseIP("ffff:ff00::"))}, nil},
+ {"abcd:2345::/24", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2300::"), IPMask(ParseIP("ffff:ff00::"))}, nil},
+ {"2001:DB8::/48", ParseIP("2001:DB8::"), &IPNet{ParseIP("2001:DB8::"), IPMask(ParseIP("ffff:ffff:ffff::"))}, nil},
+ {"2001:DB8::1/48", ParseIP("2001:DB8::1"), &IPNet{ParseIP("2001:DB8::"), IPMask(ParseIP("ffff:ffff:ffff::"))}, nil},
+ {"192.168.1.1/255.255.255.0", nil, nil, &ParseError{"CIDR address", "192.168.1.1/255.255.255.0"}},
+ {"192.168.1.1/35", nil, nil, &ParseError{"CIDR address", "192.168.1.1/35"}},
+ {"2001:db8::1/-1", nil, nil, &ParseError{"CIDR address", "2001:db8::1/-1"}},
+ {"", nil, nil, &ParseError{"CIDR address", ""}},
}
func TestParseCIDR(t *testing.T) {
for _, tt := range parsecidrtests {
- if ip, mask, err := ParseCIDR(tt.in); !isEqual(ip, tt.ip) || !isEqual(mask, tt.mask) || !reflect.DeepEqual(err, tt.err) {
- t.Errorf("ParseCIDR(%q) = %v, %v, %v; want %v, %v, %v", tt.in, ip, mask, err, tt.ip, tt.mask, tt.err)
+ ip, net, err := ParseCIDR(tt.in)
+ if !reflect.DeepEqual(err, tt.err) {
+ t.Errorf("ParseCIDR(%q) = %v, %v; want %v, %v", tt.in, ip, net, tt.ip, tt.net)
+ }
+ if err == nil && (!tt.ip.Equal(ip) || !tt.net.IP.Equal(net.IP) || !isEqual(net.Mask, tt.net.Mask)) {
+ t.Errorf("ParseCIDR(%q) = %v, {%v, %v}; want %v {%v, %v}", tt.in, ip, net.IP, net.Mask, tt.ip, tt.net.IP, tt.net.Mask)
+ }
+ }
+}
+
+var ipnetcontainstests = []struct {
+ ip IP
+ net *IPNet
+ ok bool
+}{
+ {IPv4(172, 16, 1, 1), &IPNet{IPv4(172, 16, 0, 0), CIDRMask(12, 32)}, true},
+ {IPv4(172, 24, 0, 1), &IPNet{IPv4(172, 16, 0, 0), CIDRMask(13, 32)}, false},
+ {IPv4(192, 168, 0, 3), &IPNet{IPv4(192, 168, 0, 0), IPv4Mask(0, 0, 255, 252)}, true},
+ {IPv4(192, 168, 0, 4), &IPNet{IPv4(192, 168, 0, 0), IPv4Mask(0, 255, 0, 252)}, false},
+ {ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:1::"), CIDRMask(47, 128)}, true},
+ {ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:2::"), CIDRMask(47, 128)}, false},
+ {ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:1::"), IPMask(ParseIP("ffff:0:ffff::"))}, true},
+ {ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:1::"), IPMask(ParseIP("0:0:0:ffff::"))}, false},
+}
+
+func TestIPNetContains(t *testing.T) {
+ for _, tt := range ipnetcontainstests {
+ if ok := tt.net.Contains(tt.ip); ok != tt.ok {
+ t.Errorf("IPNet(%v).Contains(%v) = %v, want %v", tt.net, tt.ip, ok, tt.ok)
+ }
+ }
+}
+
+var ipnetstringtests = []struct {
+ in *IPNet
+ out string
+}{
+ {&IPNet{IPv4(192, 168, 1, 0), CIDRMask(26, 32)}, "192.168.1.0/26"},
+ {&IPNet{IPv4(192, 168, 1, 0), IPv4Mask(255, 0, 255, 0)}, "192.168.1.0/ff00ff00"},
+ {&IPNet{ParseIP("2001:db8::"), CIDRMask(55, 128)}, "2001:db8::/55"},
+ {&IPNet{ParseIP("2001:db8::"), IPMask(ParseIP("8000:f123:0:cafe::"))}, "2001:db8::/8000f1230000cafe0000000000000000"},
+}
+
+func TestIPNetString(t *testing.T) {
+ for _, tt := range ipnetstringtests {
+ if out := tt.in.String(); out != tt.out {
+ t.Errorf("IPNet.String(%v) = %q, want %q", tt.in, out, tt.out)
+ }
+ }
+}
+
+var cidrmasktests = []struct {
+ ones int
+ bits int
+ out IPMask
+}{
+ {0, 32, IPv4Mask(0, 0, 0, 0)},
+ {12, 32, IPv4Mask(255, 240, 0, 0)},
+ {24, 32, IPv4Mask(255, 255, 255, 0)},
+ {32, 32, IPv4Mask(255, 255, 255, 255)},
+ {0, 128, IPMask{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+ {4, 128, IPMask{0xf0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+ {48, 128, IPMask{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+ {128, 128, IPMask{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
+ {33, 32, nil},
+ {32, 33, nil},
+ {-1, 128, nil},
+ {128, -1, nil},
+}
+
+func TestCIDRMask(t *testing.T) {
+ for _, tt := range cidrmasktests {
+ if out := CIDRMask(tt.ones, tt.bits); !isEqual(out, tt.out) {
+ t.Errorf("CIDRMask(%v, %v) = %v, want %v", tt.ones, tt.bits, out, tt.out)
+ }
+ }
+}
+
+var (
+ v4addr = IP{192, 168, 0, 1}
+ v4mappedv6addr = IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 0, 1}
+ v6addr = IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1}
+ v4mask = IPMask{255, 255, 255, 0}
+ v4mappedv6mask = IPMask{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 255, 255, 255, 0}
+ v6mask = IPMask{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0}
+ badaddr = IP{192, 168, 0}
+ badmask = IPMask{255, 255, 0}
+ v4maskzero = IPMask{0, 0, 0, 0}
+)
+
+var networknumberandmasktests = []struct {
+ in IPNet
+ out IPNet
+}{
+ {IPNet{v4addr, v4mask}, IPNet{v4addr, v4mask}},
+ {IPNet{v4addr, v4mappedv6mask}, IPNet{v4addr, v4mask}},
+ {IPNet{v4mappedv6addr, v4mappedv6mask}, IPNet{v4addr, v4mask}},
+ {IPNet{v4mappedv6addr, v6mask}, IPNet{v4addr, v4maskzero}},
+ {IPNet{v4addr, v6mask}, IPNet{v4addr, v4maskzero}},
+ {IPNet{v6addr, v6mask}, IPNet{v6addr, v6mask}},
+ {IPNet{v6addr, v4mappedv6mask}, IPNet{v6addr, v4mappedv6mask}},
+ {in: IPNet{v6addr, v4mask}},
+ {in: IPNet{v4addr, badmask}},
+ {in: IPNet{v4mappedv6addr, badmask}},
+ {in: IPNet{v6addr, badmask}},
+ {in: IPNet{badaddr, v4mask}},
+ {in: IPNet{badaddr, v4mappedv6mask}},
+ {in: IPNet{badaddr, v6mask}},
+ {in: IPNet{badaddr, badmask}},
+}
+
+func TestNetworkNumberAndMask(t *testing.T) {
+ for _, tt := range networknumberandmasktests {
+ ip, m := networkNumberAndMask(&tt.in)
+ out := &IPNet{ip, m}
+ if !reflect.DeepEqual(&tt.out, out) {
+ t.Errorf("networkNumberAndMask(%v) = %v; want %v", tt.in, out, &tt.out)
}
}
}
@@ -158,10 +312,10 @@ var ipaftests = []struct {
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)
+ 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)
+ t.Errorf("verifying IPv6 address family for %q = %v, want %v", tt.in, af, tt.af6)
}
}
}
@@ -209,7 +363,7 @@ func name(f interface{}) string {
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)
+ t.Errorf("%s(%q) = %v, want %v", name(tt.scope), tt.in, ok, tt.ok)
}
}
}
diff --git a/libgo/go/net/iprawsock_posix.go b/libgo/go/net/iprawsock_posix.go
index 4e11518..dafbdab 100644
--- a/libgo/go/net/iprawsock_posix.go
+++ b/libgo/go/net/iprawsock_posix.go
@@ -2,18 +2,17 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build darwin freebsd linux openbsd windows
+
// (Raw) IP sockets
package net
import (
"os"
- "sync"
"syscall"
)
-var onceReadProtocols sync.Once
-
func sockaddrToIP(sa syscall.Sockaddr) Addr {
switch sa := sa.(type) {
case *syscall.SockaddrInet4:
@@ -25,7 +24,7 @@ func sockaddrToIP(sa syscall.Sockaddr) Addr {
}
func (a *IPAddr) family() int {
- if a == nil || len(a.IP) <= 4 {
+ if a == nil || len(a.IP) <= IPv4len {
return syscall.AF_INET
}
if a.IP.To4() != nil {
@@ -158,7 +157,7 @@ func (c *IPConn) ReadFromIP(b []byte) (n int, addr *IPAddr, err os.Error) {
switch sa := sa.(type) {
case *syscall.SockaddrInet4:
addr = &IPAddr{sa.Addr[0:]}
- if len(b) >= 4 { // discard ipv4 header
+ if len(b) >= IPv4len { // discard ipv4 header
hsize := (int(b[0]) & 0xf) * 4
copy(b, b[hsize:])
n -= hsize
@@ -207,33 +206,7 @@ func (c *IPConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) {
return c.WriteToIP(b, a)
}
-var protocols map[string]int
-
-func readProtocols() {
- protocols = make(map[string]int)
- if file, err := open("/etc/protocols"); err == nil {
- for line, ok := file.readLine(); ok; line, ok = file.readLine() {
- // tcp 6 TCP # transmission control protocol
- if i := byteIndex(line, '#'); i >= 0 {
- line = line[0:i]
- }
- f := getFields(line)
- if len(f) < 2 {
- continue
- }
- if proto, _, ok := dtoi(f[1], 0); ok {
- protocols[f[0]] = proto
- for _, alias := range f[2:] {
- protocols[alias] = proto
- }
- }
- }
- file.close()
- }
-}
-
func splitNetProto(netProto string) (net string, proto int, err os.Error) {
- onceReadProtocols.Do(readProtocols)
i := last(netProto, ':')
if i < 0 { // no colon
return "", 0, os.NewError("no IP protocol specified")
@@ -242,13 +215,12 @@ func splitNetProto(netProto string) (net string, proto int, err os.Error) {
protostr := netProto[i+1:]
proto, i, ok := dtoi(protostr, 0)
if !ok || i != len(protostr) {
- // lookup by name
- proto, ok = protocols[protostr]
- if ok {
- return
+ proto, err = lookupProtocol(protostr)
+ if err != nil {
+ return "", 0, err
}
}
- return
+ return net, proto, nil
}
// DialIP connects to the remote address raddr on the network net,
@@ -303,3 +275,8 @@ func (c *IPConn) BindToDevice(device string) os.Error {
defer c.fd.decref()
return os.NewSyscallError("setsockopt", syscall.BindToDevice(c.fd.sysfd, device))
}
+
+// File returns a copy of the underlying os.File, set to blocking mode.
+// It is the caller's responsibility to close f when finished.
+// Closing c does not affect f, and closing f does not affect c.
+func (c *IPConn) File() (f *os.File, err os.Error) { return c.fd.dup() }
diff --git a/libgo/go/net/ipsock_posix.go b/libgo/go/net/ipsock_posix.go
index 0c522fb..049df9e 100644
--- a/libgo/go/net/ipsock_posix.go
+++ b/libgo/go/net/ipsock_posix.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build darwin freebsd linux openbsd windows
+
package net
import (
diff --git a/libgo/go/net/lookup_plan9.go b/libgo/go/net/lookup_plan9.go
index 37d6b8e..d779f4a 100644
--- a/libgo/go/net/lookup_plan9.go
+++ b/libgo/go/net/lookup_plan9.go
@@ -157,12 +157,21 @@ func LookupCNAME(name string) (cname string, err os.Error) {
}
// 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(). The returned records are sorted by priority
-// and randomized by weight within a priority.
+// protocol, and domain name. The proto is "tcp" or "udp".
+// The returned records are sorted by priority and randomized
+// by weight within a priority.
+//
+// LookupSRV constructs the DNS name to look up following RFC 2782.
+// That is, it looks up _service._proto.name. To accommodate services
+// publishing SRV records under non-standard names, if both service
+// and proto are empty strings, LookupSRV looks up name directly.
func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.Error) {
- target := "_" + service + "._" + proto + "." + name
+ var target string
+ if service == "" && proto == "" {
+ target = name
+ } else {
+ target = "_" + service + "._" + proto + "." + name
+ }
lines, err := queryDNS(target, "srv")
if err != nil {
return
@@ -204,6 +213,11 @@ func LookupMX(name string) (mx []*MX, err os.Error) {
return
}
+// LookupTXT returns the DNS TXT records for the given domain name.
+func LookupTXT(name string) (txt []string, err os.Error) {
+ return nil, os.NewError("net.LookupTXT is not implemented on Plan 9")
+}
+
// LookupAddr performs a reverse lookup for the given address, returning a list
// of names mapping to that address.
func LookupAddr(addr string) (name []string, err os.Error) {
diff --git a/libgo/go/net/lookup_test.go b/libgo/go/net/lookup_test.go
index 995ab03..c0fcd26 100644
--- a/libgo/go/net/lookup_test.go
+++ b/libgo/go/net/lookup_test.go
@@ -26,6 +26,15 @@ func TestGoogleSRV(t *testing.T) {
if len(addrs) == 0 {
t.Errorf("no results")
}
+
+ // Non-standard back door.
+ _, addrs, err = LookupSRV("", "", "_xmpp-server._tcp.google.com")
+ if err != nil {
+ t.Errorf("back door failed: %s", err)
+ }
+ if len(addrs) == 0 {
+ t.Errorf("back door no results")
+ }
}
func TestGmailMX(t *testing.T) {
@@ -42,6 +51,24 @@ func TestGmailMX(t *testing.T) {
}
}
+func TestGmailTXT(t *testing.T) {
+ if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
+ t.Logf("LookupTXT is not implemented on Windows or Plan 9")
+ return
+ }
+ if testing.Short() || avoidMacFirewall {
+ t.Logf("skipping test to avoid external network")
+ return
+ }
+ txt, err := LookupTXT("gmail.com")
+ if err != nil {
+ t.Errorf("failed: %s", err)
+ }
+ if len(txt) == 0 || len(txt[0]) == 0 {
+ t.Errorf("no results")
+ }
+}
+
func TestGoogleDNSAddr(t *testing.T) {
if testing.Short() || avoidMacFirewall {
t.Logf("skipping test to avoid external network")
diff --git a/libgo/go/net/lookup_unix.go b/libgo/go/net/lookup_unix.go
index 8f5e66212..6e79295 100644
--- a/libgo/go/net/lookup_unix.go
+++ b/libgo/go/net/lookup_unix.go
@@ -2,12 +2,56 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build darwin freebsd linux openbsd
+
package net
import (
"os"
+ "sync"
+)
+
+var (
+ protocols map[string]int
+ onceReadProtocols sync.Once
)
+// readProtocols loads contents of /etc/protocols into protocols map
+// for quick access.
+func readProtocols() {
+ protocols = make(map[string]int)
+ if file, err := open("/etc/protocols"); err == nil {
+ for line, ok := file.readLine(); ok; line, ok = file.readLine() {
+ // tcp 6 TCP # transmission control protocol
+ if i := byteIndex(line, '#'); i >= 0 {
+ line = line[0:i]
+ }
+ f := getFields(line)
+ if len(f) < 2 {
+ continue
+ }
+ if proto, _, ok := dtoi(f[1], 0); ok {
+ protocols[f[0]] = proto
+ for _, alias := range f[2:] {
+ protocols[alias] = proto
+ }
+ }
+ }
+ file.close()
+ }
+}
+
+// lookupProtocol looks up IP protocol name in /etc/protocols and
+// returns correspondent protocol number.
+func lookupProtocol(name string) (proto int, err os.Error) {
+ onceReadProtocols.Do(readProtocols)
+ proto, found := protocols[name]
+ if !found {
+ return 0, os.NewError("unknown IP protocol specified: " + name)
+ }
+ return
+}
+
// LookupHost looks up the given host using the local resolver.
// It returns an array of that host's addresses.
func LookupHost(host string) (addrs []string, err os.Error) {
@@ -50,12 +94,21 @@ func LookupCNAME(name string) (cname string, err os.Error) {
}
// 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(). The returned records are sorted by priority
-// and randomized by weight within a priority.
+// protocol, and domain name. The proto is "tcp" or "udp".
+// The returned records are sorted by priority and randomized
+// by weight within a priority.
+//
+// LookupSRV constructs the DNS name to look up following RFC 2782.
+// That is, it looks up _service._proto.name. To accommodate services
+// publishing SRV records under non-standard names, if both service
+// and proto are empty strings, LookupSRV looks up name directly.
func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.Error) {
- target := "_" + service + "._" + proto + "." + name
+ var target string
+ if service == "" && proto == "" {
+ target = name
+ } else {
+ target = "_" + service + "._" + proto + "." + name
+ }
var records []dnsRR
cname, records, err = lookup(target, dnsTypeSRV)
if err != nil {
@@ -72,19 +125,32 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.
// LookupMX returns the DNS MX records for the given domain name sorted by preference.
func LookupMX(name string) (mx []*MX, err os.Error) {
- _, rr, err := lookup(name, dnsTypeMX)
+ _, records, err := lookup(name, dnsTypeMX)
if err != nil {
return
}
- mx = make([]*MX, len(rr))
- for i := range rr {
- r := rr[i].(*dnsRR_MX)
+ mx = make([]*MX, len(records))
+ for i, rr := range records {
+ r := rr.(*dnsRR_MX)
mx[i] = &MX{r.Mx, r.Pref}
}
byPref(mx).sort()
return
}
+// LookupTXT returns the DNS TXT records for the given domain name.
+func LookupTXT(name string) (txt []string, err os.Error) {
+ _, records, err := lookup(name, dnsTypeTXT)
+ if err != nil {
+ return
+ }
+ txt = make([]string, len(records))
+ for i, r := range records {
+ txt[i] = r.(*dnsRR_TXT).Txt
+ }
+ return
+}
+
// LookupAddr performs a reverse lookup for the given address, returning a list
// of names mapping to that address.
func LookupAddr(addr string) (name []string, err os.Error) {
diff --git a/libgo/go/net/lookup_windows.go b/libgo/go/net/lookup_windows.go
index fa3ad7c..ea939f8 100644
--- a/libgo/go/net/lookup_windows.go
+++ b/libgo/go/net/lookup_windows.go
@@ -11,8 +11,22 @@ import (
"sync"
)
-var hostentLock sync.Mutex
-var serventLock sync.Mutex
+var (
+ protoentLock sync.Mutex
+ hostentLock sync.Mutex
+ serventLock sync.Mutex
+)
+
+// lookupProtocol looks up IP protocol name and returns correspondent protocol number.
+func lookupProtocol(name string) (proto int, err os.Error) {
+ protoentLock.Lock()
+ defer protoentLock.Unlock()
+ p, e := syscall.GetProtoByName(name)
+ if e != 0 {
+ return 0, os.NewSyscallError("GetProtoByName", e)
+ }
+ return int(p.Proto), nil
+}
func LookupHost(name string) (addrs []string, err os.Error) {
ips, err := LookupIP(name)
@@ -77,9 +91,23 @@ func LookupCNAME(name string) (cname string, err os.Error) {
return
}
+// LookupSRV tries to resolve an SRV query of the given service,
+// protocol, and domain name. The proto is "tcp" or "udp".
+// The returned records are sorted by priority and randomized
+// by weight within a priority.
+//
+// LookupSRV constructs the DNS name to look up following RFC 2782.
+// That is, it looks up _service._proto.name. To accommodate services
+// publishing SRV records under non-standard names, if both service
+// and proto are empty strings, LookupSRV looks up name directly.
func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.Error) {
+ var target string
+ if service == "" && proto == "" {
+ target = name
+ } else {
+ target = "_" + service + "._" + proto + "." + name
+ }
var r *syscall.DNSRecord
- target := "_" + service + "._" + proto + "." + name
e := syscall.DnsQuery(target, syscall.DNS_TYPE_SRV, 0, nil, &r, nil)
if int(e) != 0 {
return "", nil, os.NewSyscallError("LookupSRV", int(e))
@@ -110,6 +138,10 @@ func LookupMX(name string) (mx []*MX, err os.Error) {
return mx, nil
}
+func LookupTXT(name string) (txt []string, err os.Error) {
+ return nil, os.NewError("net.LookupTXT is not implemented on Windows")
+}
+
func LookupAddr(addr string) (name []string, err os.Error) {
arpa, err := reverseaddr(addr)
if err != nil {
diff --git a/libgo/go/net/multicast_test.go b/libgo/go/net/multicast_test.go
index be6dbf2..a66250c 100644
--- a/libgo/go/net/multicast_test.go
+++ b/libgo/go/net/multicast_test.go
@@ -6,13 +6,33 @@ package net
import (
"flag"
+ "os"
"runtime"
"testing"
)
var multicast = flag.Bool("multicast", false, "enable multicast tests")
-func TestMulticastJoinAndLeave(t *testing.T) {
+var joinAndLeaveGroupUDPTests = []struct {
+ net string
+ laddr IP
+ gaddr IP
+ flags Flags
+ ipv6 bool
+}{
+ // cf. RFC 4727: Experimental Values in IPv4, IPv6, ICMPv4, ICMPv6, UDP, and TCP Headers
+ {"udp", IPv4zero, IPv4(224, 0, 0, 254), (FlagUp | FlagLoopback), false},
+ {"udp4", IPv4zero, IPv4(224, 0, 0, 254), (FlagUp | FlagLoopback), false},
+ {"udp", IPv6unspecified, ParseIP("ff0e::114"), (FlagUp | FlagLoopback), true},
+ {"udp6", IPv6unspecified, ParseIP("ff01::114"), (FlagUp | FlagLoopback), true},
+ {"udp6", IPv6unspecified, ParseIP("ff02::114"), (FlagUp | FlagLoopback), true},
+ {"udp6", IPv6unspecified, ParseIP("ff04::114"), (FlagUp | FlagLoopback), true},
+ {"udp6", IPv6unspecified, ParseIP("ff05::114"), (FlagUp | FlagLoopback), true},
+ {"udp6", IPv6unspecified, ParseIP("ff08::114"), (FlagUp | FlagLoopback), true},
+ {"udp6", IPv6unspecified, ParseIP("ff0e::114"), (FlagUp | FlagLoopback), true},
+}
+
+func TestJoinAndLeaveGroupUDP(t *testing.T) {
if runtime.GOOS == "windows" {
return
}
@@ -21,53 +41,51 @@ func TestMulticastJoinAndLeave(t *testing.T) {
return
}
- addr := &UDPAddr{
- IP: IPv4zero,
- Port: 0,
- }
- // open a UDPConn
- conn, err := ListenUDP("udp4", addr)
- if err != nil {
- t.Fatal(err)
- }
- defer conn.Close()
-
- // try to join group
- mcast := IPv4(224, 0, 0, 254)
- err = conn.JoinGroup(mcast)
- if err != nil {
- t.Fatal(err)
- }
-
- // try to leave group
- err = conn.LeaveGroup(mcast)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func TestJoinFailureWithIPv6Address(t *testing.T) {
- if !*multicast {
- t.Logf("test disabled; use --multicast to enable")
- return
- }
- addr := &UDPAddr{
- IP: IPv4zero,
- Port: 0,
- }
-
- // open a UDPConn
- conn, err := ListenUDP("udp4", addr)
- if err != nil {
- t.Fatal(err)
- }
- defer conn.Close()
-
- // try to join group
- mcast := ParseIP("ff02::1")
- err = conn.JoinGroup(mcast)
- if err == nil {
- t.Fatal("JoinGroup succeeded, should fail")
+ for _, tt := range joinAndLeaveGroupUDPTests {
+ var (
+ ifi *Interface
+ found bool
+ )
+ if tt.ipv6 && (!supportsIPv6 || os.Getuid() != 0) {
+ continue
+ }
+ ift, err := Interfaces()
+ if err != nil {
+ t.Fatalf("Interfaces() failed: %v", err)
+ }
+ for _, x := range ift {
+ if x.Flags&tt.flags == tt.flags {
+ ifi = &x
+ break
+ }
+ }
+ if ifi == nil {
+ t.Logf("an appropriate multicast interface not found")
+ return
+ }
+ c, err := ListenUDP(tt.net, &UDPAddr{IP: tt.laddr})
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+ if err := c.JoinGroup(ifi, tt.gaddr); err != nil {
+ t.Fatal(err)
+ }
+ ifmat, err := ifi.MulticastAddrs()
+ if err != nil {
+ t.Fatalf("MulticastAddrs() failed: %v", err)
+ }
+ for _, ifma := range ifmat {
+ if ifma.(*IPAddr).IP.Equal(tt.gaddr) {
+ found = true
+ break
+ }
+ }
+ if !found {
+ t.Fatalf("%q not found in RIB", tt.gaddr.String())
+ }
+ if err := c.LeaveGroup(ifi, tt.gaddr); err != nil {
+ t.Fatal(err)
+ }
}
- t.Logf("%s", err)
}
diff --git a/libgo/go/net/net_test.go b/libgo/go/net/net_test.go
index dc0d49d..b4a6e1f 100644
--- a/libgo/go/net/net_test.go
+++ b/libgo/go/net/net_test.go
@@ -6,6 +6,7 @@ package net
import (
"flag"
+ "os"
"regexp"
"testing"
)
@@ -61,6 +62,8 @@ var dialErrorTests = []DialErrorTest{
},
}
+var duplicateErrorPattern = `dial (.*) dial (.*)`
+
func TestDialError(t *testing.T) {
if !*runErrorTest {
t.Logf("test disabled; use --run_error_test to enable")
@@ -80,6 +83,10 @@ func TestDialError(t *testing.T) {
if !match {
t.Errorf("#%d: %q, want match for %#q", i, s, tt.Pattern)
}
+ match, _ = regexp.MatchString(duplicateErrorPattern, s)
+ if match {
+ t.Errorf("#%d: %q, duplicate error return from Dial", i, s)
+ }
}
}
@@ -119,3 +126,46 @@ func TestReverseAddress(t *testing.T) {
}
}
}
+
+func TestShutdown(t *testing.T) {
+ l, err := Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ if l, err = Listen("tcp6", "[::1]:0"); err != nil {
+ t.Fatalf("ListenTCP on :0: %v", err)
+ }
+ }
+
+ go func() {
+ c, err := l.Accept()
+ if err != nil {
+ t.Fatalf("Accept: %v", err)
+ }
+ var buf [10]byte
+ n, err := c.Read(buf[:])
+ if n != 0 || err != os.EOF {
+ t.Fatalf("server Read = %d, %v; want 0, os.EOF", n, err)
+ }
+ c.Write([]byte("response"))
+ c.Close()
+ }()
+
+ c, err := Dial("tcp", l.Addr().String())
+ if err != nil {
+ t.Fatalf("Dial: %v", err)
+ }
+ defer c.Close()
+
+ err = c.(*TCPConn).CloseWrite()
+ if err != nil {
+ t.Fatalf("CloseWrite: %v", err)
+ }
+ var buf [10]byte
+ n, err := c.Read(buf[:])
+ if err != nil {
+ t.Fatalf("client Read: %d, %v", n, err)
+ }
+ got := string(buf[:n])
+ if got != "response" {
+ t.Errorf("read = %q, want \"response\"", got)
+ }
+}
diff --git a/libgo/go/net/newpollserver.go b/libgo/go/net/newpollserver.go
index 4272087..3c9a6da 100644
--- a/libgo/go/net/newpollserver.go
+++ b/libgo/go/net/newpollserver.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build darwin freebsd linux openbsd
+
package net
import (
diff --git a/libgo/go/net/parse.go b/libgo/go/net/parse.go
index de46830..0d30a7a 100644
--- a/libgo/go/net/parse.go
+++ b/libgo/go/net/parse.go
@@ -159,6 +159,18 @@ func xtoi(s string, i0 int) (n int, i int, ok bool) {
return n, i, true
}
+// xtoi2 converts the next two hex digits of s into a byte.
+// If s is longer than 2 bytes then the third byte must be e.
+// If the first two bytes of s are not hex digits or the third byte
+// does not match e, false is returned.
+func xtoi2(s string, e byte) (byte, bool) {
+ if len(s) > 2 && s[2] != e {
+ return 0, false
+ }
+ n, ei, ok := xtoi(s[:2], 0)
+ return byte(n), ok && ei == 2
+}
+
// Integer to decimal.
func itoa(i int) string {
var buf [30]byte
@@ -181,6 +193,37 @@ func itoa(i int) string {
return string(buf[n:])
}
+// Convert i to decimal string.
+func itod(i uint) string {
+ if i == 0 {
+ return "0"
+ }
+
+ // Assemble decimal in reverse order.
+ var b [32]byte
+ bp := len(b)
+ for ; i > 0; i /= 10 {
+ bp--
+ b[bp] = byte(i%10) + '0'
+ }
+
+ return string(b[bp:])
+}
+
+// Convert i to hexadecimal string.
+func itox(i uint, min int) string {
+ // Assemble hexadecimal in reverse order.
+ var b [32]byte
+ bp := len(b)
+ for ; i > 0 || min > 0; i /= 16 {
+ bp--
+ b[bp] = "0123456789abcdef"[byte(i%16)]
+ min--
+ }
+
+ return string(b[bp:])
+}
+
// Number of occurrences of b in s.
func count(s string, b byte) int {
n := 0
diff --git a/libgo/go/net/port.go b/libgo/go/net/port.go
index 8f8327a..a8ca60c 100644
--- a/libgo/go/net/port.go
+++ b/libgo/go/net/port.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build darwin freebsd linux openbsd
+
// Read system port mappings from /etc/services
package net
diff --git a/libgo/go/net/sendfile_stub.go b/libgo/go/net/sendfile_stub.go
index 43e8104..c55be6c 100644
--- a/libgo/go/net/sendfile_stub.go
+++ b/libgo/go/net/sendfile_stub.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build darwin freebsd openbsd
+
package net
import (
diff --git a/libgo/go/net/sendfile_windows.go b/libgo/go/net/sendfile_windows.go
index 3772eee..d9c2f53 100644
--- a/libgo/go/net/sendfile_windows.go
+++ b/libgo/go/net/sendfile_windows.go
@@ -54,7 +54,7 @@ func sendFile(c *netFD, r io.Reader) (written int64, err os.Error, handled bool)
defer c.decref()
var o sendfileOp
- o.Init(c)
+ o.Init(c, 'w')
o.n = uint32(n)
o.src = f.Fd()
done, err := iosrv.ExecIO(&o, 0)
diff --git a/libgo/go/net/server_test.go b/libgo/go/net/server_test.go
index 7d7f7fc..a2ff218 100644
--- a/libgo/go/net/server_test.go
+++ b/libgo/go/net/server_test.go
@@ -115,7 +115,9 @@ func doTest(t *testing.T, network, listenaddr, dialaddr string) {
}
func TestTCPServer(t *testing.T) {
- doTest(t, "tcp", "", "127.0.0.1")
+ if syscall.OS != "openbsd" {
+ 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")
doTest(t, "tcp4", "", "127.0.0.1")
diff --git a/libgo/go/net/sock.go b/libgo/go/net/sock.go
index 821716e..2359014 100644
--- a/libgo/go/net/sock.go
+++ b/libgo/go/net/sock.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build darwin freebsd linux openbsd windows
+
// Sockets
package net
@@ -50,6 +52,7 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal
if ra != nil {
if err = fd.connect(ra); err != nil {
+ closesocket(s)
fd.Close()
return nil, err
}
diff --git a/libgo/go/net/sock_bsd.go b/libgo/go/net/sock_bsd.go
index 5fd5207..c59802f 100644
--- a/libgo/go/net/sock_bsd.go
+++ b/libgo/go/net/sock_bsd.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build darwin freebsd
+
// Sockets for BSD variants
package net
diff --git a/libgo/go/net/tcpsock_posix.go b/libgo/go/net/tcpsock_posix.go
index 5560301..740a63d 100644
--- a/libgo/go/net/tcpsock_posix.go
+++ b/libgo/go/net/tcpsock_posix.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build darwin freebsd linux openbsd windows
+
// TCP sockets
package net
@@ -12,6 +14,11 @@ import (
"syscall"
)
+// BUG(rsc): On OpenBSD, listening on the "tcp" network does not listen for
+// both IPv4 and IPv6 connections. This is due to the fact that IPv4 traffic
+// will not be routed to an IPv6 socket - two separate sockets are required
+// if both AFs are to be supported. See inet6(4) on OpenBSD for details.
+
func sockaddrToTCP(sa syscall.Sockaddr) Addr {
switch sa := sa.(type) {
case *syscall.SockaddrInet4:
@@ -23,7 +30,7 @@ func sockaddrToTCP(sa syscall.Sockaddr) Addr {
}
func (a *TCPAddr) family() int {
- if a == nil || len(a.IP) <= 4 {
+ if a == nil || len(a.IP) <= IPv4len {
return syscall.AF_INET
}
if a.IP.To4() != nil {
@@ -93,6 +100,24 @@ func (c *TCPConn) Close() os.Error {
return err
}
+// CloseRead shuts down the reading side of the TCP connection.
+// Most callers should just use Close.
+func (c *TCPConn) CloseRead() os.Error {
+ if !c.ok() {
+ return os.EINVAL
+ }
+ return c.fd.CloseRead()
+}
+
+// CloseWrite shuts down the writing side of the TCP connection.
+// Most callers should just use Close.
+func (c *TCPConn) CloseWrite() os.Error {
+ if !c.ok() {
+ return os.EINVAL
+ }
+ return c.fd.CloseWrite()
+}
+
// LocalAddr returns the local network address, a *TCPAddr.
func (c *TCPConn) LocalAddr() Addr {
if !c.ok() {
diff --git a/libgo/go/net/textproto/header.go b/libgo/go/net/textproto/header.go
index 288deb2..7fb32f80 100644
--- a/libgo/go/net/textproto/header.go
+++ b/libgo/go/net/textproto/header.go
@@ -39,5 +39,5 @@ func (h MIMEHeader) Get(key string) string {
// Del deletes the values associated with key.
func (h MIMEHeader) Del(key string) {
- h[CanonicalMIMEHeaderKey(key)] = nil, false
+ delete(h, CanonicalMIMEHeaderKey(key))
}
diff --git a/libgo/go/net/textproto/pipeline.go b/libgo/go/net/textproto/pipeline.go
index 8c25884..ca50edd 100644
--- a/libgo/go/net/textproto/pipeline.go
+++ b/libgo/go/net/textproto/pipeline.go
@@ -108,7 +108,7 @@ func (s *sequencer) End(id uint) {
}
c, ok := s.wait[id]
if ok {
- s.wait[id] = nil, false
+ delete(s.wait, id)
}
s.mu.Unlock()
if ok {
diff --git a/libgo/go/net/textproto/reader.go b/libgo/go/net/textproto/reader.go
index ce0ddc7..ece9a99 100644
--- a/libgo/go/net/textproto/reader.go
+++ b/libgo/go/net/textproto/reader.go
@@ -11,6 +11,7 @@ import (
"io/ioutil"
"os"
"strconv"
+ "strings"
)
// BUG(rsc): To let callers manage exposure to denial of service
@@ -182,6 +183,10 @@ func (r *Reader) readCodeLine(expectCode int) (code int, continued bool, message
if err != nil {
return
}
+ return parseCodeLine(line, expectCode)
+}
+
+func parseCodeLine(line string, expectCode int) (code int, continued bool, message string, err os.Error) {
if len(line) < 4 || line[3] != ' ' && line[3] != '-' {
err = ProtocolError("short response: " + line)
return
@@ -224,15 +229,20 @@ func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err os.
return
}
-// ReadResponse reads a multi-line response of the form
+// ReadResponse reads a multi-line response of the form:
+//
// code-message line 1
// code-message line 2
// ...
// code message line n
-// where code is a 3-digit status code. Each line should have the same code.
-// The response is terminated by a line that uses a space between the code and
-// the message line rather than a dash. Each line in message is separated by
-// a newline (\n).
+//
+// where code is a 3-digit status code. The first line starts with the
+// code and a hyphen. The response is terminated by a line that starts
+// with the same code followed by a space. Each line in message is
+// separated by a newline (\n).
+//
+// See page 36 of RFC 959 (http://www.ietf.org/rfc/rfc959.txt) for
+// details.
//
// If the prefix of the status does not match the digits in expectCode,
// ReadResponse returns with err set to &Error{code, message}.
@@ -244,11 +254,18 @@ func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err os.
func (r *Reader) ReadResponse(expectCode int) (code int, message string, err os.Error) {
code, continued, message, err := r.readCodeLine(expectCode)
for err == nil && continued {
+ line, err := r.ReadLine()
+ if err != nil {
+ return 0, "", err
+ }
+
var code2 int
var moreMessage string
- code2, continued, moreMessage, err = r.readCodeLine(expectCode)
- if code != code2 {
- err = ProtocolError("status code mismatch: " + strconv.Itoa(code) + ", " + strconv.Itoa(code2))
+ code2, continued, moreMessage, err = parseCodeLine(line, expectCode)
+ if err != nil || code2 != code {
+ message += "\n" + strings.TrimRight(line, "\r\n")
+ continued = true
+ continue
}
message += "\n" + moreMessage
}
diff --git a/libgo/go/net/textproto/reader_test.go b/libgo/go/net/textproto/reader_test.go
index 0658e58..23ebc3f 100644
--- a/libgo/go/net/textproto/reader_test.go
+++ b/libgo/go/net/textproto/reader_test.go
@@ -138,3 +138,56 @@ func TestReadMIMEHeader(t *testing.T) {
t.Fatalf("ReadMIMEHeader: %v, %v; want %v", m, err, want)
}
}
+
+type readResponseTest struct {
+ in string
+ inCode int
+ wantCode int
+ wantMsg string
+}
+
+var readResponseTests = []readResponseTest{
+ {"230-Anonymous access granted, restrictions apply\n" +
+ "Read the file README.txt,\n" +
+ "230 please",
+ 23,
+ 230,
+ "Anonymous access granted, restrictions apply\nRead the file README.txt,\n please",
+ },
+
+ {"230 Anonymous access granted, restrictions apply\n",
+ 23,
+ 230,
+ "Anonymous access granted, restrictions apply",
+ },
+
+ {"400-A\n400-B\n400 C",
+ 4,
+ 400,
+ "A\nB\nC",
+ },
+
+ {"400-A\r\n400-B\r\n400 C\r\n",
+ 4,
+ 400,
+ "A\nB\nC",
+ },
+}
+
+// See http://www.ietf.org/rfc/rfc959.txt page 36.
+func TestRFC959Lines(t *testing.T) {
+ for i, tt := range readResponseTests {
+ r := reader(tt.in + "\nFOLLOWING DATA")
+ code, msg, err := r.ReadResponse(tt.inCode)
+ if err != nil {
+ t.Errorf("#%d: ReadResponse: %v", i, err)
+ continue
+ }
+ if code != tt.wantCode {
+ t.Errorf("#%d: code=%d, want %d", i, code, tt.wantCode)
+ }
+ if msg != tt.wantMsg {
+ t.Errorf("%#d: msg=%q, want %q", i, msg, tt.wantMsg)
+ }
+ }
+}
diff --git a/libgo/go/net/udpsock_plan9.go b/libgo/go/net/udpsock_plan9.go
index bb71960..d5c6ccb 100644
--- a/libgo/go/net/udpsock_plan9.go
+++ b/libgo/go/net/udpsock_plan9.go
@@ -169,17 +169,18 @@ func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err os.Error) {
return &UDPConn{*l.plan9Conn()}, nil
}
-// JoinGroup joins the IPv4 multicast group named by addr.
-// The UDPConn must use the "udp4" network.
-func (c *UDPConn) JoinGroup(addr IP) os.Error {
+// JoinGroup joins the IP multicast group named by addr on ifi,
+// which specifies the interface to join. JoinGroup uses the
+// default multicast interface if ifi is nil.
+func (c *UDPConn) JoinGroup(ifi *Interface, addr IP) os.Error {
if !c.ok() {
return os.EINVAL
}
return os.EPLAN9
}
-// LeaveGroup exits the IPv4 multicast group named by addr.
-func (c *UDPConn) LeaveGroup(addr IP) os.Error {
+// LeaveGroup exits the IP multicast group named by addr on ifi.
+func (c *UDPConn) LeaveGroup(ifi *Interface, addr IP) os.Error {
if !c.ok() {
return os.EINVAL
}
diff --git a/libgo/go/net/udpsock_posix.go b/libgo/go/net/udpsock_posix.go
index d4ea056..06298ee 100644
--- a/libgo/go/net/udpsock_posix.go
+++ b/libgo/go/net/udpsock_posix.go
@@ -2,11 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build darwin freebsd linux openbsd windows
+
// UDP sockets
package net
import (
+ "bytes"
"os"
"syscall"
)
@@ -22,7 +25,7 @@ func sockaddrToUDP(sa syscall.Sockaddr) Addr {
}
func (a *UDPAddr) family() int {
- if a == nil || len(a.IP) <= 4 {
+ if a == nil || len(a.IP) <= IPv4len {
return syscall.AF_INET
}
if a.IP.To4() != nil {
@@ -252,43 +255,94 @@ func (c *UDPConn) BindToDevice(device string) os.Error {
// Closing c does not affect f, and closing f does not affect c.
func (c *UDPConn) File() (f *os.File, err os.Error) { return c.fd.dup() }
-var errInvalidMulticast = os.NewError("invalid IPv4 multicast address")
+// JoinGroup joins the IP multicast group named by addr on ifi,
+// which specifies the interface to join. JoinGroup uses the
+// default multicast interface if ifi is nil.
+func (c *UDPConn) JoinGroup(ifi *Interface, addr IP) os.Error {
+ if !c.ok() {
+ return os.EINVAL
+ }
+ ip := addr.To4()
+ if ip != nil {
+ return joinIPv4GroupUDP(c, ifi, ip)
+ }
+ return joinIPv6GroupUDP(c, ifi, addr)
+}
-// JoinGroup joins the IPv4 multicast group named by addr.
-// The UDPConn must use the "udp4" network.
-func (c *UDPConn) JoinGroup(addr IP) os.Error {
+// LeaveGroup exits the IP multicast group named by addr on ifi.
+func (c *UDPConn) LeaveGroup(ifi *Interface, addr IP) os.Error {
if !c.ok() {
return os.EINVAL
}
ip := addr.To4()
- if ip == nil {
- return &OpError{"joingroup", "udp", &IPAddr{ip}, errInvalidMulticast}
+ if ip != nil {
+ return leaveIPv4GroupUDP(c, ifi, ip)
}
- mreq := &syscall.IPMreq{
- Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]},
+ return leaveIPv6GroupUDP(c, ifi, addr)
+}
+
+func joinIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) os.Error {
+ mreq := &syscall.IPMreq{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}}
+ if err := setIPv4InterfaceToJoin(mreq, ifi); err != nil {
+ return &OpError{"joinipv4group", "udp", &IPAddr{ip}, err}
}
- 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}
+ if err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)); err != nil {
+ return &OpError{"joinipv4group", "udp", &IPAddr{ip}, err}
}
return nil
}
-// LeaveGroup exits the IPv4 multicast group named by addr.
-func (c *UDPConn) LeaveGroup(addr IP) os.Error {
- if !c.ok() {
- return os.EINVAL
+func leaveIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) os.Error {
+ mreq := &syscall.IPMreq{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}}
+ if err := setIPv4InterfaceToJoin(mreq, ifi); err != nil {
+ return &OpError{"leaveipv4group", "udp", &IPAddr{ip}, err}
}
- ip := addr.To4()
- if ip == nil {
- return &OpError{"leavegroup", "udp", &IPAddr{ip}, errInvalidMulticast}
+ if err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreq)); err != nil {
+ return &OpError{"leaveipv4group", "udp", &IPAddr{ip}, err}
}
- mreq := &syscall.IPMreq{
- Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]},
+ return nil
+}
+
+func setIPv4InterfaceToJoin(mreq *syscall.IPMreq, ifi *Interface) os.Error {
+ if ifi == nil {
+ return nil
}
- err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreq))
+ ifat, err := ifi.Addrs()
if err != nil {
- return &OpError{"leavegroup", "udp", &IPAddr{ip}, err}
+ return err
+ }
+ for _, ifa := range ifat {
+ if x := ifa.(*IPAddr).IP.To4(); x != nil {
+ copy(mreq.Interface[:], x)
+ break
+ }
+ }
+ if bytes.Equal(mreq.Multiaddr[:], IPv4zero) {
+ return os.EINVAL
+ }
+ return nil
+}
+
+func joinIPv6GroupUDP(c *UDPConn, ifi *Interface, ip IP) os.Error {
+ mreq := &syscall.IPv6Mreq{}
+ copy(mreq.Multiaddr[:], ip)
+ if ifi != nil {
+ mreq.Interface = uint32(ifi.Index)
+ }
+ if err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(c.fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_JOIN_GROUP, mreq)); err != nil {
+ return &OpError{"joinipv6group", "udp", &IPAddr{ip}, err}
+ }
+ return nil
+}
+
+func leaveIPv6GroupUDP(c *UDPConn, ifi *Interface, ip IP) os.Error {
+ mreq := &syscall.IPv6Mreq{}
+ copy(mreq.Multiaddr[:], ip)
+ if ifi != nil {
+ mreq.Interface = uint32(ifi.Index)
+ }
+ if err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(c.fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_LEAVE_GROUP, mreq)); err != nil {
+ return &OpError{"leaveipv6group", "udp", &IPAddr{ip}, err}
}
return nil
}
diff --git a/libgo/go/net/unixsock_posix.go b/libgo/go/net/unixsock_posix.go
index 38c6fe9..fccf018 100644
--- a/libgo/go/net/unixsock_posix.go
+++ b/libgo/go/net/unixsock_posix.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build darwin freebsd linux openbsd windows
+
// Unix domain sockets
package net