Home | History | Annotate | Download | only in net
      1 // Copyright 2010 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 // BUG(mikio): On every POSIX platform, reads from the "ip4" network
     15 // using the ReadFrom or ReadFromIP method might not return a complete
     16 // IPv4 packet, including its header, even if there is space
     17 // available. This can occur even in cases where Read or ReadMsgIP
     18 // could return a complete packet. For this reason, it is recommended
     19 // that you do not uses these methods if it is important to receive a
     20 // full packet.
     21 //
     22 // The Go 1 compatibility guidelines make it impossible for us to
     23 // change the behavior of these methods; use Read or ReadMsgIP
     24 // instead.
     25 
     26 func sockaddrToIP(sa syscall.Sockaddr) Addr {
     27 	switch sa := sa.(type) {
     28 	case *syscall.SockaddrInet4:
     29 		return &IPAddr{IP: sa.Addr[0:]}
     30 	case *syscall.SockaddrInet6:
     31 		return &IPAddr{IP: sa.Addr[0:], Zone: zoneToString(int(sa.ZoneId))}
     32 	}
     33 	return nil
     34 }
     35 
     36 func (a *IPAddr) family() int {
     37 	if a == nil || len(a.IP) <= IPv4len {
     38 		return syscall.AF_INET
     39 	}
     40 	if a.IP.To4() != nil {
     41 		return syscall.AF_INET
     42 	}
     43 	return syscall.AF_INET6
     44 }
     45 
     46 func (a *IPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
     47 	if a == nil {
     48 		return nil, nil
     49 	}
     50 	return ipToSockaddr(family, a.IP, 0, a.Zone)
     51 }
     52 
     53 // IPConn is the implementation of the Conn and PacketConn interfaces
     54 // for IP network connections.
     55 type IPConn struct {
     56 	conn
     57 }
     58 
     59 func newIPConn(fd *netFD) *IPConn { return &IPConn{conn{fd}} }
     60 
     61 // ReadFromIP reads an IP packet from c, copying the payload into b.
     62 // It returns the number of bytes copied into b and the return address
     63 // that was on the packet.
     64 //
     65 // ReadFromIP can be made to time out and return an error with
     66 // Timeout() == true after a fixed time limit; see SetDeadline and
     67 // SetReadDeadline.
     68 func (c *IPConn) ReadFromIP(b []byte) (int, *IPAddr, error) {
     69 	if !c.ok() {
     70 		return 0, nil, syscall.EINVAL
     71 	}
     72 	// TODO(cw,rsc): consider using readv if we know the family
     73 	// type to avoid the header trim/copy
     74 	var addr *IPAddr
     75 	n, sa, err := c.fd.readFrom(b)
     76 	switch sa := sa.(type) {
     77 	case *syscall.SockaddrInet4:
     78 		addr = &IPAddr{IP: sa.Addr[0:]}
     79 		n = stripIPv4Header(n, b)
     80 	case *syscall.SockaddrInet6:
     81 		addr = &IPAddr{IP: sa.Addr[0:], Zone: zoneToString(int(sa.ZoneId))}
     82 	}
     83 	if err != nil {
     84 		err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
     85 	}
     86 	return n, addr, err
     87 }
     88 
     89 func stripIPv4Header(n int, b []byte) int {
     90 	if len(b) < 20 {
     91 		return n
     92 	}
     93 	l := int(b[0]&0x0f) << 2
     94 	if 20 > l || l > len(b) {
     95 		return n
     96 	}
     97 	if b[0]>>4 != 4 {
     98 		return n
     99 	}
    100 	copy(b, b[l:])
    101 	return n - l
    102 }
    103 
    104 // ReadFrom implements the PacketConn ReadFrom method.
    105 func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) {
    106 	if !c.ok() {
    107 		return 0, nil, syscall.EINVAL
    108 	}
    109 	n, addr, err := c.ReadFromIP(b)
    110 	if addr == nil {
    111 		return n, nil, err
    112 	}
    113 	return n, addr, err
    114 }
    115 
    116 // ReadMsgIP reads a packet from c, copying the payload into b and the
    117 // associated out-of-band data into oob.  It returns the number of
    118 // bytes copied into b, the number of bytes copied into oob, the flags
    119 // that were set on the packet and the source address of the packet.
    120 func (c *IPConn) ReadMsgIP(b, oob []byte) (n, oobn, flags int, addr *IPAddr, err error) {
    121 	if !c.ok() {
    122 		return 0, 0, 0, nil, syscall.EINVAL
    123 	}
    124 	var sa syscall.Sockaddr
    125 	n, oobn, flags, sa, err = c.fd.readMsg(b, oob)
    126 	switch sa := sa.(type) {
    127 	case *syscall.SockaddrInet4:
    128 		addr = &IPAddr{IP: sa.Addr[0:]}
    129 	case *syscall.SockaddrInet6:
    130 		addr = &IPAddr{IP: sa.Addr[0:], Zone: zoneToString(int(sa.ZoneId))}
    131 	}
    132 	if err != nil {
    133 		err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
    134 	}
    135 	return
    136 }
    137 
    138 // WriteToIP writes an IP packet to addr via c, copying the payload
    139 // from b.
    140 //
    141 // WriteToIP can be made to time out and return an error with
    142 // Timeout() == true after a fixed time limit; see SetDeadline and
    143 // SetWriteDeadline.  On packet-oriented connections, write timeouts
    144 // are rare.
    145 func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (int, error) {
    146 	if !c.ok() {
    147 		return 0, syscall.EINVAL
    148 	}
    149 	if c.fd.isConnected {
    150 		return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: ErrWriteToConnected}
    151 	}
    152 	if addr == nil {
    153 		return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: nil, Err: errMissingAddress}
    154 	}
    155 	sa, err := addr.sockaddr(c.fd.family)
    156 	if err != nil {
    157 		return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: err}
    158 	}
    159 	n, err := c.fd.writeTo(b, sa)
    160 	if err != nil {
    161 		err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: err}
    162 	}
    163 	return n, err
    164 }
    165 
    166 // WriteTo implements the PacketConn WriteTo method.
    167 func (c *IPConn) WriteTo(b []byte, addr Addr) (int, error) {
    168 	if !c.ok() {
    169 		return 0, syscall.EINVAL
    170 	}
    171 	a, ok := addr.(*IPAddr)
    172 	if !ok {
    173 		return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: syscall.EINVAL}
    174 	}
    175 	return c.WriteToIP(b, a)
    176 }
    177 
    178 // WriteMsgIP writes a packet to addr via c, copying the payload from
    179 // b and the associated out-of-band data from oob.  It returns the
    180 // number of payload and out-of-band bytes written.
    181 func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error) {
    182 	if !c.ok() {
    183 		return 0, 0, syscall.EINVAL
    184 	}
    185 	if c.fd.isConnected {
    186 		return 0, 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: ErrWriteToConnected}
    187 	}
    188 	if addr == nil {
    189 		return 0, 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: nil, Err: errMissingAddress}
    190 	}
    191 	var sa syscall.Sockaddr
    192 	sa, err = addr.sockaddr(c.fd.family)
    193 	if err != nil {
    194 		return 0, 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: err}
    195 	}
    196 	n, oobn, err = c.fd.writeMsg(b, oob, sa)
    197 	if err != nil {
    198 		err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: err}
    199 	}
    200 	return
    201 }
    202 
    203 // DialIP connects to the remote address raddr on the network protocol
    204 // netProto, which must be "ip", "ip4", or "ip6" followed by a colon
    205 // and a protocol number or name.
    206 func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) {
    207 	return dialIP(netProto, laddr, raddr, noDeadline)
    208 }
    209 
    210 func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn, error) {
    211 	net, proto, err := parseNetwork(netProto)
    212 	if err != nil {
    213 		return nil, &OpError{Op: "dial", Net: netProto, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err}
    214 	}
    215 	switch net {
    216 	case "ip", "ip4", "ip6":
    217 	default:
    218 		return nil, &OpError{Op: "dial", Net: netProto, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: UnknownNetworkError(netProto)}
    219 	}
    220 	if raddr == nil {
    221 		return nil, &OpError{Op: "dial", Net: netProto, Source: laddr.opAddr(), Addr: nil, Err: errMissingAddress}
    222 	}
    223 	fd, err := internetSocket(net, laddr, raddr, deadline, syscall.SOCK_RAW, proto, "dial")
    224 	if err != nil {
    225 		return nil, &OpError{Op: "dial", Net: netProto, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err}
    226 	}
    227 	return newIPConn(fd), nil
    228 }
    229 
    230 // ListenIP listens for incoming IP packets addressed to the local
    231 // address laddr.  The returned connection's ReadFrom and WriteTo
    232 // methods can be used to receive and send IP packets with per-packet
    233 // addressing.
    234 func ListenIP(netProto string, laddr *IPAddr) (*IPConn, error) {
    235 	net, proto, err := parseNetwork(netProto)
    236 	if err != nil {
    237 		return nil, &OpError{Op: "listen", Net: netProto, Source: nil, Addr: laddr.opAddr(), Err: err}
    238 	}
    239 	switch net {
    240 	case "ip", "ip4", "ip6":
    241 	default:
    242 		return nil, &OpError{Op: "listen", Net: netProto, Source: nil, Addr: laddr.opAddr(), Err: UnknownNetworkError(netProto)}
    243 	}
    244 	fd, err := internetSocket(net, laddr, nil, noDeadline, syscall.SOCK_RAW, proto, "listen")
    245 	if err != nil {
    246 		return nil, &OpError{Op: "listen", Net: netProto, Source: nil, Addr: laddr.opAddr(), Err: err}
    247 	}
    248 	return newIPConn(fd), nil
    249 }
    250