diff options
author | Ondřej Surý <ondrej@sury.org> | 2012-04-06 15:14:11 +0200 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2012-04-06 15:14:11 +0200 |
commit | 505c19580e0f43fe5224431459cacb7c21edd93d (patch) | |
tree | 79e2634c253d60afc0cc0b2f510dc7dcbb48497b /src/pkg/net/udpsock_posix.go | |
parent | 1336a7c91e596c423a49d1194ea42d98bca0d958 (diff) | |
download | golang-505c19580e0f43fe5224431459cacb7c21edd93d.tar.gz |
Imported Upstream version 1upstream/1
Diffstat (limited to 'src/pkg/net/udpsock_posix.go')
-rw-r--r-- | src/pkg/net/udpsock_posix.go | 246 |
1 files changed, 157 insertions, 89 deletions
diff --git a/src/pkg/net/udpsock_posix.go b/src/pkg/net/udpsock_posix.go index d4ea056f3..9e820e1c5 100644 --- a/src/pkg/net/udpsock_posix.go +++ b/src/pkg/net/udpsock_posix.go @@ -2,15 +2,21 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux netbsd openbsd windows + // UDP sockets package net import ( + "errors" "os" "syscall" + "time" ) +var ErrWriteToConnected = errors.New("use of WriteTo with pre-connected UDP") + func sockaddrToUDP(sa syscall.Sockaddr) Addr { switch sa := sa.(type) { case *syscall.SockaddrInet4: @@ -22,7 +28,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 { @@ -31,7 +37,14 @@ func (a *UDPAddr) family() int { return syscall.AF_INET6 } -func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, os.Error) { +func (a *UDPAddr) isWildcard() bool { + if a == nil || a.IP == nil { + return true + } + return a.IP.IsUnspecified() +} + +func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, error) { return ipToSockaddr(family, a.IP, a.Port) } @@ -54,26 +67,26 @@ func (c *UDPConn) ok() bool { return c != nil && c.fd != nil } // Implementation of the Conn interface - see Conn for documentation. -// Read implements the net.Conn Read method. -func (c *UDPConn) Read(b []byte) (n int, err os.Error) { +// Read implements the Conn Read method. +func (c *UDPConn) Read(b []byte) (int, error) { if !c.ok() { - return 0, os.EINVAL + return 0, syscall.EINVAL } return c.fd.Read(b) } -// Write implements the net.Conn Write method. -func (c *UDPConn) Write(b []byte) (n int, err os.Error) { +// Write implements the Conn Write method. +func (c *UDPConn) Write(b []byte) (int, error) { if !c.ok() { - return 0, os.EINVAL + return 0, syscall.EINVAL } return c.fd.Write(b) } // Close closes the UDP connection. -func (c *UDPConn) Close() os.Error { +func (c *UDPConn) Close() error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } err := c.fd.Close() c.fd = nil @@ -96,44 +109,44 @@ func (c *UDPConn) RemoteAddr() Addr { return c.fd.raddr } -// SetTimeout implements the net.Conn SetTimeout method. -func (c *UDPConn) SetTimeout(nsec int64) os.Error { +// SetDeadline implements the Conn SetDeadline method. +func (c *UDPConn) SetDeadline(t time.Time) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } - return setTimeout(c.fd, nsec) + return setDeadline(c.fd, t) } -// SetReadTimeout implements the net.Conn SetReadTimeout method. -func (c *UDPConn) SetReadTimeout(nsec int64) os.Error { +// SetReadDeadline implements the Conn SetReadDeadline method. +func (c *UDPConn) SetReadDeadline(t time.Time) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } - return setReadTimeout(c.fd, nsec) + return setReadDeadline(c.fd, t) } -// SetWriteTimeout implements the net.Conn SetWriteTimeout method. -func (c *UDPConn) SetWriteTimeout(nsec int64) os.Error { +// SetWriteDeadline implements the Conn SetWriteDeadline method. +func (c *UDPConn) SetWriteDeadline(t time.Time) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } - return setWriteTimeout(c.fd, nsec) + return setWriteDeadline(c.fd, t) } // SetReadBuffer sets the size of the operating system's // receive buffer associated with the connection. -func (c *UDPConn) SetReadBuffer(bytes int) os.Error { +func (c *UDPConn) SetReadBuffer(bytes int) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setReadBuffer(c.fd, bytes) } // SetWriteBuffer sets the size of the operating system's // transmit buffer associated with the connection. -func (c *UDPConn) SetWriteBuffer(bytes int) os.Error { +func (c *UDPConn) SetWriteBuffer(bytes int) error { if !c.ok() { - return os.EINVAL + return syscall.EINVAL } return setWriteBuffer(c.fd, bytes) } @@ -145,10 +158,10 @@ func (c *UDPConn) SetWriteBuffer(bytes int) os.Error { // that was on the packet. // // ReadFromUDP can be made to time out and return an error with Timeout() == true -// after a fixed time limit; see SetTimeout and SetReadTimeout. -func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err os.Error) { +// after a fixed time limit; see SetDeadline and SetReadDeadline. +func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) { if !c.ok() { - return 0, nil, os.EINVAL + return 0, nil, syscall.EINVAL } n, sa, err := c.fd.ReadFrom(b) switch sa := sa.(type) { @@ -160,10 +173,10 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err os.Error) { return } -// ReadFrom implements the net.PacketConn ReadFrom method. -func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { +// ReadFrom implements the PacketConn ReadFrom method. +func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) { if !c.ok() { - return 0, nil, os.EINVAL + return 0, nil, syscall.EINVAL } n, uaddr, err := c.ReadFromUDP(b) return n, uaddr.toAddr(), err @@ -173,46 +186,54 @@ func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err os.Error) { // // WriteToUDP can be made to time out and return // an error with Timeout() == true after a fixed time limit; -// see SetTimeout and SetWriteTimeout. +// see SetDeadline and SetWriteDeadline. // On packet-oriented connections, write timeouts are rare. -func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err os.Error) { +func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) { if !c.ok() { - return 0, os.EINVAL + return 0, syscall.EINVAL + } + if c.fd.isConnected { + return 0, &OpError{"write", c.fd.net, addr, ErrWriteToConnected} } - sa, err1 := addr.sockaddr(c.fd.family) - if err1 != nil { - return 0, &OpError{Op: "write", Net: "udp", Addr: addr, Error: err1} + sa, err := addr.sockaddr(c.fd.family) + if err != nil { + return 0, &OpError{"write", c.fd.net, addr, err} } return c.fd.WriteTo(b, sa) } -// WriteTo implements the net.PacketConn WriteTo method. -func (c *UDPConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) { +// WriteTo implements the PacketConn WriteTo method. +func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) { if !c.ok() { - return 0, os.EINVAL + return 0, syscall.EINVAL } a, ok := addr.(*UDPAddr) if !ok { - return 0, &OpError{"writeto", "udp", addr, os.EINVAL} + return 0, &OpError{"write", c.fd.net, addr, syscall.EINVAL} } return c.WriteToUDP(b, a) } +// 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 *UDPConn) File() (f *os.File, err error) { return c.fd.dup() } + // DialUDP connects to the remote address raddr on the network net, // which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used // as the local address for the connection. -func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err os.Error) { +func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) { switch net { case "udp", "udp4", "udp6": default: return nil, UnknownNetworkError(net) } if raddr == nil { - return nil, &OpError{"dial", "udp", nil, errMissingAddress} + return nil, &OpError{"dial", net, nil, errMissingAddress} } - fd, e := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP) - if e != nil { - return nil, e + fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP) + if err != nil { + return nil, err } return newUDPConn(fd), nil } @@ -221,74 +242,121 @@ func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err os.Error) { // local address laddr. The returned connection c's ReadFrom // and WriteTo methods can be used to receive and send UDP // packets with per-packet addressing. -func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err os.Error) { +func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) { switch net { case "udp", "udp4", "udp6": default: return nil, UnknownNetworkError(net) } if laddr == nil { - return nil, &OpError{"listen", "udp", nil, errMissingAddress} + return nil, &OpError{"listen", net, nil, errMissingAddress} } - fd, e := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP) - if e != nil { - return nil, e + fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP) + if err != nil { + return nil, err } return newUDPConn(fd), nil } -// BindToDevice binds a UDPConn to a network interface. -func (c *UDPConn) BindToDevice(device string) os.Error { - if !c.ok() { - return os.EINVAL +// ListenMulticastUDP listens for incoming multicast UDP packets +// addressed to the group address gaddr on ifi, which specifies +// the interface to join. ListenMulticastUDP uses default +// multicast interface if ifi is nil. +func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) { + switch net { + case "udp", "udp4", "udp6": + default: + return nil, UnknownNetworkError(net) } - c.fd.incref() - defer c.fd.decref() - return os.NewSyscallError("setsockopt", syscall.BindToDevice(c.fd.sysfd, device)) + if gaddr == nil || gaddr.IP == nil { + return nil, &OpError{"listenmulticast", net, nil, errMissingAddress} + } + fd, err := internetSocket(net, gaddr.toAddr(), nil, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP) + if err != nil { + return nil, err + } + c := newUDPConn(fd) + ip4 := gaddr.IP.To4() + if ip4 != nil { + err := listenIPv4MulticastUDP(c, ifi, ip4) + if err != nil { + c.Close() + return nil, err + } + } else { + err := listenIPv6MulticastUDP(c, ifi, gaddr.IP) + if err != nil { + c.Close() + return nil, err + } + } + return c, nil } -// 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 *UDPConn) File() (f *os.File, err os.Error) { return c.fd.dup() } - -var errInvalidMulticast = os.NewError("invalid IPv4 multicast address") - -// JoinGroup joins the IPv4 multicast group named by addr. -// The UDPConn must use the "udp4" network. -func (c *UDPConn) JoinGroup(addr IP) os.Error { - if !c.ok() { - return os.EINVAL +func listenIPv4MulticastUDP(c *UDPConn, ifi *Interface, ip IP) error { + if ifi != nil { + err := setIPv4MulticastInterface(c.fd, ifi) + if err != nil { + return err + } + } + err := setIPv4MulticastLoopback(c.fd, false) + if err != nil { + return err + } + err = joinIPv4GroupUDP(c, ifi, ip) + if err != nil { + return err } - ip := addr.To4() - if ip == nil { - return &OpError{"joingroup", "udp", &IPAddr{ip}, errInvalidMulticast} + return nil +} + +func listenIPv6MulticastUDP(c *UDPConn, ifi *Interface, ip IP) error { + if ifi != nil { + err := setIPv6MulticastInterface(c.fd, ifi) + if err != nil { + return err + } } - mreq := &syscall.IPMreq{ - Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}, + err := setIPv6MulticastLoopback(c.fd, false) + if err != nil { + return err } - err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)) + err = joinIPv6GroupUDP(c, ifi, ip) if err != nil { - return &OpError{"joingroup", "udp", &IPAddr{ip}, err} + return 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 joinIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) error { + err := joinIPv4Group(c.fd, ifi, ip) + if err != nil { + return &OpError{"joinipv4group", c.fd.net, &IPAddr{ip}, err} } - ip := addr.To4() - if ip == nil { - return &OpError{"leavegroup", "udp", &IPAddr{ip}, errInvalidMulticast} + return nil +} + +func leaveIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) error { + err := leaveIPv4Group(c.fd, ifi, ip) + if err != nil { + return &OpError{"leaveipv4group", c.fd.net, &IPAddr{ip}, err} } - mreq := &syscall.IPMreq{ - Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}, + return nil +} + +func joinIPv6GroupUDP(c *UDPConn, ifi *Interface, ip IP) error { + err := joinIPv6Group(c.fd, ifi, ip) + if err != nil { + return &OpError{"joinipv6group", c.fd.net, &IPAddr{ip}, err} } - err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreq)) + return nil +} + +func leaveIPv6GroupUDP(c *UDPConn, ifi *Interface, ip IP) error { + err := leaveIPv6Group(c.fd, ifi, ip) if err != nil { - return &OpError{"leavegroup", "udp", &IPAddr{ip}, err} + return &OpError{"leaveipv6group", c.fd.net, &IPAddr{ip}, err} } return nil } |