From: Mikio Hara Date: Wed, 2 Mar 2016 21:22:11 +0000 (+0900) Subject: net: deduplicate UDP socket code X-Git-Tag: go1.7beta1~1305 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=20de705b711778864f285f06e42393c9ea2dad36;p=gostls13.git net: deduplicate UDP socket code This change consolidates functions and methods related to UDPAddr and UDPConn for maintenance purpose, especially for documentation. The followup changes will update comments and examples. Updates #10624. Change-Id: Idfe9be8ea46ade1111b0ae176862b2048eafc7be Reviewed-on: https://go-review.googlesource.com/20120 TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- diff --git a/src/net/udpsock.go b/src/net/udpsock.go index ab5cee8ff3..e7e9796668 100644 --- a/src/net/udpsock.go +++ b/src/net/udpsock.go @@ -4,6 +4,8 @@ package net +import "syscall" + // UDPAddr represents the address of a UDP end point. type UDPAddr struct { IP IP @@ -59,3 +61,179 @@ func ResolveUDPAddr(net, addr string) (*UDPAddr, error) { } return addrs.first(isIPv4).(*UDPAddr), nil } + +// UDPConn is the implementation of the Conn and PacketConn interfaces +// for UDP network connections. +type UDPConn struct { + conn +} + +// ReadFromUDP reads a UDP packet from c, copying the payload into b. +// It returns the number of bytes copied into b and the return address +// 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 SetDeadline and +// SetReadDeadline. +func (c *UDPConn) ReadFromUDP(b []byte) (int, *UDPAddr, error) { + if !c.ok() { + return 0, nil, syscall.EINVAL + } + n, addr, err := c.readFrom(b) + if err != nil { + err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err} + } + return n, addr, err +} + +// ReadFrom implements the PacketConn ReadFrom method. +func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) { + if !c.ok() { + return 0, nil, syscall.EINVAL + } + n, addr, err := c.readFrom(b) + if err != nil { + err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err} + } + if addr == nil { + return n, nil, err + } + return n, addr, err +} + +// ReadMsgUDP reads a packet from c, copying the payload into b and +// the associated out-of-band data into oob. It returns the number +// of bytes copied into b, the number of bytes copied into oob, the +// flags that were set on the packet and the source address of the +// packet. +func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, err error) { + if !c.ok() { + return 0, 0, 0, nil, syscall.EINVAL + } + n, oobn, flags, addr, err = c.readMsg(b, oob) + if err != nil { + err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err} + } + return +} + +// WriteToUDP writes a UDP packet to addr via c, copying the payload +// from b. +// +// WriteToUDP can be made to time out and return an error with +// Timeout() == true after a fixed time limit; see SetDeadline and +// SetWriteDeadline. On packet-oriented connections, write timeouts +// are rare. +func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) { + if !c.ok() { + return 0, syscall.EINVAL + } + n, err := c.writeTo(b, addr) + if err != nil { + err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: err} + } + return n, err +} + +// WriteTo implements the PacketConn WriteTo method. +func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) { + if !c.ok() { + return 0, syscall.EINVAL + } + a, ok := addr.(*UDPAddr) + if !ok { + return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: syscall.EINVAL} + } + n, err := c.writeTo(b, a) + if err != nil { + err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: a.opAddr(), Err: err} + } + return n, err +} + +// WriteMsgUDP writes a packet to addr via c if c isn't connected, or +// to c's remote destination address if c is connected (in which case +// addr must be nil). The payload is copied from b and the associated +// out-of-band data is copied from oob. It returns the number of +// payload and out-of-band bytes written. +func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err error) { + if !c.ok() { + return 0, 0, syscall.EINVAL + } + n, oobn, err = c.writeMsg(b, oob, addr) + if err != nil { + err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: err} + } + return +} + +func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{conn{fd}} } + +// 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) (*UDPConn, error) { + switch net { + case "udp", "udp4", "udp6": + default: + return nil, &OpError{Op: "dial", Net: net, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: UnknownNetworkError(net)} + } + if raddr == nil { + return nil, &OpError{Op: "dial", Net: net, Source: laddr.opAddr(), Addr: nil, Err: errMissingAddress} + } + c, err := dialUDP(net, laddr, raddr, noDeadline) + if err != nil { + return nil, &OpError{Op: "dial", Net: net, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err} + } + return c, nil +} + +// ListenUDP listens for incoming UDP packets addressed to the local +// address laddr. Net must be "udp", "udp4", or "udp6". If laddr has +// a port of 0, ListenUDP will choose an available port. +// The LocalAddr method of the returned UDPConn can be used to +// discover the port. The returned connection's ReadFrom and WriteTo +// methods can be used to receive and send UDP packets with per-packet +// addressing. +func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) { + switch net { + case "udp", "udp4", "udp6": + default: + return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr.opAddr(), Err: UnknownNetworkError(net)} + } + if laddr == nil { + laddr = &UDPAddr{} + } + c, err := listenUDP(net, laddr) + if err != nil { + return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr.opAddr(), Err: err} + } + return c, nil +} + +// ListenMulticastUDP listens for incoming multicast UDP packets +// addressed to the group address gaddr on the interface ifi. +// Network must be "udp", "udp4" or "udp6". +// ListenMulticastUDP uses the system-assigned multicast interface +// when ifi is nil, although this is not recommended because the +// assignment depends on platforms and sometimes it might require +// routing configuration. +// +// ListenMulticastUDP is just for convenience of simple, small +// applications. There are golang.org/x/net/ipv4 and +// golang.org/x/net/ipv6 packages for general purpose uses. +func ListenMulticastUDP(network string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) { + switch network { + case "udp", "udp4", "udp6": + default: + return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr.opAddr(), Err: UnknownNetworkError(network)} + } + if gaddr == nil || gaddr.IP == nil { + return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr.opAddr(), Err: errMissingAddress} + } + c, err := listenMulticastUDP(network, ifi, gaddr) + if err != nil { + return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr.opAddr(), Err: err} + } + return c, nil +} diff --git a/src/net/udpsock_plan9.go b/src/net/udpsock_plan9.go index 2538909635..5f15427064 100644 --- a/src/net/udpsock_plan9.go +++ b/src/net/udpsock_plan9.go @@ -11,32 +11,14 @@ import ( "time" ) -// UDPConn is the implementation of the Conn and PacketConn interfaces -// for UDP network connections. -type UDPConn struct { - conn -} - -func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{conn{fd}} } - -// ReadFromUDP reads a UDP packet from c, copying the payload into b. -// It returns the number of bytes copied into b and the return address -// 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 SetDeadline and -// SetReadDeadline. -func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) { - if !c.ok() || c.fd.data == nil { - return 0, nil, syscall.EINVAL - } +func (c *UDPConn) readFrom(b []byte) (n int, addr *UDPAddr, err error) { buf := make([]byte, udpHeaderSize+len(b)) - m, err := c.fd.data.Read(buf) + m, err := c.fd.Read(buf) if err != nil { - return 0, nil, &OpError{Op: "read", Net: c.fd.dir, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err} + return 0, nil, err } if m < udpHeaderSize { - return 0, nil, &OpError{Op: "read", Net: c.fd.dir, Source: c.fd.laddr, Addr: c.fd.raddr, Err: errors.New("short read reading UDP header")} + return 0, nil, errors.New("short read reading UDP header") } buf = buf[:m] @@ -45,36 +27,13 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) { return n, &UDPAddr{IP: h.raddr, Port: int(h.rport)}, nil } -// ReadFrom implements the PacketConn ReadFrom method. -func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) { - if !c.ok() { - return 0, nil, syscall.EINVAL - } - return c.ReadFromUDP(b) +func (c *UDPConn) readMsg(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, err error) { + return 0, 0, 0, nil, syscall.EPLAN9 } -// ReadMsgUDP reads a packet from c, copying the payload into b and -// the associated out-of-band data into oob. It returns the number -// of bytes copied into b, the number of bytes copied into oob, the -// flags that were set on the packet and the source address of the -// packet. -func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, err error) { - return 0, 0, 0, nil, &OpError{Op: "read", Net: c.fd.dir, Source: c.fd.laddr, Addr: c.fd.raddr, Err: syscall.EPLAN9} -} - -// WriteToUDP writes a UDP packet to addr via c, copying the payload -// from b. -// -// WriteToUDP can be made to time out and return an error with -// Timeout() == true after a fixed time limit; see SetDeadline and -// SetWriteDeadline. On packet-oriented connections, write timeouts -// are rare. -func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) { - if !c.ok() || c.fd.data == nil { - return 0, syscall.EINVAL - } +func (c *UDPConn) writeTo(b []byte, addr *UDPAddr) (int, error) { if addr == nil { - return 0, &OpError{Op: "write", Net: c.fd.dir, Source: c.fd.laddr, Addr: nil, Err: errMissingAddress} + return 0, errMissingAddress } h := new(udpHeader) h.raddr = addr.IP.To16() @@ -86,56 +45,20 @@ func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) { buf := make([]byte, udpHeaderSize+len(b)) i := copy(buf, h.Bytes()) copy(buf[i:], b) - if _, err := c.fd.data.Write(buf); err != nil { - return 0, &OpError{Op: "write", Net: c.fd.dir, Source: c.fd.laddr, Addr: addr.opAddr(), Err: err} + if _, err := c.fd.Write(buf); err != nil { + return 0, err } return len(b), nil } -// WriteTo implements the PacketConn WriteTo method. -func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) { - if !c.ok() { - return 0, syscall.EINVAL - } - a, ok := addr.(*UDPAddr) - if !ok { - return 0, &OpError{Op: "write", Net: c.fd.dir, Source: c.fd.laddr, Addr: addr, Err: syscall.EINVAL} - } - return c.WriteToUDP(b, a) -} - -// WriteMsgUDP writes a packet to addr via c if c isn't connected, or -// to c's remote destination address if c is connected (in which case -// addr must be nil). The payload is copied from b and the associated -// out-of-band data is copied from oob. It returns the number of -// payload and out-of-band bytes written. -func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err error) { - return 0, 0, &OpError{Op: "write", Net: c.fd.dir, Source: c.fd.laddr, Addr: addr.opAddr(), Err: syscall.EPLAN9} -} - -// 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) (*UDPConn, error) { - c, err := dialUDP(net, laddr, raddr, noDeadline) - if err != nil { - return nil, &OpError{Op: "dial", Net: net, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err} - } - return c, nil +func (c *UDPConn) writeMsg(b, oob []byte, addr *UDPAddr) (n, oobn int, err error) { + return 0, 0, syscall.EPLAN9 } func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, error) { if !deadline.IsZero() { panic("net.dialUDP: deadline not implemented on Plan 9") } - switch net { - case "udp", "udp4", "udp6": - default: - return nil, UnknownNetworkError(net) - } - if raddr == nil { - return nil, errMissingAddress - } fd, err := dialPlan9(net, laddr, raddr) if err != nil { return nil, err @@ -171,49 +94,23 @@ func unmarshalUDPHeader(b []byte) (*udpHeader, []byte) { return h, b } -// ListenUDP listens for incoming UDP packets addressed to the local -// address laddr. Net must be "udp", "udp4", or "udp6". If laddr has -// a port of 0, ListenUDP will choose an available port. -// The LocalAddr method of the returned UDPConn can be used to -// discover the port. The returned connection's ReadFrom and WriteTo -// methods can be used to receive and send UDP packets with per-packet -// addressing. -func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) { - switch net { - case "udp", "udp4", "udp6": - default: - return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr.opAddr(), Err: UnknownNetworkError(net)} - } - if laddr == nil { - laddr = &UDPAddr{} - } - l, err := listenPlan9(net, laddr) +func listenUDP(network string, laddr *UDPAddr) (*UDPConn, error) { + l, err := listenPlan9(network, laddr) if err != nil { return nil, err } _, err = l.ctl.WriteString("headers") if err != nil { - return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr, Err: err} + return nil, err } l.data, err = os.OpenFile(l.dir+"/data", os.O_RDWR, 0) if err != nil { - return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr, Err: err} + return nil, err } fd, err := l.netFD() return newUDPConn(fd), err } -// ListenMulticastUDP listens for incoming multicast UDP packets -// addressed to the group address gaddr on the interface ifi. -// Network must be "udp", "udp4" or "udp6". -// ListenMulticastUDP uses the system-assigned multicast interface -// when ifi is nil, although this is not recommended because the -// assignment depends on platforms and sometimes it might require -// routing configuration. -// -// ListenMulticastUDP is just for convenience of simple, small -// applications. There are golang.org/x/net/ipv4 and -// golang.org/x/net/ipv6 packages for general purpose uses. -func ListenMulticastUDP(network string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) { - return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr.opAddr(), Err: syscall.EPLAN9} +func listenMulticastUDP(network string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) { + return nil, syscall.EPLAN9 } diff --git a/src/net/udpsock_posix.go b/src/net/udpsock_posix.go index 37ead64810..4d3255c996 100644 --- a/src/net/udpsock_posix.go +++ b/src/net/udpsock_posix.go @@ -38,25 +38,7 @@ func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, error) { return ipToSockaddr(family, a.IP, a.Port, a.Zone) } -// UDPConn is the implementation of the Conn and PacketConn interfaces -// for UDP network connections. -type UDPConn struct { - conn -} - -func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{conn{fd}} } - -// ReadFromUDP reads a UDP packet from c, copying the payload into b. -// It returns the number of bytes copied into b and the return address -// 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 SetDeadline and -// SetReadDeadline. -func (c *UDPConn) ReadFromUDP(b []byte) (int, *UDPAddr, error) { - if !c.ok() { - return 0, nil, syscall.EINVAL - } +func (c *UDPConn) readFrom(b []byte) (int, *UDPAddr, error) { var addr *UDPAddr n, sa, err := c.fd.readFrom(b) switch sa := sa.(type) { @@ -65,33 +47,10 @@ func (c *UDPConn) ReadFromUDP(b []byte) (int, *UDPAddr, error) { case *syscall.SockaddrInet6: addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))} } - if err != nil { - err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err} - } return n, addr, err } -// ReadFrom implements the PacketConn ReadFrom method. -func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) { - if !c.ok() { - return 0, nil, syscall.EINVAL - } - n, addr, err := c.ReadFromUDP(b) - if addr == nil { - return n, nil, err - } - return n, addr, err -} - -// ReadMsgUDP reads a packet from c, copying the payload into b and -// the associated out-of-band data into oob. It returns the number -// of bytes copied into b, the number of bytes copied into oob, the -// flags that were set on the packet and the source address of the -// packet. -func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, err error) { - if !c.ok() { - return 0, 0, 0, nil, syscall.EINVAL - } +func (c *UDPConn) readMsg(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, err error) { var sa syscall.Sockaddr n, oobn, flags, sa, err = c.fd.readMsg(b, oob) switch sa := sa.(type) { @@ -100,96 +59,35 @@ func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, case *syscall.SockaddrInet6: addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))} } - if err != nil { - err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err} - } return } -// WriteToUDP writes a UDP packet to addr via c, copying the payload -// from b. -// -// WriteToUDP can be made to time out and return an error with -// Timeout() == true after a fixed time limit; see SetDeadline and -// SetWriteDeadline. On packet-oriented connections, write timeouts -// are rare. -func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) { - if !c.ok() { - return 0, syscall.EINVAL - } +func (c *UDPConn) writeTo(b []byte, addr *UDPAddr) (int, error) { if c.fd.isConnected { - return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: ErrWriteToConnected} + return 0, ErrWriteToConnected } if addr == nil { - return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: nil, Err: errMissingAddress} + return 0, errMissingAddress } sa, err := addr.sockaddr(c.fd.family) if err != nil { - return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: err} + return 0, err } - n, err := c.fd.writeTo(b, sa) - if err != nil { - err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: err} - } - return n, err + return c.fd.writeTo(b, sa) } -// WriteTo implements the PacketConn WriteTo method. -func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) { - if !c.ok() { - return 0, syscall.EINVAL - } - a, ok := addr.(*UDPAddr) - if !ok { - return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: syscall.EINVAL} - } - return c.WriteToUDP(b, a) -} - -// WriteMsgUDP writes a packet to addr via c if c isn't connected, or -// to c's remote destination address if c is connected (in which case -// addr must be nil). The payload is copied from b and the associated -// out-of-band data is copied from oob. It returns the number of -// payload and out-of-band bytes written. -func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err error) { - if !c.ok() { - return 0, 0, syscall.EINVAL - } +func (c *UDPConn) writeMsg(b, oob []byte, addr *UDPAddr) (n, oobn int, err error) { if c.fd.isConnected && addr != nil { - return 0, 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: ErrWriteToConnected} + return 0, 0, ErrWriteToConnected } if !c.fd.isConnected && addr == nil { - return 0, 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: errMissingAddress} - } - var sa syscall.Sockaddr - sa, err = addr.sockaddr(c.fd.family) - if err != nil { - return 0, 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: err} - } - n, oobn, err = c.fd.writeMsg(b, oob, sa) - if err != nil { - err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: err} - } - return -} - -// 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) (*UDPConn, error) { - switch net { - case "udp", "udp4", "udp6": - default: - return nil, &OpError{Op: "dial", Net: net, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: UnknownNetworkError(net)} - } - if raddr == nil { - return nil, &OpError{Op: "dial", Net: net, Source: laddr.opAddr(), Addr: nil, Err: errMissingAddress} + return 0, 0, errMissingAddress } - c, err := dialUDP(net, laddr, raddr, noDeadline) + sa, err := addr.sockaddr(c.fd.family) if err != nil { - return nil, &OpError{Op: "dial", Net: net, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err} + return 0, 0, err } - return c, nil + return c.fd.writeMsg(b, oob, sa) } func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, error) { @@ -200,63 +98,29 @@ func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, e return newUDPConn(fd), nil } -// ListenUDP listens for incoming UDP packets addressed to the local -// address laddr. Net must be "udp", "udp4", or "udp6". If laddr has -// a port of 0, ListenUDP will choose an available port. -// The LocalAddr method of the returned UDPConn can be used to -// discover the port. The returned connection's ReadFrom and WriteTo -// methods can be used to receive and send UDP packets with per-packet -// addressing. -func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) { - switch net { - case "udp", "udp4", "udp6": - default: - return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr.opAddr(), Err: UnknownNetworkError(net)} - } - if laddr == nil { - laddr = &UDPAddr{} - } - fd, err := internetSocket(net, laddr, nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", noCancel) +func listenUDP(network string, laddr *UDPAddr) (*UDPConn, error) { + fd, err := internetSocket(network, laddr, nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", noCancel) if err != nil { - return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr, Err: err} + return nil, err } return newUDPConn(fd), nil } -// ListenMulticastUDP listens for incoming multicast UDP packets -// addressed to the group address gaddr on the interface ifi. -// Network must be "udp", "udp4" or "udp6". -// ListenMulticastUDP uses the system-assigned multicast interface -// when ifi is nil, although this is not recommended because the -// assignment depends on platforms and sometimes it might require -// routing configuration. -// -// ListenMulticastUDP is just for convenience of simple, small -// applications. There are golang.org/x/net/ipv4 and -// golang.org/x/net/ipv6 packages for general purpose uses. -func ListenMulticastUDP(network string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) { - switch network { - case "udp", "udp4", "udp6": - default: - return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr.opAddr(), Err: UnknownNetworkError(network)} - } - if gaddr == nil || gaddr.IP == nil { - return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr.opAddr(), Err: errMissingAddress} - } +func listenMulticastUDP(network string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) { fd, err := internetSocket(network, gaddr, nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", noCancel) if err != nil { - return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr, Err: err} + return nil, err } c := newUDPConn(fd) if ip4 := gaddr.IP.To4(); ip4 != nil { if err := listenIPv4MulticastUDP(c, ifi, ip4); err != nil { c.Close() - return nil, &OpError{Op: "listen", Net: network, Source: c.fd.laddr, Addr: &IPAddr{IP: ip4}, Err: err} + return nil, err } } else { if err := listenIPv6MulticastUDP(c, ifi, gaddr.IP); err != nil { c.Close() - return nil, &OpError{Op: "listen", Net: network, Source: c.fd.laddr, Addr: &IPAddr{IP: gaddr.IP}, Err: err} + return nil, err } } return c, nil