1 #pragma once
2 
3 
4 #include <type_traits>
5 #include <algorithm>
6 #include <iterator>
7 #include <utility>
8 #include <cstddef>
9 #include <memory>
10 #include <string>
11 #include <uv.h>
12 #include "request.hpp"
13 #include "handle.hpp"
14 #include "util.hpp"
15 
16 
17 namespace uvw {
18 
19 
20 /**
21  * @brief SendEvent event.
22  *
23  * It will be emitted by UDPHandle according with its functionalities.
24  */
25 struct SendEvent {};
26 
27 
28 /**
29  * @brief UDPDataEvent event.
30  *
31  * It will be emitted by UDPHandle according with its functionalities.
32  */
33 struct UDPDataEvent {
UDPDataEventuvw::UDPDataEvent34     explicit UDPDataEvent(Addr sndr, std::unique_ptr<const char[]> buf, std::size_t len, bool part) noexcept
35         : data{std::move(buf)}, length{len}, sender{std::move(sndr)}, partial{part}
36     {}
37 
38     std::unique_ptr<const char[]> data; /*!< A bunch of data read on the stream. */
39     std::size_t length;  /*!< The amount of data read on the stream. */
40     Addr sender; /*!< A valid instance of Addr. */
41     bool partial; /*!< True if the message was truncated, false otherwise. */
42 };
43 
44 
45 namespace details {
46 
47 
48 enum class UVUdpFlags: std::underlying_type_t<uv_udp_flags> {
49     IPV6ONLY = UV_UDP_IPV6ONLY,
50     REUSEADDR = UV_UDP_REUSEADDR
51 };
52 
53 
54 enum class UVMembership: std::underlying_type_t<uv_membership> {
55     LEAVE_GROUP = UV_LEAVE_GROUP,
56     JOIN_GROUP = UV_JOIN_GROUP
57 };
58 
59 
60 class SendReq final: public Request<SendReq, uv_udp_send_t> {
61 public:
62     using Deleter = void(*)(char *);
63 
SendReq(ConstructorAccess ca,std::shared_ptr<Loop> loop,std::unique_ptr<char[],Deleter> dt,unsigned int len)64     SendReq(ConstructorAccess ca, std::shared_ptr<Loop> loop, std::unique_ptr<char[], Deleter> dt, unsigned int len)
65         : Request<SendReq, uv_udp_send_t>{ca, std::move(loop)},
66           data{std::move(dt)},
67           buf{uv_buf_init(data.get(), len)}
68     {}
69 
send(uv_udp_t * handle,const struct sockaddr * addr)70     void send(uv_udp_t *handle, const struct sockaddr* addr) {
71         invoke(&uv_udp_send, get(), handle, &buf, 1, addr, &defaultCallback<SendEvent>);
72     }
73 
74 private:
75     std::unique_ptr<char[], Deleter> data;
76     uv_buf_t buf;
77 };
78 
79 
80 }
81 
82 
83 /**
84  * @brief The UDPHandle handle.
85  *
86  * UDP handles encapsulate UDP communication for both clients and servers.<br/>
87  * By default, _IPv4_ is used as a template parameter. The handle already
88  * supports _IPv6_ out-of-the-box by using `uvw::IPv6`.
89  *
90  * To create an `UDPHandle` through a `Loop`, arguments follow:
91  *
92  * * An optional integer value that indicates optional flags used to initialize
93  * the socket.
94  *
95  * See the official
96  * [documentation](http://docs.libuv.org/en/v1.x/udp.html#c.uv_udp_init_ex)
97  * for further details.
98  */
99 class UDPHandle final: public Handle<UDPHandle, uv_udp_t> {
100     template<typename I>
recvCallback(uv_udp_t * handle,ssize_t nread,const uv_buf_t * buf,const sockaddr * addr,unsigned flags)101     static void recvCallback(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf, const sockaddr *addr, unsigned flags) {
102         const typename details::IpTraits<I>::Type *aptr = reinterpret_cast<const typename details::IpTraits<I>::Type *>(addr);
103 
104         UDPHandle &udp = *(static_cast<UDPHandle*>(handle->data));
105         // data will be destroyed no matter of what the value of nread is
106         std::unique_ptr<const char[]> data{buf->base};
107 
108         if(nread > 0) {
109             // data available (can be truncated)
110             udp.publish(UDPDataEvent{details::address<I>(aptr), std::move(data), static_cast<std::size_t>(nread), !(0 == (flags & UV_UDP_PARTIAL))});
111         } else if(nread == 0 && addr == nullptr) {
112             // no more data to be read, doing nothing is fine
113         } else if(nread == 0 && addr != nullptr) {
114             // empty udp packet
115             udp.publish(UDPDataEvent{details::address<I>(aptr), std::move(data), static_cast<std::size_t>(nread), false});
116         } else {
117             // transmission error
118             udp.publish(ErrorEvent(nread));
119         }
120     }
121 
122 public:
123     using Membership = details::UVMembership;
124     using Bind = details::UVUdpFlags;
125     using IPv4 = uvw::IPv4;
126     using IPv6 = uvw::IPv6;
127 
128     using Handle::Handle;
129 
UDPHandle(ConstructorAccess ca,std::shared_ptr<Loop> ref,unsigned int f)130     explicit UDPHandle(ConstructorAccess ca, std::shared_ptr<Loop> ref, unsigned int f)
131         : Handle{ca, std::move(ref)}, tag{FLAGS}, flags{f}
132     {}
133 
134     /**
135      * @brief Initializes the handle. The actual socket is created lazily.
136      * @return True in case of success, false otherwise.
137      */
init()138     bool init() {
139         return (tag == FLAGS)
140                 ? initialize(&uv_udp_init_ex, flags)
141                 : initialize(&uv_udp_init);
142     }
143 
144     /**
145      * @brief Opens an existing file descriptor or SOCKET as a UDP handle.
146      *
147      * The passed file descriptor or SOCKET is not checked for its type, but
148      * it’s required that it represents a valid datagram socket.
149      *
150      * See the official
151      * [documentation](http://docs.libuv.org/en/v1.x/udp.html#c.uv_udp_open)
152      * for further details.
153      *
154      * @param socket A valid socket handle (either a file descriptor or a SOCKET).
155      */
open(OSSocketHandle socket)156     void open(OSSocketHandle socket) {
157         invoke(&uv_udp_open, get(), socket);
158     }
159 
160     /**
161      * @brief Binds the UDP handle to an IP address and port.
162      *
163      * Available flags are:
164      *
165      * * `UDPHandle::Bind::IPV6ONLY`
166      * * `UDPHandle::Bind::REUSEADDR`
167      *
168      * See the official
169      * [documentation](http://docs.libuv.org/en/v1.x/udp.html#c.uv_udp_flags)
170      * for further details.
171      *
172      * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
173      * @param opts Optional additional flags.
174      */
bind(const sockaddr & addr,Flags<Bind> opts=Flags<Bind>{})175     void bind(const sockaddr &addr, Flags<Bind> opts = Flags<Bind>{}) {
176         invoke(&uv_udp_bind, get(), &addr, opts);
177     }
178 
179     /**
180      * @brief Binds the UDP handle to an IP address and port.
181      *
182      * Available flags are:
183      *
184      * * `UDPHandle::Bind::IPV6ONLY`
185      * * `UDPHandle::Bind::REUSEADDR`
186      *
187      * See the official
188      * [documentation](http://docs.libuv.org/en/v1.x/udp.html#c.uv_udp_flags)
189      * for further details.
190      *
191      * @param ip The IP address to which to bind.
192      * @param port The port to which to bind.
193      * @param opts Optional additional flags.
194      */
195     template<typename I = IPv4>
bind(std::string ip,unsigned int port,Flags<Bind> opts=Flags<Bind>{})196     void bind(std::string ip, unsigned int port, Flags<Bind> opts = Flags<Bind>{}) {
197         typename details::IpTraits<I>::Type addr;
198         details::IpTraits<I>::addrFunc(ip.data(), port, &addr);
199         bind(reinterpret_cast<const sockaddr &>(addr), std::move(opts));
200     }
201 
202     /**
203      * @brief Binds the UDP handle to an IP address and port.
204      *
205      * Available flags are:
206      *
207      * * `UDPHandle::Bind::IPV6ONLY`
208      * * `UDPHandle::Bind::REUSEADDR`
209      *
210      * See the official
211      * [documentation](http://docs.libuv.org/en/v1.x/udp.html#c.uv_udp_flags)
212      * for further details.
213      *
214      * @param addr A valid instance of Addr.
215      * @param opts Optional additional flags.
216      */
217     template<typename I = IPv4>
bind(Addr addr,Flags<Bind> opts=Flags<Bind>{})218     void bind(Addr addr, Flags<Bind> opts = Flags<Bind>{}) {
219         bind<I>(std::move(addr.ip), addr.port, std::move(opts));
220     }
221 
222     /**
223      * @brief Get the local IP and port of the UDP handle.
224      * @return A valid instance of Addr, an empty one in case of errors.
225      */
226     template<typename I = IPv4>
sock() const227     Addr sock() const noexcept {
228         return details::address<I>(&uv_udp_getsockname, get());
229     }
230 
231     /**
232      * @brief Sets membership for a multicast address.
233      *
234      * Available values for `membership` are:
235      *
236      * * `UDPHandle::Membership::LEAVE_GROUP`
237      * * `UDPHandle::Membership::JOIN_GROUP`
238      *
239      * @param multicast Multicast address to set membership for.
240      * @param iface Interface address.
241      * @param membership Action to be performed.
242      * @return True in case of success, false otherwise.
243      */
244     template<typename I = IPv4>
multicastMembership(std::string multicast,std::string iface,Membership membership)245     bool multicastMembership(std::string multicast, std::string iface, Membership membership) {
246         return (0 == uv_udp_set_membership(get(), multicast.data(), iface.data(), static_cast<uv_membership>(membership)));
247     }
248 
249     /**
250      * @brief Sets IP multicast loop flag.
251      *
252      * This makes multicast packets loop back to local sockets.
253      *
254      * @param enable True to enable multicast loop, false otherwise.
255      * @return True in case of success, false otherwise.
256      */
multicastLoop(bool enable=true)257     bool multicastLoop(bool enable = true) {
258         return (0 == uv_udp_set_multicast_loop(get(), enable));
259     }
260 
261     /**
262      * @brief Sets the multicast ttl.
263      * @param val A value in the range `[1, 255]`.
264      * @return True in case of success, false otherwise.
265      */
multicastTtl(int val)266     bool multicastTtl(int val) {
267         return (0 == uv_udp_set_multicast_ttl(get(), val > 255 ? 255 : val));
268     }
269 
270     /**
271      * @brief Sets the multicast interface to send or receive data on.
272      * @param iface Interface address.
273      * @return True in case of success, false otherwise.
274      */
275     template<typename I = IPv4>
multicastInterface(std::string iface)276     bool multicastInterface(std::string iface) {
277         return (0 == uv_udp_set_multicast_interface(get(), iface.data()));
278     }
279 
280     /**
281      * @brief Sets broadcast on or off.
282      * @param enable True to set broadcast on, false otherwise.
283      * @return True in case of success, false otherwise.
284      */
broadcast(bool enable=false)285     bool broadcast(bool enable = false) {
286         return (0 == uv_udp_set_broadcast(get(), enable));
287     }
288 
289     /**
290      * @brief Sets the time to live.
291      * @param val A value in the range `[1, 255]`.
292      * @return True in case of success, false otherwise.
293      */
ttl(int val)294     bool ttl(int val) {
295         return (0 == uv_udp_set_ttl(get(), val > 255 ? 255 : val));
296     }
297 
298     /**
299      * @brief Sends data over the UDP socket.
300      *
301      * Note that if the socket has not previously been bound with `bind()`, it
302      * will be bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a
303      * random port number.
304      *
305      * The handle takes the ownership of the data and it is in charge of delete
306      * them.
307      *
308      * A SendEvent event will be emitted when the data have been sent.<br/>
309      * An ErrorEvent event will be emitted in case of errors.
310      *
311      * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
312      * @param data The data to be sent.
313      * @param len The lenght of the submitted data.
314      */
send(const sockaddr & addr,std::unique_ptr<char[]> data,unsigned int len)315     void send(const sockaddr &addr, std::unique_ptr<char[]> data, unsigned int len) {
316         auto req = loop().resource<details::SendReq>(
317                     std::unique_ptr<char[], details::SendReq::Deleter>{
318                         data.release(), [](char *ptr) { delete[] ptr; }
319                     }, len);
320 
321         auto listener = [ptr = shared_from_this()](const auto &event, const auto &) {
322             ptr->publish(event);
323         };
324 
325         req->once<ErrorEvent>(listener);
326         req->once<SendEvent>(listener);
327         req->send(get(), &addr);
328     }
329 
330     /**
331      * @brief Sends data over the UDP socket.
332      *
333      * Note that if the socket has not previously been bound with `bind()`, it
334      * will be bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a
335      * random port number.
336      *
337      * The handle takes the ownership of the data and it is in charge of delete
338      * them.
339      *
340      * A SendEvent event will be emitted when the data have been sent.<br/>
341      * An ErrorEvent event will be emitted in case of errors.
342      *
343      * @param ip The address to which to send data.
344      * @param port The port to which to send data.
345      * @param data The data to be sent.
346      * @param len The lenght of the submitted data.
347      */
348     template<typename I = IPv4>
send(std::string ip,unsigned int port,std::unique_ptr<char[]> data,unsigned int len)349     void send(std::string ip, unsigned int port, std::unique_ptr<char[]> data, unsigned int len) {
350         typename details::IpTraits<I>::Type addr;
351         details::IpTraits<I>::addrFunc(ip.data(), port, &addr);
352         send(reinterpret_cast<const sockaddr &>(addr), std::move(data), len);
353     }
354 
355     /**
356      * @brief Sends data over the UDP socket.
357      *
358      * Note that if the socket has not previously been bound with `bind()`, it
359      * will be bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a
360      * random port number.
361      *
362      * The handle takes the ownership of the data and it is in charge of delete
363      * them.
364      *
365      * A SendEvent event will be emitted when the data have been sent.<br/>
366      * An ErrorEvent event will be emitted in case of errors.
367      *
368      * @param addr A valid instance of Addr.
369      * @param data The data to be sent.
370      * @param len The lenght of the submitted data.
371      */
372     template<typename I = IPv4>
send(Addr addr,std::unique_ptr<char[]> data,unsigned int len)373     void send(Addr addr, std::unique_ptr<char[]> data, unsigned int len) {
374         send<I>(std::move(addr.ip), addr.port, std::move(data), len);
375     }
376 
377     /**
378      * @brief Sends data over the UDP socket.
379      *
380      * Note that if the socket has not previously been bound with `bind()`, it
381      * will be bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a
382      * random port number.
383      *
384      * The handle doesn't take the ownership of the data. Be sure that their
385      * lifetime overcome the one of the request.
386      *
387      * A SendEvent event will be emitted when the data have been sent.<br/>
388      * An ErrorEvent event will be emitted in case of errors.
389      *
390      * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
391      * @param data The data to be sent.
392      * @param len The lenght of the submitted data.
393      */
send(const sockaddr & addr,char * data,unsigned int len)394     void send(const sockaddr &addr, char *data, unsigned int len) {
395         auto req = loop().resource<details::SendReq>(
396                     std::unique_ptr<char[], details::SendReq::Deleter>{
397                         data, [](char *) {}
398                     }, len);
399 
400         auto listener = [ptr = shared_from_this()](const auto &event, const auto &) {
401             ptr->publish(event);
402         };
403 
404         req->once<ErrorEvent>(listener);
405         req->once<SendEvent>(listener);
406         req->send(get(), &addr);
407     }
408 
409     /**
410      * @brief Sends data over the UDP socket.
411      *
412      * Note that if the socket has not previously been bound with `bind()`, it
413      * will be bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a
414      * random port number.
415      *
416      * The handle doesn't take the ownership of the data. Be sure that their
417      * lifetime overcome the one of the request.
418      *
419      * A SendEvent event will be emitted when the data have been sent.<br/>
420      * An ErrorEvent event will be emitted in case of errors.
421      *
422      * @param ip The address to which to send data.
423      * @param port The port to which to send data.
424      * @param data The data to be sent.
425      * @param len The lenght of the submitted data.
426      */
427     template<typename I = IPv4>
send(std::string ip,unsigned int port,char * data,unsigned int len)428     void send(std::string ip, unsigned int port, char *data, unsigned int len) {
429         typename details::IpTraits<I>::Type addr;
430         details::IpTraits<I>::addrFunc(ip.data(), port, &addr);
431         send(reinterpret_cast<const sockaddr &>(addr), data, len);
432     }
433 
434     /**
435      * @brief Sends data over the UDP socket.
436      *
437      * Note that if the socket has not previously been bound with `bind()`, it
438      * will be bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a
439      * random port number.
440      *
441      * The handle doesn't take the ownership of the data. Be sure that their
442      * lifetime overcome the one of the request.
443      *
444      * A SendEvent event will be emitted when the data have been sent.<br/>
445      * An ErrorEvent event will be emitted in case of errors.
446      *
447      * @param addr A valid instance of Addr.
448      * @param data The data to be sent.
449      * @param len The lenght of the submitted data.
450      */
451     template<typename I = IPv4>
send(Addr addr,char * data,unsigned int len)452     void send(Addr addr, char *data, unsigned int len) {
453         send<I>(std::move(addr.ip), addr.port, data, len);
454     }
455 
456     /**
457      * @brief Sends data over the UDP socket.
458      *
459      * Same as `send()`, but it won’t queue a send request if it can’t be
460      * completed immediately.
461      *
462      * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
463      * @param data The data to be sent.
464      * @param len The lenght of the submitted data.
465      * @return Number of bytes written.
466      */
467     template<typename I = IPv4>
trySend(const sockaddr & addr,std::unique_ptr<char[]> data,unsigned int len)468     int trySend(const sockaddr &addr, std::unique_ptr<char[]> data, unsigned int len) {
469         uv_buf_t bufs[] = { uv_buf_init(data.get(), len) };
470         auto bw = uv_udp_try_send(get(), bufs, 1, &addr);
471 
472         if(bw < 0) {
473             publish(ErrorEvent{bw});
474             bw = 0;
475         }
476 
477         return bw;
478     }
479 
480     /**
481      * @brief Sends data over the UDP socket.
482      *
483      * Same as `send()`, but it won’t queue a send request if it can’t be
484      * completed immediately.
485      *
486      * @param ip The address to which to send data.
487      * @param port The port to which to send data.
488      * @param data The data to be sent.
489      * @param len The lenght of the submitted data.
490      * @return Number of bytes written.
491      */
492     template<typename I = IPv4>
trySend(std::string ip,unsigned int port,std::unique_ptr<char[]> data,unsigned int len)493     int trySend(std::string ip, unsigned int port, std::unique_ptr<char[]> data, unsigned int len) {
494         typename details::IpTraits<I>::Type addr;
495         details::IpTraits<I>::addrFunc(ip.data(), port, &addr);
496         return trySend(reinterpret_cast<const sockaddr &>(addr), std::move(data), len);
497     }
498 
499     /**
500      * @brief Sends data over the UDP socket.
501      *
502      * Same as `send()`, but it won’t queue a send request if it can’t be
503      * completed immediately.
504      *
505      * @param addr A valid instance of Addr.
506      * @param data The data to be sent.
507      * @param len The lenght of the submitted data.
508      * @return Number of bytes written.
509      */
510     template<typename I = IPv4>
trySend(Addr addr,std::unique_ptr<char[]> data,unsigned int len)511     int trySend(Addr addr, std::unique_ptr<char[]> data, unsigned int len) {
512         return trySend<I>(std::move(addr.ip), addr.port, std::move(data), len);
513     }
514 
515     /**
516      * @brief Sends data over the UDP socket.
517      *
518      * Same as `send()`, but it won’t queue a send request if it can’t be
519      * completed immediately.
520      *
521      * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
522      * @param data The data to be sent.
523      * @param len The lenght of the submitted data.
524      * @return Number of bytes written.
525      */
526     template<typename I = IPv4>
trySend(const sockaddr & addr,char * data,unsigned int len)527     int trySend(const sockaddr &addr, char *data, unsigned int len) {
528         uv_buf_t bufs[] = { uv_buf_init(data, len) };
529         auto bw = uv_udp_try_send(get(), bufs, 1, &addr);
530 
531         if(bw < 0) {
532             publish(ErrorEvent{bw});
533             bw = 0;
534         }
535 
536         return bw;
537     }
538 
539     /**
540      * @brief Sends data over the UDP socket.
541      *
542      * Same as `send()`, but it won’t queue a send request if it can’t be
543      * completed immediately.
544      *
545      * @param ip The address to which to send data.
546      * @param port The port to which to send data.
547      * @param data The data to be sent.
548      * @param len The lenght of the submitted data.
549      * @return Number of bytes written.
550      */
551     template<typename I = IPv4>
trySend(std::string ip,unsigned int port,char * data,unsigned int len)552     int trySend(std::string ip, unsigned int port, char *data, unsigned int len) {
553         typename details::IpTraits<I>::Type addr;
554         details::IpTraits<I>::addrFunc(ip.data(), port, &addr);
555         return trySend(reinterpret_cast<const sockaddr &>(addr), data, len);
556     }
557 
558     /**
559      * @brief Sends data over the UDP socket.
560      *
561      * Same as `send()`, but it won’t queue a send request if it can’t be
562      * completed immediately.
563      *
564      * @param addr A valid instance of Addr.
565      * @param data The data to be sent.
566      * @param len The lenght of the submitted data.
567      * @return Number of bytes written.
568      */
569     template<typename I = IPv4>
trySend(Addr addr,char * data,unsigned int len)570     int trySend(Addr addr, char *data, unsigned int len) {
571         return trySend<I>(std::move(addr.ip), addr.port, data, len);
572     }
573 
574     /**
575      * @brief Prepares for receiving data.
576      *
577      * Note that if the socket has not previously been bound with `bind()`, it
578      * is bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a random
579      * port number.
580      *
581      * An UDPDataEvent event will be emitted when the handle receives data.<br/>
582      * An ErrorEvent event will be emitted in case of errors.
583      */
584     template<typename I = IPv4>
recv()585     void recv() {
586         invoke(&uv_udp_recv_start, get(), &allocCallback, &recvCallback<I>);
587     }
588 
589     /**
590      * @brief Stops listening for incoming datagrams.
591      */
stop()592     void stop() {
593         invoke(&uv_udp_recv_stop, get());
594     }
595 
596     /**
597      * @brief Gets the number of bytes queued for sending.
598      *
599      * It strictly shows how much information is currently queued.
600      *
601      * @return Number of bytes queued for sending.
602      */
sendQueueSize() const603     size_t sendQueueSize() const noexcept {
604         return uv_udp_get_send_queue_size(get());
605     }
606 
607     /**
608      * @brief Number of send requests currently in the queue awaiting to be processed.
609      * @return Number of send requests currently in the queue.
610      */
sendQueueCount() const611     size_t sendQueueCount() const noexcept {
612         return uv_udp_get_send_queue_count(get());
613     }
614 
615 private:
616     enum { DEFAULT, FLAGS } tag{DEFAULT};
617     unsigned int flags{};
618 };
619 
620 
621 }
622