1 #define EFL_NET_SOCKET_UDP_PROTECTED 1
2 #define EFL_NET_SOCKET_FD_PROTECTED 1
3 #define EFL_LOOP_FD_PROTECTED 1
4 #define EFL_IO_READER_FD_PROTECTED 1
5 #define EFL_IO_WRITER_FD_PROTECTED 1
6 #define EFL_IO_CLOSER_FD_PROTECTED 1
7 #define EFL_IO_READER_PROTECTED 1
8 #define EFL_IO_WRITER_PROTECTED 1
9 #define EFL_IO_CLOSER_PROTECTED 1
10 #define EFL_NET_SOCKET_PROTECTED 1
11 
12 #ifdef HAVE_CONFIG_H
13 # include <config.h>
14 #endif
15 
16 #include "Ecore.h"
17 #include "Ecore_Con.h"
18 #include "ecore_con_private.h"
19 
20 #ifdef HAVE_SYS_SOCKET_H
21 # include <sys/socket.h>
22 #endif
23 #ifdef HAVE_NETINET_UDP_H
24 # include <netinet/udp.h>
25 #endif
26 #ifdef HAVE_NETINET_IN_H
27 # include <netinet/in.h>
28 #endif
29 #ifdef HAVE_ARPA_INET_H
30 # include <arpa/inet.h>
31 #endif
32 
33 #define MY_CLASS EFL_NET_SOCKET_UDP_CLASS
34 
35 typedef struct _Efl_Net_Socket_Udp_Data
36 {
37    struct {
38       Eina_List *groups; /* list of newly allocated strings */
39       Eina_List *pending; /* list of nodes of groups pending join */
40       uint8_t ttl;
41       Eina_Bool loopback;
42       Eina_Bool ttl_set;
43    } multicast;
44    Eina_Stringshare *address_bind;
45    struct sockaddr *addr_remote;
46    socklen_t addr_remote_len;
47    Eina_Bool cork;
48    Eina_Bool dont_route;
49    Eina_Bool reuse_address;
50    Eina_Bool reuse_port;
51 } Efl_Net_Socket_Udp_Data;
52 
53 void
_efl_net_socket_udp_init(Eo * o,Efl_Net_Socket_Udp_Data * pd,Efl_Net_Ip_Address * remote_address)54 _efl_net_socket_udp_init(Eo *o, Efl_Net_Socket_Udp_Data *pd, Efl_Net_Ip_Address *remote_address)
55 {
56    const struct sockaddr *addr = efl_net_ip_address_sockaddr_get(remote_address);
57    socklen_t addrlen;
58 
59    EINA_SAFETY_ON_NULL_RETURN(addr);
60 
61    if (addr->sa_family == AF_INET) addrlen = sizeof(struct sockaddr_in);
62    else addrlen = sizeof(struct sockaddr_in6);
63 
64    pd->addr_remote = malloc(addrlen);
65    EINA_SAFETY_ON_NULL_RETURN(pd->addr_remote);
66    memcpy(pd->addr_remote, addr, addrlen);
67    pd->addr_remote_len = addrlen;
68    efl_net_socket_address_remote_set(o, efl_net_ip_address_string_get(remote_address));
69 }
70 
71 static Eina_Error
_efl_net_socket_udp_bind(Eo * o,Efl_Net_Socket_Udp_Data * pd)72 _efl_net_socket_udp_bind(Eo *o, Efl_Net_Socket_Udp_Data *pd)
73 {
74    const char *bhost, *bport;
75    struct sockaddr_in bsa4 = {.sin_family = AF_INET};
76    struct sockaddr_in6 bsa6 = {.sin6_family = AF_INET6};
77    char *str = NULL, *endptr;
78    unsigned long ul;
79    Eina_Error err = 0;
80    SOCKET fd = efl_loop_fd_get(o);
81    int family = efl_net_socket_fd_family_get(o);
82    int r;
83 
84    if (!pd->address_bind) return 0;
85 
86    str = strdup(pd->address_bind);
87    EINA_SAFETY_ON_NULL_GOTO(str, error_bind);
88    if (!efl_net_ip_port_split(str, &bhost, &bport))
89      {
90         bhost = (family == AF_INET) ? "0.0.0.0" : "::";
91         bport = "0";
92         err = EINVAL;
93         ERR("invalid bind address '%s', using host='%s' port=%s", pd->address_bind, bhost, bport);
94      }
95    else if (!bport) bport = "0";
96 
97    if (family == AF_INET)
98      r = inet_pton(AF_INET, bhost, &bsa4.sin_addr);
99    else
100      r = inet_pton(AF_INET6, bhost, &bsa6.sin6_addr);
101    if (r != 1)
102      {
103         if (r < 0) err = efl_net_socket_error_get();
104         else err = EINVAL;
105         ERR("invalid host='%s': %s", bhost, eina_error_msg_get(err));
106         goto error_bind;
107      }
108 
109    errno = 0;
110    ul = strtoul(bport, &endptr, 10);
111    if ((endptr == bport) || (endptr[0] != '\0'))
112      errno = EINVAL;
113    else if (ul > UINT16_MAX)
114      errno = ERANGE;
115 
116    if (errno)
117      {
118         err = errno;
119         ERR("invalid port numer '%s': %s", bport, eina_error_msg_get(errno));
120         goto error_bind;
121      }
122 
123    if (family == AF_INET)
124      bsa4.sin_port = eina_htons(ul);
125    else
126      bsa6.sin6_port = eina_htons(ul);
127 
128    if (family == AF_INET)
129      r = bind(fd, (struct sockaddr *)&bsa4, sizeof(bsa4));
130    else
131      r = bind(fd, (struct sockaddr *)&bsa6, sizeof(bsa6));
132    if (r != 0)
133      {
134         err = efl_net_socket_error_get();
135         ERR("could not bind to host='%s', port=%s: %s", bhost, bport, eina_error_msg_get(err));
136         goto error_bind;
137      }
138 
139  error_bind:
140    free(str);
141    return err;
142 }
143 
144 EOLIAN static void
_efl_net_socket_udp_efl_loop_fd_fd_set(Eo * o,Efl_Net_Socket_Udp_Data * pd,int pfd)145 _efl_net_socket_udp_efl_loop_fd_fd_set(Eo *o, Efl_Net_Socket_Udp_Data *pd, int pfd)
146 {
147    SOCKET fd = (SOCKET)pfd;
148    Eina_List *node;
149    struct sockaddr_storage addr;
150    socklen_t addrlen;
151    int family;
152 
153    efl_loop_fd_set(efl_super(o, MY_CLASS), pfd);
154 
155    if (fd == INVALID_SOCKET) return;
156 
157    family = efl_net_socket_fd_family_get(o);
158    if (family == AF_UNSPEC) return;
159 
160    /* apply postponed values or fetch & sync */
161    if (pd->cork == 0xff)
162      efl_net_socket_udp_cork_get(o);
163    else
164      efl_net_socket_udp_cork_set(o, pd->cork);
165 
166    if (pd->dont_route == 0xff)
167      efl_net_socket_udp_dont_route_get(o);
168    else
169      efl_net_socket_udp_dont_route_set(o, pd->dont_route);
170 
171    if (pd->reuse_address == 0xff)
172      efl_net_socket_udp_reuse_address_get(o);
173    else
174      efl_net_socket_udp_reuse_address_set(o, pd->reuse_address);
175 
176    if (pd->reuse_port == 0xff)
177      efl_net_socket_udp_reuse_port_get(o);
178    else
179      efl_net_socket_udp_reuse_port_set(o, pd->reuse_port);
180 
181    _efl_net_socket_udp_bind(o, pd);
182 
183    EINA_LIST_FREE(pd->multicast.pending, node)
184      {
185         const char *mcast_addr = node->data;
186         Eina_Error mr = efl_net_multicast_join(fd, family, mcast_addr);
187         if (mr)
188           ERR("could not join pending multicast group '%s': %s", mcast_addr, eina_error_msg_get(mr));
189      }
190 
191    if (!pd->multicast.ttl_set)
192      efl_net_socket_udp_multicast_time_to_live_get(o);
193    else
194      efl_net_socket_udp_multicast_time_to_live_set(o, pd->multicast.ttl);
195 
196    if (pd->multicast.loopback == 0xff)
197      efl_net_socket_udp_multicast_loopback_get(o);
198    else
199      efl_net_socket_udp_multicast_loopback_set(o, pd->multicast.loopback);
200 
201    addrlen = sizeof(addr);
202    if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) != 0)
203      ERR("getsockname(" SOCKET_FMT "): %s", fd, eina_error_msg_get(efl_net_socket_error_get()));
204    else
205      {
206         char str[INET6_ADDRSTRLEN + sizeof("[]:65536")];
207         if (efl_net_ip_port_fmt(str, sizeof(str), (struct sockaddr *)&addr))
208           efl_net_socket_address_local_set(o, str);
209      }
210 }
211 
212 EOLIAN static size_t
_efl_net_socket_udp_next_datagram_size_query(Eo * o,Efl_Net_Socket_Udp_Data * pd EINA_UNUSED)213 _efl_net_socket_udp_next_datagram_size_query(Eo *o, Efl_Net_Socket_Udp_Data *pd EINA_UNUSED)
214 {
215    SOCKET fd = efl_loop_fd_get(o);
216    if (fd == INVALID_SOCKET) return 0;
217    return efl_net_udp_datagram_size_query(fd);
218 }
219 
220 static inline int
_cork_option_get(void)221 _cork_option_get(void)
222 {
223 #if defined(HAVE_UDP_CORK)
224    return UDP_CORK;
225 #else
226    return -1;
227 #endif
228 }
229 
230 EOLIAN static Eina_Bool
_efl_net_socket_udp_cork_set(Eo * o,Efl_Net_Socket_Udp_Data * pd,Eina_Bool cork)231 _efl_net_socket_udp_cork_set(Eo *o, Efl_Net_Socket_Udp_Data *pd, Eina_Bool cork)
232 {
233    SOCKET fd;
234    int value, option;
235    Eina_Bool old = pd->cork;
236 
237    option = _cork_option_get();
238    if (EINA_UNLIKELY(option < 0))
239      {
240         if (cork)
241           ERR("Could not find a UDP_CORK equivalent on your system");
242         return EINA_FALSE;
243      }
244 
245    pd->cork = cork;
246 
247    fd = efl_loop_fd_get(o);
248    if (fd == INVALID_SOCKET) return EINA_TRUE; /* postpone until fd_set() */
249 
250    value = cork;
251    if (setsockopt(fd, IPPROTO_UDP, option, (const char *)&value, sizeof(value)) != 0)
252      {
253         ERR("setsockopt(" SOCKET_FMT ", IPPROTO_UDP, 0x%x, %d): %s",
254             fd, option, value, eina_error_msg_get(efl_net_socket_error_get()));
255         pd->cork = old;
256         return EINA_FALSE;
257      }
258 
259    return EINA_TRUE;
260 }
261 
262 EOLIAN static Eina_Bool
_efl_net_socket_udp_cork_get(const Eo * o,Efl_Net_Socket_Udp_Data * pd)263 _efl_net_socket_udp_cork_get(const Eo *o, Efl_Net_Socket_Udp_Data *pd)
264 {
265    SOCKET fd;
266    int value = 0;
267    socklen_t valuelen;
268    int option;
269 
270    option = _cork_option_get();
271    if (EINA_UNLIKELY(option < 0))
272      {
273         WRN("Could not find a UDP_CORK equivalent on your system");
274         return EINA_FALSE;
275      }
276 
277    fd = efl_loop_fd_get(o);
278    if (fd == INVALID_SOCKET) return pd->cork;
279 
280    /* if there is a fd, always query it directly as it may be modified
281     * elsewhere by nasty users.
282     */
283    valuelen = sizeof(value);
284    if (getsockopt(fd, IPPROTO_UDP, option, (char *)&value, &valuelen) != 0)
285      {
286         ERR("getsockopt(" SOCKET_FMT ", IPPROTO_UDP, 0x%x): %s",
287             fd, option, eina_error_msg_get(efl_net_socket_error_get()));
288         return EINA_FALSE;
289      }
290 
291    pd->cork = !!value; /* sync */
292    return pd->cork;
293 }
294 
295 EOLIAN static Eina_Bool
_efl_net_socket_udp_dont_route_set(Eo * o,Efl_Net_Socket_Udp_Data * pd,Eina_Bool dont_route)296 _efl_net_socket_udp_dont_route_set(Eo *o, Efl_Net_Socket_Udp_Data *pd, Eina_Bool dont_route)
297 {
298    Eina_Bool old = pd->dont_route;
299    SOCKET fd = efl_loop_fd_get(o);
300 #ifdef _WIN32
301    DWORD value = dont_route;
302 #else
303    int value = dont_route;
304 #endif
305 
306    pd->dont_route = dont_route;
307 
308    if (fd == INVALID_SOCKET) return EINA_TRUE;
309 
310    if (setsockopt(fd, SOL_SOCKET, SO_DONTROUTE, (const char *)&value, sizeof(value)) != 0)
311      {
312         Eina_Error err = efl_net_socket_error_get();
313         ERR("setsockopt(" SOCKET_FMT ", SOL_SOCKET, SO_DONTROUTE, %u): %s", fd, dont_route, eina_error_msg_get(err));
314         pd->dont_route = old;
315         return EINA_FALSE;
316      }
317 
318    return EINA_TRUE;
319 }
320 
321 EOLIAN static Eina_Bool
_efl_net_socket_udp_dont_route_get(const Eo * o,Efl_Net_Socket_Udp_Data * pd)322 _efl_net_socket_udp_dont_route_get(const Eo *o, Efl_Net_Socket_Udp_Data *pd)
323 {
324    SOCKET fd = efl_loop_fd_get(o);
325 #ifdef _WIN32
326    DWORD value;
327 #else
328    int value;
329 #endif
330    socklen_t valuelen;
331 
332    if (fd == INVALID_SOCKET) return pd->dont_route;
333 
334    /* if there is a fd, always query it directly as it may be modified
335     * elsewhere by nasty users.
336     */
337    valuelen = sizeof(value);
338    if (getsockopt(fd, SOL_SOCKET, SO_DONTROUTE, (char *)&value, &valuelen) != 0)
339      {
340         Eina_Error err = efl_net_socket_error_get();
341         ERR("getsockopt(" SOCKET_FMT ", SOL_SOCKET, SO_DONTROUTE): %s", fd, eina_error_msg_get(err));
342         return EINA_FALSE;
343      }
344 
345    pd->dont_route = !!value; /* sync */
346    return pd->dont_route;
347 }
348 
349 
350 EOLIAN static Eina_Bool
_efl_net_socket_udp_reuse_address_set(Eo * o,Efl_Net_Socket_Udp_Data * pd,Eina_Bool reuse_address)351 _efl_net_socket_udp_reuse_address_set(Eo *o, Efl_Net_Socket_Udp_Data *pd, Eina_Bool reuse_address)
352 {
353    SOCKET fd;
354    int value;
355    Eina_Bool old = pd->reuse_address;
356 
357    pd->reuse_address = reuse_address;
358 
359    fd = efl_loop_fd_get(o);
360    if (fd == INVALID_SOCKET) return EINA_TRUE; /* postpone until fd_set() */
361 
362    value = reuse_address;
363    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&value, sizeof(value)) != 0)
364      {
365         ERR("setsockopt(" SOCKET_FMT ", SOL_SOCKET, SO_REUSEADDR, %d): %s",
366             fd, value, eina_error_msg_get(efl_net_socket_error_get()));
367         pd->reuse_address = old;
368         return EINA_FALSE;
369      }
370 
371    return EINA_TRUE;
372 }
373 
374 EOLIAN static Eina_Bool
_efl_net_socket_udp_reuse_address_get(const Eo * o,Efl_Net_Socket_Udp_Data * pd)375 _efl_net_socket_udp_reuse_address_get(const Eo *o, Efl_Net_Socket_Udp_Data *pd)
376 {
377    SOCKET fd;
378    int value = 0;
379    socklen_t valuelen;
380 
381    fd = efl_loop_fd_get(o);
382    if (fd == INVALID_SOCKET) return pd->reuse_address;
383 
384    /* if there is a fd, always query it directly as it may be modified
385     * elsewhere by nasty users.
386     */
387    valuelen = sizeof(value);
388    if (getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&value, &valuelen) != 0)
389      {
390         ERR("getsockopt(" SOCKET_FMT ", SOL_SOCKET, SO_REUSEADDR): %s",
391             fd, eina_error_msg_get(efl_net_socket_error_get()));
392         return EINA_FALSE;
393      }
394 
395    pd->reuse_address = !!value; /* sync */
396    return pd->reuse_address;
397 }
398 
399 EOLIAN static Eina_Bool
_efl_net_socket_udp_reuse_port_set(Eo * o,Efl_Net_Socket_Udp_Data * pd,Eina_Bool reuse_port)400 _efl_net_socket_udp_reuse_port_set(Eo *o, Efl_Net_Socket_Udp_Data *pd, Eina_Bool reuse_port)
401 {
402 #ifdef SO_REUSEPORT
403    SOCKET fd;
404    int value;
405    Eina_Bool old = pd->reuse_port;
406 #endif
407 
408    pd->reuse_port = reuse_port;
409 
410 #ifdef SO_REUSEPORT
411    fd = efl_loop_fd_get(o);
412    if (fd == INVALID_SOCKET) return EINA_TRUE; /* postpone until fd_set() */
413 
414    value = reuse_port;
415    if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (const char *)&value, sizeof(value)) != 0)
416      {
417         ERR("setsockopt(" SOCKET_FMT ", SOL_SOCKET, SO_REUSEPORT, %d): %s",
418             fd, value, eina_error_msg_get(efl_net_socket_error_get()));
419         pd->reuse_port = old;
420         return EINA_FALSE;
421      }
422 #else
423    (void)o;
424 #endif
425 
426    return EINA_TRUE;
427 }
428 
429 EOLIAN static Eina_Bool
_efl_net_socket_udp_reuse_port_get(const Eo * o,Efl_Net_Socket_Udp_Data * pd)430 _efl_net_socket_udp_reuse_port_get(const Eo *o, Efl_Net_Socket_Udp_Data *pd)
431 {
432 #ifdef SO_REUSEPORT
433    SOCKET fd;
434    int value = 0;
435    socklen_t valuelen;
436 
437    fd = efl_loop_fd_get(o);
438    if (fd == INVALID_SOCKET) return pd->reuse_port;
439 
440    /* if there is a fd, always query it directly as it may be modified
441     * elsewhere by nasty users.
442     */
443    valuelen = sizeof(value);
444    if (getsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *)&value, &valuelen) != 0)
445      {
446         ERR("getsockopt(" SOCKET_FMT ", SOL_SOCKET, SO_REUSEPORT): %s",
447             fd, eina_error_msg_get(efl_net_socket_error_get()));
448         return EINA_FALSE;
449      }
450 
451    pd->reuse_port = !!value; /* sync */
452 #else
453    (void)o;
454 #endif
455 
456    return pd->reuse_port;
457 }
458 
459 EOLIAN static Eina_Error
_efl_net_socket_udp_bind_set(Eo * o,Efl_Net_Socket_Udp_Data * pd,const char * address)460 _efl_net_socket_udp_bind_set(Eo *o, Efl_Net_Socket_Udp_Data *pd, const char *address)
461 {
462    SOCKET fd = efl_loop_fd_get(o);
463 
464    EINA_SAFETY_ON_TRUE_RETURN_VAL(fd != INVALID_SOCKET, EALREADY);
465 
466    eina_stringshare_replace(&pd->address_bind, address);
467    return 0;
468 }
469 
470 EOLIAN static const char *
_efl_net_socket_udp_bind_get(const Eo * o EINA_UNUSED,Efl_Net_Socket_Udp_Data * pd)471 _efl_net_socket_udp_bind_get(const Eo *o EINA_UNUSED, Efl_Net_Socket_Udp_Data *pd)
472 {
473    return pd->address_bind;
474 }
475 
476 EOLIAN Efl_Object *
_efl_net_socket_udp_efl_object_constructor(Eo * o,Efl_Net_Socket_Udp_Data * pd)477 _efl_net_socket_udp_efl_object_constructor(Eo *o, Efl_Net_Socket_Udp_Data *pd)
478 {
479    pd->multicast.ttl = 1;
480    pd->multicast.ttl_set = EINA_FALSE;
481    pd->multicast.loopback = 0xff;
482    pd->cork = 0xff;
483    pd->dont_route = 0xff;
484    pd->reuse_address = 0xff;
485    pd->reuse_port = 0xff;
486    return efl_constructor(efl_super(o, MY_CLASS));
487 }
488 
489 EOLIAN void
_efl_net_socket_udp_efl_object_destructor(Eo * o,Efl_Net_Socket_Udp_Data * pd)490 _efl_net_socket_udp_efl_object_destructor(Eo *o, Efl_Net_Socket_Udp_Data *pd)
491 {
492    if (pd->multicast.pending)
493      {
494         eina_list_free(pd->multicast.pending);
495         pd->multicast.pending = NULL;
496      }
497 
498    while (pd->multicast.groups)
499      efl_net_socket_udp_multicast_leave(o, pd->multicast.groups->data);
500 
501    efl_destructor(efl_super(o, MY_CLASS));
502 
503    eina_stringshare_replace(&pd->address_bind, NULL);
504 
505    free(pd->addr_remote);
506    pd->addr_remote = NULL;
507    pd->addr_remote_len = 0;
508 }
509 
510 EOLIAN static Eina_Error
_efl_net_socket_udp_efl_io_reader_read(Eo * o,Efl_Net_Socket_Udp_Data * pd,Eina_Rw_Slice * rw_slice)511 _efl_net_socket_udp_efl_io_reader_read(Eo *o, Efl_Net_Socket_Udp_Data *pd, Eina_Rw_Slice *rw_slice)
512 {
513    SOCKET fd = efl_io_reader_fd_get(o);
514    ssize_t r;
515 
516    EINA_SAFETY_ON_NULL_RETURN_VAL(rw_slice, EINVAL);
517    if (fd == INVALID_SOCKET) goto error;
518    do
519      {
520         struct sockaddr_storage addr = {};
521         socklen_t addrlen = sizeof(addr);
522         r = recvfrom(fd, rw_slice->mem, rw_slice->len, 0, (struct sockaddr *)&addr, &addrlen);
523         if (r == SOCKET_ERROR)
524           {
525              Eina_Error err = efl_net_socket_error_get();
526 
527              if (err == EINTR) continue;
528 
529              rw_slice->len = 0;
530              rw_slice->mem = NULL;
531 
532              efl_io_reader_can_read_set(o, EINA_FALSE);
533              return err;
534           }
535 
536         if (addr.ss_family == AF_INET)
537           {
538              const struct sockaddr_in *a = (const struct sockaddr_in *)pd->addr_remote;
539              uint32_t ipv4 = eina_ntohl(a->sin_addr.s_addr);
540              if ((ipv4 != INADDR_BROADCAST) && (ipv4 != INADDR_ANY) && (!IN_MULTICAST(ipv4)))
541                {
542                   if ((addrlen != pd->addr_remote_len) ||
543                       (memcmp(&addr, pd->addr_remote, addrlen) != 0))
544                     {
545                        char buf[INET_ADDRSTRLEN + sizeof(":65536")];
546                        efl_net_ip_port_fmt(buf, sizeof(buf), (struct sockaddr *)&addr);
547                        ERR("dropping spurious datagram from %s (expected %s)", buf, efl_net_socket_address_remote_get(o));
548                        rw_slice->len = 0;
549                        rw_slice->mem = NULL;
550                        efl_io_reader_can_read_set(o, EINA_FALSE); /* wait Efl.Loop.Fd "read" */
551                        return EAGAIN;
552                     }
553                }
554           }
555         else
556           {
557              const struct sockaddr_in6 *a = (const struct sockaddr_in6 *)pd->addr_remote;
558              if ((!IN6_IS_ADDR_MULTICAST(&a->sin6_addr)) && (memcmp(&a->sin6_addr, &in6addr_any, sizeof(in6addr_any)) != 0))
559                {
560                   if ((addrlen != pd->addr_remote_len) ||
561                       (memcmp(&addr, pd->addr_remote, addrlen) != 0))
562                     {
563                        char buf[INET6_ADDRSTRLEN + sizeof("[]:65536")];
564                        efl_net_ip_port_fmt(buf, sizeof(buf), (struct sockaddr *)&addr);
565                        ERR("dropping spurious datagram from %s (expected %s)", buf, efl_net_socket_address_remote_get(o));
566                        rw_slice->len = 0;
567                        rw_slice->mem = NULL;
568                        efl_io_reader_can_read_set(o, EINA_FALSE); /* wait Efl.Loop.Fd "read" */
569                        return EAGAIN;
570                     }
571                }
572           }
573      }
574    while (r == SOCKET_ERROR);
575 
576    rw_slice->len = r;
577    efl_io_reader_can_read_set(o, EINA_FALSE); /* wait Efl.Loop.Fd "read" */
578    if (r == 0)
579      efl_io_reader_eos_set(o, EINA_TRUE);
580 
581    return 0;
582 
583  error:
584    rw_slice->len = 0;
585    rw_slice->mem = NULL;
586    efl_io_reader_can_read_set(o, EINA_FALSE);
587    return EINVAL;
588 }
589 
590 EOLIAN static Eina_Error
_efl_net_socket_udp_efl_io_writer_write(Eo * o,Efl_Net_Socket_Udp_Data * pd,Eina_Slice * ro_slice,Eina_Slice * remaining)591 _efl_net_socket_udp_efl_io_writer_write(Eo *o, Efl_Net_Socket_Udp_Data *pd, Eina_Slice *ro_slice, Eina_Slice *remaining)
592 {
593    SOCKET fd = efl_io_writer_fd_get(o);
594    ssize_t r;
595 
596    EINA_SAFETY_ON_NULL_RETURN_VAL(ro_slice, EINVAL);
597    if (fd == INVALID_SOCKET) goto error;
598 
599    do
600      {
601         r = sendto(fd, ro_slice->mem, ro_slice->len, 0, pd->addr_remote, pd->addr_remote_len);
602         if (r == SOCKET_ERROR)
603           {
604              Eina_Error err = efl_net_socket_error_get();
605 
606              if (err == EINTR) continue;
607 
608              if (remaining) *remaining = *ro_slice;
609              ro_slice->len = 0;
610              ro_slice->mem = NULL;
611              efl_io_writer_can_write_set(o, EINA_FALSE);
612              return err;
613           }
614      }
615    while (r == SOCKET_ERROR);
616 
617    if (remaining)
618      {
619         remaining->len = ro_slice->len - r;
620         remaining->bytes = ro_slice->bytes + r;
621      }
622    ro_slice->len = r;
623    efl_io_writer_can_write_set(o, EINA_FALSE); /* wait Efl.Loop.Fd "write" */
624 
625    return 0;
626 
627  error:
628    if (remaining) *remaining = *ro_slice;
629    ro_slice->len = 0;
630    ro_slice->mem = NULL;
631    efl_io_writer_can_write_set(o, EINA_FALSE);
632    return EINVAL;
633 }
634 
635 static Eina_List *
_efl_net_socket_udp_multicast_find(const Eina_List * lst,const char * address)636 _efl_net_socket_udp_multicast_find(const Eina_List *lst, const char *address)
637 {
638    const char *str;
639    const Eina_List *node;
640 
641    EINA_LIST_FOREACH(lst, node, str)
642      {
643         if (strcmp(str, address) == 0)
644           return (Eina_List *)node;
645      }
646 
647    return NULL;
648 }
649 
650 EOLIAN static Eina_Error
_efl_net_socket_udp_multicast_join(Eo * o,Efl_Net_Socket_Udp_Data * pd,const char * address)651 _efl_net_socket_udp_multicast_join(Eo *o, Efl_Net_Socket_Udp_Data *pd, const char *address)
652 {
653    const Eina_List *found;
654    SOCKET fd = efl_loop_fd_get(o);
655 
656    EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
657 
658    found = _efl_net_socket_udp_multicast_find(pd->multicast.groups, address);
659    if (found) return EEXIST;
660 
661    pd->multicast.groups = eina_list_append(pd->multicast.groups, strdup(address));
662 
663    if (fd == INVALID_SOCKET)
664      {
665         pd->multicast.pending = eina_list_append(pd->multicast.pending, eina_list_last(pd->multicast.groups));
666         return 0;
667      }
668 
669    return efl_net_multicast_join(fd, efl_net_socket_fd_family_get(o), address);
670 }
671 
672 EOLIAN static Eina_Error
_efl_net_socket_udp_multicast_leave(Eo * o,Efl_Net_Socket_Udp_Data * pd,const char * address)673 _efl_net_socket_udp_multicast_leave(Eo *o, Efl_Net_Socket_Udp_Data *pd, const char *address)
674 {
675    Eina_List *found;
676    SOCKET fd = efl_loop_fd_get(o);
677    Eina_Error err;
678 
679    EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
680 
681    found = _efl_net_socket_udp_multicast_find(pd->multicast.groups, address);
682    if (!found) return ENOENT;
683 
684    if (fd == INVALID_SOCKET)
685      {
686         free(found->data);
687         pd->multicast.pending = eina_list_remove(pd->multicast.pending, found);
688         pd->multicast.groups = eina_list_remove_list(pd->multicast.groups, found);
689         return 0;
690      }
691 
692    err = efl_net_multicast_leave(fd, efl_net_socket_fd_family_get(o), address);
693 
694    free(found->data);
695    pd->multicast.groups = eina_list_remove_list(pd->multicast.groups, found);
696    return err;
697 }
698 
699 EOLIAN static Eina_Iterator *
_efl_net_socket_udp_multicast_groups_get(Eo * o EINA_UNUSED,Efl_Net_Socket_Udp_Data * pd)700 _efl_net_socket_udp_multicast_groups_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Udp_Data *pd)
701 {
702    return eina_list_iterator_new(pd->multicast.groups);
703 }
704 
705 EOLIAN static Eina_Error
_efl_net_socket_udp_multicast_time_to_live_set(Eo * o,Efl_Net_Socket_Udp_Data * pd,uint8_t ttl)706 _efl_net_socket_udp_multicast_time_to_live_set(Eo *o, Efl_Net_Socket_Udp_Data *pd, uint8_t ttl)
707 {
708    SOCKET fd = efl_loop_fd_get(o);
709    Eina_Error err;
710    uint8_t old = pd->multicast.ttl;
711 
712    pd->multicast.ttl_set = EINA_TRUE;
713    pd->multicast.ttl = ttl;
714 
715    if (fd == INVALID_SOCKET) return 0;
716 
717    err = efl_net_multicast_ttl_set(fd, efl_net_socket_fd_family_get(o), ttl);
718    if (err)
719      {
720         ERR("could not set multicast time to live=%hhu: %s", ttl, eina_error_msg_get(err));
721         pd->multicast.ttl = old;
722      }
723 
724    return err;
725 }
726 
727 EOLIAN static uint8_t
_efl_net_socket_udp_multicast_time_to_live_get(const Eo * o,Efl_Net_Socket_Udp_Data * pd)728 _efl_net_socket_udp_multicast_time_to_live_get(const Eo *o, Efl_Net_Socket_Udp_Data *pd)
729 {
730    SOCKET fd = efl_loop_fd_get(o);
731    Eina_Error err;
732    uint8_t ttl = pd->multicast.ttl;
733 
734    if (fd == INVALID_SOCKET) return pd->multicast.ttl;
735 
736    err = efl_net_multicast_ttl_get(fd, efl_net_socket_fd_family_get(o), &ttl);
737    if (err)
738      ERR("could not get multicast time to live: %s", eina_error_msg_get(err));
739    else
740      pd->multicast.ttl = ttl;
741 
742    return pd->multicast.ttl;
743 }
744 
745 EOLIAN static Eina_Error
_efl_net_socket_udp_multicast_loopback_set(Eo * o,Efl_Net_Socket_Udp_Data * pd,Eina_Bool loopback)746 _efl_net_socket_udp_multicast_loopback_set(Eo *o, Efl_Net_Socket_Udp_Data *pd, Eina_Bool loopback)
747 {
748    SOCKET fd = efl_loop_fd_get(o);
749    Eina_Error err;
750    Eina_Bool old = pd->multicast.loopback;
751 
752    pd->multicast.loopback = loopback;
753 
754    if (fd == INVALID_SOCKET) return 0;
755 
756    err = efl_net_multicast_loopback_set(fd, efl_net_socket_fd_family_get(o), loopback);
757    if (err)
758      {
759         ERR("could not set multicast loopback=%hhu: %s", loopback, eina_error_msg_get(err));
760         pd->multicast.loopback = old;
761      }
762 
763    return err;
764 }
765 
766 EOLIAN static Eina_Bool
_efl_net_socket_udp_multicast_loopback_get(const Eo * o,Efl_Net_Socket_Udp_Data * pd)767 _efl_net_socket_udp_multicast_loopback_get(const Eo *o, Efl_Net_Socket_Udp_Data *pd)
768 {
769    SOCKET fd = efl_loop_fd_get(o);
770    Eina_Error err;
771    Eina_Bool loopback = pd->multicast.loopback;
772 
773    if (fd == INVALID_SOCKET) return pd->multicast.loopback;
774 
775    err = efl_net_multicast_loopback_get(fd, efl_net_socket_fd_family_get(o), &loopback);
776    if (err)
777      ERR("could not get multicast loopback: %s", eina_error_msg_get(err));
778    else
779      pd->multicast.loopback = loopback;
780 
781    return pd->multicast.loopback;
782 }
783 
784 #include "efl_net_socket_udp.eo.c"
785