blob: 2cfcc609d435c85a6f60c5610c85b0e9cb6c7bef [file] [log] [blame]
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -04001// 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
Russ Cox27159562011-09-15 16:48:57 -04005// +build darwin freebsd linux openbsd windows
6
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -04007// UDP sockets
8
9package net
10
11import (
Mikio Harafca50822011-08-18 12:22:02 -040012 "bytes"
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -040013 "os"
14 "syscall"
15)
16
17func sockaddrToUDP(sa syscall.Sockaddr) Addr {
18 switch sa := sa.(type) {
19 case *syscall.SockaddrInet4:
20 return &UDPAddr{sa.Addr[0:], sa.Port}
21 case *syscall.SockaddrInet6:
22 return &UDPAddr{sa.Addr[0:], sa.Port}
23 }
24 return nil
25}
26
27func (a *UDPAddr) family() int {
Mikio Hara80f79ad2011-08-24 13:59:33 -040028 if a == nil || len(a.IP) <= IPv4len {
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -040029 return syscall.AF_INET
30 }
31 if a.IP.To4() != nil {
32 return syscall.AF_INET
33 }
34 return syscall.AF_INET6
35}
36
Russ Coxeb692922011-11-01 22:05:34 -040037func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -040038 return ipToSockaddr(family, a.IP, a.Port)
39}
40
41func (a *UDPAddr) toAddr() sockaddr {
42 if a == nil { // nil *UDPAddr
43 return nil // nil interface
44 }
45 return a
46}
47
48// UDPConn is the implementation of the Conn and PacketConn
49// interfaces for UDP network connections.
50type UDPConn struct {
51 fd *netFD
52}
53
54func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{fd} }
55
56func (c *UDPConn) ok() bool { return c != nil && c.fd != nil }
57
58// Implementation of the Conn interface - see Conn for documentation.
59
60// Read implements the net.Conn Read method.
Russ Coxeb692922011-11-01 22:05:34 -040061func (c *UDPConn) Read(b []byte) (n int, err error) {
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -040062 if !c.ok() {
63 return 0, os.EINVAL
64 }
65 return c.fd.Read(b)
66}
67
68// Write implements the net.Conn Write method.
Russ Coxeb692922011-11-01 22:05:34 -040069func (c *UDPConn) Write(b []byte) (n int, err error) {
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -040070 if !c.ok() {
71 return 0, os.EINVAL
72 }
73 return c.fd.Write(b)
74}
75
76// Close closes the UDP connection.
Russ Coxeb692922011-11-01 22:05:34 -040077func (c *UDPConn) Close() error {
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -040078 if !c.ok() {
79 return os.EINVAL
80 }
81 err := c.fd.Close()
82 c.fd = nil
83 return err
84}
85
86// LocalAddr returns the local network address.
87func (c *UDPConn) LocalAddr() Addr {
88 if !c.ok() {
89 return nil
90 }
91 return c.fd.laddr
92}
93
94// RemoteAddr returns the remote network address, a *UDPAddr.
95func (c *UDPConn) RemoteAddr() Addr {
96 if !c.ok() {
97 return nil
98 }
99 return c.fd.raddr
100}
101
102// SetTimeout implements the net.Conn SetTimeout method.
Russ Coxeb692922011-11-01 22:05:34 -0400103func (c *UDPConn) SetTimeout(nsec int64) error {
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400104 if !c.ok() {
105 return os.EINVAL
106 }
107 return setTimeout(c.fd, nsec)
108}
109
110// SetReadTimeout implements the net.Conn SetReadTimeout method.
Russ Coxeb692922011-11-01 22:05:34 -0400111func (c *UDPConn) SetReadTimeout(nsec int64) error {
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400112 if !c.ok() {
113 return os.EINVAL
114 }
115 return setReadTimeout(c.fd, nsec)
116}
117
118// SetWriteTimeout implements the net.Conn SetWriteTimeout method.
Russ Coxeb692922011-11-01 22:05:34 -0400119func (c *UDPConn) SetWriteTimeout(nsec int64) error {
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400120 if !c.ok() {
121 return os.EINVAL
122 }
123 return setWriteTimeout(c.fd, nsec)
124}
125
126// SetReadBuffer sets the size of the operating system's
127// receive buffer associated with the connection.
Russ Coxeb692922011-11-01 22:05:34 -0400128func (c *UDPConn) SetReadBuffer(bytes int) error {
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400129 if !c.ok() {
130 return os.EINVAL
131 }
132 return setReadBuffer(c.fd, bytes)
133}
134
135// SetWriteBuffer sets the size of the operating system's
136// transmit buffer associated with the connection.
Russ Coxeb692922011-11-01 22:05:34 -0400137func (c *UDPConn) SetWriteBuffer(bytes int) error {
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400138 if !c.ok() {
139 return os.EINVAL
140 }
141 return setWriteBuffer(c.fd, bytes)
142}
143
144// UDP-specific methods.
145
146// ReadFromUDP reads a UDP packet from c, copying the payload into b.
147// It returns the number of bytes copied into b and the return address
148// that was on the packet.
149//
150// ReadFromUDP can be made to time out and return an error with Timeout() == true
151// after a fixed time limit; see SetTimeout and SetReadTimeout.
Russ Coxeb692922011-11-01 22:05:34 -0400152func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400153 if !c.ok() {
154 return 0, nil, os.EINVAL
155 }
156 n, sa, err := c.fd.ReadFrom(b)
157 switch sa := sa.(type) {
158 case *syscall.SockaddrInet4:
159 addr = &UDPAddr{sa.Addr[0:], sa.Port}
160 case *syscall.SockaddrInet6:
161 addr = &UDPAddr{sa.Addr[0:], sa.Port}
162 }
163 return
164}
165
166// ReadFrom implements the net.PacketConn ReadFrom method.
Russ Coxeb692922011-11-01 22:05:34 -0400167func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err error) {
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400168 if !c.ok() {
169 return 0, nil, os.EINVAL
170 }
171 n, uaddr, err := c.ReadFromUDP(b)
172 return n, uaddr.toAddr(), err
173}
174
175// WriteToUDP writes a UDP packet to addr via c, copying the payload from b.
176//
177// WriteToUDP can be made to time out and return
178// an error with Timeout() == true after a fixed time limit;
179// see SetTimeout and SetWriteTimeout.
180// On packet-oriented connections, write timeouts are rare.
Russ Coxeb692922011-11-01 22:05:34 -0400181func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err error) {
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400182 if !c.ok() {
183 return 0, os.EINVAL
184 }
185 sa, err1 := addr.sockaddr(c.fd.family)
186 if err1 != nil {
Russ Coxeb692922011-11-01 22:05:34 -0400187 return 0, &OpError{Op: "write", Net: "udp", Addr: addr, Err: err1}
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400188 }
189 return c.fd.WriteTo(b, sa)
190}
191
192// WriteTo implements the net.PacketConn WriteTo method.
Russ Coxeb692922011-11-01 22:05:34 -0400193func (c *UDPConn) WriteTo(b []byte, addr Addr) (n int, err error) {
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400194 if !c.ok() {
195 return 0, os.EINVAL
196 }
197 a, ok := addr.(*UDPAddr)
198 if !ok {
199 return 0, &OpError{"writeto", "udp", addr, os.EINVAL}
200 }
201 return c.WriteToUDP(b, a)
202}
203
204// DialUDP connects to the remote address raddr on the network net,
205// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used
206// as the local address for the connection.
Russ Coxeb692922011-11-01 22:05:34 -0400207func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err error) {
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400208 switch net {
209 case "udp", "udp4", "udp6":
210 default:
211 return nil, UnknownNetworkError(net)
212 }
213 if raddr == nil {
214 return nil, &OpError{"dial", "udp", nil, errMissingAddress}
215 }
216 fd, e := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP)
217 if e != nil {
218 return nil, e
219 }
220 return newUDPConn(fd), nil
221}
222
223// ListenUDP listens for incoming UDP packets addressed to the
224// local address laddr. The returned connection c's ReadFrom
225// and WriteTo methods can be used to receive and send UDP
226// packets with per-packet addressing.
Russ Coxeb692922011-11-01 22:05:34 -0400227func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err error) {
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400228 switch net {
229 case "udp", "udp4", "udp6":
230 default:
231 return nil, UnknownNetworkError(net)
232 }
233 if laddr == nil {
234 return nil, &OpError{"listen", "udp", nil, errMissingAddress}
235 }
236 fd, e := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP)
237 if e != nil {
238 return nil, e
239 }
240 return newUDPConn(fd), nil
241}
242
243// BindToDevice binds a UDPConn to a network interface.
Russ Coxeb692922011-11-01 22:05:34 -0400244func (c *UDPConn) BindToDevice(device string) error {
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400245 if !c.ok() {
246 return os.EINVAL
247 }
248 c.fd.incref()
249 defer c.fd.decref()
250 return os.NewSyscallError("setsockopt", syscall.BindToDevice(c.fd.sysfd, device))
251}
252
253// File returns a copy of the underlying os.File, set to blocking mode.
254// It is the caller's responsibility to close f when finished.
255// Closing c does not affect f, and closing f does not affect c.
Russ Coxeb692922011-11-01 22:05:34 -0400256func (c *UDPConn) File() (f *os.File, err error) { return c.fd.dup() }
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400257
Mikio Harafca50822011-08-18 12:22:02 -0400258// JoinGroup joins the IP multicast group named by addr on ifi,
259// which specifies the interface to join. JoinGroup uses the
260// default multicast interface if ifi is nil.
Russ Coxeb692922011-11-01 22:05:34 -0400261func (c *UDPConn) JoinGroup(ifi *Interface, addr IP) error {
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400262 if !c.ok() {
263 return os.EINVAL
264 }
265 ip := addr.To4()
Mikio Harafca50822011-08-18 12:22:02 -0400266 if ip != nil {
267 return joinIPv4GroupUDP(c, ifi, ip)
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400268 }
Mikio Harafca50822011-08-18 12:22:02 -0400269 return joinIPv6GroupUDP(c, ifi, addr)
270}
271
272// LeaveGroup exits the IP multicast group named by addr on ifi.
Russ Coxeb692922011-11-01 22:05:34 -0400273func (c *UDPConn) LeaveGroup(ifi *Interface, addr IP) error {
Mikio Harafca50822011-08-18 12:22:02 -0400274 if !c.ok() {
275 return os.EINVAL
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400276 }
Mikio Harafca50822011-08-18 12:22:02 -0400277 ip := addr.To4()
278 if ip != nil {
279 return leaveIPv4GroupUDP(c, ifi, ip)
280 }
281 return leaveIPv6GroupUDP(c, ifi, addr)
282}
283
Russ Coxeb692922011-11-01 22:05:34 -0400284func joinIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
Mikio Harafca50822011-08-18 12:22:02 -0400285 mreq := &syscall.IPMreq{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}}
286 if err := setIPv4InterfaceToJoin(mreq, ifi); err != nil {
287 return &OpError{"joinipv4group", "udp", &IPAddr{ip}, err}
288 }
289 if err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)); err != nil {
290 return &OpError{"joinipv4group", "udp", &IPAddr{ip}, err}
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400291 }
292 return nil
293}
294
Russ Coxeb692922011-11-01 22:05:34 -0400295func leaveIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
Mikio Harafca50822011-08-18 12:22:02 -0400296 mreq := &syscall.IPMreq{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}}
297 if err := setIPv4InterfaceToJoin(mreq, ifi); err != nil {
298 return &OpError{"leaveipv4group", "udp", &IPAddr{ip}, err}
299 }
300 if err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreq)); err != nil {
301 return &OpError{"leaveipv4group", "udp", &IPAddr{ip}, err}
302 }
303 return nil
304}
305
Russ Coxeb692922011-11-01 22:05:34 -0400306func setIPv4InterfaceToJoin(mreq *syscall.IPMreq, ifi *Interface) error {
Mikio Harafca50822011-08-18 12:22:02 -0400307 if ifi == nil {
308 return nil
309 }
310 ifat, err := ifi.Addrs()
311 if err != nil {
312 return err
313 }
314 for _, ifa := range ifat {
315 if x := ifa.(*IPAddr).IP.To4(); x != nil {
316 copy(mreq.Interface[:], x)
317 break
318 }
319 }
320 if bytes.Equal(mreq.Multiaddr[:], IPv4zero) {
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400321 return os.EINVAL
322 }
Mikio Harafca50822011-08-18 12:22:02 -0400323 return nil
324}
325
Russ Coxeb692922011-11-01 22:05:34 -0400326func joinIPv6GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
Mikio Harafca50822011-08-18 12:22:02 -0400327 mreq := &syscall.IPv6Mreq{}
328 copy(mreq.Multiaddr[:], ip)
329 if ifi != nil {
330 mreq.Interface = uint32(ifi.Index)
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400331 }
Mikio Harafca50822011-08-18 12:22:02 -0400332 if err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(c.fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_JOIN_GROUP, mreq)); err != nil {
333 return &OpError{"joinipv6group", "udp", &IPAddr{ip}, err}
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400334 }
Mikio Harafca50822011-08-18 12:22:02 -0400335 return nil
336}
337
Russ Coxeb692922011-11-01 22:05:34 -0400338func leaveIPv6GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
Mikio Harafca50822011-08-18 12:22:02 -0400339 mreq := &syscall.IPv6Mreq{}
340 copy(mreq.Multiaddr[:], ip)
341 if ifi != nil {
342 mreq.Interface = uint32(ifi.Index)
343 }
344 if err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(c.fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_LEAVE_GROUP, mreq)); err != nil {
345 return &OpError{"leaveipv6group", "udp", &IPAddr{ip}, err}
Fazlul Shahriar0f7bc922011-08-17 13:28:29 -0400346 }
347 return nil
348}