| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // UDP for Plan 9 |
| |
| package net |
| |
| import ( |
| "errors" |
| "os" |
| "syscall" |
| "time" |
| ) |
| |
| // UDPConn is the implementation of the Conn and PacketConn |
| // interfaces for UDP network connections. |
| type UDPConn struct { |
| plan9Conn |
| } |
| |
| // SetDeadline implements the Conn SetDeadline method. |
| func (c *UDPConn) SetDeadline(t time.Time) error { |
| return syscall.EPLAN9 |
| } |
| |
| // SetReadDeadline implements the Conn SetReadDeadline method. |
| func (c *UDPConn) SetReadDeadline(t time.Time) error { |
| return syscall.EPLAN9 |
| } |
| |
| // SetWriteDeadline implements the Conn SetWriteDeadline method. |
| func (c *UDPConn) SetWriteDeadline(t time.Time) error { |
| return syscall.EPLAN9 |
| } |
| |
| // UDP-specific methods. |
| |
| // 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() { |
| return 0, nil, syscall.EINVAL |
| } |
| if c.data == nil { |
| c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0) |
| if err != nil { |
| return 0, nil, err |
| } |
| } |
| buf := make([]byte, udpHeaderSize+len(b)) |
| m, err := c.data.Read(buf) |
| if err != nil { |
| return |
| } |
| if m < udpHeaderSize { |
| return 0, nil, errors.New("short read reading UDP header") |
| } |
| buf = buf[:m] |
| |
| h, buf := unmarshalUDPHeader(buf) |
| n = copy(b, buf) |
| return n, &UDPAddr{h.raddr, int(h.rport)}, nil |
| } |
| |
| // ReadFrom implements the PacketConn ReadFrom method. |
| func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err error) { |
| if !c.ok() { |
| return 0, nil, syscall.EINVAL |
| } |
| return c.ReadFromUDP(b) |
| } |
| |
| // 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) (n int, err error) { |
| if !c.ok() { |
| return 0, syscall.EINVAL |
| } |
| if c.data == nil { |
| c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0) |
| if err != nil { |
| return 0, err |
| } |
| } |
| h := new(udpHeader) |
| h.raddr = addr.IP.To16() |
| h.laddr = c.laddr.(*UDPAddr).IP.To16() |
| h.ifcaddr = IPv6zero // ignored (receive only) |
| h.rport = uint16(addr.Port) |
| h.lport = uint16(c.laddr.(*UDPAddr).Port) |
| |
| buf := make([]byte, udpHeaderSize+len(b)) |
| i := copy(buf, h.Bytes()) |
| copy(buf[i:], b) |
| return c.data.Write(buf) |
| } |
| |
| // WriteTo implements the PacketConn WriteTo method. |
| func (c *UDPConn) WriteTo(b []byte, addr Addr) (n int, err error) { |
| if !c.ok() { |
| return 0, syscall.EINVAL |
| } |
| a, ok := addr.(*UDPAddr) |
| if !ok { |
| return 0, &OpError{"write", c.dir, addr, syscall.EINVAL} |
| } |
| return c.WriteToUDP(b, a) |
| } |
| |
| // 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 error) { |
| switch net { |
| case "udp", "udp4", "udp6": |
| default: |
| return nil, UnknownNetworkError(net) |
| } |
| if raddr == nil { |
| return nil, &OpError{"dial", net, nil, errMissingAddress} |
| } |
| c1, err := dialPlan9(net, laddr, raddr) |
| if err != nil { |
| return |
| } |
| return &UDPConn{*c1}, nil |
| } |
| |
| const udpHeaderSize = 16*3 + 2*2 |
| |
| type udpHeader struct { |
| raddr, laddr, ifcaddr IP |
| rport, lport uint16 |
| } |
| |
| func (h *udpHeader) Bytes() []byte { |
| b := make([]byte, udpHeaderSize) |
| i := 0 |
| i += copy(b[i:i+16], h.raddr) |
| i += copy(b[i:i+16], h.laddr) |
| i += copy(b[i:i+16], h.ifcaddr) |
| b[i], b[i+1], i = byte(h.rport>>8), byte(h.rport), i+2 |
| b[i], b[i+1], i = byte(h.lport>>8), byte(h.lport), i+2 |
| return b |
| } |
| |
| func unmarshalUDPHeader(b []byte) (*udpHeader, []byte) { |
| h := new(udpHeader) |
| h.raddr, b = IP(b[:16]), b[16:] |
| h.laddr, b = IP(b[:16]), b[16:] |
| h.ifcaddr, b = IP(b[:16]), b[16:] |
| h.rport, b = uint16(b[0])<<8|uint16(b[1]), b[2:] |
| h.lport, b = uint16(b[0])<<8|uint16(b[1]), b[2:] |
| return h, b |
| } |
| |
| // ListenUDP listens for incoming UDP packets addressed to the |
| // 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 error) { |
| switch net { |
| case "udp", "udp4", "udp6": |
| default: |
| return nil, UnknownNetworkError(net) |
| } |
| if laddr == nil { |
| return nil, &OpError{"listen", net, nil, errMissingAddress} |
| } |
| l, err := listenPlan9(net, laddr) |
| if err != nil { |
| return |
| } |
| _, err = l.ctl.WriteString("headers") |
| if err != nil { |
| return |
| } |
| return &UDPConn{*l.plan9Conn()}, nil |
| } |
| |
| // 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) { |
| return nil, syscall.EPLAN9 |
| } |