Home | History | Annotate | Download | only in net
      1 // Copyright 2009 The Go Authors.  All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
      6 
      7 package net
      8 
      9 import (
     10 	"syscall"
     11 	"time"
     12 )
     13 
     14 func sockaddrToUDP(sa syscall.Sockaddr) Addr {
     15 	switch sa := sa.(type) {
     16 	case *syscall.SockaddrInet4:
     17 		return &UDPAddr{IP: sa.Addr[0:], Port: sa.Port}
     18 	case *syscall.SockaddrInet6:
     19 		return &UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))}
     20 	}
     21 	return nil
     22 }
     23 
     24 func (a *UDPAddr) family() int {
     25 	if a == nil || len(a.IP) <= IPv4len {
     26 		return syscall.AF_INET
     27 	}
     28 	if a.IP.To4() != nil {
     29 		return syscall.AF_INET
     30 	}
     31 	return syscall.AF_INET6
     32 }
     33 
     34 func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
     35 	if a == nil {
     36 		return nil, nil
     37 	}
     38 	return ipToSockaddr(family, a.IP, a.Port, a.Zone)
     39 }
     40 
     41 // UDPConn is the implementation of the Conn and PacketConn interfaces
     42 // for UDP network connections.
     43 type UDPConn struct {
     44 	conn
     45 }
     46 
     47 func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{conn{fd}} }
     48 
     49 // ReadFromUDP reads a UDP packet from c, copying the payload into b.
     50 // It returns the number of bytes copied into b and the return address
     51 // that was on the packet.
     52 //
     53 // ReadFromUDP can be made to time out and return an error with
     54 // Timeout() == true after a fixed time limit; see SetDeadline and
     55 // SetReadDeadline.
     56 func (c *UDPConn) ReadFromUDP(b []byte) (int, *UDPAddr, error) {
     57 	if !c.ok() {
     58 		return 0, nil, syscall.EINVAL
     59 	}
     60 	var addr *UDPAddr
     61 	n, sa, err := c.fd.readFrom(b)
     62 	switch sa := sa.(type) {
     63 	case *syscall.SockaddrInet4:
     64 		addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port}
     65 	case *syscall.SockaddrInet6:
     66 		addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))}
     67 	}
     68 	if err != nil {
     69 		err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
     70 	}
     71 	return n, addr, err
     72 }
     73 
     74 // ReadFrom implements the PacketConn ReadFrom method.
     75 func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) {
     76 	if !c.ok() {
     77 		return 0, nil, syscall.EINVAL
     78 	}
     79 	n, addr, err := c.ReadFromUDP(b)
     80 	if addr == nil {
     81 		return n, nil, err
     82 	}
     83 	return n, addr, err
     84 }
     85 
     86 // ReadMsgUDP reads a packet from c, copying the payload into b and
     87 // the associated out-of-band data into oob.  It returns the number
     88 // of bytes copied into b, the number of bytes copied into oob, the
     89 // flags that were set on the packet and the source address of the
     90 // packet.
     91 func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, err error) {
     92 	if !c.ok() {
     93 		return 0, 0, 0, nil, syscall.EINVAL
     94 	}
     95 	var sa syscall.Sockaddr
     96 	n, oobn, flags, sa, err = c.fd.readMsg(b, oob)
     97 	switch sa := sa.(type) {
     98 	case *syscall.SockaddrInet4:
     99 		addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port}
    100 	case *syscall.SockaddrInet6:
    101 		addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))}
    102 	}
    103 	if err != nil {
    104 		err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
    105 	}
    106 	return
    107 }
    108 
    109 // WriteToUDP writes a UDP packet to addr via c, copying the payload
    110 // from b.
    111 //
    112 // WriteToUDP can be made to time out and return an error with
    113 // Timeout() == true after a fixed time limit; see SetDeadline and
    114 // SetWriteDeadline.  On packet-oriented connections, write timeouts
    115 // are rare.
    116 func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) {
    117 	if !c.ok() {
    118 		return 0, syscall.EINVAL
    119 	}
    120 	if c.fd.isConnected {
    121 		return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: ErrWriteToConnected}
    122 	}
    123 	if addr == nil {
    124 		return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: nil, Err: errMissingAddress}
    125 	}
    126 	sa, err := addr.sockaddr(c.fd.family)
    127 	if err != nil {
    128 		return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: err}
    129 	}
    130 	n, err := c.fd.writeTo(b, sa)
    131 	if err != nil {
    132 		err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: err}
    133 	}
    134 	return n, err
    135 }
    136 
    137 // WriteTo implements the PacketConn WriteTo method.
    138 func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) {
    139 	if !c.ok() {
    140 		return 0, syscall.EINVAL
    141 	}
    142 	a, ok := addr.(*UDPAddr)
    143 	if !ok {
    144 		return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: syscall.EINVAL}
    145 	}
    146 	return c.WriteToUDP(b, a)
    147 }
    148 
    149 // WriteMsgUDP writes a packet to addr via c if c isn't connected, or
    150 // to c's remote destination address if c is connected (in which case
    151 // addr must be nil).  The payload is copied from b and the associated
    152 // out-of-band data is copied from oob.  It returns the number of
    153 // payload and out-of-band bytes written.
    154 func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err error) {
    155 	if !c.ok() {
    156 		return 0, 0, syscall.EINVAL
    157 	}
    158 	if c.fd.isConnected && addr != nil {
    159 		return 0, 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: ErrWriteToConnected}
    160 	}
    161 	if !c.fd.isConnected && addr == nil {
    162 		return 0, 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: errMissingAddress}
    163 	}
    164 	var sa syscall.Sockaddr
    165 	sa, err = addr.sockaddr(c.fd.family)
    166 	if err != nil {
    167 		return 0, 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: err}
    168 	}
    169 	n, oobn, err = c.fd.writeMsg(b, oob, sa)
    170 	if err != nil {
    171 		err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: err}
    172 	}
    173 	return
    174 }
    175 
    176 // DialUDP connects to the remote address raddr on the network net,
    177 // which must be "udp", "udp4", or "udp6".  If laddr is not nil, it is
    178 // used as the local address for the connection.
    179 func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) {
    180 	switch net {
    181 	case "udp", "udp4", "udp6":
    182 	default:
    183 		return nil, &OpError{Op: "dial", Net: net, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: UnknownNetworkError(net)}
    184 	}
    185 	if raddr == nil {
    186 		return nil, &OpError{Op: "dial", Net: net, Source: laddr.opAddr(), Addr: nil, Err: errMissingAddress}
    187 	}
    188 	return dialUDP(net, laddr, raddr, noDeadline)
    189 }
    190 
    191 func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, error) {
    192 	fd, err := internetSocket(net, laddr, raddr, deadline, syscall.SOCK_DGRAM, 0, "dial")
    193 	if err != nil {
    194 		return nil, &OpError{Op: "dial", Net: net, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err}
    195 	}
    196 	return newUDPConn(fd), nil
    197 }
    198 
    199 // ListenUDP listens for incoming UDP packets addressed to the local
    200 // address laddr.  Net must be "udp", "udp4", or "udp6".  If laddr has
    201 // a port of 0, ListenUDP will choose an available port.
    202 // The LocalAddr method of the returned UDPConn can be used to
    203 // discover the port.  The returned connection's ReadFrom and WriteTo
    204 // methods can be used to receive and send UDP packets with per-packet
    205 // addressing.
    206 func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
    207 	switch net {
    208 	case "udp", "udp4", "udp6":
    209 	default:
    210 		return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr.opAddr(), Err: UnknownNetworkError(net)}
    211 	}
    212 	if laddr == nil {
    213 		laddr = &UDPAddr{}
    214 	}
    215 	fd, err := internetSocket(net, laddr, nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen")
    216 	if err != nil {
    217 		return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: laddr, Err: err}
    218 	}
    219 	return newUDPConn(fd), nil
    220 }
    221 
    222 // ListenMulticastUDP listens for incoming multicast UDP packets
    223 // addressed to the group address gaddr on the interface ifi.
    224 // Network must be "udp", "udp4" or "udp6".
    225 // ListenMulticastUDP uses the system-assigned multicast interface
    226 // when ifi is nil, although this is not recommended because the
    227 // assignment depends on platforms and sometimes it might require
    228 // routing configuration.
    229 //
    230 // ListenMulticastUDP is just for convenience of simple, small
    231 // applications. There are golang.org/x/net/ipv4 and
    232 // golang.org/x/net/ipv6 packages for general purpose uses.
    233 func ListenMulticastUDP(network string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
    234 	switch network {
    235 	case "udp", "udp4", "udp6":
    236 	default:
    237 		return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr.opAddr(), Err: UnknownNetworkError(network)}
    238 	}
    239 	if gaddr == nil || gaddr.IP == nil {
    240 		return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr.opAddr(), Err: errMissingAddress}
    241 	}
    242 	fd, err := internetSocket(network, gaddr, nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen")
    243 	if err != nil {
    244 		return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr, Err: err}
    245 	}
    246 	c := newUDPConn(fd)
    247 	if ip4 := gaddr.IP.To4(); ip4 != nil {
    248 		if err := listenIPv4MulticastUDP(c, ifi, ip4); err != nil {
    249 			c.Close()
    250 			return nil, &OpError{Op: "listen", Net: network, Source: c.fd.laddr, Addr: &IPAddr{IP: ip4}, Err: err}
    251 		}
    252 	} else {
    253 		if err := listenIPv6MulticastUDP(c, ifi, gaddr.IP); err != nil {
    254 			c.Close()
    255 			return nil, &OpError{Op: "listen", Net: network, Source: c.fd.laddr, Addr: &IPAddr{IP: gaddr.IP}, Err: err}
    256 		}
    257 	}
    258 	return c, nil
    259 }
    260 
    261 func listenIPv4MulticastUDP(c *UDPConn, ifi *Interface, ip IP) error {
    262 	if ifi != nil {
    263 		if err := setIPv4MulticastInterface(c.fd, ifi); err != nil {
    264 			return err
    265 		}
    266 	}
    267 	if err := setIPv4MulticastLoopback(c.fd, false); err != nil {
    268 		return err
    269 	}
    270 	if err := joinIPv4Group(c.fd, ifi, ip); err != nil {
    271 		return err
    272 	}
    273 	return nil
    274 }
    275 
    276 func listenIPv6MulticastUDP(c *UDPConn, ifi *Interface, ip IP) error {
    277 	if ifi != nil {
    278 		if err := setIPv6MulticastInterface(c.fd, ifi); err != nil {
    279 			return err
    280 		}
    281 	}
    282 	if err := setIPv6MulticastLoopback(c.fd, false); err != nil {
    283 		return err
    284 	}
    285 	if err := joinIPv6Group(c.fd, ifi, ip); err != nil {
    286 		return err
    287 	}
    288 	return nil
    289 }
    290