diff options
author | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2011-10-26 23:57:58 +0000 |
---|---|---|
committer | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2011-10-26 23:57:58 +0000 |
commit | fa5d125b5cfa5c935e46d27a2cbcd71ae37687ac (patch) | |
tree | 19d182df05ead7ff8ba7ee00a7d57555e1383fdf /libgo/go/net | |
parent | e3d46e67996cf20ca3a75fccbb5a0007bfa3f992 (diff) | |
download | gcc-fa5d125b5cfa5c935e46d27a2cbcd71ae37687ac.tar.gz |
Update Go library to last weekly.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@180552 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/net')
42 files changed, 1110 insertions, 348 deletions
diff --git a/libgo/go/net/cgo_bsd.go b/libgo/go/net/cgo_bsd.go index 3951d847416..b8c6ef974e4 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 c6277cb657c..565cbe7fece 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 d088eabc2b5..bfb3dfddff9 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 10c67dcc40a..85d54b3703c 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 f407b177830..eb7db5e2707 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 54e334342ad..afc05991773 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 707dccaa421..80d40af7662 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 70fc344b2a0..c860c842af7 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 3757e143dca..8e8b3b746d4 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 0e411a192f2..ed2559d8c30 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 9a8c2dcbc4c..0fa6740769b 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 8a14cb23202..2696b7f4c58 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 2675f94b973..54fa5ddeb69 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 3d2a0bb9f8a..36ae04ffa71 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 950de6c5926..282b38b5e49 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 0e4089abf8a..c918f247f96 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 b0e2c42053f..61dc3be909c 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 b189b10c4fd..07e627aef4f 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 4e115180061..dafbdab7804 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 0c522fb7fbe..049df9ea4cb 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 37d6b8e315a..d779f4a5d71 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 995ab03d090..c0fcd260472 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 8f5e66212b3..6e79295a948 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 fa3ad7c7f42..ea939f85986 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 be6dbf2dc19..a66250c844b 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 dc0d49d23ac..b4a6e1f5d69 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 427208701b3..3c9a6da5373 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 de46830d292..0d30a7ac609 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 8f8327a3733..a8ca60c60aa 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 43e8104e94c..c55be6c0801 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 3772eee2490..d9c2f537a3d 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 7d7f7fc01c4..a2ff218e708 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 821716e43bd..2359014ad63 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 5fd52074ad3..c59802fecb3 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 5560301b40f..740a63d3038 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 288deb2ceb4..7fb32f8045e 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 8c25884b3bf..ca50eddac38 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 ce0ddc73f84..ece9a99ffbb 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 0658e58b82d..23ebc3f61e8 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 bb7196041a4..d5c6ccb9046 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 d4ea056f3c7..06298ee40c8 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 38c6fe9eb1e..fccf0189c05 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 |