udpsock_plan9.go 4.85 KB
Newer Older
Fazlul Shahriar's avatar
Fazlul Shahriar committed
1 2 3 4 5 6 7 8 9
// 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 (
10
	"errors"
Fazlul Shahriar's avatar
Fazlul Shahriar committed
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
	"os"
)

// UDPConn is the implementation of the Conn and PacketConn
// interfaces for UDP network connections.
type UDPConn struct {
	plan9Conn
}

// 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 SetTimeout and SetReadTimeout.
28
func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
Fazlul Shahriar's avatar
Fazlul Shahriar committed
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
	if !c.ok() {
		return 0, nil, os.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 {
44
		return 0, nil, errors.New("short read reading UDP header")
Fazlul Shahriar's avatar
Fazlul Shahriar committed
45 46 47 48 49 50 51 52 53
	}
	buf = buf[:m]

	h, buf := unmarshalUDPHeader(buf)
	n = copy(b, buf)
	return n, &UDPAddr{h.raddr, int(h.rport)}, nil
}

// ReadFrom implements the net.PacketConn ReadFrom method.
54
func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err error) {
Fazlul Shahriar's avatar
Fazlul Shahriar committed
55 56 57 58 59 60 61 62 63 64 65 66
	if !c.ok() {
		return 0, nil, os.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 SetTimeout and SetWriteTimeout.
// On packet-oriented connections, write timeouts are rare.
67
func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err error) {
Fazlul Shahriar's avatar
Fazlul Shahriar committed
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
	if !c.ok() {
		return 0, os.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 net.PacketConn WriteTo method.
91
func (c *UDPConn) WriteTo(b []byte, addr Addr) (n int, err error) {
Fazlul Shahriar's avatar
Fazlul Shahriar committed
92 93 94 95 96 97 98 99 100 101 102 103 104
	if !c.ok() {
		return 0, os.EINVAL
	}
	a, ok := addr.(*UDPAddr)
	if !ok {
		return 0, &OpError{"writeto", "udp", addr, os.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.
105
func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err error) {
Fazlul Shahriar's avatar
Fazlul Shahriar committed
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
	switch net {
	case "udp", "udp4", "udp6":
	default:
		return nil, UnknownNetworkError(net)
	}
	if raddr == nil {
		return nil, &OpError{"dial", "udp", 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.
153
func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err error) {
Fazlul Shahriar's avatar
Fazlul Shahriar committed
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
	switch net {
	case "udp", "udp4", "udp6":
	default:
		return nil, UnknownNetworkError(net)
	}
	if laddr == nil {
		return nil, &OpError{"listen", "udp", 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
}

173 174 175
// JoinGroup joins the IP multicast group named by addr on ifi,
// which specifies the interface to join.  JoinGroup uses the
// default multicast interface if ifi is nil.
176
func (c *UDPConn) JoinGroup(ifi *Interface, addr IP) error {
Fazlul Shahriar's avatar
Fazlul Shahriar committed
177 178 179 180 181 182
	if !c.ok() {
		return os.EINVAL
	}
	return os.EPLAN9
}

183
// LeaveGroup exits the IP multicast group named by addr on ifi.
184
func (c *UDPConn) LeaveGroup(ifi *Interface, addr IP) error {
Fazlul Shahriar's avatar
Fazlul Shahriar committed
185 186 187 188 189
	if !c.ok() {
		return os.EINVAL
	}
	return os.EPLAN9
}