Fazlul Shahriar | 0f7bc92 | 2011-08-17 13:28:29 -0400 | [diff] [blame] | 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 | // UDP for Plan 9 |
| 6 | |
| 7 | package net |
| 8 | |
| 9 | import ( |
Russ Cox | eb69292 | 2011-11-01 22:05:34 -0400 | [diff] [blame] | 10 | "errors" |
Fazlul Shahriar | 0f7bc92 | 2011-08-17 13:28:29 -0400 | [diff] [blame] | 11 | "os" |
Mikio Hara | 03d4c7c | 2012-02-17 10:59:30 +0900 | [diff] [blame] | 12 | "syscall" |
Mikio Hara | b58b5ba | 2012-01-19 12:25:37 +0900 | [diff] [blame] | 13 | "time" |
Fazlul Shahriar | 0f7bc92 | 2011-08-17 13:28:29 -0400 | [diff] [blame] | 14 | ) |
| 15 | |
| 16 | // UDPConn is the implementation of the Conn and PacketConn |
| 17 | // interfaces for UDP network connections. |
| 18 | type UDPConn struct { |
| 19 | plan9Conn |
| 20 | } |
| 21 | |
Mikio Hara | 3d400db | 2012-01-29 19:11:05 +0900 | [diff] [blame] | 22 | // SetDeadline implements the Conn SetDeadline method. |
Mikio Hara | b58b5ba | 2012-01-19 12:25:37 +0900 | [diff] [blame] | 23 | func (c *UDPConn) SetDeadline(t time.Time) error { |
Mikio Hara | 03d4c7c | 2012-02-17 10:59:30 +0900 | [diff] [blame] | 24 | return syscall.EPLAN9 |
Mikio Hara | b58b5ba | 2012-01-19 12:25:37 +0900 | [diff] [blame] | 25 | } |
| 26 | |
Mikio Hara | 3d400db | 2012-01-29 19:11:05 +0900 | [diff] [blame] | 27 | // SetReadDeadline implements the Conn SetReadDeadline method. |
Mikio Hara | b58b5ba | 2012-01-19 12:25:37 +0900 | [diff] [blame] | 28 | func (c *UDPConn) SetReadDeadline(t time.Time) error { |
Mikio Hara | 03d4c7c | 2012-02-17 10:59:30 +0900 | [diff] [blame] | 29 | return syscall.EPLAN9 |
Mikio Hara | b58b5ba | 2012-01-19 12:25:37 +0900 | [diff] [blame] | 30 | } |
| 31 | |
Mikio Hara | 3d400db | 2012-01-29 19:11:05 +0900 | [diff] [blame] | 32 | // SetWriteDeadline implements the Conn SetWriteDeadline method. |
Mikio Hara | b58b5ba | 2012-01-19 12:25:37 +0900 | [diff] [blame] | 33 | func (c *UDPConn) SetWriteDeadline(t time.Time) error { |
Mikio Hara | 03d4c7c | 2012-02-17 10:59:30 +0900 | [diff] [blame] | 34 | return syscall.EPLAN9 |
Mikio Hara | b58b5ba | 2012-01-19 12:25:37 +0900 | [diff] [blame] | 35 | } |
| 36 | |
Fazlul Shahriar | 0f7bc92 | 2011-08-17 13:28:29 -0400 | [diff] [blame] | 37 | // UDP-specific methods. |
| 38 | |
| 39 | // ReadFromUDP reads a UDP packet from c, copying the payload into b. |
| 40 | // It returns the number of bytes copied into b and the return address |
| 41 | // that was on the packet. |
| 42 | // |
| 43 | // ReadFromUDP can be made to time out and return an error with Timeout() == true |
Mikio Hara | b58b5ba | 2012-01-19 12:25:37 +0900 | [diff] [blame] | 44 | // after a fixed time limit; see SetDeadline and SetReadDeadline. |
Russ Cox | eb69292 | 2011-11-01 22:05:34 -0400 | [diff] [blame] | 45 | func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) { |
Fazlul Shahriar | 0f7bc92 | 2011-08-17 13:28:29 -0400 | [diff] [blame] | 46 | if !c.ok() { |
Rob Pike | 56069f0 | 2012-02-17 10:04:29 +1100 | [diff] [blame] | 47 | return 0, nil, syscall.EINVAL |
Fazlul Shahriar | 0f7bc92 | 2011-08-17 13:28:29 -0400 | [diff] [blame] | 48 | } |
| 49 | if c.data == nil { |
| 50 | c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0) |
| 51 | if err != nil { |
| 52 | return 0, nil, err |
| 53 | } |
| 54 | } |
| 55 | buf := make([]byte, udpHeaderSize+len(b)) |
| 56 | m, err := c.data.Read(buf) |
| 57 | if err != nil { |
| 58 | return |
| 59 | } |
| 60 | if m < udpHeaderSize { |
Russ Cox | eb69292 | 2011-11-01 22:05:34 -0400 | [diff] [blame] | 61 | return 0, nil, errors.New("short read reading UDP header") |
Fazlul Shahriar | 0f7bc92 | 2011-08-17 13:28:29 -0400 | [diff] [blame] | 62 | } |
| 63 | buf = buf[:m] |
| 64 | |
| 65 | h, buf := unmarshalUDPHeader(buf) |
| 66 | n = copy(b, buf) |
| 67 | return n, &UDPAddr{h.raddr, int(h.rport)}, nil |
| 68 | } |
| 69 | |
Mikio Hara | 3d400db | 2012-01-29 19:11:05 +0900 | [diff] [blame] | 70 | // ReadFrom implements the PacketConn ReadFrom method. |
Russ Cox | eb69292 | 2011-11-01 22:05:34 -0400 | [diff] [blame] | 71 | func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err error) { |
Fazlul Shahriar | 0f7bc92 | 2011-08-17 13:28:29 -0400 | [diff] [blame] | 72 | if !c.ok() { |
Rob Pike | 56069f0 | 2012-02-17 10:04:29 +1100 | [diff] [blame] | 73 | return 0, nil, syscall.EINVAL |
Fazlul Shahriar | 0f7bc92 | 2011-08-17 13:28:29 -0400 | [diff] [blame] | 74 | } |
| 75 | return c.ReadFromUDP(b) |
| 76 | } |
| 77 | |
| 78 | // WriteToUDP writes a UDP packet to addr via c, copying the payload from b. |
| 79 | // |
| 80 | // WriteToUDP can be made to time out and return |
| 81 | // an error with Timeout() == true after a fixed time limit; |
Mikio Hara | b58b5ba | 2012-01-19 12:25:37 +0900 | [diff] [blame] | 82 | // see SetDeadline and SetWriteDeadline. |
Fazlul Shahriar | 0f7bc92 | 2011-08-17 13:28:29 -0400 | [diff] [blame] | 83 | // On packet-oriented connections, write timeouts are rare. |
Russ Cox | eb69292 | 2011-11-01 22:05:34 -0400 | [diff] [blame] | 84 | func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err error) { |
Fazlul Shahriar | 0f7bc92 | 2011-08-17 13:28:29 -0400 | [diff] [blame] | 85 | if !c.ok() { |
Rob Pike | 56069f0 | 2012-02-17 10:04:29 +1100 | [diff] [blame] | 86 | return 0, syscall.EINVAL |
Fazlul Shahriar | 0f7bc92 | 2011-08-17 13:28:29 -0400 | [diff] [blame] | 87 | } |
| 88 | if c.data == nil { |
| 89 | c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0) |
| 90 | if err != nil { |
| 91 | return 0, err |
| 92 | } |
| 93 | } |
| 94 | h := new(udpHeader) |
| 95 | h.raddr = addr.IP.To16() |
| 96 | h.laddr = c.laddr.(*UDPAddr).IP.To16() |
| 97 | h.ifcaddr = IPv6zero // ignored (receive only) |
| 98 | h.rport = uint16(addr.Port) |
| 99 | h.lport = uint16(c.laddr.(*UDPAddr).Port) |
| 100 | |
| 101 | buf := make([]byte, udpHeaderSize+len(b)) |
| 102 | i := copy(buf, h.Bytes()) |
| 103 | copy(buf[i:], b) |
| 104 | return c.data.Write(buf) |
| 105 | } |
| 106 | |
Mikio Hara | 3d400db | 2012-01-29 19:11:05 +0900 | [diff] [blame] | 107 | // WriteTo implements the PacketConn WriteTo method. |
Russ Cox | eb69292 | 2011-11-01 22:05:34 -0400 | [diff] [blame] | 108 | func (c *UDPConn) WriteTo(b []byte, addr Addr) (n int, err error) { |
Fazlul Shahriar | 0f7bc92 | 2011-08-17 13:28:29 -0400 | [diff] [blame] | 109 | if !c.ok() { |
Rob Pike | 56069f0 | 2012-02-17 10:04:29 +1100 | [diff] [blame] | 110 | return 0, syscall.EINVAL |
Fazlul Shahriar | 0f7bc92 | 2011-08-17 13:28:29 -0400 | [diff] [blame] | 111 | } |
| 112 | a, ok := addr.(*UDPAddr) |
| 113 | if !ok { |
Rob Pike | 56069f0 | 2012-02-17 10:04:29 +1100 | [diff] [blame] | 114 | return 0, &OpError{"write", c.dir, addr, syscall.EINVAL} |
Fazlul Shahriar | 0f7bc92 | 2011-08-17 13:28:29 -0400 | [diff] [blame] | 115 | } |
| 116 | return c.WriteToUDP(b, a) |
| 117 | } |
| 118 | |
| 119 | // DialUDP connects to the remote address raddr on the network net, |
| 120 | // which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used |
| 121 | // as the local address for the connection. |
Russ Cox | eb69292 | 2011-11-01 22:05:34 -0400 | [diff] [blame] | 122 | func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err error) { |
Fazlul Shahriar | 0f7bc92 | 2011-08-17 13:28:29 -0400 | [diff] [blame] | 123 | switch net { |
| 124 | case "udp", "udp4", "udp6": |
| 125 | default: |
| 126 | return nil, UnknownNetworkError(net) |
| 127 | } |
| 128 | if raddr == nil { |
Mikio Hara | 77cb895 | 2012-01-24 02:59:43 +0900 | [diff] [blame] | 129 | return nil, &OpError{"dial", net, nil, errMissingAddress} |
Fazlul Shahriar | 0f7bc92 | 2011-08-17 13:28:29 -0400 | [diff] [blame] | 130 | } |
| 131 | c1, err := dialPlan9(net, laddr, raddr) |
| 132 | if err != nil { |
| 133 | return |
| 134 | } |
| 135 | return &UDPConn{*c1}, nil |
| 136 | } |
| 137 | |
| 138 | const udpHeaderSize = 16*3 + 2*2 |
| 139 | |
| 140 | type udpHeader struct { |
| 141 | raddr, laddr, ifcaddr IP |
| 142 | rport, lport uint16 |
| 143 | } |
| 144 | |
| 145 | func (h *udpHeader) Bytes() []byte { |
| 146 | b := make([]byte, udpHeaderSize) |
| 147 | i := 0 |
| 148 | i += copy(b[i:i+16], h.raddr) |
| 149 | i += copy(b[i:i+16], h.laddr) |
| 150 | i += copy(b[i:i+16], h.ifcaddr) |
| 151 | b[i], b[i+1], i = byte(h.rport>>8), byte(h.rport), i+2 |
| 152 | b[i], b[i+1], i = byte(h.lport>>8), byte(h.lport), i+2 |
| 153 | return b |
| 154 | } |
| 155 | |
| 156 | func unmarshalUDPHeader(b []byte) (*udpHeader, []byte) { |
| 157 | h := new(udpHeader) |
| 158 | h.raddr, b = IP(b[:16]), b[16:] |
| 159 | h.laddr, b = IP(b[:16]), b[16:] |
| 160 | h.ifcaddr, b = IP(b[:16]), b[16:] |
| 161 | h.rport, b = uint16(b[0])<<8|uint16(b[1]), b[2:] |
| 162 | h.lport, b = uint16(b[0])<<8|uint16(b[1]), b[2:] |
| 163 | return h, b |
| 164 | } |
| 165 | |
| 166 | // ListenUDP listens for incoming UDP packets addressed to the |
| 167 | // local address laddr. The returned connection c's ReadFrom |
| 168 | // and WriteTo methods can be used to receive and send UDP |
| 169 | // packets with per-packet addressing. |
Russ Cox | eb69292 | 2011-11-01 22:05:34 -0400 | [diff] [blame] | 170 | func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err error) { |
Fazlul Shahriar | 0f7bc92 | 2011-08-17 13:28:29 -0400 | [diff] [blame] | 171 | switch net { |
| 172 | case "udp", "udp4", "udp6": |
| 173 | default: |
| 174 | return nil, UnknownNetworkError(net) |
| 175 | } |
| 176 | if laddr == nil { |
Mikio Hara | 77cb895 | 2012-01-24 02:59:43 +0900 | [diff] [blame] | 177 | return nil, &OpError{"listen", net, nil, errMissingAddress} |
Fazlul Shahriar | 0f7bc92 | 2011-08-17 13:28:29 -0400 | [diff] [blame] | 178 | } |
| 179 | l, err := listenPlan9(net, laddr) |
| 180 | if err != nil { |
| 181 | return |
| 182 | } |
| 183 | _, err = l.ctl.WriteString("headers") |
| 184 | if err != nil { |
| 185 | return |
| 186 | } |
| 187 | return &UDPConn{*l.plan9Conn()}, nil |
| 188 | } |
| 189 | |
Mikio Hara | 2f63afd | 2012-02-01 01:53:26 +0900 | [diff] [blame] | 190 | // ListenMulticastUDP listens for incoming multicast UDP packets |
| 191 | // addressed to the group address gaddr on ifi, which specifies |
| 192 | // the interface to join. ListenMulticastUDP uses default |
| 193 | // multicast interface if ifi is nil. |
| 194 | func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) { |
Mikio Hara | 03d4c7c | 2012-02-17 10:59:30 +0900 | [diff] [blame] | 195 | return nil, syscall.EPLAN9 |
Fazlul Shahriar | 0f7bc92 | 2011-08-17 13:28:29 -0400 | [diff] [blame] | 196 | } |