v / vlib / net / udp.c.v
513 lines · 452 sloc · 13.5 KB · 9e1d3ca05db333f60c4201e1d35db705c319d4dd
Raw
1module net
2
3import time
4
5const udp_default_read_timeout = time.second / 10
6const udp_default_write_timeout = time.second / 10
7
8struct UdpSocket {
9 Socket
10 l Addr
11 // TODO(emily): replace with option again
12 // when i figure out how to coerce it properly
13mut:
14 has_r bool
15 r Addr
16}
17
18pub struct UdpConn {
19pub mut:
20 sock UdpSocket
21mut:
22 write_deadline time.Time
23 read_deadline time.Time
24 read_timeout time.Duration
25 write_timeout time.Duration
26 is_blocking bool = true
27}
28
29@[_pack: '1']
30struct Ipv4MulticastRequest {
31 multiaddr [4]u8
32 interface_ip [4]u8
33}
34
35@[_pack: '1']
36struct Ipv6MulticastRequest {
37 multiaddr [16]u8
38 interface_index u32
39}
40
41pub fn dial_udp(raddr string) !&UdpConn {
42 addrs := resolve_addrs_fuzzy(raddr, .udp)!
43
44 for addr in addrs {
45 // create a local socket for this
46 // bind to any port (or file) (we dont care in this
47 // case because we only care about the remote)
48 if sock := new_udp_socket_for_remote(addr) {
49 mut conn := &UdpConn{
50 sock: sock
51 read_timeout: udp_default_read_timeout
52 write_timeout: udp_default_write_timeout
53 }
54 $if net_nonblocking_sockets ? {
55 conn.is_blocking = false
56 }
57 return conn
58 }
59 }
60
61 return error('none')
62}
63
64// pub fn dial_udp(laddr string, raddr string) ?&UdpConn {
65// local := resolve_addr(laddr, .inet, .udp)?
66
67// sbase := new_udp_socket()?
68
69// sock := UdpSocket{
70// handle: sbase.handle
71// l: local
72// r: resolve_wrapper(raddr)
73// }
74// }
75
76pub fn (mut c UdpConn) write_ptr(b &u8, len int) !int {
77 remote := c.sock.remote() or { return err_no_udp_remote }
78 return c.write_to_ptr(remote, b, len)
79}
80
81pub fn (mut c UdpConn) write(buf []u8) !int {
82 return c.write_ptr(buf.data, buf.len)
83}
84
85pub fn (mut c UdpConn) write_string(s string) !int {
86 return c.write_ptr(s.str, s.len)
87}
88
89pub fn (mut c UdpConn) write_to_ptr(addr Addr, b &u8, len int) !int {
90 res := C.sendto(c.sock.handle, b, len, 0, voidptr(&addr), addr.len())
91 if res >= 0 {
92 return res
93 }
94 code := error_code()
95 if code == int(error_ewouldblock) {
96 c.wait_for_write()!
97 socket_error(C.sendto(c.sock.handle, b, len, 0, voidptr(&addr), addr.len()))!
98 } else {
99 wrap_error(code)!
100 }
101 return error('none')
102}
103
104// write_to blocks and writes the buf to the remote addr specified
105pub fn (mut c UdpConn) write_to(addr Addr, buf []u8) !int {
106 return c.write_to_ptr(addr, buf.data, buf.len)
107}
108
109// write_to_string blocks and writes the buf to the remote addr specified
110pub fn (mut c UdpConn) write_to_string(addr Addr, s string) !int {
111 return c.write_to_ptr(addr, s.str, s.len)
112}
113
114// read_ptr reads from the socket into `buf_ptr` up to `len` bytes, returning the number of bytes read and the `Addr` read from.
115pub fn (c &UdpConn) read_ptr(buf_ptr &u8, len int) !(int, Addr) {
116 mut addr := Addr{
117 addr: AddrData{
118 Ip6: Ip6{}
119 }
120 }
121 addr_len := sizeof(Addr)
122 mut res := 0
123 if c.is_blocking {
124 // Honor read deadlines/timeouts before entering a blocking recvfrom call.
125 c.wait_for_read()!
126 res = wrap_read_result(C.recvfrom(c.sock.handle, voidptr(buf_ptr), len, 0, voidptr(&addr),
127 &addr_len))!
128 } else {
129 res = wrap_read_result(C.recvfrom(c.sock.handle, voidptr(buf_ptr), len, 0, voidptr(&addr),
130 &addr_len))!
131 }
132 if res > 0 {
133 return res, addr
134 }
135 code := error_code()
136 if code in [int(error_ewouldblock), int(error_eagain), C.EINTR] {
137 c.wait_for_read()!
138 // same setup as in tcp
139 res = wrap_read_result(C.recvfrom(c.sock.handle, voidptr(buf_ptr), len, 0, voidptr(&addr),
140 &addr_len))!
141 res2 := socket_error(res)!
142 return res2, addr
143 } else {
144 wrap_error(code)!
145 }
146 return error('none')
147}
148
149// read reads from the socket into buf up to buf.len returning the number of bytes read
150pub fn (mut c UdpConn) read(mut buf []u8) !(int, Addr) {
151 return c.read_ptr(buf.data, buf.len)!
152}
153
154pub fn (c &UdpConn) read_deadline() !time.Time {
155 if c.read_deadline.unix() == 0 {
156 return c.read_deadline
157 }
158 return error('none')
159}
160
161pub fn (mut c UdpConn) set_read_deadline(deadline time.Time) {
162 c.read_deadline = deadline
163}
164
165pub fn (c &UdpConn) write_deadline() !time.Time {
166 if c.write_deadline.unix() == 0 {
167 return c.write_deadline
168 }
169 return error('none')
170}
171
172pub fn (mut c UdpConn) set_write_deadline(deadline time.Time) {
173 c.write_deadline = deadline
174}
175
176pub fn (c &UdpConn) read_timeout() time.Duration {
177 return c.read_timeout
178}
179
180pub fn (mut c UdpConn) set_read_timeout(t time.Duration) {
181 c.read_timeout = t
182}
183
184pub fn (c &UdpConn) write_timeout() time.Duration {
185 return c.write_timeout
186}
187
188pub fn (mut c UdpConn) set_write_timeout(t time.Duration) {
189 c.write_timeout = t
190}
191
192@[inline]
193pub fn (c &UdpConn) wait_for_read() ! {
194 return wait_for_read(c.sock.handle, c.read_deadline, c.read_timeout)
195}
196
197@[inline]
198pub fn (mut c UdpConn) wait_for_write() ! {
199 return wait_for_write(c.sock.handle, c.write_deadline, c.write_timeout)
200}
201
202pub fn (c &UdpConn) str() string {
203 // TODO
204 return 'UdpConn'
205}
206
207pub fn (mut c UdpConn) close() ! {
208 return c.sock.close()
209}
210
211// join_multicast_group joins the UDP socket to an IPv4 or IPv6 multicast group.
212// For IPv4 sockets, `iface_addr` must be an IPv4 address or `''` to use the default interface.
213// For IPv6 sockets, `iface_addr` must be a numeric interface index or `''` to use the default interface.
214pub fn (mut c UdpConn) join_multicast_group(multicast_addr string, iface_addr string) ! {
215 family := c.multicast_family()!
216 match family {
217 .ip {
218 mreq := Ipv4MulticastRequest{
219 multiaddr: parse_ipv4_multicast_addr(multicast_addr)!
220 interface_ip: parse_ipv4_interface_addr(iface_addr)!
221 }
222 c.sock.set_option_raw(C.IPPROTO_IP, C.IP_ADD_MEMBERSHIP, &mreq,
223 sizeof(Ipv4MulticastRequest))!
224 }
225 .ip6 {
226 mreq := Ipv6MulticastRequest{
227 multiaddr: parse_ipv6_multicast_addr(multicast_addr)!
228 interface_index: parse_ipv6_interface_index(iface_addr)!
229 }
230 c.sock.set_option_raw(C.IPPROTO_IPV6, ipv6_membership_socket_option(true), &mreq,
231 sizeof(Ipv6MulticastRequest))!
232 }
233 else {
234 return error('net: udp multicast is only supported on ip and ip6 sockets')
235 }
236 }
237}
238
239// leave_multicast_group leaves an IPv4 or IPv6 multicast group previously joined by the socket.
240// `iface_addr` follows the same rules as `join_multicast_group`.
241pub fn (mut c UdpConn) leave_multicast_group(multicast_addr string, iface_addr string) ! {
242 family := c.multicast_family()!
243 match family {
244 .ip {
245 mreq := Ipv4MulticastRequest{
246 multiaddr: parse_ipv4_multicast_addr(multicast_addr)!
247 interface_ip: parse_ipv4_interface_addr(iface_addr)!
248 }
249 c.sock.set_option_raw(C.IPPROTO_IP, C.IP_DROP_MEMBERSHIP, &mreq,
250 sizeof(Ipv4MulticastRequest))!
251 }
252 .ip6 {
253 mreq := Ipv6MulticastRequest{
254 multiaddr: parse_ipv6_multicast_addr(multicast_addr)!
255 interface_index: parse_ipv6_interface_index(iface_addr)!
256 }
257 c.sock.set_option_raw(C.IPPROTO_IPV6, ipv6_membership_socket_option(false), &mreq,
258 sizeof(Ipv6MulticastRequest))!
259 }
260 else {
261 return error('net: udp multicast is only supported on ip and ip6 sockets')
262 }
263 }
264}
265
266// set_multicast_ttl sets the IPv4 TTL or IPv6 hop limit used for outgoing multicast traffic.
267// Valid values are between 0 and 255 inclusive.
268pub fn (mut c UdpConn) set_multicast_ttl(ttl int) ! {
269 if ttl < 0 || ttl > 255 {
270 return error('net: multicast ttl must be between 0 and 255')
271 }
272 match c.multicast_family()! {
273 .ip {
274 c.sock.set_option_int(C.IPPROTO_IP, C.IP_MULTICAST_TTL, ttl)!
275 }
276 .ip6 {
277 c.sock.set_option_int(C.IPPROTO_IPV6, C.IPV6_MULTICAST_HOPS, ttl)!
278 }
279 else {
280 return error('net: udp multicast is only supported on ip and ip6 sockets')
281 }
282 }
283}
284
285// set_multicast_loop enables or disables local loopback for outgoing multicast packets.
286pub fn (mut c UdpConn) set_multicast_loop(enable bool) ! {
287 value := int(enable)
288 match c.multicast_family()! {
289 .ip {
290 c.sock.set_option_int(C.IPPROTO_IP, C.IP_MULTICAST_LOOP, value)!
291 }
292 .ip6 {
293 c.sock.set_option_int(C.IPPROTO_IPV6, C.IPV6_MULTICAST_LOOP, value)!
294 }
295 else {
296 return error('net: udp multicast is only supported on ip and ip6 sockets')
297 }
298 }
299}
300
301// set_multicast_interface sets the outgoing multicast interface for the UDP socket.
302// For IPv4 sockets, `iface_addr` must be an IPv4 address or `''` to clear it to the default interface.
303// For IPv6 sockets, `iface_addr` must be a numeric interface index or `''` to use the default interface.
304pub fn (mut c UdpConn) set_multicast_interface(iface_addr string) ! {
305 match c.multicast_family()! {
306 .ip {
307 addr := parse_ipv4_interface_addr(iface_addr)!
308 c.sock.set_option_raw(C.IPPROTO_IP, C.IP_MULTICAST_IF, &addr[0], sizeof([4]u8))!
309 }
310 .ip6 {
311 index := parse_ipv6_interface_index(iface_addr)!
312 c.sock.set_option_int(C.IPPROTO_IPV6, C.IPV6_MULTICAST_IF, int(index))!
313 }
314 else {
315 return error('net: udp multicast is only supported on ip and ip6 sockets')
316 }
317 }
318}
319
320pub fn listen_udp(laddr string) !&UdpConn {
321 addrs := resolve_addrs_fuzzy(laddr, .udp)!
322 // TODO(emily):
323 // here we are binding to the first address
324 // and that is probably not ideal
325 addr := addrs[0]
326 mut conn := &UdpConn{
327 sock: new_udp_socket(addr)!
328 read_timeout: udp_default_read_timeout
329 write_timeout: udp_default_write_timeout
330 }
331 $if net_nonblocking_sockets ? {
332 conn.is_blocking = false
333 }
334 return conn
335}
336
337fn new_udp_socket(local_addr Addr) !&UdpSocket {
338 family := local_addr.family()
339
340 sockfd := socket_error(C.socket(i32(family), i32(SocketType.udp), 0))!
341 mut s := &UdpSocket{
342 handle: sockfd
343 l: local_addr
344 r: Addr{
345 addr: AddrData{
346 Ip6: Ip6{}
347 }
348 }
349 }
350
351 s.set_option_bool(.reuse_addr, true)!
352
353 if family == .ip6 {
354 s.set_dualstack(true)!
355 }
356
357 $if net_nonblocking_sockets ? {
358 set_blocking(sockfd, false)!
359 }
360
361 // cast to the correct type
362 socket_error(C.bind(s.handle, voidptr(&local_addr), local_addr.len()))!
363 return s
364}
365
366fn new_udp_socket_for_remote(raddr Addr) !&UdpSocket {
367 // Invent a sutible local address for this remote addr
368 // Appease compiler
369 mut addr := Addr{
370 addr: AddrData{
371 Ip6: Ip6{}
372 }
373 }
374 match raddr.family() {
375 .ip {
376 // Use ip dualstack
377 addr = new_ip(0, addr_ip_any)
378 }
379 .ip6 {
380 // Use ip6 dualstack
381 addr = new_ip6(0, addr_ip6_any)
382 }
383 .unix {
384 addr = temp_unix()!
385 }
386 else {
387 panic('Invalid family')
388 }
389 }
390
391 mut sock := new_udp_socket(addr)!
392 sock.has_r = true
393 sock.r = raddr
394
395 return sock
396}
397
398fn (c &UdpConn) multicast_family() !AddrFamily {
399 family := c.sock.l.family()
400 if family !in [.ip, .ip6] {
401 return error('net: udp multicast is only supported on ip and ip6 sockets')
402 }
403 return family
404}
405
406fn normalize_ip_literal(address string) string {
407 if address.len >= 2 && address[0] == `[` && address[address.len - 1] == `]` {
408 return address[1..address.len - 1]
409 }
410 return address
411}
412
413fn parse_ipv4_interface_addr(iface_addr string) ![4]u8 {
414 if iface_addr.len == 0 {
415 return [4]u8{}
416 }
417 address := normalize_ip_literal(iface_addr)
418 mut parsed := [4]u8{}
419 if C.inet_pton(i32(AddrFamily.ip), &char(address.str), &parsed[0]) != 1 {
420 return error('net: ipv4 multicast interface must be an ipv4 address')
421 }
422 return parsed
423}
424
425fn parse_ipv4_multicast_addr(multicast_addr string) ![4]u8 {
426 address := normalize_ip_literal(multicast_addr)
427 mut parsed := [4]u8{}
428 if C.inet_pton(i32(AddrFamily.ip), &char(address.str), &parsed[0]) != 1 {
429 return error('net: invalid ipv4 multicast address `${multicast_addr}`')
430 }
431 if parsed[0] < 224 || parsed[0] > 239 {
432 return error('net: `${multicast_addr}` is not an ipv4 multicast address')
433 }
434 return parsed
435}
436
437fn parse_ipv6_multicast_addr(multicast_addr string) ![16]u8 {
438 address := normalize_ip_literal(multicast_addr)
439 mut parsed := [16]u8{}
440 if C.inet_pton(i32(AddrFamily.ip6), &char(address.str), &parsed[0]) != 1 {
441 return error('net: invalid ipv6 multicast address `${multicast_addr}`')
442 }
443 if parsed[0] != 0xff {
444 return error('net: `${multicast_addr}` is not an ipv6 multicast address')
445 }
446 return parsed
447}
448
449fn parse_ipv6_interface_index(iface_addr string) !u32 {
450 if iface_addr.len == 0 {
451 return 0
452 }
453 for ch in iface_addr {
454 if ch < `0` || ch > `9` {
455 return error('net: ipv6 multicast interface must be a numeric interface index')
456 }
457 }
458 return iface_addr.u32()
459}
460
461fn ipv6_membership_socket_option(join bool) int {
462 $if windows {
463 return if join { C.IPV6_ADD_MEMBERSHIP } else { C.IPV6_DROP_MEMBERSHIP }
464 } $else {
465 return if join { C.IPV6_JOIN_GROUP } else { C.IPV6_LEAVE_GROUP }
466 }
467}
468
469fn (mut s UdpSocket) set_option_raw(level int, opt int, value voidptr, value_len u32) ! {
470 socket_error(C.setsockopt(s.handle, level, opt, value, value_len))!
471}
472
473fn (mut s UdpSocket) set_option_int(level int, opt int, value int) ! {
474 s.set_option_raw(level, opt, &value, sizeof(int))!
475}
476
477// set_option_bool sets a boolean socket option on the UDP socket.
478pub fn (mut s UdpSocket) set_option_bool(opt SocketOption, value bool) ! {
479 // TODO: reenable when this `in` operation works again
480 // if opt !in opts_can_set {
481 // return err_option_not_settable
482 // }
483 // if opt !in opts_bool {
484 // return err_option_wrong_type
485 // }
486 x := int(value)
487 s.set_option_int(C.SOL_SOCKET, int(opt), x)!
488}
489
490// set_dualstack enables or disables dual-stack behavior for IPv6 UDP sockets.
491pub fn (mut s UdpSocket) set_dualstack(on bool) ! {
492 x := int(!on)
493 s.set_option_int(C.IPPROTO_IPV6, int(SocketOption.ipv6_only), x)!
494}
495
496// close shuts down and closes the socket for communication.
497pub fn (mut s UdpSocket) close() ! {
498 shutdown(s.handle)
499 return close(s.handle)
500}
501
502// select waits for no more than `timeout` for the IO operation, defined by `test`, to be available.
503pub fn (mut s UdpSocket) select(test Select, timeout time.Duration) !bool {
504 return select(s.handle, test, timeout)
505}
506
507// remote returns the remote `Addr` address of the socket or `none` if no remote is has been resolved.
508pub fn (s &UdpSocket) remote() ?Addr {
509 if s.has_r {
510 return s.r
511 }
512 return none
513}
514