• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ucode/lib/socket.c

  1 /*
  2  * Copyright (C) 2024 Jo-Philipp Wich <jo@mein.io>
  3  *
  4  * Permission to use, copy, modify, and/or distribute this software for any
  5  * purpose with or without fee is hereby granted, provided that the above
  6  * copyright notice and this permission notice appear in all copies.
  7  *
  8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 15  */
 16 
 17 /**
 18  * # Socket Module
 19  *
 20  * The `socket` module provides functions for interacting with sockets.
 21  *
 22  * Functions can be individually imported and directly accessed using the
 23  * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#named_import named import}
 24  * syntax:
 25  *
 26  *   ```javascript
 27  *   import { AF_INET, SOCK_STREAM, create as socket } from 'socket';
 28  *
 29  *   let sock = socket(AF_INET, SOCK_STREAM, 0);
 30  *   sock.connect('192.168.1.1', 80);
 31  *   sock.send(…);
 32  *   sock.recv(…);
 33  *   sock.close();
 34  *   ```
 35  *
 36  * Alternatively, the module namespace can be imported
 37  * using a wildcard import statement:
 38  *
 39  *   ```javascript
 40  *   import * as socket from 'socket';
 41  *
 42  *   let sock = socket.create(socket.AF_INET, socket.SOCK_STREAM, 0);
 43  *   sock.connect('192.168.1.1', 80);
 44  *   sock.send(…);
 45  *   sock.recv(…);
 46  *   sock.close();
 47  *   ```
 48  *
 49  * Additionally, the socket module namespace may also be imported by invoking
 50  * the `ucode` interpreter with the `-lsocket` switch.
 51  *
 52  * @module socket
 53  */
 54 
 55 #include <stdio.h>
 56 #include <errno.h>
 57 #include <string.h>
 58 #include <ctype.h>
 59 #include <sys/types.h>
 60 #include <sys/socket.h>
 61 #include <sys/stat.h>
 62 #include <sys/un.h>
 63 #include <netinet/in.h>
 64 #include <netinet/tcp.h>
 65 #include <netinet/udp.h>
 66 #include <arpa/inet.h>
 67 #include <unistd.h>
 68 #include <fcntl.h>
 69 #include <net/if.h>
 70 #include <netdb.h>
 71 #include <poll.h>
 72 #include <limits.h>
 73 #include <dirent.h>
 74 #include <assert.h>
 75 
 76 #include "ucode/module.h"
 77 #include "ucode/platform.h"
 78 
 79 #if defined(__linux__)
 80 # include <linux/if_packet.h>
 81 
 82 # ifndef SO_TIMESTAMP_OLD
 83 #  define SO_TIMESTAMP_OLD SO_TIMESTAMP
 84 # endif
 85 
 86 # ifndef SO_TIMESTAMPNS_OLD
 87 #  define SO_TIMESTAMPNS_OLD SO_TIMESTAMP
 88 # endif
 89 #endif
 90 
 91 #if defined(__APPLE__)
 92 # include <sys/ucred.h>
 93 
 94 # define SOCK_NONBLOCK (1 << 16)
 95 # define SOCK_CLOEXEC  (1 << 17)
 96 #endif
 97 
 98 #ifndef NI_IDN
 99 # define NI_IDN 0
100 #endif
101 
102 #ifndef AI_IDN
103 # define AI_IDN 0
104 #endif
105 
106 #ifndef AI_CANONIDN
107 # define AI_CANONIDN 0
108 #endif
109 
110 #ifndef IPV6_FLOWINFO
111 # define IPV6_FLOWINFO 11
112 #endif
113 
114 #ifndef IPV6_FLOWLABEL_MGR
115 # define IPV6_FLOWLABEL_MGR 32
116 #endif
117 
118 #ifndef IPV6_FLOWINFO_SEND
119 # define IPV6_FLOWINFO_SEND 33
120 #endif
121 
122 #define ok_return(expr) do { set_error(0, NULL); return (expr); } while(0)
123 #define err_return(err, ...) do { set_error(err, __VA_ARGS__); return NULL; } while(0)
124 
125 static struct {
126         int code;
127         char *msg;
128 } last_error;
129 
130 __attribute__((format(printf, 2, 3))) static void
131 set_error(int errcode, const char *fmt, ...)
132 {
133         va_list ap;
134 
135         free(last_error.msg);
136 
137         last_error.code = errcode;
138         last_error.msg = NULL;
139 
140         if (fmt) {
141                 va_start(ap, fmt);
142                 xvasprintf(&last_error.msg, fmt, ap);
143                 va_end(ap);
144         }
145 }
146 
147 static char *
148 arg_type_(uc_type_t type)
149 {
150         switch (type) {
151         case UC_INTEGER:  return "an integer value";
152         case UC_BOOLEAN:  return "a boolean value";
153         case UC_STRING:   return "a string value";
154         case UC_DOUBLE:   return "a double value";
155         case UC_ARRAY:    return "an array";
156         case UC_OBJECT:   return "an object";
157         case UC_REGEXP:   return "a regular expression";
158         case UC_CLOSURE:  return "a function";
159         case UC_RESOURCE: return "a resource value";
160         default:          return "the expected type";
161         }
162 }
163 
164 static bool
165 args_get_(uc_vm_t *vm, size_t nargs, int *fdptr, ...)
166 {
167         const char *name, *rtype = NULL;
168         uc_value_t **ptr, *arg;
169         uc_type_t type, t;
170         size_t index = 0;
171         int *sockfd;
172         va_list ap;
173         bool opt;
174 
175         if (fdptr) {
176                 sockfd = uc_fn_this("socket");
177 
178                 if (!sockfd || *sockfd == -1)
179                         err_return(EBADF, "Invalid socket context");
180 
181                 *fdptr = *sockfd;
182         }
183 
184         va_start(ap, fdptr);
185 
186         while (true) {
187                 name = va_arg(ap, const char *);
188 
189                 if (!name)
190                         break;
191 
192                 arg = uc_fn_arg(index++);
193 
194                 type = va_arg(ap, uc_type_t);
195                 opt = va_arg(ap, int);
196                 ptr = va_arg(ap, uc_value_t **);
197 
198                 if (type == UC_RESOURCE) {
199                         rtype = name;
200                         name = strrchr(rtype, '.');
201                         name = name ? name + 1 : rtype;
202 
203                         if (arg && !ucv_resource_dataptr(arg, rtype))
204                                 err_return(EINVAL,
205                                         "Argument %s is not a %s resource", name, rtype);
206                 }
207 
208                 if (!opt && !arg)
209                         err_return(EINVAL,
210                                 "Argument %s is required", name);
211 
212                 t = ucv_type(arg);
213 
214                 if (t == UC_CFUNCTION)
215                         t = UC_CLOSURE;
216 
217                 if (arg && type != UC_NULL && t != type)
218                         err_return(EINVAL,
219                                 "Argument %s is not %s", name, arg_type_(type));
220 
221                 *ptr = arg;
222         }
223 
224         va_end(ap);
225 
226         ok_return(true);
227 }
228 
229 #define args_get(vm, nargs, fdptr, ...) do { \
230         if (!args_get_(vm, nargs, fdptr, ##__VA_ARGS__, NULL)) \
231                 return NULL; \
232 } while(0)
233 
234 static void
235 strbuf_free(uc_stringbuf_t *sb)
236 {
237         printbuf_free(sb);
238 }
239 
240 static bool
241 strbuf_grow(uc_stringbuf_t *sb, size_t size)
242 {
243         if (size > 0) {
244                 if (printbuf_memset(sb, sizeof(uc_string_t) + size - 1, '\0', 1))
245                         err_return(ENOMEM, "Out of memory");
246         }
247 
248         return true;
249 }
250 
251 static char *
252 strbuf_data(uc_stringbuf_t *sb)
253 {
254         return sb->buf + sizeof(uc_string_t);
255 }
256 
257 static size_t
258 strbuf_size(uc_stringbuf_t *sb)
259 {
260         return (size_t)sb->bpos - sizeof(uc_string_t);
261 }
262 
263 static uc_value_t *
264 strbuf_finish(uc_stringbuf_t **sb, size_t final_size)
265 {
266         size_t buffer_size;
267         uc_string_t *us;
268 
269         if (!sb || !*sb)
270                 return NULL;
271 
272         buffer_size = strbuf_size(*sb);
273         us = (uc_string_t *)(*sb)->buf;
274 
275         if (final_size > buffer_size)
276                 final_size = buffer_size;
277 
278         free(*sb);
279         *sb = NULL;
280 
281         us = xrealloc(us, sizeof(uc_string_t) + final_size + 1);
282         us->length = final_size;
283         us->str[us->length] = 0;
284 
285         return &us->header;
286 }
287 
288 static uc_stringbuf_t *
289 strbuf_alloc(size_t size)
290 {
291         uc_stringbuf_t *sb = ucv_stringbuf_new();
292 
293         if (!strbuf_grow(sb, size)) {
294                 printbuf_free(sb);
295 
296                 return NULL;
297         }
298 
299         return sb;
300 }
301 
302 #if defined(__linux__)
303 static uc_value_t *
304 hwaddr_to_uv(uint8_t *addr, size_t alen)
305 {
306         char buf[sizeof("FF:FF:FF:FF:FF:FF:FF:FF")], *p = buf;
307         const char *hex = "0123456789ABCDEF";
308 
309         for (size_t i = 0; i < alen && i < 8; i++) {
310                 if (i) *p++ = ':';
311                 *p++ = hex[addr[i] / 16];
312                 *p++ = hex[addr[i] % 16];
313         }
314 
315         return ucv_string_new(buf);
316 }
317 
318 static bool
319 uv_to_hwaddr(uc_value_t *addr, uint8_t *out, size_t *outlen)
320 {
321         const char *p;
322         size_t len;
323 
324         memset(out, 0, 8);
325         *outlen = 0;
326 
327         if (ucv_type(addr) != UC_STRING)
328                 goto err;
329 
330         len = ucv_string_length(addr);
331         p = ucv_string_get(addr);
332 
333         while (len > 0 && isxdigit(*p) && *outlen < 8) {
334                 uint8_t n = (*p > '9') ? 10 + (*p|32) - 'a' : *p - '';
335                 p++, len--;
336 
337                 if (len > 0 && isxdigit(*p)) {
338                         n = n * 16 + ((*p > '9') ? 10 + (*p|32) - 'a' : *p - '');
339                         p++, len--;
340                 }
341 
342                 if (len > 0 && (*p == ':' || *p == '-' || *p == '.'))
343                         p++, len--;
344 
345                 out[(*outlen)++] = n;
346         }
347 
348         if (len == 0 || *p == 0)
349                 return true;
350 
351 err:
352         err_return(EINVAL, "Invalid hardware address");
353 }
354 #endif
355 
356 static bool
357 sockaddr_to_uv(struct sockaddr_storage *ss, uc_value_t *addrobj)
358 {
359         char *ifname, addrstr[INET6_ADDRSTRLEN];
360         struct sockaddr_in6 *s6;
361         struct sockaddr_in *s4;
362         struct sockaddr_un *su;
363 #if defined(__linux__)
364         struct sockaddr_ll *sl;
365 #endif
366 
367         ucv_object_add(addrobj, "family", ucv_uint64_new(ss->ss_family));
368 
369         switch (ss->ss_family) {
370         case AF_INET6:
371                 s6 = (struct sockaddr_in6 *)ss;
372 
373                 inet_ntop(AF_INET6, &s6->sin6_addr, addrstr, sizeof(addrstr));
374                 ucv_object_add(addrobj, "address",
375                         ucv_string_new(addrstr));
376 
377                 ucv_object_add(addrobj, "port",
378                         ucv_uint64_new(ntohs(s6->sin6_port)));
379 
380                 ucv_object_add(addrobj, "flowinfo",
381                         ucv_uint64_new(ntohl(s6->sin6_flowinfo)));
382 
383                 if (s6->sin6_scope_id) {
384                         ifname = if_indextoname(s6->sin6_scope_id, addrstr);
385 
386                         if (ifname)
387                                 ucv_object_add(addrobj, "interface",
388                                         ucv_string_new(ifname));
389                         else
390                                 ucv_object_add(addrobj, "interface",
391                                         ucv_uint64_new(s6->sin6_scope_id));
392                 }
393 
394                 return true;
395 
396         case AF_INET:
397                 s4 = (struct sockaddr_in *)ss;
398 
399                 inet_ntop(AF_INET, &s4->sin_addr, addrstr, sizeof(addrstr));
400                 ucv_object_add(addrobj, "address",
401                         ucv_string_new(addrstr));
402 
403                 ucv_object_add(addrobj, "port",
404                         ucv_uint64_new(ntohs(s4->sin_port)));
405 
406                 return true;
407 
408         case AF_UNIX:
409                 su = (struct sockaddr_un *)ss;
410 
411                 ucv_object_add(addrobj, "path",
412                         ucv_string_new(su->sun_path));
413 
414                 return true;
415 
416 #if defined(__linux__)
417         case AF_PACKET:
418                 sl = (struct sockaddr_ll *)ss;
419 
420                 ucv_object_add(addrobj, "protocol",
421                         ucv_uint64_new(ntohs(sl->sll_protocol)));
422 
423                 ifname = (sl->sll_ifindex > 0)
424                         ? if_indextoname(sl->sll_ifindex, addrstr) : NULL;
425 
426                 if (ifname)
427                         ucv_object_add(addrobj, "interface",
428                                 ucv_string_new(ifname));
429                 else if (sl->sll_ifindex != 0)
430                         ucv_object_add(addrobj, "interface",
431                                 ucv_int64_new(sl->sll_ifindex));
432 
433                 ucv_object_add(addrobj, "hardware_type",
434                         ucv_uint64_new(sl->sll_hatype));
435 
436                 ucv_object_add(addrobj, "packet_type",
437                         ucv_uint64_new(sl->sll_pkttype));
438 
439                 ucv_object_add(addrobj, "address",
440                         hwaddr_to_uv(sl->sll_addr, sl->sll_halen));
441 
442                 return true;
443 #endif
444         }
445 
446         return false;
447 }
448 
449 static int64_t
450 parse_integer(char *s, size_t len)
451 {
452         union { int8_t i8; int16_t i16; int32_t i32; int64_t i64; } v;
453 
454         memcpy(&v, s, len < sizeof(v) ? len : sizeof(v));
455 
456         switch (len) {
457         case 1:  return v.i8;
458         case 2:  return v.i16;
459         case 4:  return v.i32;
460         case 8:  return v.i64;
461         default: return 0;
462         }
463 }
464 
465 static uint64_t
466 parse_unsigned(char *s, size_t len)
467 {
468         union { uint8_t u8; uint16_t u16; uint32_t u32; uint64_t u64; } v;
469 
470         memcpy(&v, s, len < sizeof(v) ? len : sizeof(v));
471 
472         switch (len) {
473         case 1:  return v.u8;
474         case 2:  return v.u16;
475         case 4:  return v.u32;
476         case 8:  return v.u64;
477         default: return 0;
478         }
479 }
480 
481 static bool
482 parse_addr(char *addr, struct sockaddr_storage *ss)
483 {
484         bool v6 = (ss->ss_family == 0 || ss->ss_family == AF_INET6);
485         bool v4 = (ss->ss_family == 0 || ss->ss_family == AF_INET);
486         struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)ss;
487         struct sockaddr_in *s4 = (struct sockaddr_in *)ss;
488         unsigned long n;
489         char *scope, *e;
490 
491         if (v6 && (scope = strchr(addr, '%')) != NULL) {
492                 *scope++ = 0;
493                 n = strtoul(scope, &e, 10);
494 
495                 if (e == scope || *e != 0) {
496                         n = if_nametoindex(scope);
497 
498                         if (n == 0)
499                                 err_return(errno, "Unable to resolve interface %s", scope);
500                 }
501 
502                 if (inet_pton(AF_INET6, addr, &s6->sin6_addr) != 1)
503                         err_return(errno, "Invalid IPv6 address");
504 
505                 s6->sin6_family = AF_INET6;
506                 s6->sin6_scope_id = n;
507 
508                 return true;
509         }
510         else if (v6 && inet_pton(AF_INET6, addr, &s6->sin6_addr) == 1) {
511                 s6->sin6_family = AF_INET6;
512 
513                 return true;
514         }
515         else if (v4 && inet_pton(AF_INET, addr, &s4->sin_addr) == 1) {
516                 s4->sin_family = AF_INET;
517 
518                 return true;
519         }
520 
521         err_return(EINVAL, "Unable to parse IP address");
522 }
523 
524 static bool
525 uv_to_sockaddr(uc_value_t *addr, struct sockaddr_storage *ss, socklen_t *slen)
526 {
527         char *s, *p, addrstr[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255%2147483648")];
528         struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)ss;
529         struct sockaddr_in *s4 = (struct sockaddr_in *)ss;
530         struct sockaddr_un *su = (struct sockaddr_un *)ss;
531 #if defined(__linux__)
532         struct sockaddr_ll *sl = (struct sockaddr_ll *)ss;
533 #endif
534         uc_value_t *item;
535         unsigned long n;
536         size_t len;
537 
538         memset(ss, 0, sizeof(*ss));
539 
540         if (ucv_type(addr) == UC_STRING) {
541                 s = ucv_string_get(addr);
542                 len = ucv_string_length(addr);
543 
544                 if (memchr(s, '/', len) != NULL) {
545                         if (len >= sizeof(su->sun_path))
546                                 len = sizeof(su->sun_path) - 1;
547 
548                         memcpy(su->sun_path, s, len);
549                         su->sun_path[len++] = 0;
550                         su->sun_family = AF_UNIX;
551                         *slen = sizeof(*su);
552 
553                         ok_return(true);
554                 }
555 
556                 if (len == 0)
557                         err_return(EINVAL, "Invalid IP address");
558 
559                 if (*s == '[') {
560                         p = memchr(++s, ']', --len);
561 
562                         if (!p || (size_t)(p - s) >= sizeof(addrstr))
563                                 err_return(EINVAL, "Invalid IPv6 address");
564 
565                         memcpy(addrstr, s, p - s);
566                         addrstr[(p - s) + 1] = 0;
567 
568                         ss->ss_family = AF_INET6;
569                         len -= ((p - s) + 1);
570                         s = p + 1;
571                 }
572                 else if ((p = memchr(s, ':', len)) != NULL &&
573                          memchr(p + 1, ':', len - ((p - s) + 1)) == NULL) {
574                         if ((size_t)(p - s) >= sizeof(addrstr))
575                                 err_return(EINVAL, "Invalid IP address");
576 
577                         memcpy(addrstr, s, p - s);
578                         addrstr[p - s + 1] = 0;
579 
580                         ss->ss_family = AF_INET;
581                         len -= (p - s);
582                         s = p;
583                 }
584                 else {
585                         if (len >= sizeof(addrstr))
586                                 err_return(EINVAL, "Invalid IP address");
587 
588                         memcpy(addrstr, s, len);
589                         addrstr[len] = 0;
590 
591                         ss->ss_family = 0;
592                         len = 0;
593                         s = NULL;
594                 }
595 
596                 if (!parse_addr(addrstr, ss))
597                         return NULL;
598 
599                 if (s && *s == ':') {
600                         if (len <= 1)
601                                 err_return(EINVAL, "Invalid port number");
602 
603                         for (s++, len--, n = 0; len > 0; len--, s++) {
604                                 if (*s < '' || *s > '9')
605                                         err_return(EINVAL, "Invalid port number");
606 
607                                 n = n * 10 + (*s - '');
608                         }
609 
610                         if (n > 65535)
611                                 err_return(EINVAL, "Invalid port number");
612 
613                         s6->sin6_port = htons(n);
614                 }
615 
616                 *slen = (ss->ss_family == AF_INET6) ? sizeof(*s6) : sizeof(*s4);
617 
618                 ok_return(true);
619         }
620         else if (ucv_type(addr) == UC_ARRAY) {
621                 if (ucv_array_length(addr) == 16) {
622                         uint8_t *u8 = (uint8_t *)&s6->sin6_addr;
623 
624                         for (size_t i = 0; i < 16; i++) {
625                                 item = ucv_array_get(addr, i);
626                                 n = ucv_uint64_get(item);
627 
628                                 if (ucv_type(item) != UC_INTEGER || errno != 0 || n > 255)
629                                         err_return(EINVAL, "Invalid IP address array");
630 
631                                 u8[i] = n;
632                         }
633 
634                         s6->sin6_family = AF_INET6;
635                         *slen = sizeof(*s6);
636 
637                         ok_return(true);
638                 }
639                 else if (ucv_array_length(addr) == 4) {
640                         s4->sin_addr.s_addr = 0;
641 
642                         for (size_t i = 0; i < 4; i++) {
643                                 item = ucv_array_get(addr, i);
644                                 n = ucv_uint64_get(item);
645 
646                                 if (ucv_type(item) != UC_INTEGER || errno != 0 || n > 255)
647                                         err_return(EINVAL, "Invalid IP address array");
648 
649                                 s4->sin_addr.s_addr = s4->sin_addr.s_addr * 256 + n;
650                         }
651 
652                         s4->sin_addr.s_addr = htonl(s4->sin_addr.s_addr);
653                         s4->sin_family = AF_INET;
654                         *slen = sizeof(*s4);
655 
656                         ok_return(true);
657                 }
658 
659                 err_return(EINVAL, "Invalid IP address array");
660         }
661         else if (ucv_type(addr) == UC_OBJECT) {
662                 n = ucv_to_unsigned(ucv_object_get(addr, "family", NULL));
663 
664                 if (n == 0) {
665                         if (ucv_type(ucv_object_get(addr, "path", NULL)) == UC_STRING) {
666                                 n = AF_UNIX;
667                         }
668                         else {
669                                 item = ucv_object_get(addr, "address", NULL);
670                                 len = ucv_string_length(item);
671                                 s = ucv_string_get(item);
672                                 n = (s && memchr(s, ':', len) != NULL) ? AF_INET6 : AF_INET;
673                         }
674 
675                         if (n == 0)
676                                 err_return(EINVAL, "Invalid address object");
677                 }
678 
679                 switch (n) {
680                 case AF_INET6:
681                         item = ucv_object_get(addr, "flowinfo", NULL);
682                         s6->sin6_flowinfo = htonl(ucv_to_unsigned(item));
683 
684                         item = ucv_object_get(addr, "interface", NULL);
685 
686                         if (ucv_type(item) == UC_STRING) {
687                                 s6->sin6_scope_id = if_nametoindex(ucv_string_get(item));
688 
689                                 if (s6->sin6_scope_id == 0)
690                                         err_return(errno, "Unable to resolve interface %s",
691                                                 ucv_string_get(item));
692                         }
693                         else if (item != NULL) {
694                                 s6->sin6_scope_id = ucv_to_unsigned(item);
695 
696                                 if (errno != 0)
697                                         err_return(errno, "Invalid scope ID");
698                         }
699 
700                         /* fall through */
701 
702                 case AF_INET:
703                         ss->ss_family = n;
704                         *slen = (n == AF_INET6) ? sizeof(*s6) : sizeof(*s4);
705 
706                         item = ucv_object_get(addr, "port", NULL);
707                         n = ucv_to_unsigned(item);
708 
709                         if (errno != 0 || n > 65535)
710                                 err_return(EINVAL, "Invalid port number");
711 
712                         s6->sin6_port = htons(n);
713 
714                         item = ucv_object_get(addr, "address", NULL);
715                         len = ucv_string_length(item);
716                         s = ucv_string_get(item);
717 
718                         if (len >= sizeof(addrstr))
719                                 err_return(EINVAL, "Invalid IP address");
720 
721                         if (len > 0) {
722                                 memcpy(addrstr, s, len);
723                                 addrstr[len] = 0;
724 
725                                 if (!parse_addr(addrstr, ss))
726                                         return NULL;
727                         }
728 
729                         ok_return(true);
730 
731                 case AF_UNIX:
732                         item = ucv_object_get(addr, "path", NULL);
733                         len = ucv_string_length(item);
734 
735                         if (len == 0 || len >= sizeof(su->sun_path))
736                                 err_return(EINVAL, "Invalid path value");
737 
738                         memcpy(su->sun_path, ucv_string_get(item), len);
739                         su->sun_path[len++] = 0;
740                         su->sun_family = AF_UNIX;
741                         *slen = sizeof(*su);
742 
743                         ok_return(true);
744 
745 #if defined(__linux__)
746                 case AF_PACKET:
747                         item = ucv_object_get(addr, "protocol", NULL);
748 
749                         if (item) {
750                                 n = ucv_to_unsigned(item);
751 
752                                 if (errno != 0 || n > 65535)
753                                         err_return(EINVAL, "Invalid protocol number");
754 
755                                 sl->sll_protocol = htons(n);
756                         }
757 
758                         item = ucv_object_get(addr, "address", NULL);
759 
760                         if (uv_to_hwaddr(item, sl->sll_addr, &len))
761                                 sl->sll_halen = len;
762                         else
763                                 return false;
764 
765                         item = ucv_object_get(addr, "interface", NULL);
766 
767                         if (ucv_type(item) == UC_STRING) {
768                                 sl->sll_ifindex = if_nametoindex(ucv_string_get(item));
769 
770                                 if (sl->sll_ifindex == 0)
771                                         err_return(errno, "Unable to resolve interface %s",
772                                                 ucv_string_get(item));
773                         }
774                         else if (item != NULL) {
775                                 sl->sll_ifindex = ucv_to_integer(item);
776 
777                                 if (errno)
778                                         err_return(errno, "Unable to convert interface to integer");
779                         }
780 
781                         item = ucv_object_get(addr, "hardware_type", NULL);
782 
783                         if (item) {
784                                 n = ucv_to_unsigned(item);
785 
786                                 if (errno != 0 || n > 65535)
787                                         err_return(EINVAL, "Invalid hardware type");
788 
789                                 sl->sll_hatype = n;
790                         }
791 
792                         item = ucv_object_get(addr, "packet_type", NULL);
793 
794                         if (item) {
795                                 n = ucv_to_unsigned(item);
796 
797                                 if (errno != 0 || n > 255)
798                                         err_return(EINVAL, "Invalid packet type");
799 
800                                 sl->sll_pkttype = n;
801                         }
802 
803                         sl->sll_family = AF_PACKET;
804                         *slen = sizeof(*sl);
805 
806                         ok_return(true);
807 #endif
808                 }
809         }
810 
811         err_return(EINVAL, "Invalid address value");
812 }
813 
814 static bool
815 uv_to_fileno(uc_vm_t *vm, uc_value_t *val, int *fileno)
816 {
817         uc_value_t *fn;
818         int *fdptr;
819 
820         fdptr = (int *)ucv_resource_dataptr(val, "socket");
821 
822         if (fdptr) {
823                 if (*fdptr < 0)
824                         err_return(EBADF, "Socket is closed");
825 
826                 *fileno = *fdptr;
827 
828                 return true;
829         }
830 
831         fn = ucv_property_get(val, "fileno");
832 
833         if (ucv_is_callable(fn)) {
834                 uc_vm_stack_push(vm, ucv_get(val));
835                 uc_vm_stack_push(vm, ucv_get(fn));
836 
837                 if (uc_vm_call(vm, true, 0) != EXCEPTION_NONE)
838                         return false;
839 
840                 val = uc_vm_stack_pop(vm);
841         }
842         else {
843                 ucv_get(val);
844         }
845 
846         *fileno = ucv_int64_get(val);
847 
848         ucv_put(val);
849 
850         if (errno != 0 || *fileno < 0)
851                 err_return(EBADF, "Invalid file descriptor number");
852 
853         return true;
854 }
855 
856 static uc_value_t *
857 uv_to_pollfd(uc_vm_t *vm, uc_value_t *val, struct pollfd *pfd)
858 {
859         uc_value_t *rv;
860         int64_t flags;
861 
862         if (ucv_type(val) == UC_ARRAY) {
863                 if (!uv_to_fileno(vm, ucv_array_get(val, 0), &pfd->fd))
864                         return NULL;
865 
866                 flags = ucv_to_integer(ucv_array_get(val, 1));
867 
868                 if (errno != 0 || flags < -32768 || flags > 32767)
869                         err_return(ERANGE, "Flags value out of range");
870 
871                 pfd->events = flags;
872                 pfd->revents = 0;
873 
874                 return ucv_get(val);
875         }
876 
877         if (!uv_to_fileno(vm, val, &pfd->fd))
878                 return NULL;
879 
880         pfd->events = POLLIN | POLLERR | POLLHUP;
881         pfd->revents = 0;
882 
883         rv = ucv_array_new_length(vm, 2);
884 
885         ucv_array_set(rv, 0, ucv_get(val));
886         ucv_array_set(rv, 1, ucv_uint64_new(pfd->events));
887 
888         return rv;
889 }
890 
891 static uc_value_t *
892 ucv_socket_new(uc_vm_t *vm, int fd)
893 {
894         return ucv_resource_new(
895                 ucv_resource_type_lookup(vm, "socket"),
896                 (void *)(intptr_t)fd
897         );
898 }
899 
900 static bool
901 xclose(int *fdptr)
902 {
903         bool rv = true;
904 
905         if (fdptr) {
906                 if (*fdptr >= 0)
907                         rv = (close(*fdptr) == 0);
908 
909                 *fdptr = -1;
910         }
911 
912         return rv;
913 }
914 
915 
916 typedef struct {
917     const char *name;
918     enum { DT_SIGNED, DT_UNSIGNED, DT_IPV4ADDR, DT_IPV6ADDR, DT_CALLBACK } type;
919         union {
920         size_t offset;
921                 bool (*to_c)(void *, uc_value_t *);
922         } u1;
923         union {
924                 size_t size;
925                 uc_value_t *(*to_uv)(void *);
926         } u2;
927 } member_t;
928 
929 typedef struct {
930         size_t size;
931         member_t *members;
932 } struct_t;
933 
934 typedef struct {
935         int level;
936         int option;
937         struct_t *ctype;
938 } sockopt_t;
939 
940 typedef struct {
941         int level;
942         int type;
943         struct_t *ctype;
944 } cmsgtype_t;
945 
946 #define STRUCT_MEMBER_NP(struct_name, member_name, data_type)   \
947         { #member_name, data_type,                                                                      \
948           { .offset = offsetof(struct struct_name, member_name) },      \
949           { .size = sizeof(((struct struct_name *)NULL)->member_name) } }
950 
951 #define STRUCT_MEMBER_CB(member_name, to_c_fn, to_uv_fn)                \
952         { #member_name, DT_CALLBACK, { .to_c = to_c_fn }, { .to_uv = to_uv_fn } }
953 
954 #define STRUCT_MEMBER(struct_name, member_prefix, member_name, data_type)                       \
955         { #member_name, data_type,                                                                                                              \
956           { .offset = offsetof(struct struct_name, member_prefix##_##member_name) },    \
957           { .size = sizeof(((struct struct_name *)NULL)->member_prefix##_##member_name) } }
958 
959 static struct_t st_timeval = {
960         .size = sizeof(struct timeval),
961         .members = (member_t []){
962                 STRUCT_MEMBER(timeval, tv, sec, DT_SIGNED),
963                 STRUCT_MEMBER(timeval, tv, usec, DT_SIGNED),
964                 { 0 }
965         }
966 };
967 
968 #if defined(__linux__)
969 static struct_t st_ucred = {
970         .size = sizeof(struct ucred),
971         .members = (member_t []){
972                 STRUCT_MEMBER_NP(ucred, pid, DT_SIGNED),
973                 STRUCT_MEMBER_NP(ucred, uid, DT_SIGNED),
974                 STRUCT_MEMBER_NP(ucred, gid, DT_SIGNED),
975                 { 0 }
976         }
977 };
978 #endif
979 
980 static struct_t st_linger = {
981         .size = sizeof(struct linger),
982         .members = (member_t []){
983                 STRUCT_MEMBER(linger, l, onoff, DT_SIGNED),
984                 STRUCT_MEMBER(linger, l, linger, DT_SIGNED),
985                 { 0 }
986         }
987 };
988 
989 static struct_t st_ip_mreqn = {
990         .size = sizeof(struct ip_mreqn),
991         .members = (member_t []){
992                 STRUCT_MEMBER(ip_mreqn, imr, multiaddr, DT_IPV4ADDR),
993                 STRUCT_MEMBER(ip_mreqn, imr, address, DT_IPV4ADDR),
994                 STRUCT_MEMBER(ip_mreqn, imr, ifindex, DT_SIGNED),
995                 { 0 }
996         }
997 };
998 
999 static struct_t st_ip_mreq_source = {
1000         .size = sizeof(struct ip_mreq_source),
1001         .members = (member_t []){
1002                 STRUCT_MEMBER(ip_mreq_source, imr, multiaddr, DT_IPV4ADDR),
1003                 STRUCT_MEMBER(ip_mreq_source, imr, interface, DT_IPV4ADDR),
1004                 STRUCT_MEMBER(ip_mreq_source, imr, sourceaddr, DT_IPV4ADDR),
1005                 { 0 }
1006         }
1007 };
1008 
1009 /* This structure is declared in kernel, but not libc headers, so redeclare it
1010    locally */
1011 struct in6_flowlabel_req_local {
1012         struct in6_addr flr_dst;
1013         uint32_t flr_label;
1014         uint8_t flr_action;
1015         uint8_t flr_share;
1016         uint16_t flr_flags;
1017         uint16_t flr_expires;
1018         uint16_t flr_linger;
1019 };
1020 
1021 static struct_t st_in6_flowlabel_req = {
1022         .size = sizeof(struct in6_flowlabel_req_local),
1023         .members = (member_t []){
1024                 STRUCT_MEMBER(in6_flowlabel_req_local, flr, dst, DT_IPV6ADDR),
1025                 STRUCT_MEMBER(in6_flowlabel_req_local, flr, label, DT_UNSIGNED),
1026                 STRUCT_MEMBER(in6_flowlabel_req_local, flr, action, DT_UNSIGNED),
1027                 STRUCT_MEMBER(in6_flowlabel_req_local, flr, share, DT_UNSIGNED),
1028                 STRUCT_MEMBER(in6_flowlabel_req_local, flr, flags, DT_UNSIGNED),
1029                 STRUCT_MEMBER(in6_flowlabel_req_local, flr, expires, DT_UNSIGNED),
1030                 STRUCT_MEMBER(in6_flowlabel_req_local, flr, linger, DT_UNSIGNED),
1031                 { 0 }
1032         }
1033 };
1034 
1035 #if defined(__linux__)
1036 static uc_value_t *
1037 in6_ifindex_to_uv(void *st)
1038 {
1039         char ifname[IF_NAMESIZE] = { 0 };
1040         struct ipv6_mreq *mr = st;
1041 
1042         if (mr->ipv6mr_interface > 0 && if_indextoname(mr->ipv6mr_interface, ifname))
1043                 return ucv_string_new(ifname);
1044 
1045         return ucv_int64_new(mr->ipv6mr_interface);
1046 }
1047 
1048 static bool
1049 in6_ifindex_to_c(void *st, uc_value_t *uv)
1050 {
1051         struct ipv6_mreq *mr = st;
1052 
1053         if (ucv_type(uv) == UC_STRING) {
1054                 mr->ipv6mr_interface = if_nametoindex(ucv_string_get(uv));
1055 
1056                 if (mr->ipv6mr_interface == 0)
1057                         err_return(errno, "Unable to resolve interface %s",
1058                                 ucv_string_get(uv));
1059         }
1060         else {
1061                 mr->ipv6mr_interface = ucv_to_integer(uv);
1062 
1063                 if (errno)
1064                         err_return(errno, "Unable to convert interface to integer");
1065         }
1066 
1067         return true;
1068 }
1069 
1070 static struct_t st_ipv6_mreq = {
1071         .size = sizeof(struct ipv6_mreq),
1072         .members = (member_t []){
1073                 STRUCT_MEMBER(ipv6_mreq, ipv6mr, multiaddr, DT_IPV6ADDR),
1074                 STRUCT_MEMBER_CB(interface, in6_ifindex_to_c, in6_ifindex_to_uv),
1075                 { 0 }
1076         }
1077 };
1078 
1079 /* NB: this is the same layout as struct ipv6_mreq, so we reuse the callbacks */
1080 static struct_t st_in6_pktinfo = {
1081         .size = sizeof(struct in6_pktinfo),
1082         .members = (member_t []){
1083                 STRUCT_MEMBER(in6_pktinfo, ipi6, addr, DT_IPV6ADDR),
1084                 STRUCT_MEMBER_CB(interface, in6_ifindex_to_c, in6_ifindex_to_uv),
1085                 { 0 }
1086         }
1087 };
1088 
1089 struct ipv6_recv_error_local {
1090         struct {
1091                 uint32_t ee_errno;
1092                 uint8_t ee_origin;
1093                 uint8_t ee_type;
1094                 uint8_t ee_code;
1095                 uint8_t ee_pad;
1096                 uint32_t ee_info;
1097                 union {
1098                         uint32_t ee_data;
1099                         struct {
1100                                 uint16_t ee_len;
1101                                 uint8_t ee_flags;
1102                                 uint8_t ee_reserved;
1103                         } ee_rfc4884;
1104                 } u;
1105         } ee;
1106         struct sockaddr_in6 offender;
1107 };
1108 
1109 static uc_value_t *
1110 offender_to_uv(void *st)
1111 {
1112         struct ipv6_recv_error_local *e = st;
1113         uc_value_t *addr = ucv_object_new(NULL);
1114 
1115         if (sockaddr_to_uv((struct sockaddr_storage *)&e->offender, addr))
1116                 return addr;
1117 
1118         ucv_put(addr);
1119 
1120         return NULL;
1121 }
1122 
1123 static struct_t st_ip_recv_error = {
1124         .size = sizeof(struct ipv6_recv_error_local),
1125         .members = (member_t []){
1126                 STRUCT_MEMBER(ipv6_recv_error_local, ee.ee, errno, DT_UNSIGNED),
1127                 STRUCT_MEMBER(ipv6_recv_error_local, ee.ee, origin, DT_UNSIGNED),
1128                 STRUCT_MEMBER(ipv6_recv_error_local, ee.ee, type, DT_UNSIGNED),
1129                 STRUCT_MEMBER(ipv6_recv_error_local, ee.ee, code, DT_UNSIGNED),
1130                 STRUCT_MEMBER(ipv6_recv_error_local, ee.ee, info, DT_UNSIGNED),
1131                 STRUCT_MEMBER(ipv6_recv_error_local, ee.u.ee, data, DT_UNSIGNED),
1132                 STRUCT_MEMBER(ipv6_recv_error_local, ee.u.ee_rfc4884.ee, len, DT_UNSIGNED),
1133                 STRUCT_MEMBER(ipv6_recv_error_local, ee.u.ee_rfc4884.ee, flags, DT_UNSIGNED),
1134                 STRUCT_MEMBER_CB(offender, NULL, offender_to_uv),
1135                 { 0 }
1136         }
1137 };
1138 
1139 static uc_value_t *
1140 ip6m_addr_to_uv(void *st)
1141 {
1142         struct ip6_mtuinfo *mi = st;
1143         uc_value_t *addr = ucv_object_new(NULL);
1144 
1145         if (sockaddr_to_uv((struct sockaddr_storage *)&mi->ip6m_addr, addr))
1146                 return addr;
1147 
1148         ucv_put(addr);
1149 
1150         return NULL;
1151 }
1152 
1153 static struct_t st_ip6_mtuinfo = {
1154         .size = sizeof(struct ip6_mtuinfo),
1155         .members = (member_t []){
1156                 STRUCT_MEMBER_CB(addr, NULL, ip6m_addr_to_uv),
1157                 STRUCT_MEMBER(ip6_mtuinfo, ip6m, mtu, DT_UNSIGNED),
1158                 { 0 }
1159         }
1160 };
1161 
1162 static struct_t st_ip_msfilter = {
1163         .size = sizeof(struct ip_msfilter),
1164         .members = (member_t []){
1165                 STRUCT_MEMBER(ip_msfilter, imsf, multiaddr, DT_IPV4ADDR),
1166                 STRUCT_MEMBER(ip_msfilter, imsf, interface, DT_IPV4ADDR),
1167                 STRUCT_MEMBER(ip_msfilter, imsf, fmode, DT_SIGNED),
1168                 STRUCT_MEMBER(ip_msfilter, imsf, numsrc, DT_SIGNED),
1169                 STRUCT_MEMBER(ip_msfilter, imsf, slist, DT_SIGNED),
1170                 { 0 }
1171         }
1172 };
1173 
1174 static uc_value_t *
1175 snd_wscale_to_uv(void *st)
1176 {
1177         return ucv_uint64_new(((struct tcp_info *)st)->tcpi_snd_wscale);
1178 }
1179 
1180 static uc_value_t *
1181 rcv_wscale_to_uv(void *st)
1182 {
1183         return ucv_uint64_new(((struct tcp_info *)st)->tcpi_rcv_wscale);
1184 }
1185 
1186 static bool
1187 snd_wscale_to_c(void *st, uc_value_t *uv)
1188 {
1189         ((struct tcp_info *)st)->tcpi_snd_wscale = ucv_to_unsigned(uv);
1190 
1191         if (errno)
1192                 err_return(errno, "Unable to convert field snd_wscale to unsigned");
1193 
1194         return true;
1195 }
1196 
1197 static bool
1198 rcv_wscale_to_c(void *st, uc_value_t *uv)
1199 {
1200         ((struct tcp_info *)st)->tcpi_rcv_wscale = ucv_to_unsigned(uv);
1201 
1202         if (errno)
1203                 err_return(errno, "Unable to convert field rcv_wscale to unsigned");
1204 
1205         return true;
1206 }
1207 
1208 static struct_t st_tcp_info = {
1209         .size = sizeof(struct tcp_info),
1210         .members = (member_t []){
1211                 STRUCT_MEMBER(tcp_info, tcpi, state, DT_UNSIGNED),
1212                 STRUCT_MEMBER(tcp_info, tcpi, ca_state, DT_UNSIGNED),
1213                 STRUCT_MEMBER(tcp_info, tcpi, retransmits, DT_UNSIGNED),
1214                 STRUCT_MEMBER(tcp_info, tcpi, probes, DT_UNSIGNED),
1215                 STRUCT_MEMBER(tcp_info, tcpi, backoff, DT_UNSIGNED),
1216                 STRUCT_MEMBER(tcp_info, tcpi, options, DT_UNSIGNED),
1217                 STRUCT_MEMBER_CB(snd_wscale, snd_wscale_to_c, snd_wscale_to_uv),
1218                 STRUCT_MEMBER_CB(rcv_wscale, rcv_wscale_to_c, rcv_wscale_to_uv),
1219                 STRUCT_MEMBER(tcp_info, tcpi, rto, DT_UNSIGNED),
1220                 STRUCT_MEMBER(tcp_info, tcpi, ato, DT_UNSIGNED),
1221                 STRUCT_MEMBER(tcp_info, tcpi, snd_mss, DT_UNSIGNED),
1222                 STRUCT_MEMBER(tcp_info, tcpi, rcv_mss, DT_UNSIGNED),
1223                 STRUCT_MEMBER(tcp_info, tcpi, unacked, DT_UNSIGNED),
1224                 STRUCT_MEMBER(tcp_info, tcpi, sacked, DT_UNSIGNED),
1225                 STRUCT_MEMBER(tcp_info, tcpi, lost, DT_UNSIGNED),
1226                 STRUCT_MEMBER(tcp_info, tcpi, retrans, DT_UNSIGNED),
1227                 STRUCT_MEMBER(tcp_info, tcpi, fackets, DT_UNSIGNED),
1228                 STRUCT_MEMBER(tcp_info, tcpi, last_data_sent, DT_UNSIGNED),
1229                 STRUCT_MEMBER(tcp_info, tcpi, last_ack_sent, DT_UNSIGNED),
1230                 STRUCT_MEMBER(tcp_info, tcpi, last_data_recv, DT_UNSIGNED),
1231                 STRUCT_MEMBER(tcp_info, tcpi, last_ack_recv, DT_UNSIGNED),
1232                 STRUCT_MEMBER(tcp_info, tcpi, pmtu, DT_UNSIGNED),
1233                 STRUCT_MEMBER(tcp_info, tcpi, rcv_ssthresh, DT_UNSIGNED),
1234                 STRUCT_MEMBER(tcp_info, tcpi, rtt, DT_UNSIGNED),
1235                 STRUCT_MEMBER(tcp_info, tcpi, rttvar, DT_UNSIGNED),
1236                 STRUCT_MEMBER(tcp_info, tcpi, snd_ssthresh, DT_UNSIGNED),
1237                 STRUCT_MEMBER(tcp_info, tcpi, snd_cwnd, DT_UNSIGNED),
1238                 STRUCT_MEMBER(tcp_info, tcpi, advmss, DT_UNSIGNED),
1239                 STRUCT_MEMBER(tcp_info, tcpi, reordering, DT_UNSIGNED),
1240                 STRUCT_MEMBER(tcp_info, tcpi, rcv_rtt, DT_UNSIGNED),
1241                 STRUCT_MEMBER(tcp_info, tcpi, rcv_space, DT_UNSIGNED),
1242                 STRUCT_MEMBER(tcp_info, tcpi, total_retrans, DT_UNSIGNED),
1243                 { 0 }
1244         }
1245 };
1246 #endif
1247 
1248 static uc_value_t *
1249 ai_addr_to_uv(void *st)
1250 {
1251         uc_value_t *rv = ucv_object_new(NULL);
1252         struct sockaddr_storage ss = { 0 };
1253         struct addrinfo *ai = st;
1254 
1255         memcpy(&ss, ai->ai_addr, ai->ai_addrlen);
1256 
1257         if (!sockaddr_to_uv(&ss, rv)) {
1258                 ucv_put(rv);
1259                 return NULL;
1260         }
1261 
1262         return rv;
1263 }
1264 
1265 static uc_value_t *
1266 ai_canonname_to_uv(void *st)
1267 {
1268         struct addrinfo *ai = st;
1269         return ai->ai_canonname ? ucv_string_new(ai->ai_canonname) : NULL;
1270 }
1271 
1272 /**
1273  * Represents a network address information object returned by
1274  * {@link module:socket#addrinfo|`addrinfo()`}.
1275  *
1276  * @typedef {Object} module:socket.AddressInfo
1277  *
1278  * @property {module:socket.socket.SocketAddress} addr - A socket address structure.
1279  * @property {string} [canonname=null] - The canonical hostname associated with the address.
1280  * @property {number} family - The address family (e.g., `2` for `AF_INET`, `10` for `AF_INET6`).
1281  * @property {number} flags - Additional flags indicating properties of the address.
1282  * @property {number} protocol - The protocol number.
1283  * @property {number} socktype - The socket type (e.g., `1` for `SOCK_STREAM`, `2` for `SOCK_DGRAM`).
1284  */
1285 static struct_t st_addrinfo = {
1286         .size = sizeof(struct addrinfo),
1287         .members = (member_t []){
1288                 STRUCT_MEMBER(addrinfo, ai, flags, DT_SIGNED),
1289                 STRUCT_MEMBER(addrinfo, ai, family, DT_SIGNED),
1290                 STRUCT_MEMBER(addrinfo, ai, socktype, DT_SIGNED),
1291                 STRUCT_MEMBER(addrinfo, ai, protocol, DT_SIGNED),
1292                 STRUCT_MEMBER_CB(addr, NULL, ai_addr_to_uv),
1293                 STRUCT_MEMBER_CB(canonname, NULL, ai_canonname_to_uv),
1294                 { 0 }
1295         }
1296 };
1297 
1298 #if defined(__linux__)
1299 static uc_value_t *
1300 mr_ifindex_to_uv(void *st)
1301 {
1302         char ifname[IF_NAMESIZE] = { 0 };
1303         struct packet_mreq *mr = st;
1304 
1305         if (mr->mr_ifindex > 0 && if_indextoname(mr->mr_ifindex, ifname))
1306                 return ucv_string_new(ifname);
1307 
1308         return ucv_int64_new(mr->mr_ifindex);
1309 }
1310 
1311 static bool
1312 mr_ifindex_to_c(void *st, uc_value_t *uv)
1313 {
1314         struct packet_mreq *mr = st;
1315 
1316         if (ucv_type(uv) == UC_STRING) {
1317                 mr->mr_ifindex = if_nametoindex(ucv_string_get(uv));
1318 
1319                 if (mr->mr_ifindex == 0)
1320                         err_return(errno, "Unable to resolve interface %s",
1321                                 ucv_string_get(uv));
1322         }
1323         else {
1324                 mr->mr_ifindex = ucv_to_integer(uv);
1325 
1326                 if (errno)
1327                         err_return(errno, "Unable to convert interface to integer");
1328         }
1329 
1330         return true;
1331 }
1332 
1333 static uc_value_t *
1334 mr_address_to_uv(void *st)
1335 {
1336         struct packet_mreq *mr = st;
1337 
1338         return hwaddr_to_uv(mr->mr_address, mr->mr_alen);
1339 }
1340 
1341 static bool
1342 mr_address_to_c(void *st, uc_value_t *uv)
1343 {
1344         struct packet_mreq *mr = st;
1345         size_t len;
1346 
1347         if (!uv_to_hwaddr(uv, mr->mr_address, &len))
1348                 return false;
1349 
1350         mr->mr_alen = len;
1351 
1352         return true;
1353 }
1354 
1355 static struct_t st_packet_mreq = {
1356         .size = sizeof(struct packet_mreq),
1357         .members = (member_t []){
1358                 STRUCT_MEMBER_CB(interface, mr_ifindex_to_c, mr_ifindex_to_uv),
1359                 STRUCT_MEMBER(packet_mreq, mr, type, DT_UNSIGNED),
1360                 STRUCT_MEMBER_CB(address, mr_address_to_c, mr_address_to_uv),
1361                 { 0 }
1362         }
1363 };
1364 
1365 static struct_t st_tpacket_req = {
1366         .size = sizeof(struct tpacket_req),
1367         .members = (member_t []){
1368                 STRUCT_MEMBER(tpacket_req, tp, block_size, DT_UNSIGNED),
1369                 STRUCT_MEMBER(tpacket_req, tp, block_nr, DT_UNSIGNED),
1370                 STRUCT_MEMBER(tpacket_req, tp, frame_size, DT_UNSIGNED),
1371                 STRUCT_MEMBER(tpacket_req, tp, frame_nr, DT_UNSIGNED),
1372                 { 0 }
1373         }
1374 };
1375 
1376 static struct_t st_tpacket_stats = {
1377         .size = sizeof(struct tpacket_stats),
1378         .members = (member_t []){
1379                 STRUCT_MEMBER(tpacket_stats, tp, packets, DT_UNSIGNED),
1380                 STRUCT_MEMBER(tpacket_stats, tp, drops, DT_UNSIGNED),
1381                 { 0 }
1382         }
1383 };
1384 
1385 static struct_t st_tpacket_auxdata = {
1386         .size = sizeof(struct tpacket_auxdata),
1387         .members = (member_t []){
1388                 STRUCT_MEMBER(tpacket_auxdata, tp, status, DT_UNSIGNED),
1389                 STRUCT_MEMBER(tpacket_auxdata, tp, len, DT_UNSIGNED),
1390                 STRUCT_MEMBER(tpacket_auxdata, tp, snaplen, DT_UNSIGNED),
1391                 STRUCT_MEMBER(tpacket_auxdata, tp, mac, DT_UNSIGNED),
1392                 STRUCT_MEMBER(tpacket_auxdata, tp, net, DT_UNSIGNED),
1393                 STRUCT_MEMBER(tpacket_auxdata, tp, vlan_tci, DT_UNSIGNED),
1394                 STRUCT_MEMBER(tpacket_auxdata, tp, vlan_tpid, DT_UNSIGNED),
1395                 { 0 }
1396         }
1397 };
1398 
1399 static struct_t st_fanout_args = {
1400         .size = sizeof(struct fanout_args),
1401         .members = (member_t []){
1402                 STRUCT_MEMBER_NP(fanout_args, id, DT_UNSIGNED),
1403                 STRUCT_MEMBER_NP(fanout_args, type_flags, DT_UNSIGNED),
1404                 STRUCT_MEMBER_NP(fanout_args, max_num_members, DT_UNSIGNED),
1405                 { 0 }
1406         }
1407 };
1408 
1409 struct timeval_old_local {
1410         long tv_sec;
1411 #if defined(__sparc__) && defined(__arch64__)
1412         int tv_usec;
1413 #else
1414         long tv_usec;
1415 #endif
1416 };
1417 
1418 static struct_t st_timeval_old = {
1419         .size = sizeof(struct timeval_old_local),
1420         .members = (member_t []){
1421                 STRUCT_MEMBER(timeval_old_local, tv, sec, DT_SIGNED),
1422                 STRUCT_MEMBER(timeval_old_local, tv, usec, DT_SIGNED),
1423                 { 0 }
1424         }
1425 };
1426 
1427 # ifdef SO_TIMESTAMP_NEW
1428 struct timeval_new_local { int64_t tv_sec; int64_t tv_usec; };
1429 static struct_t st_timeval_new = {
1430         .size = sizeof(struct timeval_old_local),
1431         .members = (member_t []){
1432                 STRUCT_MEMBER(timeval_new_local, tv, sec, DT_SIGNED),
1433                 STRUCT_MEMBER(timeval_new_local, tv, usec, DT_SIGNED),
1434                 { 0 }
1435         }
1436 };
1437 # endif
1438 
1439 struct timespec_old_local { long tv_sec; long tv_nsec; };
1440 static struct_t st_timespec_old = {
1441         .size = sizeof(struct timespec_old_local),
1442         .members = (member_t []){
1443                 STRUCT_MEMBER(timespec_old_local, tv, sec, DT_SIGNED),
1444                 STRUCT_MEMBER(timespec_old_local, tv, nsec, DT_SIGNED),
1445                 { 0 }
1446         }
1447 };
1448 
1449 # ifdef SO_TIMESTAMPNS_NEW
1450 struct timespec_new_local { long long tv_sec; long long tv_nsec; };
1451 static struct_t st_timespec_new = {
1452         .size = sizeof(struct timespec_new_local),
1453         .members = (member_t []){
1454                 STRUCT_MEMBER(timespec_new_local, tv, sec, DT_SIGNED),
1455                 STRUCT_MEMBER(timespec_new_local, tv, nsec, DT_SIGNED),
1456                 { 0 }
1457         }
1458 };
1459 # endif
1460 #endif
1461 
1462 #define SV_VOID         (struct_t *)0
1463 #define SV_INT          (struct_t *)1
1464 #define SV_INT_RO       (struct_t *)2
1465 #define SV_BOOL         (struct_t *)3
1466 #define SV_STRING       (struct_t *)4
1467 #define SV_IFNAME       (struct_t *)5
1468 
1469 #define CV_INT          (struct_t *)0
1470 #define CV_UINT         (struct_t *)1
1471 #define CV_BE32         (struct_t *)2
1472 #define CV_STRING       (struct_t *)3
1473 #define CV_SOCKADDR     (struct_t *)4
1474 #define CV_FDS          (struct_t *)5
1475 
1476 static sockopt_t sockopts[] = {
1477     { SOL_SOCKET, SO_ACCEPTCONN, SV_BOOL },
1478     { SOL_SOCKET, SO_BROADCAST, SV_BOOL },
1479     { SOL_SOCKET, SO_DEBUG, SV_BOOL },
1480     { SOL_SOCKET, SO_ERROR, SV_INT_RO },
1481     { SOL_SOCKET, SO_DONTROUTE, SV_BOOL },
1482     { SOL_SOCKET, SO_KEEPALIVE, SV_BOOL },
1483     { SOL_SOCKET, SO_LINGER, &st_linger },
1484     { SOL_SOCKET, SO_OOBINLINE, SV_BOOL },
1485     { SOL_SOCKET, SO_RCVBUF, SV_INT },
1486     { SOL_SOCKET, SO_RCVLOWAT, SV_INT },
1487     { SOL_SOCKET, SO_RCVTIMEO, &st_timeval },
1488     { SOL_SOCKET, SO_REUSEADDR, SV_BOOL },
1489     { SOL_SOCKET, SO_REUSEPORT, SV_BOOL },
1490     { SOL_SOCKET, SO_SNDBUF, SV_INT },
1491     { SOL_SOCKET, SO_SNDLOWAT, SV_INT },
1492     { SOL_SOCKET, SO_SNDTIMEO, &st_timeval },
1493     { SOL_SOCKET, SO_TIMESTAMP, SV_BOOL },
1494     { SOL_SOCKET, SO_TYPE, SV_INT },
1495 #if defined(__linux__)
1496     { SOL_SOCKET, SO_ATTACH_FILTER, SV_STRING },
1497     { SOL_SOCKET, SO_ATTACH_BPF, SV_INT },
1498     { SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, SV_STRING },
1499     { SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, SV_INT },
1500     { SOL_SOCKET, SO_BINDTODEVICE, SV_STRING },
1501     { SOL_SOCKET, SO_DETACH_FILTER, SV_VOID },
1502     { SOL_SOCKET, SO_DETACH_BPF, SV_VOID },
1503     { SOL_SOCKET, SO_DOMAIN, SV_INT_RO },
1504     { SOL_SOCKET, SO_INCOMING_CPU, SV_INT },
1505     { SOL_SOCKET, SO_INCOMING_NAPI_ID, SV_INT_RO },
1506     { SOL_SOCKET, SO_LOCK_FILTER, SV_INT },
1507     { SOL_SOCKET, SO_MARK, SV_INT },
1508     { SOL_SOCKET, SO_PASSCRED, SV_BOOL },
1509     { SOL_SOCKET, SO_PASSSEC, SV_BOOL },
1510     { SOL_SOCKET, SO_PEEK_OFF, SV_INT },
1511     { SOL_SOCKET, SO_PEERCRED, &st_ucred },
1512     { SOL_SOCKET, SO_PEERSEC, SV_STRING },
1513     { SOL_SOCKET, SO_PRIORITY, SV_INT },
1514     { SOL_SOCKET, SO_PROTOCOL, SV_INT },
1515     { SOL_SOCKET, SO_RCVBUFFORCE, SV_INT },
1516     { SOL_SOCKET, SO_RXQ_OVFL, SV_BOOL },
1517     { SOL_SOCKET, SO_SNDBUFFORCE, SV_INT },
1518     { SOL_SOCKET, SO_TIMESTAMPNS, SV_BOOL },
1519     { SOL_SOCKET, SO_BUSY_POLL, SV_INT },
1520 #endif
1521 
1522     { IPPROTO_IP, IP_ADD_MEMBERSHIP, &st_ip_mreqn },
1523     { IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, &st_ip_mreq_source },
1524     { IPPROTO_IP, IP_BLOCK_SOURCE, &st_ip_mreq_source },
1525     { IPPROTO_IP, IP_DROP_MEMBERSHIP, &st_ip_mreqn },
1526     { IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP, &st_ip_mreq_source },
1527     { IPPROTO_IP, IP_HDRINCL, SV_BOOL },
1528     { IPPROTO_IP, IP_MULTICAST_IF, &st_ip_mreqn },
1529     { IPPROTO_IP, IP_MULTICAST_LOOP, SV_BOOL },
1530     { IPPROTO_IP, IP_MULTICAST_TTL, SV_INT },
1531     { IPPROTO_IP, IP_OPTIONS, SV_STRING },
1532     { IPPROTO_IP, IP_PKTINFO, SV_BOOL },
1533     { IPPROTO_IP, IP_RECVOPTS, SV_BOOL },
1534     { IPPROTO_IP, IP_RECVTOS, SV_BOOL },
1535     { IPPROTO_IP, IP_RECVTTL, SV_BOOL },
1536     { IPPROTO_IP, IP_RETOPTS, SV_BOOL },
1537     { IPPROTO_IP, IP_TOS, SV_INT },
1538     { IPPROTO_IP, IP_TTL, SV_INT },
1539     { IPPROTO_IP, IP_UNBLOCK_SOURCE, &st_ip_mreq_source },
1540 #if defined(__linux__)
1541     { IPPROTO_IP, IP_MSFILTER, &st_ip_msfilter },
1542     { IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, SV_BOOL },
1543     { IPPROTO_IP, IP_FREEBIND, SV_BOOL },
1544     { IPPROTO_IP, IP_MTU, SV_INT },
1545     { IPPROTO_IP, IP_MTU_DISCOVER, SV_INT },
1546     { IPPROTO_IP, IP_MULTICAST_ALL, SV_BOOL },
1547     { IPPROTO_IP, IP_NODEFRAG, SV_BOOL },
1548     { IPPROTO_IP, IP_PASSSEC, SV_BOOL },
1549     { IPPROTO_IP, IP_RECVERR, SV_BOOL },
1550     { IPPROTO_IP, IP_RECVORIGDSTADDR, SV_BOOL },
1551     { IPPROTO_IP, IP_ROUTER_ALERT, SV_BOOL },
1552     { IPPROTO_IP, IP_TRANSPARENT, SV_BOOL },
1553 #endif
1554 
1555         { IPPROTO_IPV6, IPV6_FLOWINFO_SEND, SV_BOOL },
1556         { IPPROTO_IPV6, IPV6_FLOWINFO, SV_BOOL },
1557         { IPPROTO_IPV6, IPV6_FLOWLABEL_MGR, &st_in6_flowlabel_req },
1558         { IPPROTO_IPV6, IPV6_MULTICAST_HOPS, SV_INT },
1559         { IPPROTO_IPV6, IPV6_MULTICAST_IF, SV_IFNAME },
1560         { IPPROTO_IPV6, IPV6_MULTICAST_LOOP, SV_BOOL },
1561         { IPPROTO_IPV6, IPV6_RECVTCLASS, SV_BOOL },
1562         { IPPROTO_IPV6, IPV6_TCLASS, SV_INT },
1563         { IPPROTO_IPV6, IPV6_UNICAST_HOPS, SV_INT },
1564         { IPPROTO_IPV6, IPV6_V6ONLY, SV_BOOL },
1565 #if defined(__linux__)
1566         { IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &st_ipv6_mreq },
1567         { IPPROTO_IPV6, IPV6_ADDR_PREFERENCES, SV_INT },
1568         { IPPROTO_IPV6, IPV6_ADDRFORM, SV_INT },
1569         { IPPROTO_IPV6, IPV6_AUTHHDR, SV_BOOL },
1570         { IPPROTO_IPV6, IPV6_AUTOFLOWLABEL, SV_BOOL },
1571         { IPPROTO_IPV6, IPV6_DONTFRAG, SV_BOOL },
1572         { IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &st_ipv6_mreq },
1573         { IPPROTO_IPV6, IPV6_DSTOPTS, SV_STRING },
1574         { IPPROTO_IPV6, IPV6_FREEBIND, SV_BOOL },
1575         { IPPROTO_IPV6, IPV6_HOPLIMIT, SV_BOOL },
1576         { IPPROTO_IPV6, IPV6_HOPOPTS, SV_STRING },
1577         { IPPROTO_IPV6, IPV6_JOIN_ANYCAST, &st_ipv6_mreq },
1578         { IPPROTO_IPV6, IPV6_LEAVE_ANYCAST, &st_ipv6_mreq },
1579         { IPPROTO_IPV6, IPV6_MINHOPCOUNT, SV_INT },
1580         { IPPROTO_IPV6, IPV6_MTU_DISCOVER, SV_INT },
1581         { IPPROTO_IPV6, IPV6_MTU, SV_INT },
1582         { IPPROTO_IPV6, IPV6_MULTICAST_ALL, SV_BOOL },
1583         { IPPROTO_IPV6, IPV6_PKTINFO, &st_in6_pktinfo },
1584         { IPPROTO_IPV6, IPV6_RECVDSTOPTS, SV_BOOL },
1585         { IPPROTO_IPV6, IPV6_RECVERR, SV_BOOL },
1586         { IPPROTO_IPV6, IPV6_RECVFRAGSIZE, SV_BOOL },
1587         { IPPROTO_IPV6, IPV6_RECVHOPLIMIT, SV_BOOL },
1588         { IPPROTO_IPV6, IPV6_RECVHOPOPTS, SV_BOOL },
1589         { IPPROTO_IPV6, IPV6_RECVORIGDSTADDR, SV_BOOL },
1590         { IPPROTO_IPV6, IPV6_RECVPATHMTU, SV_BOOL },
1591         { IPPROTO_IPV6, IPV6_RECVPKTINFO, SV_BOOL },
1592         { IPPROTO_IPV6, IPV6_RECVRTHDR, SV_BOOL },
1593         { IPPROTO_IPV6, IPV6_ROUTER_ALERT_ISOLATE, SV_BOOL },
1594         { IPPROTO_IPV6, IPV6_ROUTER_ALERT, SV_BOOL },
1595         { IPPROTO_IPV6, IPV6_RTHDR, SV_STRING },
1596         { IPPROTO_IPV6, IPV6_RTHDRDSTOPTS, SV_STRING },
1597         { IPPROTO_IPV6, IPV6_TRANSPARENT, SV_BOOL },
1598         { IPPROTO_IPV6, IPV6_UNICAST_IF, SV_IFNAME },
1599 #endif
1600 
1601     { IPPROTO_TCP, TCP_KEEPCNT, SV_INT },
1602     { IPPROTO_TCP, TCP_KEEPINTVL, SV_INT },
1603     { IPPROTO_TCP, TCP_MAXSEG, SV_INT },
1604     { IPPROTO_TCP, TCP_NODELAY, SV_BOOL },
1605     { IPPROTO_TCP, TCP_FASTOPEN, SV_INT },
1606 #if defined(__linux__)
1607     { IPPROTO_TCP, TCP_CONGESTION, SV_STRING },
1608     { IPPROTO_TCP, TCP_CORK, SV_BOOL },
1609     { IPPROTO_TCP, TCP_DEFER_ACCEPT, SV_INT },
1610         { IPPROTO_TCP, TCP_INFO, &st_tcp_info },
1611     { IPPROTO_TCP, TCP_KEEPIDLE, SV_INT },
1612     { IPPROTO_TCP, TCP_LINGER2, SV_INT },
1613     { IPPROTO_TCP, TCP_QUICKACK, SV_BOOL },
1614     { IPPROTO_TCP, TCP_SYNCNT, SV_INT },
1615     { IPPROTO_TCP, TCP_USER_TIMEOUT, SV_INT },
1616     { IPPROTO_TCP, TCP_WINDOW_CLAMP, SV_INT },
1617     { IPPROTO_TCP, TCP_FASTOPEN_CONNECT, SV_INT },
1618 #endif
1619 
1620 #if defined(__linux__)
1621     { IPPROTO_UDP, UDP_CORK, SV_BOOL },
1622 #endif
1623 
1624 #if defined(__linux__)
1625         { SOL_PACKET, PACKET_ADD_MEMBERSHIP, &st_packet_mreq },
1626         { SOL_PACKET, PACKET_DROP_MEMBERSHIP, &st_packet_mreq },
1627         { SOL_PACKET, PACKET_AUXDATA, SV_BOOL },
1628         { SOL_PACKET, PACKET_FANOUT, &st_fanout_args },
1629         { SOL_PACKET, PACKET_LOSS, SV_BOOL },
1630         { SOL_PACKET, PACKET_RESERVE, SV_INT },
1631         { SOL_PACKET, PACKET_RX_RING, &st_tpacket_req },
1632         { SOL_PACKET, PACKET_STATISTICS, &st_tpacket_stats },
1633         { SOL_PACKET, PACKET_TIMESTAMP, SV_INT },
1634         { SOL_PACKET, PACKET_TX_RING, &st_tpacket_req },
1635         { SOL_PACKET, PACKET_VERSION, SV_INT },
1636         { SOL_PACKET, PACKET_QDISC_BYPASS, SV_BOOL },
1637 #endif
1638 };
1639 
1640 static cmsgtype_t cmsgtypes[] = {
1641 #if defined(__linux__)
1642         { SOL_PACKET, PACKET_AUXDATA, &st_tpacket_auxdata },
1643 
1644         { SOL_SOCKET, SO_TIMESTAMP_OLD, &st_timeval_old },
1645 # ifdef SO_TIMESTAMP_NEW
1646         { SOL_SOCKET, SO_TIMESTAMP_NEW, &st_timeval_new },
1647 # endif
1648         { SOL_SOCKET, SO_TIMESTAMPNS_OLD, &st_timespec_old },
1649 # ifdef SO_TIMESTAMPNS_NEW
1650         { SOL_SOCKET, SO_TIMESTAMPNS_NEW, &st_timespec_new },
1651 # endif
1652 
1653         { SOL_SOCKET, SCM_CREDENTIALS, &st_ucred },
1654         { SOL_SOCKET, SCM_RIGHTS, CV_FDS },
1655 #endif
1656 
1657         { IPPROTO_IP, IP_RECVOPTS, SV_STRING },
1658         { IPPROTO_IP, IP_RETOPTS, SV_STRING },
1659         { IPPROTO_IP, IP_TOS, CV_INT },
1660         { IPPROTO_IP, IP_TTL, CV_INT },
1661 #if defined(__linux__)
1662         { IPPROTO_IP, IP_CHECKSUM, CV_UINT },
1663         { IPPROTO_IP, IP_ORIGDSTADDR, CV_SOCKADDR },
1664         { IPPROTO_IP, IP_RECVERR, &st_ip_recv_error },
1665         { IPPROTO_IP, IP_RECVFRAGSIZE, CV_INT },
1666 #endif
1667 
1668         { IPPROTO_IPV6, IPV6_TCLASS, CV_INT },
1669         { IPPROTO_IPV6, IPV6_FLOWINFO, CV_BE32 },
1670 #if defined(__linux__)
1671         { IPPROTO_IPV6, IPV6_DSTOPTS, CV_STRING },
1672         { IPPROTO_IPV6, IPV6_HOPLIMIT, CV_INT },
1673         { IPPROTO_IPV6, IPV6_HOPOPTS, CV_STRING },
1674         { IPPROTO_IPV6, IPV6_ORIGDSTADDR, CV_SOCKADDR },
1675         { IPPROTO_IPV6, IPV6_PATHMTU, &st_ip6_mtuinfo },
1676         { IPPROTO_IPV6, IPV6_PKTINFO, &st_in6_pktinfo },
1677         { IPPROTO_IPV6, IPV6_RECVERR, &st_ip_recv_error },
1678         { IPPROTO_IPV6, IPV6_RECVFRAGSIZE, CV_INT },
1679         { IPPROTO_IPV6, IPV6_RTHDR, CV_STRING },
1680 
1681         { IPPROTO_TCP, TCP_CM_INQ, CV_INT },
1682         { IPPROTO_UDP, UDP_GRO, CV_INT },
1683 #endif
1684 };
1685 
1686 
1687 static char *
1688 uv_to_struct(uc_value_t *uv, struct_t *spec)
1689 {
1690         uc_value_t *fv;
1691         const char *s;
1692         uint64_t u64;
1693         int64_t s64;
1694         member_t *m;
1695         bool found;
1696         char *st;
1697 
1698         union {
1699                 int8_t s8;
1700                 int16_t s16;
1701                 int32_t s32;
1702                 int64_t s64;
1703                 uint8_t u8;
1704                 uint16_t u16;
1705                 uint32_t u32;
1706                 uint64_t u64;
1707         } v;
1708 
1709         st = xalloc(spec->size);
1710 
1711         for (size_t i = 0; spec->members[i].name; i++) {
1712                 m = &spec->members[i];
1713                 fv = ucv_object_get(uv, m->name, &found);
1714 
1715                 if (!found || !fv)
1716                         continue;
1717 
1718                 switch (spec->members[i].type) {
1719                 case DT_UNSIGNED:
1720                         u64 = ucv_to_unsigned(fv);
1721 
1722                         if (errno) {
1723                                 free(st);
1724                                 err_return(errno,
1725                                         "Unable to convert field %s to unsigned",
1726                                         m->name);
1727                         }
1728 
1729                         switch (m->u2.size) {
1730                         case 1:  v.u8 =  (uint8_t)u64; break;
1731                         case 2: v.u16 = (uint16_t)u64; break;
1732                         case 4: v.u32 = (uint32_t)u64; break;
1733                         case 8: v.u64 = (uint64_t)u64; break;
1734                         }
1735 
1736                         memcpy(st + m->u1.offset, &v, m->u2.size);
1737                         break;
1738 
1739                 case DT_SIGNED:
1740                         s64 = ucv_to_integer(fv);
1741 
1742                         if (errno) {
1743                                 free(st);
1744                                 err_return(errno,
1745                                         "Unable to convert field %s to integer", m->name);
1746                         }
1747 
1748                         switch (m->u2.size) {
1749                         case 1:  v.s8 =  (int8_t)s64; break;
1750                         case 2: v.s16 = (int16_t)s64; break;
1751                         case 4: v.s32 = (int32_t)s64; break;
1752                         case 8: v.s64 = (int64_t)s64; break;
1753                         }
1754 
1755                         memcpy(st + m->u1.offset, &v, m->u2.size);
1756                         break;
1757 
1758                 case DT_IPV4ADDR:
1759                         s = ucv_string_get(fv);
1760 
1761                         if (!s || inet_pton(AF_INET, s, st + m->u1.offset) != 1) {
1762                                 free(st);
1763                                 err_return(EINVAL,
1764                                         "Unable to convert field %s to IP address", m->name);
1765                         }
1766 
1767                         break;
1768 
1769                 case DT_IPV6ADDR:
1770                         s = ucv_string_get(fv);
1771 
1772                         if (!s || inet_pton(AF_INET6, s, st + m->u1.offset) != 1) {
1773                                 free(st);
1774                                 err_return(EINVAL,
1775                                         "Unable to convert field %s to IPv6 address", m->name);
1776                         }
1777 
1778                         break;
1779 
1780                 case DT_CALLBACK:
1781                         if (m->u1.to_c && !m->u1.to_c(st, fv)) {
1782                                 free(st);
1783                                 return NULL;
1784                         }
1785 
1786                         break;
1787                 }
1788         }
1789 
1790         return st;
1791 }
1792 
1793 static uc_value_t *
1794 struct_to_uv(char *st, struct_t *spec)
1795 {
1796         char s[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
1797         uc_value_t *uv, *fv;
1798         member_t *m;
1799 
1800         uv = ucv_object_new(NULL);
1801 
1802         for (size_t i = 0; spec->members[i].name; i++) {
1803                 m = &spec->members[i];
1804                 fv = NULL;
1805 
1806                 switch (spec->members[i].type) {
1807                 case DT_UNSIGNED:
1808                         switch (spec->members[i].u2.size) {
1809                         case 1:
1810                                 fv = ucv_uint64_new(*(uint8_t *)(st + m->u1.offset));
1811                                 break;
1812 
1813                         case 2:
1814                                 fv = ucv_uint64_new(*(uint16_t *)(st + m->u1.offset));
1815                                 break;
1816 
1817                         case 4:
1818                                 fv = ucv_uint64_new(*(uint32_t *)(st + m->u1.offset));
1819                                 break;
1820 
1821                         case 8:
1822                                 fv = ucv_uint64_new(*(uint64_t *)(st + m->u1.offset));
1823                                 break;
1824                         }
1825 
1826                         break;
1827 
1828                 case DT_SIGNED:
1829                         switch (spec->members[i].u2.size) {
1830                         case 1:
1831                                 fv = ucv_int64_new(*(int8_t *)(st + m->u1.offset));
1832                                 break;
1833 
1834                         case 2:
1835                                 fv = ucv_int64_new(*(int16_t *)(st + m->u1.offset));
1836                                 break;
1837 
1838                         case 4:
1839                                 fv = ucv_int64_new(*(int32_t *)(st + m->u1.offset));
1840                                 break;
1841 
1842                         case 8:
1843                                 fv = ucv_int64_new(*(int64_t *)(st + m->u1.offset));
1844                                 break;
1845                         }
1846 
1847                         break;
1848 
1849                 case DT_IPV4ADDR:
1850                         if (inet_ntop(AF_INET, st + m->u1.offset, s, sizeof(s)))
1851                                 fv = ucv_string_new(s);
1852 
1853                         break;
1854 
1855                 case DT_IPV6ADDR:
1856                         if (inet_ntop(AF_INET6, st + m->u1.offset, s, sizeof(s)))
1857                                 fv = ucv_string_new(s);
1858 
1859                         break;
1860 
1861                 case DT_CALLBACK:
1862                         fv = m->u2.to_uv ? m->u2.to_uv(st) : NULL;
1863                         break;
1864                 }
1865 
1866                 ucv_object_add(uv, m->name, fv);
1867         }
1868 
1869         return uv;
1870 }
1871 
1872 /**
1873  * Sets options on the socket.
1874  *
1875  * Sets the specified option on the socket to the given value.
1876  *
1877  * Returns `true` if the option was successfully set.
1878  *
1879  * Returns `null` if an error occurred.
1880  *
1881  * @function module:socket.socket#setopt
1882  *
1883  * @param {number} level
1884  * The protocol level at which the option resides. This can be a level such as
1885  * `SOL_SOCKET` for the socket API level or a specific protocol level defined
1886  * by the system.
1887  *
1888  * @param {number} option
1889  * The socket option to set. This can be an integer representing the option,
1890  * such as `SO_REUSEADDR`, or a constant defined by the system.
1891  *
1892  * @param {*} value
1893  * The value to set the option to. The type of this argument depends on the
1894  * specific option being set. It can be an integer, a boolean, a string, or a
1895  * dictionary representing the value to set. If a dictionary is provided, it is
1896  * internally translated to the corresponding C struct type required by the
1897  * option.
1898  *
1899  * @returns {?boolean}
1900  */
1901 static uc_value_t *
1902 uc_socket_inst_setopt(uc_vm_t *vm, size_t nargs)
1903 {
1904         int sockfd, solvl, soopt, soval, ret;
1905         uc_value_t *level, *option, *value;
1906         void *valptr = NULL, *st = NULL;
1907         socklen_t vallen = 0;
1908         size_t i;
1909 
1910         args_get(vm, nargs, &sockfd,
1911                 "level", UC_INTEGER, false, &level,
1912                 "option", UC_INTEGER, false, &option,
1913                 "value", UC_NULL, false, &value);
1914 
1915         solvl = ucv_int64_get(level);
1916         soopt = ucv_int64_get(option);
1917 
1918         for (i = 0; i < ARRAY_SIZE(sockopts); i++) {
1919                 if (sockopts[i].level != solvl || sockopts[i].option != soopt)
1920                         continue;
1921 
1922                 switch ((uintptr_t)sockopts[i].ctype) {
1923                 case (uintptr_t)SV_INT_RO:
1924                         err_return(EOPNOTSUPP, "Socket option is read only");
1925 
1926                 case (uintptr_t)SV_VOID:
1927                         valptr = NULL;
1928                         vallen = 0;
1929                         break;
1930 
1931                 case (uintptr_t)SV_INT:
1932                         soval = ucv_to_integer(value);
1933 
1934                         if (errno)
1935                                 err_return(errno, "Unable to convert value to integer");
1936 
1937                         valptr = &soval;
1938                         vallen = sizeof(int);
1939                         break;
1940 
1941                 case (uintptr_t)SV_BOOL:
1942                         soval = ucv_to_unsigned(value) ? 1 : 0;
1943 
1944                         if (errno)
1945                                 err_return(errno, "Unable to convert value to boolean");
1946 
1947                         valptr = &soval;
1948                         vallen = sizeof(int);
1949                         break;
1950 
1951                 case (uintptr_t)SV_STRING:
1952                         valptr = ucv_string_get(value);
1953                         vallen = ucv_string_length(value);
1954                         break;
1955 
1956                 case (uintptr_t)SV_IFNAME:
1957                         if (ucv_type(value) == UC_STRING) {
1958                                 soval = if_nametoindex(ucv_string_get(value));
1959 
1960                                 if (soval <= 0)
1961                                         err_return(errno, "Unable to resolve interface %s",
1962                                                 ucv_string_get(value));
1963                         }
1964                         else {
1965                                 soval = ucv_to_integer(value);
1966 
1967                                 if (errno)
1968                                         err_return(errno, "Unable to convert value to integer");
1969                         }
1970 
1971                         valptr = &soval;
1972                         vallen = sizeof(int);
1973                         break;
1974 
1975                 default:
1976                         st = uv_to_struct(value, sockopts[i].ctype);
1977                         valptr = st;
1978                         vallen = sockopts[i].ctype->size;
1979                         break;
1980                 }
1981 
1982                 break;
1983         }
1984 
1985         if (i == ARRAY_SIZE(sockopts))
1986                 err_return(EINVAL, "Unknown socket level or option");
1987 
1988         ret = setsockopt(sockfd, solvl, soopt, valptr, vallen);
1989 
1990         free(st);
1991 
1992         if (ret == -1)
1993                 err_return(errno, "setsockopt()");
1994 
1995         ok_return(ucv_boolean_new(true));
1996 }
1997 
1998 /**
1999  * Gets options from the socket.
2000  *
2001  * Retrieves the value of the specified option from the socket.
2002  *
2003  * Returns the value of the requested option.
2004  *
2005  * Returns `null` if an error occurred or if the option is not supported.
2006  *
2007  * @function module:socket.socket#getopt
2008  *
2009  * @param {number} level
2010  * The protocol level at which the option resides. This can be a level such as
2011  * `SOL_SOCKET` for the socket API level or a specific protocol level defined
2012  * by the system.
2013  *
2014  * @param {number} option
2015  * The socket option to retrieve. This can be an integer representing the
2016  * option, such as `SO_REUSEADDR`, or a constant defined by the system.
2017  *
2018  * @returns {?*}
2019  * The value of the requested option. The type of the returned value depends
2020  * on the specific option being retrieved. It can be an integer, a boolean, a
2021  * string, or a dictionary representing a complex data structure.
2022  */
2023 static uc_value_t *
2024 uc_socket_inst_getopt(uc_vm_t *vm, size_t nargs)
2025 {
2026         uc_value_t *level, *option, *value = NULL;
2027         char ival[sizeof(int64_t)] = { 0 };
2028         void *valptr = NULL, *st = NULL;
2029         int sockfd, solvl, soopt, ret;
2030         uc_stringbuf_t *sb = NULL;
2031         socklen_t vallen;
2032         size_t i;
2033 
2034         args_get(vm, nargs, &sockfd,
2035                 "level", UC_INTEGER, false, &level,
2036                 "option", UC_INTEGER, false, &option);
2037 
2038         solvl = ucv_int64_get(level);
2039         soopt = ucv_int64_get(option);
2040 
2041         for (i = 0; i < ARRAY_SIZE(sockopts); i++) {
2042                 if (sockopts[i].level != solvl || sockopts[i].option != soopt)
2043                         continue;
2044 
2045                 switch ((uintptr_t)sockopts[i].ctype) {
2046                 case (uintptr_t)SV_VOID:
2047                         err_return(EOPNOTSUPP, "Socket option is write only");
2048 
2049                 case (uintptr_t)SV_INT:
2050                 case (uintptr_t)SV_INT_RO:
2051                 case (uintptr_t)SV_BOOL:
2052                 case (uintptr_t)SV_IFNAME:
2053                         valptr = ival;
2054                         vallen = sizeof(ival);
2055                         break;
2056 
2057                 case (uintptr_t)SV_STRING:
2058                         sb = strbuf_alloc(64);
2059                         valptr = strbuf_data(sb);
2060                         vallen = strbuf_size(sb);
2061                         break;
2062 
2063                 default:
2064                         st = xalloc(sockopts[i].ctype->size);
2065                         valptr = st;
2066                         vallen = sockopts[i].ctype->size;
2067                         break;
2068                 }
2069 
2070                 break;
2071         }
2072 
2073         if (i == ARRAY_SIZE(sockopts))
2074                 err_return(EINVAL, "Unknown socket level or option");
2075 
2076         while (true) {
2077                 ret = getsockopt(sockfd, solvl, soopt, valptr, &vallen);
2078 
2079                 if (sockopts[i].ctype == SV_STRING &&
2080                     (ret == 0 || (ret == -1 && errno == ERANGE)) &&
2081                     vallen > strbuf_size(sb)) {
2082 
2083                         if (!strbuf_grow(sb, vallen))
2084                                 return NULL;
2085 
2086                         valptr = strbuf_data(sb);
2087                         continue;
2088                 }
2089 
2090                 break;
2091         }
2092 
2093         if (ret == 0) {
2094                 char ifname[IF_NAMESIZE];
2095                 int ifidx;
2096 
2097                 switch ((uintptr_t)sockopts[i].ctype) {
2098                 case (uintptr_t)SV_VOID:
2099                         break;
2100 
2101                 case (uintptr_t)SV_INT:
2102                 case (uintptr_t)SV_INT_RO:
2103                         value = ucv_int64_new(parse_integer(ival, vallen));
2104                         break;
2105 
2106                 case (uintptr_t)SV_BOOL:
2107                         value = ucv_boolean_new(parse_integer(ival, vallen) != 0);
2108                         break;
2109 
2110                 case (uintptr_t)SV_STRING:
2111                         value = strbuf_finish(&sb, vallen);
2112                         break;
2113 
2114                 case (uintptr_t)SV_IFNAME:
2115                         ifidx = parse_integer(ival, vallen);
2116                         if (if_indextoname(ifidx, ifname))
2117                                 value = ucv_string_new(ifname);
2118                         else
2119                                 value = ucv_int64_new(ifidx);
2120                         break;
2121 
2122                 default:
2123                         value = struct_to_uv(st, sockopts[i].ctype);
2124                         break;
2125                 }
2126         }
2127 
2128         strbuf_free(sb);
2129         free(st);
2130 
2131         if (ret == -1)
2132                 err_return(errno, "getsockopt()");
2133 
2134         ok_return(value);
2135 }
2136 
2137 /**
2138  * Returns the UNIX file descriptor number associated with the socket.
2139  *
2140  * Returns the file descriptor number.
2141  *
2142  * Returns `-1` if an error occurred.
2143  *
2144  * @function module:socket.socket#fileno
2145  *
2146  * @returns {number}
2147  */
2148 static uc_value_t *
2149 uc_socket_inst_fileno(uc_vm_t *vm, size_t nargs)
2150 {
2151         int sockfd;
2152 
2153         args_get(vm, nargs, &sockfd);
2154 
2155         ok_return(ucv_int64_new(sockfd));
2156 }
2157 
2158 /**
2159  * Query error information.
2160  *
2161  * Returns a string containing a description of the last occurred error when
2162  * the *numeric* argument is absent or false.
2163  *
2164  * Returns a positive (`errno`) or negative (`EAI_*` constant) error code number
2165  * when the *numeric* argument is `true`.
2166  *
2167  * Returns `null` if there is no error information.
2168  *
2169  * @function module:socket#error
2170  *
2171  * @param {boolean} [numeric]
2172  * Whether to return a numeric error code (`true`) or a human readable error
2173  * message (false).
2174  *
2175  * @returns {?string|?number}
2176  *
2177  * @example
2178  * // Trigger socket error by attempting to bind IPv6 address with IPv4 socket
2179  * socket.create(socket.AF_INET, socket.SOCK_STREAM, 0).bind("::", 8080);
2180  *
2181  * // Print error (should yield "Address family not supported by protocol")
2182  * print(socket.error(), "\n");
2183  *
2184  * // Trigger resolve error
2185  * socket.addrinfo("doesnotexist.org");
2186  *
2187  * // Query error code (should yield -2 for EAI_NONAME)
2188  * print(socket.error(true), "\n");  //
2189  */
2190 static uc_value_t *
2191 uc_socket_error(uc_vm_t *vm, size_t nargs)
2192 {
2193         uc_value_t *numeric = uc_fn_arg(0), *rv;
2194         uc_stringbuf_t *buf;
2195 
2196         if (last_error.code == 0)
2197                 return NULL;
2198 
2199         if (ucv_is_truish(numeric)) {
2200                 rv = ucv_int64_new(last_error.code);
2201         }
2202         else {
2203                 buf = ucv_stringbuf_new();
2204 
2205                 if (last_error.msg)
2206                         ucv_stringbuf_printf(buf, "%s: ", last_error.msg);
2207 
2208                 if (last_error.code >= 0)
2209                         ucv_stringbuf_printf(buf, "%s", strerror(last_error.code));
2210                 else
2211                         ucv_stringbuf_printf(buf, "%s", gai_strerror(last_error.code));
2212 
2213                 rv = ucv_stringbuf_finish(buf);
2214         }
2215 
2216         ok_return(rv);
2217 }
2218 
2219 /**
2220  * @typedef {Object} module:socket.socket.SocketAddress
2221  * @property {number} family
2222  * Address family, one of AF_INET, AF_INET6, AF_UNIX or AF_PACKET.
2223  *
2224  * @property {string} address
2225  * IPv4/IPv6 address string (AF_INET or AF_INET6 only) or hardware address in
2226  * hexadecimal notation (AF_PACKET only).
2227  *
2228  * @property {number} [port]
2229  * Port number (AF_INET or AF_INET6 only).
2230  *
2231  * @property {number} [flowinfo]
2232  * IPv6 flow information (AF_INET6 only).
2233  *
2234  * @property {string|number} [interface]
2235  * Link local address scope (for IPv6 sockets) or bound network interface
2236  * (for packet sockets), either a network device name string or a nonzero
2237  * positive integer representing a network interface index (AF_INET6 and
2238  * AF_PACKET only).
2239  *
2240  * @property {string} path
2241  * Domain socket filesystem path (AF_UNIX only).
2242  *
2243  * @property {number} [protocol=0]
2244  * Physical layer protocol (AF_PACKET only).
2245  *
2246  * @property {number} [hardware_type=0]
2247  * ARP hardware type (AF_PACKET only).
2248  *
2249  * @property {number} [packet_type=PACKET_HOST]
2250  * Packet type (AF_PACKET only).
2251  */
2252 
2253 /**
2254  * Parses the provided address value into a socket address representation.
2255  *
2256  * This function parses the given address value into a socket address
2257  * representation required for a number of socket operations. The address value
2258  * can be provided in various formats:
2259  * - For IPv4 addresses, it can be a string representing the IP address,
2260  *   optionally followed by a port number separated by colon, e.g.
2261  *   `192.168.0.1:8080`.
2262  * - For IPv6 addresses, it must be an address string enclosed in square
2263  *   brackets if a port number is specified, otherwise the brackets are
2264  *   optional. The address string may also include a scope ID in the form
2265  *   `%ifname` or `%number`, e.g. `[fe80::1%eth0]:8080` or `fe80::1%15`.
2266  * - Any string value containing a slash is treated as UNIX domain socket path.
2267  * - Alternatively, it can be provided as an array returned by
2268  *   {@link module:core#iptoarr|iptoarr()}, representing the address octets.
2269  * - It can also be an object representing a network address, with properties
2270  *   for `address` (the IP address) and `port` or a single property `path` to
2271  *   denote a UNIX domain socket address.
2272  *
2273  * @function module:socket#sockaddr
2274  *
2275  * @param {string|number[]|module:socket.socket.SocketAddress} address
2276  * The address value to parse.
2277  *
2278  * @returns {?module:socket.socket.SocketAddress}
2279  * A socket address representation of the provided address value, or `null` if
2280  * the address could not be parsed.
2281  *
2282  * @example
2283  * // Parse an IP address string with port
2284  * const address1 = sockaddr('192.168.0.1:8080');
2285  *
2286  * // Parse an IPv6 address string with port and scope identifier
2287  * const address2 = sockaddr('[fe80::1%eth0]:8080');
2288  *
2289  * // Parse an array representing an IP address
2290  * const address3 = sockaddr([192, 168, 0, 1]);
2291  *
2292  * // Parse a network address object
2293  * const address4 = sockaddr({ address: '192.168.0.1', port: 8080 });
2294  *
2295  * // Convert a path value to a UNIX domain socket address
2296  * const address5 = sockaddr('/var/run/daemon.sock');
2297  */
2298 static uc_value_t *
2299 uc_socket_sockaddr(uc_vm_t *vm, size_t nargs)
2300 {
2301         struct sockaddr_storage ss = { 0 };
2302         uc_value_t *addr, *rv;
2303         socklen_t slen;
2304 
2305         args_get(vm, nargs, NULL,
2306                 "address", UC_NULL, false, &addr);
2307 
2308         if (!uv_to_sockaddr(addr, &ss, &slen))
2309                 return NULL;
2310 
2311         rv = ucv_object_new(vm);
2312 
2313         if (!sockaddr_to_uv(&ss, rv)) {
2314                 ucv_put(rv);
2315                 return NULL;
2316         }
2317 
2318         ok_return(rv);
2319 }
2320 
2321 /**
2322  * Resolves the given network address into hostname and service name.
2323  *
2324  * The `nameinfo()` function provides an API for reverse DNS lookup and service
2325  * name resolution. It returns an object containing the following properties:
2326  * - `hostname`: The resolved hostname.
2327  * - `service`: The resolved service name.
2328  *
2329  * Returns an object representing the resolved hostname and service name.
2330  * Return `null` if an error occurred during resolution.
2331  *
2332  * @function module:socket#nameinfo
2333  *
2334  * @param {string|module:socket.socket.SocketAddress} address
2335  * The network address to resolve. It can be specified as:
2336  * - A string representing the IP address.
2337  * - An object representing the address with properties `address` and `port`.
2338  *
2339  * @param {number} [flags]
2340  * Optional flags that provide additional control over the resolution process,
2341  * specified as bitwise OR-ed number of `NI_*` constants.
2342  *
2343  * @returns {?{hostname: string, service: string}}
2344  *
2345  * @see {@link module:socket~"Socket Types"|Socket Types}
2346  * @see {@link module:socket~"Name Info Constants"|AName Info Constants}
2347  *
2348  * @example
2349  * // Resolve a network address into hostname and service name
2350  * const result = network.getnameinfo('192.168.1.1:80');
2351  * print(result); // { "hostname": "example.com", "service": "http" }
2352  */
2353 static uc_value_t *
2354 uc_socket_nameinfo(uc_vm_t *vm, size_t nargs)
2355 {
2356         char host[NI_MAXHOST], serv[NI_MAXSERV];
2357         uc_value_t *addr, *flags, *rv;
2358         struct sockaddr_storage ss;
2359         socklen_t slen;
2360         int ret;
2361 
2362         args_get(vm, nargs, NULL,
2363                 "address", UC_NULL, false, &addr,
2364                 "flags", UC_INTEGER, true, &flags);
2365 
2366         if (!uv_to_sockaddr(addr, &ss, &slen))
2367                 return NULL;
2368 
2369         ret = getnameinfo((struct sockaddr *)&ss, slen,
2370                 host, sizeof(host), serv, sizeof(serv),
2371                 flags ? ucv_int64_get(flags) : 0);
2372 
2373         if (ret != 0)
2374                 err_return((ret == EAI_SYSTEM) ? errno : ret, "getnameinfo()");
2375 
2376         rv = ucv_object_new(vm);
2377 
2378         ucv_object_add(rv, "hostname", ucv_string_new(host));
2379         ucv_object_add(rv, "service", ucv_string_new(serv));
2380 
2381         ok_return(rv);
2382 }
2383 
2384 /**
2385  * Resolves the given hostname and optional service name into a list of network
2386  * addresses, according to the provided hints.
2387  *
2388  * The `addrinfo()` function provides an API for performing DNS and service name
2389  * resolution. It returns an array of objects, each representing a resolved
2390  * address.
2391  *
2392  * Returns an array of resolved addresses.
2393  * Returns `null` if an error occurred during resolution.
2394  *
2395  * @function module:socket#addrinfo
2396  *
2397  * @param {string} hostname
2398  * The hostname to resolve.
2399  *
2400  * @param {string} [service]
2401  * Optional service name to resolve. If not provided, the service field of the
2402  * resulting address information structures is left uninitialized.
2403  *
2404  * @param {Object} [hints]
2405  * Optional hints object that provides additional control over the resolution
2406  * process. It can contain the following properties:
2407  * - `family`: The preferred address family (`AF_INET` or `AF_INET6`).
2408  * - `socktype`: The socket type (`SOCK_STREAM`, `SOCK_DGRAM`, etc.).
2409  * - `protocol`: The protocol of returned addresses.
2410  * - `flags`: Bitwise OR-ed `AI_*` flags to control the resolution behavior.
2411  *
2412  * @returns {?module:socket.AddressInfo[]}
2413  *
2414  * @see {@link module:socket~"Socket Types"|Socket Types}
2415  * @see {@link module:socket~"Address Info Flags"|Address Info Flags}
2416  *
2417  * @example
2418  * // Resolve all addresses
2419  * const addresses = socket.addrinfo('example.org');
2420  *
2421  * // Resolve IPv4 addresses for a given hostname and service
2422  * const ipv4addresses = socket.addrinfo('example.com', 'http', { family: socket.AF_INET });
2423  *
2424  * // Resolve IPv6 addresses without specifying a service
2425  * const ipv6Addresses = socket.addrinfo('example.com', null, { family: socket.AF_INET6 });
2426  */
2427 
2428 static uc_value_t *
2429 uc_socket_addrinfo(uc_vm_t *vm, size_t nargs)
2430 {
2431         struct addrinfo *ai_hints = NULL, *ai_res;
2432         uc_value_t *host, *serv, *hints, *rv;
2433         char *servstr;
2434         int ret;
2435 
2436         args_get(vm, nargs, NULL,
2437                 "hostname", UC_STRING, false, &host,
2438                 "service", UC_NULL, true, &serv,
2439                 "hints", UC_OBJECT, true, &hints);
2440 
2441         if (hints) {
2442                 ai_hints = (struct addrinfo *)uv_to_struct(hints, &st_addrinfo);
2443 
2444                 if (!ai_hints)
2445                         return NULL;
2446         }
2447 
2448         servstr = (serv && ucv_type(serv) != UC_STRING) ? ucv_to_string(vm, serv) : NULL;
2449         ret = getaddrinfo(ucv_string_get(host),
2450                 servstr ? servstr : ucv_string_get(serv),
2451                 ai_hints, &ai_res);
2452 
2453         free(ai_hints);
2454         free(servstr);
2455 
2456         if (ret != 0)
2457                 err_return((ret == EAI_SYSTEM) ? errno : ret, "getaddrinfo()");
2458 
2459         rv = ucv_array_new(vm);
2460 
2461         for (struct addrinfo *ai = ai_res; ai; ai = ai->ai_next) {
2462                 uc_value_t *item = struct_to_uv((char *)ai, &st_addrinfo);
2463 
2464                 if (item)
2465                         ucv_array_push(rv, item);
2466         }
2467 
2468         freeaddrinfo(ai_res);
2469 
2470         ok_return(rv);
2471 }
2472 
2473 /**
2474  * Represents a poll state serving as input parameter and return value type for
2475  * {@link module:socket#poll|`poll()`}.
2476  *
2477  * @typedef {Array} module:socket.PollSpec
2478  * @property {module:socket.socket} 0
2479  * The polled socket instance.
2480  *
2481  * @property {number} 1
2482  * Requested or returned status flags of the polled socket instance.
2483  */
2484 
2485 /**
2486  * Polls a number of sockets for state changes.
2487  *
2488  * Returns an array of `[socket, flags]` tuples for each socket with pending
2489  * events. When a tuple is passed as socket argument, it is included as-is into
2490  * the result tuple array, with the flags entry changed to a bitwise OR-ed value
2491  * describing the pending events for this socket. When a plain socket instance
2492  * (or another kind of handle) is passed, a new tuple array is created for this
2493  * socket within the result tuple array, containing this socket as first and the
2494  * bitwise OR-ed pending events as second element.
2495  *
2496  * Returns `null` if an error occurred.
2497  *
2498  * @function module:socket#poll
2499  *
2500  * @param {number} timeout
2501  * Amount of milliseconds to wait for socket activity before aborting the poll
2502  * call. If set to `0`, the poll call will return immediately if none of the
2503  * provided sockets has pending events, if set to a negative value, the poll
2504  * call will wait indefinitely, in all other cases the poll call will wait at
2505  * most for the given amount of milliseconds before returning.
2506  *
2507  * @param {...(module:socket.socket|module:socket.PollSpec)} sockets
2508  * An arbitrary amount of socket arguments. Each argument may be either a plain
2509  * {@link module:socket.socket|socket instance} (or any other kind of handle
2510  * implementing a `fileno()` method) or a `[socket, flags]` tuple specifying the
2511  * socket and requested poll flags. If a plain socket (or other kind of handle)
2512  * instead of a tuple is provided, the requested poll flags default to
2513  * `POLLIN|POLLERR|POLLHUP` for this socket.
2514  *
2515  * @returns {module:socket.PollSpec[]}
2516  *
2517  * @example
2518  * let x = socket.connect("example.org", 80);
2519  * let y = socket.connect("example.com", 80);
2520  *
2521  * // Pass plain socket arguments
2522  * let events = socket.poll(10, x, y);
2523  * print(events); // [ [ "<socket 0x7>", 0 ], [ "<socket 0x8>", 0 ] ]
2524  *
2525  * // Passing tuples allows attaching state information and requesting
2526  * // different I/O events
2527  * let events = socket.poll(10,
2528  *      [ x, socket.POLLOUT | socket.POLLHUP, "This is example.org" ],
2529  *      [ y, socket.POLLOUT | socket.POLLHUP, "This is example.com" ]
2530  * );
2531  * print(events); // [ [ "<socket 0x7>", 4, "This is example.org" ],
2532  *                //   [ "<socket 0x8>", 4, "This is example.com" ] ]
2533  */
2534 static uc_value_t *
2535 uc_socket_poll(uc_vm_t *vm, size_t nargs)
2536 {
2537         struct { struct pollfd *entries; size_t count; } pfds = { 0 };
2538         uc_value_t *timeoutarg, *rv, *item;
2539         int64_t timeout;
2540         int ret;
2541 
2542         args_get(vm, nargs, NULL, "timeout", UC_INTEGER, false, &timeoutarg);
2543 
2544         timeout = ucv_to_integer(timeoutarg);
2545 
2546         if (errno != 0 || timeout < (int64_t)INT_MIN || timeout > (int64_t)INT_MAX)
2547                 err_return(ERANGE, "Invalid timeout value");
2548 
2549         rv = ucv_array_new(vm);
2550 
2551         for (size_t i = 1; i < nargs; i++) {
2552                 uc_vector_grow(&pfds);
2553                 item = uv_to_pollfd(vm, uc_fn_arg(i), &pfds.entries[pfds.count]);
2554 
2555                 if (item)
2556                         ucv_array_set(rv, pfds.count++, item);
2557         }
2558 
2559         ret = poll(pfds.entries, pfds.count, timeout);
2560 
2561         if (ret == -1) {
2562                 ucv_put(rv);
2563                 uc_vector_clear(&pfds);
2564                 err_return(errno, "poll()");
2565         }
2566 
2567         for (size_t i = 0; i < pfds.count; i++)
2568                 ucv_array_set(ucv_array_get(rv, i), 1,
2569                         ucv_int64_new(pfds.entries[i].revents));
2570 
2571         uc_vector_clear(&pfds);
2572         ok_return(rv);
2573 }
2574 
2575 static bool
2576 should_resolve(uc_value_t *host)
2577 {
2578         char *s = ucv_string_get(host);
2579 
2580         return (s != NULL && memchr(s, '/', ucv_string_length(host)) == NULL);
2581 }
2582 
2583 /**
2584  * Creates a network socket and connects it to the specified host and service.
2585  *
2586  * This high level function combines the functionality of
2587  * {@link module:socket#create|create()},
2588  * {@link module:socket#addrinfo|addrinfo()} and
2589  * {@link module:socket.socket#connect|connect()} to simplify connection
2590  * establishment with the socket module.
2591  *
2592  * @function module:socket#connect
2593  *
2594  * @param {string|number[]|module:socket.socket.SocketAddress} host
2595  * The host to connect to, can be an IP address, hostname,
2596  * {@link module:socket.socket.SocketAddress|SocketAddress}, or an array value
2597  * returned by {@link module:core#iptoarr|iptoarr()}.
2598  *
2599  * @param {string|number} [service]
2600  * The service to connect to, can be a symbolic service name (such as "http") or
2601  * a port number. Optional if host is specified as
2602  * {@link module:socket.socket.SocketAddress|SocketAddress}.
2603  *
2604  * @param {Object} [hints]
2605  * Optional preferences for the socket. It can contain the following properties:
2606  * - `family`: The preferred address family (`AF_INET` or `AF_INET6`).
2607  * - `socktype`: The socket type (`SOCK_STREAM`, `SOCK_DGRAM`, etc.).
2608  * - `protocol`: The protocol of the created socket.
2609  * - `flags`: Bitwise OR-ed `AI_*` flags to control the resolution behavior.
2610  *
2611  * If no hints are not provided, the default socket type preference is set to
2612  * `SOCK_STREAM`.
2613  *
2614  * @param {number} [timeout=-1]
2615  * The timeout in milliseconds for socket connect operations. If set to a
2616  * negative value, no specifc time limit is imposed and the function will
2617  * block until either a connection was successfull or the underlying operating
2618  * system timeout is reached.
2619  *
2620  * @returns {module:socket.socket}
2621  *
2622  * @example
2623  * // Resolve host, try to connect to both resulting IPv4 and IPv6 addresses
2624  * let conn = socket.connect("example.org", 80);
2625  *
2626  * // Enforce usage of IPv6
2627  * let conn = socket.connect("example.com", 80, { family: socket.AF_INET6 });
2628  *
2629  * // Connect a UDP socket
2630  * let conn = socket.connect("192.168.1.1", 53, { socktype: socket.SOCK_DGRAM });
2631  *
2632  * // Bypass name resolution by specifying a SocketAddress structure
2633  * let conn = socket.connect({ address: "127.0.0.1", port: 9000 });
2634  *
2635  * // Use SocketAddress structure to connect a UNIX domain socket
2636  * let conn = socket.connect({ path: "/var/run/daemon.sock" });
2637  */
2638 static uc_value_t *
2639 uc_socket_connect(uc_vm_t *vm, size_t nargs)
2640 {
2641         struct address {
2642                 struct sockaddr_storage ss;
2643                 struct addrinfo ai;
2644                 int flags;
2645                 int fd;
2646         } *ap;
2647 
2648         struct { struct address *entries; size_t count; } addresses = { 0 };
2649         struct { struct pollfd *entries; size_t count; } pollfds = { 0 };
2650         struct addrinfo *ai_results, *ai_hints, *ai;
2651         uc_value_t *host, *serv, *hints, *timeout;
2652         const char *errmsg = NULL;
2653         struct pollfd *pp = NULL;
2654         size_t slot, connected;
2655         int ret, err;
2656 
2657         args_get(vm, nargs, NULL,
2658                 "host", UC_NULL, false, &host,
2659                 "service", UC_NULL, true, &serv,
2660                 "hints", UC_OBJECT, true, &hints,
2661                 "timeout", UC_INTEGER, true, &timeout);
2662 
2663         ai_hints = hints
2664                 ? (struct addrinfo *)uv_to_struct(hints, &st_addrinfo) : NULL;
2665 
2666         if (should_resolve(host)) {
2667                 char *servstr = (ucv_type(serv) != UC_STRING)
2668                         ? ucv_to_string(vm, serv) : NULL;
2669 
2670                 ret = getaddrinfo(ucv_string_get(host),
2671                         servstr ? servstr : ucv_string_get(serv),
2672                         ai_hints ? ai_hints : &(struct addrinfo){
2673                                 .ai_socktype = SOCK_STREAM
2674                         }, &ai_results);
2675 
2676                 if (ret != 0) {
2677                         free(servstr);
2678                         free(ai_hints);
2679                         err_return((ret == EAI_SYSTEM) ? errno : ret,
2680                                 "getaddrinfo()");
2681                 }
2682 
2683                 for (ai = ai_results; ai != NULL; ai = ai->ai_next) {
2684                         if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
2685                                 continue;
2686 
2687                         uc_vector_grow(&addresses);
2688                         ap = &addresses.entries[addresses.count++];
2689                         memcpy(&ap->ss, ai->ai_addr, ai->ai_addrlen);
2690                         memcpy(&ap->ai, ai, sizeof(*ai));
2691                         ap->ai.ai_addr = (struct sockaddr *)&ap->ss;
2692                 }
2693 
2694                 freeaddrinfo(ai_results);
2695                 free(servstr);
2696         }
2697         else {
2698                 uc_vector_grow(&addresses);
2699                 ap = &addresses.entries[addresses.count++];
2700 
2701                 if (!uv_to_sockaddr(host, &ap->ss, &ap->ai.ai_addrlen)) {
2702                         free(ai_hints);
2703                         uc_vector_clear(&addresses);
2704                         return NULL;
2705                 }
2706 
2707                 if (serv) {
2708                         uint64_t port = ucv_to_unsigned(serv);
2709 
2710                         if (port > 65535)
2711                                 errno = ERANGE;
2712 
2713                         if (errno != 0) {
2714                                 free(ai_hints);
2715                                 uc_vector_clear(&addresses);
2716                                 err_return(errno, "Invalid port number");
2717                         }
2718 
2719                         ((struct sockaddr_in *)&ap->ss)->sin_port = htons(port);
2720                 }
2721 
2722                 ap->ai.ai_addr = (struct sockaddr *)&ap->ss;
2723                 ap->ai.ai_family = ap->ss.ss_family;
2724                 ap->ai.ai_socktype = ai_hints ? ai_hints->ai_socktype : SOCK_STREAM;
2725                 ap->ai.ai_protocol = ai_hints ? ai_hints->ai_protocol : 0;
2726         }
2727 
2728         free(ai_hints);
2729 
2730         for (connected = 0, slot = 0, ap = &addresses.entries[slot];
2731              slot < addresses.count;
2732              slot++, ap = &addresses.entries[slot])
2733         {
2734                 uc_vector_grow(&pollfds);
2735                 pp = &pollfds.entries[pollfds.count++];
2736                 pp->events = POLLIN | POLLOUT | POLLHUP | POLLERR;
2737                 pp->fd = socket(ap->ai.ai_family, ap->ai.ai_socktype, ap->ai.ai_protocol);
2738 
2739                 if (pp->fd == -1)
2740                         continue;
2741 
2742                 if ((ap->flags = fcntl(pp->fd, F_GETFL, 0)) == -1) {
2743                         xclose(&pp->fd);
2744                         continue;
2745                 }
2746 
2747                 if (fcntl(pp->fd, F_SETFL, ap->flags | O_NONBLOCK) == -1) {
2748                         xclose(&pp->fd);
2749                         continue;
2750                 }
2751 
2752                 ret = connect(pp->fd, ap->ai.ai_addr, ap->ai.ai_addrlen);
2753 
2754                 if (ret == -1 && errno != EINPROGRESS) {
2755                         xclose(&pp->fd);
2756                         continue;
2757                 }
2758 
2759                 connected++;
2760         }
2761 
2762         if (connected == 0) {
2763                 err = EAI_NONAME;
2764                 errmsg = "Could not connect to any host address";
2765                 goto out;
2766         }
2767 
2768         ret = poll(pollfds.entries, pollfds.count,
2769                 timeout ? ucv_int64_get(timeout) : -1);
2770 
2771         if (ret == -1) {
2772                 err = errno;
2773                 errmsg = "poll()";
2774                 goto out;
2775         }
2776 
2777         for (slot = 0, ap = NULL, pp = NULL; slot < pollfds.count; slot++) {
2778                 if (pollfds.entries[slot].revents & (POLLIN|POLLOUT)) {
2779                         ap = &addresses.entries[slot];
2780                         pp = &pollfds.entries[slot];
2781                         break;
2782                 }
2783         }
2784 
2785         if (!ap) {
2786                 err = ETIMEDOUT;
2787                 errmsg = "Connection timed out";
2788                 goto out;
2789         }
2790 
2791         if (fcntl(pp->fd, F_SETFL, ap->flags) == -1) {
2792                 err = errno;
2793                 errmsg = "fcntl(F_SETFL)";
2794                 goto out;
2795         }
2796 
2797 out:
2798         for (slot = 0, ret = -1; slot < pollfds.count; slot++) {
2799                 if (pp == &pollfds.entries[slot])
2800                         ret = pollfds.entries[slot].fd;
2801                 else
2802                         xclose(&pollfds.entries[slot].fd);
2803         }
2804 
2805         uc_vector_clear(&addresses);
2806         uc_vector_clear(&pollfds);
2807 
2808         if (errmsg)
2809                 err_return(err, "%s", errmsg);
2810 
2811         ok_return(ucv_socket_new(vm, ret));
2812 }
2813 
2814 /**
2815  * Binds a listening network socket to the specified host and service.
2816  *
2817  * This high-level function combines the functionality of
2818  * {@link module:socket#create|create()},
2819  * {@link module:socket#addrinfo|addrinfo()},
2820  * {@link module:socket.socket#bind|bind()}, and
2821  * {@link module:socket.socket#listen|listen()} to simplify setting up a
2822  * listening socket with the socket module.
2823  *
2824  * @function module:socket#listen
2825  *
2826  * @param {string|number[]|module:socket.socket.SocketAddress} host
2827  * The host to bind to, can be an IP address, hostname,
2828  * {@link module:socket.socket.SocketAddress|SocketAddress}, or an array value
2829  * returned by {@link module:core#iptoarr|iptoarr()}.
2830  *
2831  * @param {string|number} [service]
2832  * The service to listen on, can be a symbolic service name (such as "http") or
2833  * a port number. Optional if host is specified as
2834  * {@link module:socket.socket.SocketAddress|SocketAddress}.
2835  *
2836  * @param {Object} [hints]
2837  * Optional preferences for the socket. It can contain the following properties:
2838  * - `family`: The preferred address family (`AF_INET` or `AF_INET6`).
2839  * - `socktype`: The socket type (`SOCK_STREAM`, `SOCK_DGRAM`, etc.).
2840  * - `protocol`: The protocol of the created socket.
2841  * - `flags`: Bitwise OR-ed `AI_*` flags to control the resolution behavior.
2842  *
2843  * If no hints are provided, the default socket type preference is set to
2844  * `SOCK_STREAM`.
2845  *
2846  * @param {number} [backlog=128]
2847  * The maximum length of the queue of pending connections.
2848  *
2849  * @returns {module:socket.socket}
2850  *
2851  * @example
2852  * // Listen for incoming TCP connections on port 80
2853  * let server = socket.listen("localhost", 80);
2854  *
2855  * // Listen on IPv6 address only
2856  * let server = socket.listen("machine.local", 8080, { family: socket.AF_INET6 });
2857  *
2858  * // Listen on a UNIX domain socket
2859  * let server = socket.listen({ path: "/var/run/server.sock" });
2860  */
2861 static uc_value_t *
2862 uc_socket_listen(uc_vm_t *vm, size_t nargs)
2863 {
2864         int ret, fd, curr_weight, prev_weight, socktype = 0, protocol = 0;
2865         struct addrinfo *ai_results, *ai_hints, *ai;
2866         uc_value_t *host, *serv, *hints, *backlog;
2867         struct sockaddr_storage ss = { 0 };
2868         bool v6, lo, ll;
2869         socklen_t slen;
2870 
2871         args_get(vm, nargs, NULL,
2872                 "host", UC_NULL, true, &host,
2873                 "service", UC_NULL, true, &serv,
2874                 "hints", UC_OBJECT, true, &hints,
2875                 "backlog", UC_INTEGER, true, &backlog);
2876 
2877         ai_hints = hints
2878                 ? (struct addrinfo *)uv_to_struct(hints, &st_addrinfo) : NULL;
2879 
2880         if (host == NULL || should_resolve(host)) {
2881                 char *servstr = (ucv_type(serv) != UC_STRING)
2882                         ? ucv_to_string(vm, serv) : NULL;
2883 
2884                 ret = getaddrinfo(ucv_string_get(host),
2885                         servstr ? servstr : ucv_string_get(serv),
2886                         ai_hints ? ai_hints : &(struct addrinfo){
2887                                 .ai_flags = AI_PASSIVE | AI_ADDRCONFIG,
2888                                 .ai_socktype = SOCK_STREAM
2889                         }, &ai_results);
2890 
2891                 free(servstr);
2892 
2893                 if (ret != 0) {
2894                         free(ai_hints);
2895                         err_return((ret == EAI_SYSTEM) ? errno : ret,
2896                                 "getaddrinfo()");
2897                 }
2898 
2899                 for (ai = ai_results, prev_weight = -1; ai != NULL; ai = ai->ai_next) {
2900                         struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)ai->ai_addr;
2901                         struct sockaddr_in *s4 = (struct sockaddr_in *)ai->ai_addr;
2902 
2903                         v6 = (s6->sin6_family == AF_INET6);
2904                         ll = v6
2905                                 ? IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr)
2906                                 : ((ntohl(s4->sin_addr.s_addr) & 0xffff0000) == 0xa9fe0000);
2907                         lo = v6
2908                                 ? IN6_IS_ADDR_LOOPBACK(&s6->sin6_addr)
2909                                 : ((ntohl(s4->sin_addr.s_addr) & 0xff000000) == 0x7f000000);
2910 
2911                         curr_weight = (!lo << 2) | (v6 << 1) | (!ll << 0);
2912 
2913                         if (curr_weight > prev_weight) {
2914                                 prev_weight = curr_weight;
2915                                 socktype = ai->ai_socktype;
2916                                 protocol = ai->ai_protocol;
2917                                 slen     = ai->ai_addrlen;
2918                                 memcpy(&ss, ai->ai_addr, slen);
2919                         }
2920                 }
2921 
2922                 freeaddrinfo(ai_results);
2923         }
2924         else {
2925                 if (!uv_to_sockaddr(host, &ss, &slen)) {
2926                         free(ai_hints);
2927                         return NULL;
2928                 }
2929 
2930                 if (serv) {
2931                         uint64_t port = ucv_to_unsigned(serv);
2932 
2933                         if (port > 65535)
2934                                 errno = ERANGE;
2935 
2936                         if (errno != 0) {
2937                                 free(ai_hints);
2938                                 err_return(errno, "Invalid port number");
2939                         }
2940 
2941                         ((struct sockaddr_in *)&ss)->sin_port = htons(port);
2942                 }
2943 
2944                 int default_socktype = SOCK_STREAM;
2945 
2946                 if (ss.ss_family != AF_INET && ss.ss_family != AF_INET6)
2947                         default_socktype = SOCK_DGRAM;
2948 
2949                 socktype = ai_hints ? ai_hints->ai_socktype : default_socktype;
2950                 protocol = ai_hints ? ai_hints->ai_protocol : 0;
2951         }
2952 
2953         free(ai_hints);
2954 
2955         if (ss.ss_family == AF_UNSPEC)
2956                 err_return(EAI_NONAME, "Could not resolve host address");
2957 
2958         fd = socket(ss.ss_family, socktype, protocol);
2959 
2960         if (fd == -1)
2961                 err_return(errno, "socket()");
2962 
2963         ret = bind(fd, (struct sockaddr *)&ss, slen);
2964 
2965         if (ret == -1) {
2966                 close(fd);
2967                 err_return(errno, "bind()");
2968         }
2969 
2970         ret = listen(fd, backlog ? ucv_to_unsigned(backlog) : 128);
2971 
2972         if (ret == -1 && errno != EOPNOTSUPP) {
2973                 close(fd);
2974                 err_return(errno, "listen()");
2975         }
2976 
2977         ok_return(ucv_socket_new(vm, fd));
2978 }
2979 
2980 /**
2981  * Represents a socket handle.
2982  *
2983  * @class module:socket.socket
2984  * @hideconstructor
2985  *
2986  * @borrows module:socket#error as module:socket.socket#error
2987  *
2988  * @see {@link module:socket#create|create()}
2989  *
2990  * @example
2991  *
2992  * const sock = create(…);
2993  *
2994  * sock.getopt(…);
2995  * sock.setopt(…);
2996  *
2997  * sock.connect(…);
2998  * sock.listen(…);
2999  * sock.accept(…);
3000  * sock.bind(…);
3001  *
3002  * sock.send(…);
3003  * sock.recv(…);
3004  *
3005  * sock.shutdown(…);
3006  *
3007  * sock.fileno();
3008  * sock.peername();
3009  * sock.sockname();
3010  *
3011  * sock.close();
3012  *
3013  * sock.error();
3014  */
3015 
3016 /**
3017  * Creates a network socket instance.
3018  *
3019  * This function creates a new network socket with the specified domain and
3020  * type, determined by one of the modules `AF_*` and `SOCK_*` constants
3021  * respectively, and returns the resulting socket instance for use in subsequent
3022  * socket operations.
3023  *
3024  * The domain argument specifies the protocol family, such as AF_INET or
3025  * AF_INET6, and defaults to AF_INET if not provided.
3026  *
3027  * The type argument specifies the socket type, such as SOCK_STREAM or
3028  * SOCK_DGRAM, and defaults to SOCK_STREAM if not provided. It may also
3029  * be bitwise OR-ed with SOCK_NONBLOCK to enable non-blocking mode or
3030  * SOCK_CLOEXEC to enable close-on-exec semantics.
3031  *
3032  * The protocol argument may be used to indicate a particular protocol
3033  * to be used with the socket, and it defaults to 0 (automatically
3034  * determined protocol) if not provided.
3035  *
3036  * Returns a socket descriptor representing the newly created socket.
3037  *
3038  * Returns `null` if an error occurred during socket creation.
3039  *
3040  * @function module:socket#create
3041  *
3042  * @param {number} [domain=AF_INET]
3043  * The communication domain for the socket, e.g., AF_INET or AF_INET6.
3044  *
3045  * @param {number} [type=SOCK_STREAM]
3046  * The socket type, e.g., SOCK_STREAM or SOCK_DGRAM. It may also be
3047  * bitwise OR-ed with SOCK_NONBLOCK or SOCK_CLOEXEC.
3048  *
3049  * @param {number} [protocol=0]
3050  * The protocol to be used with the socket.
3051  *
3052  * @returns {?module:socket.socket}
3053  * A socket instance representing the newly created socket.
3054  *
3055  * @example
3056  * // Create a TCP socket
3057  * const tcp_socket = create(AF_INET, SOCK_STREAM);
3058  *
3059  * // Create a nonblocking IPv6 UDP socket
3060  * const udp_socket = create(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK);
3061  */
3062 static uc_value_t *
3063 uc_socket_create(uc_vm_t *vm, size_t nargs)
3064 {
3065         uc_value_t *domain, *type, *protocol;
3066         int sockfd, socktype;
3067 
3068         args_get(vm, nargs, NULL,
3069                 "domain", UC_INTEGER, true, &domain,
3070                 "type", UC_INTEGER, true, &type,
3071                 "protocol", UC_INTEGER, true, &protocol);
3072 
3073         socktype = type ? (int)ucv_int64_get(type) : SOCK_STREAM;
3074 
3075         sockfd = socket(
3076                 domain ? (int)ucv_int64_get(domain) : AF_INET,
3077 #if defined(__APPLE__)
3078                 socktype & ~(SOCK_NONBLOCK|SOCK_CLOEXEC),
3079 #else
3080                 socktype,
3081 #endif
3082                 protocol ? (int)ucv_int64_get(protocol) : 0);
3083 
3084         if (sockfd == -1)
3085                 err_return(errno, "socket()");
3086 
3087 #if defined(__APPLE__)
3088         if (socktype & SOCK_NONBLOCK) {
3089                 int flags = fcntl(sockfd, F_GETFL);
3090 
3091                 if (flags == -1) {
3092                         close(sockfd);
3093                         err_return(errno, "fcntl(F_GETFL)");
3094                 }
3095 
3096                 if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) == -1) {
3097                         close(sockfd);
3098                         err_return(errno, "fcntl(F_SETFL)");
3099                 }
3100         }
3101 
3102         if (socktype & SOCK_CLOEXEC) {
3103                 if (fcntl(sockfd, F_SETFD, FD_CLOEXEC) == -1) {
3104                         close(sockfd);
3105                         err_return(errno, "fcntl(F_SETFD)");
3106                 }
3107         }
3108 #endif
3109 
3110         ok_return(ucv_socket_new(vm, sockfd));
3111 }
3112 
3113 /**
3114  * Connects the socket to a remote address.
3115  *
3116  * Attempts to establish a connection to the specified remote address.
3117  *
3118  * Returns `true` if the connection is successfully established.
3119  * Returns `null` if an error occurred during the connection attempt.
3120  *
3121  * @function module:socket.socket#connect
3122  *
3123  * @param {string|module:socket.socket.SocketAddress} address
3124  * The address of the remote endpoint to connect to.
3125  *
3126  * @param {number} port
3127  * The port number of the remote endpoint to connect to.
3128  *
3129  * @returns {?boolean}
3130  */
3131 static uc_value_t *
3132 uc_socket_inst_connect(uc_vm_t *vm, size_t nargs)
3133 {
3134         struct sockaddr_storage ss;
3135         uc_value_t *addr;
3136         int ret, sockfd;
3137         socklen_t slen;
3138 
3139         args_get(vm, nargs, &sockfd,
3140                 "address", UC_NULL, false, &addr);
3141 
3142         if (!uv_to_sockaddr(addr, &ss, &slen))
3143                 return NULL;
3144 
3145         ret = connect(sockfd, (struct sockaddr *)&ss, slen);
3146 
3147         if (ret == -1)
3148                 err_return(errno, "connect()");
3149 
3150         ok_return(ucv_boolean_new(true));
3151 }
3152 
3153 /**
3154  * Sends data through the socket.
3155  *
3156  * Sends the provided data through the socket handle to the specified remote
3157  * address, if provided.
3158  *
3159  * Returns the number of bytes sent.
3160  * Returns `null` if an error occurred during the send operation.
3161  *
3162  * @function module:socket.socket#send
3163  *
3164  * @param {*} data
3165  * The data to be sent through the socket. String data is sent as-is, any other
3166  * type is implicitly converted to a string first before being sent on the
3167  * socket.
3168  *
3169  * @param {number} [flags]
3170  * Optional flags that modify the behavior of the send operation.
3171  *
3172  * @param {module:socket.socket.SocketAddress|number[]|string} [address]
3173  * The address of the remote endpoint to send the data to. It can be either an
3174  * IP address string, an array returned by {@link module:core#iptoarr|iptoarr()},
3175  * or an object representing a network address. If not provided, the data is
3176  * sent to the remote endpoint the socket is connected to.
3177  *
3178  * @returns {?number}
3179  *
3180  * @see {@link module:socket#sockaddr|sockaddr()}
3181  *
3182  * @example
3183  * // Send to connected socket
3184  * let tcp_sock = socket.create(socket.AF_INET, socket.SOCK_STREAM);
3185  * tcp_sock.connect("192.168.1.1", 80);
3186  * tcp_sock.send("GET / HTTP/1.0\r\n\r\n");
3187  *
3188  * // Send a datagram on unconnected socket
3189  * let udp_sock = socket.create(socket.AF_INET, socket.SOCK_DGRAM);
3190  * udp_sock.send("Hello there!", 0, "255.255.255.255:9000");
3191  * udp_sock.send("Hello there!", 0, {
3192  *   family: socket.AF_INET,      // optional
3193  *   address: "255.255.255.255",
3194  *   port: 9000
3195  * });
3196  */
3197 static uc_value_t *
3198 uc_socket_inst_send(uc_vm_t *vm, size_t nargs)
3199 {
3200         uc_value_t *data, *flags, *addr;
3201         struct sockaddr_storage ss = { 0 };
3202         struct sockaddr *sa = NULL;
3203         socklen_t salen = 0;
3204         char *buf = NULL;
3205         ssize_t ret;
3206         int sockfd;
3207 
3208         args_get(vm, nargs, &sockfd,
3209                 "data", UC_NULL, false, &data,
3210                 "flags", UC_INTEGER, true, &flags,
3211                 "address", UC_NULL, true, &addr);
3212 
3213         if (addr) {
3214                 if (!uv_to_sockaddr(addr, &ss, &salen))
3215                         return NULL;
3216 
3217                 sa = (struct sockaddr *)&ss;
3218         }
3219 
3220         if (ucv_type(data) != UC_STRING)
3221                 buf = ucv_to_string(vm, data);
3222 
3223         ret = sendto(sockfd,
3224                 buf ? buf : ucv_string_get(data),
3225                 buf ? strlen(buf) : ucv_string_length(data),
3226                 (flags ? ucv_int64_get(flags) : 0) | MSG_NOSIGNAL, sa, salen);
3227 
3228         free(buf);
3229 
3230         if (ret == -1)
3231                 err_return(errno, "send()");
3232 
3233         ok_return(ucv_int64_new(ret));
3234 }
3235 
3236 /**
3237  * Receives data from the socket.
3238  *
3239  * Receives data from the socket handle, optionally specifying the maximum
3240  * length of data to receive, flags to modify the receive behavior, and an
3241  * optional address dictionary where the function will place the address from
3242  * which the data was received (for unconnected sockets).
3243  *
3244  * Returns a string containing the received data.
3245  * Returns an empty string if the remote side closed the socket.
3246  * Returns `null` if an error occurred during the receive operation.
3247  *
3248  * @function module:socket.socket#recv
3249  *
3250  * @param {number} [length=4096]
3251  * The maximum number of bytes to receive.
3252  *
3253  * @param {number} [flags]
3254  * Optional flags that modify the behavior of the receive operation.
3255  *
3256  * @param {Object} [address]
3257  * An object where the function will store the address from which the data was
3258  * received. If provided, it will be filled with the details obtained from the
3259  * sockaddr argument of the underlying `recvfrom()` syscall. See the type
3260  * definition of {@link module:socket.socket.SocketAddress|SocketAddress} for
3261  * details on the format.
3262  *
3263  * @returns {?string}
3264  */
3265 static uc_value_t *
3266 uc_socket_inst_recv(uc_vm_t *vm, size_t nargs)
3267 {
3268         uc_value_t *length, *flags, *addrobj;
3269         struct sockaddr_storage ss = { 0 };
3270         uc_stringbuf_t *buf;
3271         ssize_t len, ret;
3272         socklen_t sslen;
3273         int sockfd;
3274 
3275         args_get(vm, nargs, &sockfd,
3276                 "length", UC_INTEGER, true, &length,
3277                 "flags", UC_INTEGER, true, &flags,
3278                 "address", UC_OBJECT, true, &addrobj);
3279 
3280         len = length ? ucv_to_integer(length) : 4096;
3281 
3282         if (errno || len <= 0)
3283                 err_return(errno, "Invalid length argument");
3284 
3285         buf = strbuf_alloc(len);
3286 
3287         if (!buf)
3288                 return NULL;
3289 
3290         do {
3291                 sslen = sizeof(ss);
3292                 ret = recvfrom(sockfd, strbuf_data(buf), len,
3293                         flags ? ucv_int64_get(flags) : 0, (struct sockaddr *)&ss, &sslen);
3294         } while (ret == -1 && errno == EINTR);
3295 
3296         if (ret == -1) {
3297                 strbuf_free(buf);
3298                 err_return(errno, "recv()");
3299         }
3300 
3301         if (addrobj)
3302                 sockaddr_to_uv(&ss, addrobj);
3303 
3304         ok_return(strbuf_finish(&buf, ret));
3305 }
3306 
3307 uc_declare_vector(strbuf_array_t, uc_stringbuf_t *);
3308 
3309 #if defined(__linux__)
3310 static void optmem_max(size_t *sz) {
3311         char buf[sizeof("18446744073709551615")] = { 0 };
3312         int fd, rv;
3313 
3314         fd = open("/proc/sys/net/core/optmem_max", O_RDONLY);
3315 
3316         if (fd >= 0) {
3317                 if (read(fd, buf, sizeof(buf) - 1) > 0) {
3318                         rv = strtol(buf, NULL, 10);
3319 
3320                         if (rv > 0 && (size_t)rv < *sz)
3321                                 *sz = rv;
3322                 }
3323 
3324                 if (fd > 2)
3325                         close(fd);
3326         }
3327 }
3328 #else
3329 # define optmem_max(x)
3330 #endif
3331 
3332 
3333 /**
3334  * Represents a single control (ancillary data) message returned
3335  * in the *ancillary* array by {@link module:socket.socket#recvmsg|`recvmsg()`}.
3336  *
3337  * @typedef {Object} module:socket.socket.ControlMessage
3338  * @property {number} level
3339  * The message socket level (`cmsg_level`), e.g. `SOL_SOCKET`.
3340  *
3341  * @property {number} type
3342  * The protocol specific message type (`cmsg_type`), e.g. `SCM_RIGHTS`.
3343  *
3344  * @property {*} data
3345  * The payload of the control message. If the control message type is known by
3346  * the socket module, it is represented as a mixed value (array, object, number,
3347  * etc.) with structure specific to the control message type. If the control
3348  * message cannot be decoded, *data* is set to a string value containing the raw
3349  * payload.
3350  */
3351 static uc_value_t *
3352 decode_cmsg(uc_vm_t *vm, struct cmsghdr *cmsg)
3353 {
3354         char *s = (char *)CMSG_DATA(cmsg);
3355         size_t sz = cmsg->cmsg_len - sizeof(*cmsg);
3356         struct sockaddr_storage *ss;
3357         uc_value_t *fdarr;
3358         struct stat st;
3359         int *fds;
3360 
3361         for (size_t i = 0; i < ARRAY_SIZE(cmsgtypes); i++) {
3362 
3363                 if (cmsgtypes[i].level != cmsg->cmsg_level)
3364                         continue;
3365 
3366                 if (cmsgtypes[i].type != cmsg->cmsg_type)
3367                         continue;
3368 
3369                 switch ((uintptr_t)cmsgtypes[i].ctype) {
3370                 case (uintptr_t)CV_INT:
3371                         return ucv_int64_new(parse_integer(s, sz));
3372 
3373                 case (uintptr_t)CV_UINT:
3374                 case (uintptr_t)CV_BE32:
3375                         return ucv_uint64_new(parse_unsigned(s, sz));
3376 
3377                 case (uintptr_t)CV_SOCKADDR:
3378                         ss = (struct sockaddr_storage *)s;
3379 
3380                         if ((sz >= sizeof(struct sockaddr_in) &&
3381                              ss->ss_family == AF_INET) ||
3382                             (sz >= sizeof(struct sockaddr_in6) &&
3383                              ss->ss_family == AF_INET6))
3384                         {
3385                                 uc_value_t *addr = ucv_object_new(vm);
3386 
3387                                 if (sockaddr_to_uv(ss, addr))
3388                                         return addr;
3389 
3390                                 ucv_put(addr);
3391                         }
3392 
3393                         return NULL;
3394 
3395                 case (uintptr_t)CV_FDS:
3396                         fdarr = ucv_array_new_length(vm, sz / sizeof(int));
3397                         fds = (int *)s;
3398 
3399                         for (size_t i = 0; i < sz / sizeof(int); i++) {
3400                                 if (fstat(fds[i], &st) == 0) {
3401                                         uc_resource_type_t *t;
3402 
3403                                         if (S_ISSOCK(st.st_mode)) {
3404                                                 t = ucv_resource_type_lookup(vm, "socket");
3405 
3406                                                 ucv_array_push(fdarr,
3407                                                         ucv_resource_new(t, (void *)(intptr_t)fds[i]));
3408 
3409                                                 continue;
3410                                         }
3411                                         else if (S_ISDIR(st.st_mode)) {
3412                                                 t = ucv_resource_type_lookup(vm, "fs.dir");
3413 
3414                                                 if (t) {
3415                                                         DIR *d = fdopendir(fds[i]);
3416 
3417                                                         if (d) {
3418                                                                 ucv_array_push(fdarr, ucv_resource_new(t, d));
3419                                                                 continue;
3420                                                         }
3421                                                 }
3422                                         }
3423                                         else {
3424                                                 t = ucv_resource_type_lookup(vm, "fs.file");
3425 
3426                                                 if (t) {
3427                                                         int n = fcntl(fds[i], F_GETFL);
3428                                                         const char *mode;
3429 
3430                                                         if (n <= 0 || (n & O_ACCMODE) == O_RDONLY)
3431                                                                 mode = "r";
3432                                                         else if ((n & O_ACCMODE) == O_WRONLY)
3433                                                                 mode = (n & O_APPEND) ? "a" : "w";
3434                                                         else
3435                                                                 mode = (n & O_APPEND) ? "a+" : "w+";
3436 
3437                                                         FILE *f = fdopen(fds[i], mode);
3438 
3439                                                         if (f) {
3440                                                                 ucv_array_push(fdarr, uc_resource_new(t, f));
3441                                                                 continue;
3442                                                         }
3443                                                 }
3444                                         }
3445                                 }
3446 
3447                                 ucv_array_push(fdarr, ucv_int64_new(fds[i]));
3448                         }
3449 
3450                         return fdarr;
3451 
3452                 case (uintptr_t)CV_STRING:
3453                         break;
3454 
3455                 default:
3456                         if (sz >= cmsgtypes[i].ctype->size)
3457                                 return struct_to_uv(s, cmsgtypes[i].ctype);
3458                 }
3459 
3460                 break;
3461         }
3462 
3463         return ucv_string_new_length(s, sz);
3464 }
3465 
3466 static size_t
3467 estimate_cmsg_size(uc_value_t *uv)
3468 {
3469         int cmsg_level = ucv_to_integer(ucv_object_get(uv, "level", NULL));
3470         int cmsg_type = ucv_to_integer(ucv_object_get(uv, "type", NULL));
3471         uc_value_t *val = ucv_object_get(uv, "data", NULL);
3472 
3473         for (size_t i = 0; i < ARRAY_SIZE(cmsgtypes); i++) {
3474                 if (cmsgtypes[i].level != cmsg_level)
3475                         continue;
3476 
3477                 if (cmsgtypes[i].type != cmsg_type)
3478                         continue;
3479 
3480                 switch ((uintptr_t)cmsgtypes[i].ctype) {
3481                 case (uintptr_t)CV_INT:      return sizeof(int);
3482                 case (uintptr_t)CV_UINT:     return sizeof(unsigned int);
3483                 case (uintptr_t)CV_BE32:     return sizeof(uint32_t);
3484                 case (uintptr_t)CV_SOCKADDR: return sizeof(struct sockaddr_storage);
3485                 case (uintptr_t)CV_FDS:      return ucv_array_length(val) * sizeof(int);
3486                 case (uintptr_t)CV_STRING:   return ucv_string_length(val);
3487                 default:                     return cmsgtypes[i].ctype->size;
3488                 }
3489         }
3490 
3491         switch (ucv_type(val)) {
3492                 case UC_BOOLEAN: return sizeof(unsigned int);
3493                 case UC_INTEGER: return sizeof(int);
3494                 case UC_STRING:  return ucv_string_length(val);
3495                 default:         return 0;
3496         }
3497 }
3498 
3499 static bool
3500 encode_cmsg(uc_vm_t *vm, uc_value_t *uv, struct cmsghdr *cmsg)
3501 {
3502         struct { int *entries; size_t count; } fds = { 0 };
3503         void *dataptr = NULL;
3504         socklen_t datasz = 0;
3505         char *st = NULL;
3506         size_t i;
3507         union {
3508                 int i;
3509                 unsigned int u;
3510                 uint32_t u32;
3511                 struct sockaddr_storage ss;
3512         } val;
3513 
3514         cmsg->cmsg_level = ucv_to_integer(ucv_object_get(uv, "level", NULL));
3515         cmsg->cmsg_type = ucv_to_integer(ucv_object_get(uv, "type", NULL));
3516 
3517         uc_value_t *data = ucv_object_get(uv, "data", NULL);
3518 
3519         for (i = 0; i < ARRAY_SIZE(cmsgtypes); i++) {
3520                 if (cmsgtypes[i].level != cmsg->cmsg_level)
3521                         continue;
3522 
3523                 if (cmsgtypes[i].type != cmsg->cmsg_type)
3524                         continue;
3525 
3526                 switch ((uintptr_t)cmsgtypes[i].ctype) {
3527                 case (uintptr_t)CV_INT:
3528                         val.i = ucv_to_integer(data);
3529                         datasz = sizeof(val.i);
3530                         dataptr = &val;
3531                         break;
3532 
3533                 case (uintptr_t)CV_UINT:
3534                         val.u = ucv_to_unsigned(data);
3535                         datasz = sizeof(val.u);
3536                         dataptr = &val;
3537                         break;
3538 
3539                 case (uintptr_t)CV_BE32:
3540                         val.u32 = ucv_to_unsigned(data);
3541                         datasz = sizeof(val.u32);
3542                         dataptr = &val;
3543                         break;
3544 
3545                 case (uintptr_t)CV_SOCKADDR:
3546                         if (uv_to_sockaddr(data, &val.ss, &datasz))
3547                                 dataptr = &val;
3548                         else
3549                                 datasz = 0, dataptr = NULL;
3550                         break;
3551 
3552                 case (uintptr_t)CV_FDS:
3553                         if (ucv_type(data) == UC_ARRAY) {
3554                                 for (size_t i = 0; i < ucv_array_length(data); i++) {
3555                                         int fd;
3556 
3557                                         if (uv_to_fileno(vm, ucv_array_get(data, i), &fd))
3558                                                 uc_vector_push(&fds, fd);
3559                                 }
3560                         }
3561 
3562                         datasz = sizeof(fds.entries[0]) * fds.count;
3563                         dataptr = fds.entries;
3564                         break;
3565 
3566                 case (uintptr_t)CV_STRING:
3567                         datasz = ucv_string_length(data);
3568                         dataptr = ucv_string_get(data);
3569                         break;
3570 
3571                 default:
3572                         st = uv_to_struct(data, cmsgtypes[i].ctype);
3573                         datasz = st ? cmsgtypes[i].ctype->size : 0;
3574                         dataptr = st;
3575                         break;
3576                 }
3577 
3578                 break;
3579         }
3580 
3581         /* we don't know this kind of control message, guess encoding */
3582         if (i == ARRAY_SIZE(cmsgtypes)) {
3583                 switch (ucv_type(data)) {
3584                 /* treat boolean as int with values 1 or 0 */
3585                 case UC_BOOLEAN:
3586                         val.u = ucv_boolean_get(data);
3587                         dataptr = &val;
3588                         datasz = sizeof(val.u);
3589                         break;
3590 
3591                 /* treat integers as int */
3592                 case UC_INTEGER:
3593                         if (ucv_is_u64(data)) {
3594                                 val.u = ucv_uint64_get(data);
3595                                 datasz = sizeof(val.u);
3596                         }
3597                         else {
3598                                 val.i = ucv_int64_get(data);
3599                                 datasz = sizeof(val.i);
3600                         }
3601 
3602                         dataptr = &val;
3603                         break;
3604 
3605                 /* pass strings as-is */
3606                 case UC_STRING:
3607                         dataptr = ucv_string_get(data);
3608                         datasz = ucv_string_length(data);
3609                         break;
3610 
3611                 default:
3612                         break;
3613                 }
3614         }
3615 
3616         cmsg->cmsg_len = CMSG_LEN(datasz);
3617 
3618         if (dataptr)
3619                 memcpy(CMSG_DATA(cmsg), dataptr, datasz);
3620 
3621         uc_vector_clear(&fds);
3622         free(st);
3623 
3624         return true;
3625 }
3626 
3627 /**
3628  * Sends a message through the socket.
3629  *
3630  * Sends a message through the socket handle, supporting complex message
3631  * structures including multiple data buffers and ancillary data. This function
3632  * allows for precise control over the message content and delivery behavior.
3633  *
3634  * Returns the number of sent bytes.
3635  *
3636  * Returns `null` if an error occurred.
3637  *
3638  * @function module:socket.socket#sendmsg
3639  *
3640  * @param {*} [data]
3641  * The data to be sent. If a string is provided, it is sent as is. If an array
3642  * is specified, each item is sent as a separate `struct iovec`. Non-string
3643  * values are implicitly converted to a string and sent. If omitted, only
3644  * ancillary data and address are considered.
3645  *
3646  * @param {module:socket.socket.ControlMessage[]|string} [ancillaryData]
3647  * Optional ancillary data to be sent. If an array is provided, each element is
3648  * converted to a control message. If a string is provided, it is sent as-is
3649  * without further interpretation. Refer to
3650  * {@link module:socket.socket#recvmsg|`recvmsg()`} and
3651  * {@link module:socket.socket.ControlMessage|ControlMessage} for details.
3652  *
3653  * @param {module:socket.socket.SocketAddress} [address]
3654  * The destination address for the message. If provided, it sets or overrides
3655  * the packet destination address.
3656  *
3657  * @param {number} [flags]
3658  * Optional flags to modify the behavior of the send operation. This should be a
3659  * bitwise OR-ed combination of `MSG_*` flag values.
3660  *
3661  * @returns {?number}
3662  * Returns the number of bytes sent on success, or `null` if an error occurred.
3663  *
3664  * @example
3665  * // Send file descriptors over domain socket
3666  * const f1 = fs.open("example.txt", "w");
3667  * const f2 = fs.popen("date +%s", "r");
3668  * const sk = socket.connect({ family: socket.AF_UNIX, path: "/tmp/socket" });
3669 
3670  * sk.sendmsg("Hi there, here's some descriptors!", [
3671  *      { level: socket.SOL_SOCKET, type: socket.SCM_RIGHTS, data: [ f1, f2 ] }
3672  * ]);
3673  *
3674  * // Send multiple values in one datagram
3675  * sk.sendmsg([ "This", "is", "one", "message" ]);
3676  */
3677 static uc_value_t *
3678 uc_socket_inst_sendmsg(uc_vm_t *vm, size_t nargs)
3679 {
3680         uc_value_t *data, *ancdata, *addr, *flags;
3681         struct sockaddr_storage ss = { 0 };
3682         strbuf_array_t sbarr = { 0 };
3683         struct msghdr msg = { 0 };
3684         struct iovec vec = { 0 };
3685         int flagval, sockfd;
3686         socklen_t slen;
3687         ssize_t ret;
3688 
3689         args_get(vm, nargs, &sockfd,
3690                 "data", UC_NULL, true, &data,
3691                 "ancillary data", UC_NULL, true, &ancdata,
3692                 "address", UC_OBJECT, true, &addr,
3693                 "flags", UC_INTEGER, true, &flags);
3694 
3695         flagval = flags ? ucv_int64_get(flags) : 0;
3696 
3697         /* treat string ancdata arguemnt as raw controldata buffer */
3698         if (ucv_type(ancdata) == UC_STRING) {
3699                 msg.msg_control = ucv_string_get(ancdata);
3700                 msg.msg_controllen = ucv_string_length(ancdata);
3701         }
3702         /* encode ancdata passed as array */
3703         else if (ucv_type(ancdata) == UC_ARRAY) {
3704                 msg.msg_controllen = 0;
3705 
3706                 for (size_t i = 0; i < ucv_array_length(ancdata); i++) {
3707                         size_t sz = estimate_cmsg_size(ucv_array_get(ancdata, i));
3708 
3709                         if (sz > 0)
3710                                 msg.msg_controllen += CMSG_SPACE(sz);
3711                 }
3712 
3713                 if (msg.msg_controllen > 0) {
3714                         msg.msg_control = xalloc(msg.msg_controllen);
3715 
3716                         struct cmsghdr *cmsg = NULL;
3717 
3718                         for (size_t i = 0; i < ucv_array_length(ancdata); i++) {
3719 #ifdef __clang_analyzer__
3720                                 /* Clang static analyzer assumes that CMSG_*HDR() returns
3721                                  * allocated heap pointers and not pointers into the
3722                                  * msg.msg_control buffer. Nudge it. */
3723                                 cmsg = (struct cmsghdr *)msg.msg_control;
3724 #else
3725                                 cmsg = cmsg ? CMSG_NXTHDR(&msg, cmsg) : CMSG_FIRSTHDR(&msg);
3726 #endif
3727 
3728                                 if (!cmsg) {
3729                                         free(msg.msg_control);
3730                                         err_return(ENOBUFS, "Not enough CMSG buffer space");
3731                                 }
3732 
3733                                 if (!encode_cmsg(vm, ucv_array_get(ancdata, i), cmsg)) {
3734                                         free(msg.msg_control);
3735                                         return NULL;
3736                                 }
3737                         }
3738 
3739                         msg.msg_controllen = (cmsg != NULL)
3740                                 ? (char *)cmsg - (char *)msg.msg_control + CMSG_SPACE(cmsg->cmsg_len)
3741                                 : 0;
3742                 }
3743         }
3744         else if (ancdata) {
3745                 err_return(EINVAL, "Ancillary data must be string or array value");
3746         }
3747 
3748         /* prepare iov array */
3749         if (ucv_type(data) == UC_ARRAY) {
3750                 msg.msg_iovlen = ucv_array_length(data);
3751                 msg.msg_iov = (msg.msg_iovlen > 1)
3752                         ? xalloc(sizeof(vec) * msg.msg_iovlen) : &vec;
3753 
3754                 for (size_t i = 0; i < (size_t)msg.msg_iovlen; i++) {
3755                         uc_value_t *item = ucv_array_get(data, i);
3756 
3757                         if (ucv_type(item) == UC_STRING) {
3758                                 msg.msg_iov[i].iov_base = _ucv_string_get(&((uc_array_t *)data)->entries[i]);
3759                                 msg.msg_iov[i].iov_len = ucv_string_length(item);
3760                         }
3761                         else if (item) {
3762                                 struct printbuf *pb = xprintbuf_new();
3763                                 uc_vector_push(&sbarr, pb);
3764                                 ucv_to_stringbuf(vm, pb, item, false);
3765                                 msg.msg_iov[i].iov_base = pb->buf;
3766                                 msg.msg_iov[i].iov_len = pb->bpos;
3767                         }
3768                 }
3769         }
3770         else if (ucv_type(data) == UC_STRING) {
3771                 msg.msg_iovlen = 1;
3772                 msg.msg_iov = &vec;
3773                 vec.iov_base = ucv_string_get(data);
3774                 vec.iov_len = ucv_string_length(data);
3775         }
3776         else if (data) {
3777                 struct printbuf *pb = xprintbuf_new();
3778                 uc_vector_push(&sbarr, pb);
3779                 ucv_to_stringbuf(vm, pb, data, false);
3780                 msg.msg_iovlen = 1;
3781                 msg.msg_iov = &vec;
3782                 vec.iov_base = pb->buf;
3783                 vec.iov_len = pb->bpos;
3784         }
3785 
3786         /* prepare address */
3787         if (addr && uv_to_sockaddr(addr, &ss, &slen)) {
3788                 msg.msg_name = &ss;
3789                 msg.msg_namelen = slen;
3790         }
3791 
3792         /* now send actual data */
3793         do {
3794                 ret = sendmsg(sockfd, &msg, flagval);
3795         } while (ret == -1 && errno == EINTR);
3796 
3797         while (sbarr.count > 0)
3798                 printbuf_free(sbarr.entries[--sbarr.count]);
3799 
3800         uc_vector_clear(&sbarr);
3801 
3802         if (msg.msg_iov != &vec)
3803                 free(msg.msg_iov);
3804 
3805         free(msg.msg_control);
3806 
3807         if (ret == -1)
3808                 err_return(errno, "sendmsg()");
3809 
3810         ok_return(ucv_int64_new(ret));
3811 }
3812 
3813 
3814 
3815 /**
3816  * Represents a message object returned by
3817  * {@link module:socket.socket#recvmsg|`recvmsg()`}.
3818  *
3819  * @typedef {Object} module:socket.socket.ReceivedMessage
3820  * @property {number} flags
3821  * Integer value containing bitwise OR-ed `MSG_*` result flags returned by the
3822  * underlying receive call.
3823  *
3824  * @property {number} length
3825  * Integer value containing the number of bytes returned by the `recvmsg()`
3826  * syscall, which might be larger than the received data in case `MSG_TRUNC`
3827  * was passed.
3828  *
3829  * @property {module:socket.socket.SocketAddress} address
3830  * The address from which the message was received.
3831  *
3832  * @property {string[]|string} data
3833  * An array of strings, each representing the received message data.
3834  * Each string corresponds to one buffer size specified in the *sizes* argument.
3835  * If a single receive size was passed instead of an array of sizes, *data* will
3836  * hold a string containing the received data.
3837  *
3838  * @property {module:socket.socket.ControlMessage[]} [ancillary]
3839  * An array of received control messages. Only included if a non-zero positive
3840  * *ancillarySize* was passed to `recvmsg()`.
3841  */
3842 
3843 /**
3844  * Receives a message from the socket.
3845  *
3846  * Receives a message from the socket handle, allowing for more complex data
3847  * reception compared to `recv()`. This includes the ability to receive
3848  * ancillary data (such as file descriptors, credentials, etc.), multiple
3849  * message segments, and optional flags to modify the receive behavior.
3850  *
3851  * Returns an object containing the received message data, ancillary data,
3852  * and the sender's address.
3853  *
3854  * Returns `null` if an error occurred during the receive operation.
3855  *
3856  * @function module:socket.socket#recvmsg
3857  *
3858  * @param {number[]|number} [sizes]
3859  * Specifies the sizes of the buffers used for receiving the message. If an
3860  * array of numbers is provided, each number determines the size of an
3861  * individual buffer segment, creating multiple `struct iovec` for reception.
3862  * If a single number is provided, a single buffer of that size is used.
3863  *
3864  * @param {number} [ancillarySize]
3865  * The size allocated for the ancillary data buffer. If not provided, ancillary
3866  * data is not processed.
3867  *
3868  * @param {number} [flags]
3869  * Optional flags to modify the behavior of the receive operation. This should
3870  * be a bitwise OR-ed combination of flag values.
3871  *
3872  * @returns {?module:socket.socket.ReceivedMessage}
3873  * An object containing the received message data, ancillary data,
3874  * and the sender's address.
3875  *
3876  * @example
3877  * // Receive file descriptors over domain socket
3878  * const sk = socket.listen({ family: socket.AF_UNIX, path: "/tmp/socket" });
3879  * sk.setopt(socket.SOL_SOCKET, socket.SO_PASSCRED, true);
3880  *
3881  * const msg = sk.recvmsg(1024, 1024); *
3882  * for (let cmsg in msg.ancillary)
3883  *   if (cmsg.level == socket.SOL_SOCKET && cmsg.type == socket.SCM_RIGHTS)
3884  *     print(`Got some descriptors: ${cmsg.data}!\n`);
3885  *
3886  * // Receive message in segments of 10, 128 and 512 bytes
3887  * const msg = sk.recvmsg([ 10, 128, 512 ]);
3888  * print(`Message parts: ${msg.data[0]}, ${msg.data[1]}, ${msg.data[2]}\n`);
3889  *
3890  * // Peek buffer
3891  * const msg = sk.recvmsg(0, 0, socket.MSG_PEEK|socket.MSG_TRUNC);
3892  * print(`Received ${length(msg.data)} bytes, ${msg.length} bytes available\n`);
3893  */
3894 static uc_value_t *
3895 uc_socket_inst_recvmsg(uc_vm_t *vm, size_t nargs)
3896 {
3897         uc_value_t *length, *anclength, *flags, *rv;
3898         struct sockaddr_storage ss = { 0 };
3899         strbuf_array_t sbarr = { 0 };
3900         struct msghdr msg = { 0 };
3901         struct iovec vec = { 0 };
3902         int flagval, sockfd;
3903         ssize_t ret;
3904 
3905         args_get(vm, nargs, &sockfd,
3906                 "length", UC_NULL, true, &length,
3907                 "ancillary length", UC_INTEGER, true, &anclength,
3908                 "flags", UC_INTEGER, true, &flags);
3909 
3910         flagval = flags ? ucv_int64_get(flags) : 0;
3911 
3912 #if defined(__linux__)
3913         flagval |= MSG_CMSG_CLOEXEC;
3914 #endif
3915 
3916         /* prepare ancillary data buffer */
3917         if (anclength) {
3918                 size_t sz = ucv_to_unsigned(anclength);
3919 
3920                 if (errno != 0)
3921                         err_return(errno, "Invalid ancillary data length");
3922 
3923                 optmem_max(&sz);
3924 
3925                 if (sz > 0) {
3926                         msg.msg_controllen = sz;
3927                         msg.msg_control = xalloc(sz);
3928                 }
3929         }
3930 
3931         /* prepare iov array */
3932         if (ucv_type(length) == UC_ARRAY) {
3933                 msg.msg_iovlen = ucv_array_length(length);
3934                 msg.msg_iov = (msg.msg_iovlen > 1)
3935                         ? xalloc(sizeof(vec) * msg.msg_iovlen) : &vec;
3936 
3937                 for (size_t i = 0; i < (size_t)msg.msg_iovlen; i++) {
3938                         size_t sz = ucv_to_unsigned(ucv_array_get(length, i));
3939 
3940                         if (errno != 0) {
3941                                 while (sbarr.count > 0)
3942                                         strbuf_free(sbarr.entries[--sbarr.count]);
3943 
3944                                 uc_vector_clear(&sbarr);
3945 
3946                                 if (msg.msg_iov != &vec)
3947                                         free(msg.msg_iov);
3948 
3949                                 free(msg.msg_control);
3950 
3951                                 err_return(errno, "Invalid length value");
3952                         }
3953 
3954                         uc_vector_push(&sbarr, strbuf_alloc(sz));
3955                         msg.msg_iov[i].iov_base = strbuf_data(sbarr.entries[i]);
3956                         msg.msg_iov[i].iov_len = sz;
3957                 }
3958         }
3959         else {
3960                 size_t sz = ucv_to_unsigned(length);
3961 
3962                 if (errno != 0) {
3963                         free(msg.msg_control);
3964                         err_return(errno, "Invalid length value");
3965                 }
3966 
3967                 uc_vector_push(&sbarr, strbuf_alloc(sz));
3968 
3969                 msg.msg_iovlen = 1;
3970                 msg.msg_iov = &vec;
3971                 vec.iov_base = strbuf_data(sbarr.entries[0]);
3972                 vec.iov_len = sz;
3973         }
3974 
3975         /* now receive actual data */
3976         msg.msg_name = &ss;
3977         msg.msg_namelen = sizeof(ss);
3978 
3979         do {
3980                 ret = recvmsg(sockfd, &msg, flagval);
3981         } while (ret == -1 && errno == EINTR);
3982 
3983         if (ret == -1) {
3984                 while (sbarr.count > 0)
3985                         strbuf_free(sbarr.entries[--sbarr.count]);
3986 
3987                 uc_vector_clear(&sbarr);
3988 
3989                 if (msg.msg_iov != &vec)
3990                         free(msg.msg_iov);
3991 
3992                 free(msg.msg_control);
3993 
3994                 err_return(errno, "recvmsg()");
3995         }
3996 
3997         rv = ucv_object_new(vm);
3998 
3999         ucv_object_add(rv, "flags", ucv_int64_new(msg.msg_flags));
4000         ucv_object_add(rv, "length", ucv_int64_new(ret));
4001 
4002         if (msg.msg_namelen > 0) {
4003                 uc_value_t *addr = ucv_object_new(vm);
4004 
4005                 if (sockaddr_to_uv(&ss, addr))
4006                         ucv_object_add(rv, "address", addr);
4007                 else
4008                         ucv_put(addr);
4009         }
4010 
4011         if (msg.msg_controllen > 0) {
4012                 uc_value_t *ancillary = ucv_array_new(vm);
4013 
4014                 for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
4015                      cmsg != NULL;
4016                      cmsg = CMSG_NXTHDR(&msg, cmsg))
4017                 {
4018                         uc_value_t *c = ucv_object_new(vm);
4019 
4020                         ucv_object_add(c, "level", ucv_int64_new(cmsg->cmsg_level));
4021                         ucv_object_add(c, "type", ucv_int64_new(cmsg->cmsg_type));
4022                         ucv_object_add(c, "data", decode_cmsg(vm, cmsg));
4023 
4024                         ucv_array_push(ancillary, c);
4025                 }
4026 
4027                 ucv_object_add(rv, "ancillary", ancillary);
4028         }
4029 
4030         if (ret >= 0) {
4031                 if (ucv_type(length) == UC_ARRAY) {
4032                         uc_value_t *data = ucv_array_new_length(vm, msg.msg_iovlen);
4033 
4034                         for (size_t i = 0; i < (size_t)msg.msg_iovlen; i++) {
4035                                 size_t sz = ret;
4036 
4037                                 if (sz > msg.msg_iov[i].iov_len)
4038                                         sz = msg.msg_iov[i].iov_len;
4039 
4040                                 ucv_array_push(data, strbuf_finish(&sbarr.entries[i], sz));
4041                                 ret -= sz;
4042                         }
4043 
4044                         ucv_object_add(rv, "data", data);
4045                 }
4046                 else {
4047                         size_t sz = ret;
4048 
4049                         if (sz > msg.msg_iov[0].iov_len)
4050                                 sz = msg.msg_iov[0].iov_len;
4051 
4052                         ucv_object_add(rv, "data", strbuf_finish(&sbarr.entries[0], sz));
4053                 }
4054         }
4055 
4056         uc_vector_clear(&sbarr);
4057 
4058         if (msg.msg_iov != &vec)
4059                 free(msg.msg_iov);
4060 
4061         free(msg.msg_control);
4062 
4063         ok_return(rv);
4064 }
4065 
4066 /**
4067  * Binds a socket to a specific address.
4068  *
4069  * This function binds the socket to the specified address.
4070  *
4071  * Returns `true` if the socket is successfully bound.
4072  *
4073  * Returns `null` on error, e.g. when the address is in use.
4074  *
4075  * @function module:socket.socket#bind
4076  *
4077  * @param {string|module:socket.socket.SocketAddress} address
4078  * The IP address to bind the socket to.
4079  *
4080  * @returns {?boolean}
4081  *
4082  * @example
4083  * const sock = socket.create(…);
4084  * const success = sock.bind("192.168.0.1:80");
4085  *
4086  * if (success)
4087  *     print(`Socket bound successfully!\n`);
4088  * else
4089  *     print(`Failed to bind socket: ${sock.error()}.\n`);
4090  */
4091 static uc_value_t *
4092 uc_socket_inst_bind(uc_vm_t *vm, size_t nargs)
4093 {
4094         struct sockaddr_storage ss = { 0 };
4095         uc_value_t *addr;
4096         socklen_t slen;
4097         int sockfd;
4098 
4099         args_get(vm, nargs, &sockfd,
4100                 "address", UC_NULL, true, &addr);
4101 
4102         if (addr) {
4103                 if (!uv_to_sockaddr(addr, &ss, &slen))
4104                         return NULL;
4105 
4106                 if (bind(sockfd, (struct sockaddr *)&ss, slen) == -1)
4107                         err_return(errno, "bind()");
4108         }
4109         else {
4110 #if defined(__linux__)
4111                 int sval = 0;
4112                 slen = sizeof(sval);
4113 
4114                 if (getsockopt(sockfd, SOL_SOCKET, SO_DOMAIN, &sval, &slen) == -1)
4115                         err_return(errno, "getsockopt()");
4116 
4117                 switch (sval) {
4118                 case AF_INET6:
4119                         ss.ss_family = AF_INET6;
4120                         slen = sizeof(struct sockaddr_in6);
4121                         break;
4122 
4123                 case AF_INET:
4124                         ss.ss_family = AF_INET;
4125                         slen = sizeof(struct sockaddr_in);
4126                         break;
4127 
4128                 default:
4129                         err_return(EAFNOSUPPORT, "Unsupported socket address family");
4130                 }
4131 
4132                 if (bind(sockfd, (struct sockaddr *)&ss, slen) == -1)
4133                         err_return(errno, "bind()");
4134 #else
4135                 ss.ss_family = AF_INET6;
4136                 slen = sizeof(struct sockaddr_in6);
4137 
4138                 if (bind(sockfd, (struct sockaddr *)&ss, slen) == -1) {
4139                         if (errno != EAFNOSUPPORT)
4140                                 err_return(errno, "bind()");
4141 
4142                         ss.ss_family = AF_INET;
4143                         slen = sizeof(struct sockaddr_in);
4144 
4145                         if (bind(sockfd, (struct sockaddr *)&ss, slen) == -1)
4146                                 err_return(errno, "bind()");
4147                 }
4148 #endif
4149         }
4150 
4151         ok_return(ucv_boolean_new(true));
4152 }
4153 
4154 /**
4155  * Listen for connections on a socket.
4156  *
4157  * This function marks the socket as a passive socket, that is, as a socket that
4158  * will be used to accept incoming connection requests using `accept()`.
4159  *
4160  * The `backlog` parameter specifies the maximum length to which the queue of
4161  * pending connections may grow. If a connection request arrives when the queue
4162  * is full, the client connection might get refused.
4163  *
4164  * If `backlog` is not provided, it defaults to 128.
4165  *
4166  * Returns `true` if the socket is successfully marked as passive.
4167  * Returns `null` if an error occurred, e.g. when the requested port is in use.
4168  *
4169  * @function module:socket.socket#listen
4170  *
4171  * @param {number} [backlog=128]
4172  * The maximum length of the queue of pending connections.
4173  *
4174  * @returns {?boolean}
4175  *
4176  * @see {@link module:socket.socket#accept|accept()}
4177  *
4178  * @example
4179  * const sock = socket.create(…);
4180  * sock.bind(…);
4181  *
4182  * const success = sock.listen(10);
4183  * if (success)
4184  *     print(`Socket is listening for incoming connections!\n`);
4185  * else
4186  *     print(`Failed to listen on socket: ${sock.error()}\n`);
4187  */
4188 static uc_value_t *
4189 uc_socket_inst_listen(uc_vm_t *vm, size_t nargs)
4190 {
4191         uc_value_t *backlog;
4192         int ret, sockfd;
4193 
4194         args_get(vm, nargs, &sockfd,
4195                 "backlog", UC_INTEGER, true, &backlog);
4196 
4197         ret = listen(sockfd, backlog ? ucv_to_unsigned(backlog) : 128);
4198 
4199         if (ret == -1)
4200                 err_return(errno, "listen()");
4201 
4202         ok_return(ucv_boolean_new(true));
4203 }
4204 
4205 /**
4206  * Accept a connection on a socket.
4207  *
4208  * This function accepts a connection on the socket. It extracts the first
4209  * connection request on the queue of pending connections, creates a new
4210  * connected socket, and returns a new socket handle referring to that socket.
4211  * The newly created socket is not in listening state and has no backlog.
4212  *
4213  * When a optional `address` dictionary is provided, it is populated with the
4214  * remote address details of the peer socket.
4215  *
4216  * The optional `flags` parameter is a bitwise-or-ed number of flags to modify
4217  * the behavior of accepted peer socket. Possible values are:
4218  * - `SOCK_CLOEXEC`: Enable close-on-exec semantics for the new socket.
4219  * - `SOCK_NONBLOCK`: Enable nonblocking mode for the new socket.
4220  *
4221  * Returns a socket handle representing the newly created peer socket of the
4222  * accepted connection.
4223  *
4224  * Returns `null` if an error occurred.
4225  *
4226  * @function module:socket.socket#accept
4227  *
4228  * @param {object} [address]
4229  * An optional dictionary to receive the address details of the peer socket.
4230  * See {@link module:socket.socket.SocketAddress|SocketAddress} for details.
4231  *
4232  * @param {number} [flags]
4233  * Optional flags to modify the behavior of the peer socket.
4234  *
4235  * @returns {?module:socket.socket}
4236  *
4237  * @example
4238  * const sock = socket.create(…);
4239  * sock.bind(…);
4240  * sock.listen();
4241  *
4242  * const peerAddress = {};
4243  * const newSocket = sock.accept(peerAddress, socket.SOCK_CLOEXEC);
4244  * if (newSocket)
4245  *     print(`Accepted connection from: ${peerAddress}\n`);
4246  * else
4247  *     print(`Failed to accept connection: ${sock.error()}\n`);
4248  */
4249 static uc_value_t *
4250 uc_socket_inst_accept(uc_vm_t *vm, size_t nargs)
4251 {
4252         struct sockaddr_storage ss = { 0 };
4253         int peerfd, sockfd, sockflags;
4254         uc_value_t *addrobj, *flags;
4255         socklen_t slen;
4256 
4257         args_get(vm, nargs, &sockfd,
4258                 "address", UC_OBJECT, true, &addrobj,
4259                 "flags", UC_INTEGER, true, &flags);
4260 
4261         slen = sizeof(ss);
4262         sockflags = flags ? ucv_to_integer(flags) : 0;
4263 
4264 #ifdef __APPLE__
4265         peerfd = accept(sockfd, (struct sockaddr *)&ss, &slen);
4266 
4267         if (peerfd == -1)
4268                 err_return(errno, "accept()");
4269 
4270         if (sockflags & SOCK_CLOEXEC) {
4271                 if (fcntl(peerfd, F_SETFD, FD_CLOEXEC) == -1) {
4272                         close(peerfd);
4273                         err_return(errno, "fcntl(F_SETFD)");
4274                 }
4275         }
4276 
4277         if (sockflags & SOCK_NONBLOCK) {
4278                 sockflags = fcntl(peerfd, F_GETFL);
4279 
4280                 if (sockflags == -1) {
4281                         close(peerfd);
4282                         err_return(errno, "fcntl(F_GETFL)");
4283                 }
4284 
4285                 if (fcntl(peerfd, F_SETFL, sockflags | O_NONBLOCK) == -1) {
4286                         close(peerfd);
4287                         err_return(errno, "fcntl(F_SETFL)");
4288                 }
4289         }
4290 #else
4291         peerfd = accept4(sockfd, (struct sockaddr *)&ss, &slen, sockflags);
4292 
4293         if (peerfd == -1)
4294                 err_return(errno, "accept4()");
4295 #endif
4296 
4297         if (addrobj)
4298                 sockaddr_to_uv(&ss, addrobj);
4299 
4300         ok_return(ucv_socket_new(vm, peerfd));
4301 }
4302 
4303 /**
4304  * Shutdown part of a full-duplex connection.
4305  *
4306  * This function shuts down part of the full-duplex connection associated with
4307  * the socket handle. The `how` parameter specifies which half of the connection
4308  * to shut down. It can take one of the following constant values:
4309  *
4310  * - `SHUT_RD`: Disables further receive operations.
4311  * - `SHUT_WR`: Disables further send operations.
4312  * - `SHUT_RDWR`: Disables further send and receive operations.
4313  *
4314  * Returns `true` if the shutdown operation is successful.
4315  * Returns `null` if an error occurred.
4316  *
4317  * @function module:socket.socket#shutdown
4318  *
4319  * @param {number} how
4320  * Specifies which half of the connection to shut down.
4321  * It can be one of the following constant values: `SHUT_RD`, `SHUT_WR`,
4322  * or `SHUT_RDWR`.
4323  *
4324  * @returns {?boolean}
4325  *
4326  * @example
4327  * const sock = socket.create(…);
4328  * sock.connect(…);
4329  * // Perform data exchange…
4330  *
4331  * const success = sock.shutdown(socket.SHUT_WR);
4332  * if (success)
4333  *     print(`Send operations on socket shut down successfully.\n`);
4334  * else
4335  *     print(`Failed to shut down send operations: ${sock.error()}\n`);
4336  */
4337 static uc_value_t *
4338 uc_socket_inst_shutdown(uc_vm_t *vm, size_t nargs)
4339 {
4340         uc_value_t *how;
4341         int sockfd, ret;
4342 
4343         args_get(vm, nargs, &sockfd,
4344                 "how", UC_INTEGER, true, &how);
4345 
4346         ret = shutdown(sockfd, ucv_int64_get(how));
4347 
4348         if (ret == -1)
4349                 err_return(errno, "shutdown()");
4350 
4351         ok_return(ucv_boolean_new(true));
4352 }
4353 
4354 /**
4355  * Represents a credentials information object returned by
4356  * {@link module:socket.socket#peercred|`peercred()`}.
4357  *
4358  * @typedef {Object} module:socket.socket.PeerCredentials
4359  * @property {number} uid
4360  * The effective user ID the remote socket endpoint.
4361  *
4362  * @property {number} gid
4363  * The effective group ID the remote socket endpoint.
4364  *
4365  * @property {number} pid
4366  * The ID of the process the remote socket endpoint belongs to.
4367  */
4368 
4369 /**
4370  * Retrieves the peer credentials.
4371  *
4372  * This function retrieves the remote uid, gid and pid of a connected UNIX
4373  * domain socket.
4374  *
4375  * Returns the remote credentials if the operation is successful.
4376  * Returns `null` on error.
4377  *
4378  * @function module:socket.socket#peercred
4379  *
4380  * @returns {?module:socket.socket.PeerCredentials}
4381  *
4382  * @example
4383  * const sock = socket.create(socket.AF_UNIX, …);
4384  * sock.connect(…);
4385  *
4386  * const peerCredentials = sock.peercred();
4387  * if (peerCredentials)
4388  *     print(`Peer credentials: ${peerCredentials}\n`);
4389  * else
4390  *     print(`Failed to retrieve peer credentials: ${sock.error()}\n`);
4391  */
4392 static uc_value_t *
4393 uc_socket_inst_peercred(uc_vm_t *vm, size_t nargs)
4394 {
4395         uc_value_t *rv = NULL;
4396         socklen_t optlen;
4397         int ret, sockfd;
4398 
4399         args_get(vm, nargs, &sockfd);
4400 
4401 #if defined(__linux__)
4402         struct ucred cred;
4403 
4404         optlen = sizeof(cred);
4405         ret = getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &cred, &optlen);
4406 
4407         if (ret == -1)
4408                 err_return(errno, "getsockopt()");
4409 
4410         if (optlen != sizeof(cred))
4411                 err_return(EINVAL, "Invalid credentials received");
4412 
4413         rv = ucv_object_new(vm);
4414 
4415         ucv_object_add(rv, "uid", ucv_uint64_new(cred.uid));
4416         ucv_object_add(rv, "gid", ucv_uint64_new(cred.gid));
4417         ucv_object_add(rv, "pid", ucv_int64_new(cred.pid));
4418 #elif defined(__APPLE__)
4419         struct xucred cred;
4420         pid_t pid;
4421 
4422         optlen = sizeof(cred);
4423         ret = getsockopt(sockfd, SOL_LOCAL, LOCAL_PEERCRED, &cred, &optlen);
4424 
4425         if (ret == -1)
4426                 err_return(errno, "getsockopt(LOCAL_PEERCRED)");
4427 
4428         if (optlen != sizeof(cred) || cred.cr_version != XUCRED_VERSION)
4429                 err_return(EINVAL, "Invalid credentials received");
4430 
4431         rv = ucv_object_new(vm);
4432 
4433         ucv_object_add(rv, "uid", ucv_uint64_new(cred.cr_uid));
4434         ucv_object_add(rv, "gid", ucv_uint64_new(cred.cr_gid));
4435 
4436         optlen = sizeof(pid);
4437         ret = getsockopt(sockfd, SOL_LOCAL, LOCAL_PEERPID, &pid, &optlen);
4438 
4439         if (ret == -1) {
4440                 ucv_put(rv);
4441                 err_return(errno, "getsockopt(LOCAL_PEERPID)");
4442         }
4443 
4444         ucv_object_add(rv, "pid", ucv_int64_new(pid));
4445 #else
4446         err_return(ENOSYS, "Operation not supported on this system");
4447 #endif
4448 
4449         return rv;
4450 }
4451 
4452 /**
4453  * Retrieves the remote address.
4454  *
4455  * This function retrieves the remote address of a connected socket.
4456  *
4457  * Returns the remote address if the operation is successful.
4458  * Returns `null` on error.
4459  *
4460  * @function module:socket.socket#peername
4461  *
4462  * @returns {?module:socket.socket.SocketAddress}
4463  *
4464  * @see {@link module:socket.socket#sockname|sockname()}
4465  *
4466  * @example
4467  * const sock = socket.create(…);
4468  * sock.connect(…);
4469  *
4470  * const peerAddress = sock.peername();
4471  * if (peerAddress)
4472  *     print(`Connected to ${peerAddress}\n`);
4473  * else
4474  *     print(`Failed to retrieve peer address: ${sock.error()}\n`);
4475  */
4476 static uc_value_t *
4477 uc_socket_inst_peername(uc_vm_t *vm, size_t nargs)
4478 {
4479         struct sockaddr_storage ss = { 0 };
4480         uc_value_t *addr;
4481         socklen_t sslen;
4482         int sockfd, ret;
4483 
4484         args_get(vm, nargs, &sockfd);
4485 
4486         sslen = sizeof(ss);
4487         ret = getpeername(sockfd, (struct sockaddr *)&ss, &sslen);
4488 
4489         if (ret == -1)
4490                 err_return(errno, "getpeername()");
4491 
4492         addr = ucv_object_new(vm);
4493         sockaddr_to_uv(&ss, addr);
4494 
4495         ok_return(addr);
4496 }
4497 
4498 /**
4499  * Retrieves the local address.
4500  *
4501  * This function retrieves the local address of a bound or connected socket.
4502  *
4503  * Returns the local address if the operation is successful.
4504  * Returns `null` on error.
4505  *
4506  * @function module:socket.socket#sockname
4507  *
4508  * @returns {?module:socket.socket.SocketAddress}
4509  *
4510  * @see {@link module:socket.socket#peername|peername()}
4511  *
4512  * @example
4513  * const sock = socket.create(…);
4514  * sock.connect(…);
4515  *
4516  * const myAddress = sock.sockname();
4517  * if (myAddress)
4518  *     print(`My source IP address is ${myAddress}\n`);
4519  * else
4520  *     print(`Failed to retrieve peer address: ${sock.error()}\n`);
4521  */
4522 static uc_value_t *
4523 uc_socket_inst_sockname(uc_vm_t *vm, size_t nargs)
4524 {
4525         struct sockaddr_storage ss = { 0 };
4526         uc_value_t *addr;
4527         socklen_t sslen;
4528         int sockfd, ret;
4529 
4530         args_get(vm, nargs, &sockfd);
4531 
4532         sslen = sizeof(ss);
4533         ret = getsockname(sockfd, (struct sockaddr *)&ss, &sslen);
4534 
4535         if (ret == -1)
4536                 err_return(errno, "getsockname()");
4537 
4538         addr = ucv_object_new(vm);
4539         sockaddr_to_uv(&ss, addr);
4540 
4541         ok_return(addr);
4542 }
4543 
4544 /**
4545  * Closes the socket.
4546  *
4547  * This function closes the socket, releasing its resources and terminating its
4548  * associated connections.
4549  *
4550  * Returns `true` if the socket was successfully closed.
4551  * Returns `null` on error.
4552  *
4553  * @function module:socket.socket#close
4554  *
4555  * @returns {?boolean}
4556  *
4557  * @example
4558  * const sock = socket.create(…);
4559  * sock.connect(…);
4560  * // Perform operations with the socket…
4561  * sock.close();
4562  */
4563 static uc_value_t *
4564 uc_socket_inst_close(uc_vm_t *vm, size_t nargs)
4565 {
4566         int *sockfd = uc_fn_this("socket");
4567 
4568         if (!sockfd || *sockfd == -1)
4569                 err_return(EBADF, "Invalid socket context");
4570 
4571         if (!xclose(sockfd))
4572                 err_return(errno, "close()");
4573 
4574         ok_return(ucv_boolean_new(true));
4575 }
4576 
4577 static void
4578 close_socket(void *ud)
4579 {
4580         int fd = (intptr_t)ud;
4581 
4582         if (fd != -1)
4583                 close(fd);
4584 }
4585 
4586 static const uc_function_list_t socket_fns[] = {
4587         { "connect",    uc_socket_inst_connect },
4588         { "bind",               uc_socket_inst_bind },
4589         { "listen",             uc_socket_inst_listen },
4590         { "accept",             uc_socket_inst_accept },
4591         { "send",               uc_socket_inst_send },
4592         { "sendmsg",    uc_socket_inst_sendmsg },
4593         { "recv",           uc_socket_inst_recv },
4594         { "recvmsg",    uc_socket_inst_recvmsg },
4595         { "setopt",             uc_socket_inst_setopt },
4596         { "getopt",             uc_socket_inst_getopt },
4597         { "fileno",             uc_socket_inst_fileno },
4598         { "shutdown",   uc_socket_inst_shutdown },
4599         { "peercred",   uc_socket_inst_peercred },
4600         { "peername",   uc_socket_inst_peername },
4601         { "sockname",   uc_socket_inst_sockname },
4602         { "close",              uc_socket_inst_close },
4603         { "error",              uc_socket_error },
4604 };
4605 
4606 static const uc_function_list_t global_fns[] = {
4607         { "sockaddr",   uc_socket_sockaddr },
4608         { "create",             uc_socket_create },
4609         { "nameinfo",   uc_socket_nameinfo },
4610         { "addrinfo",   uc_socket_addrinfo },
4611         { "poll",               uc_socket_poll },
4612         { "connect",    uc_socket_connect },
4613         { "listen",             uc_socket_listen },
4614         { "error",              uc_socket_error },
4615 };
4616 
4617 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
4618 {
4619         uc_function_list_register(scope, global_fns);
4620 
4621 #define ADD_CONST(x) ucv_object_add(scope, #x, ucv_int64_new(x))
4622 
4623         /**
4624          * @typedef
4625          * @name Address Families
4626          * @description Constants representing address families and socket domains.
4627          * @property {number} AF_UNSPEC - Unspecified address family.
4628          * @property {number} AF_UNIX - UNIX domain sockets.
4629          * @property {number} AF_INET - IPv4 Internet protocols.
4630          * @property {number} AF_INET6 - IPv6 Internet protocols.
4631          * @property {number} AF_PACKET - Low-level packet interface.
4632          */
4633         ADD_CONST(AF_UNSPEC);
4634         ADD_CONST(AF_UNIX);
4635         ADD_CONST(AF_INET);
4636         ADD_CONST(AF_INET6);
4637 #if defined(__linux__)
4638         ADD_CONST(AF_PACKET);
4639 #endif
4640 
4641         /**
4642          * @typedef
4643          * @name Socket Types
4644          * @description
4645          * The `SOCK_*` type and flag constants are used by
4646          * {@link module:socket#create|create()} to specify the type of socket to
4647          * open. The {@link module:socket.socket#accept|accept()} function
4648          * recognizes the `SOCK_NONBLOCK` and `SOCK_CLOEXEC` flags and applies them
4649          * to accepted peer sockets.
4650          * @property {number} SOCK_STREAM - Provides sequenced, reliable, two-way, connection-based byte streams.
4651          * @property {number} SOCK_DGRAM - Supports datagrams (connectionless, unreliable messages of a fixed maximum length).
4652          * @property {number} SOCK_RAW - Provides raw network protocol access.
4653          * @property {number} SOCK_PACKET - Obsolete and should not be used.
4654          * @property {number} SOCK_NONBLOCK - Enables non-blocking operation.
4655          * @property {number} SOCK_CLOEXEC - Sets the close-on-exec flag on the new file descriptor.
4656          */
4657         ADD_CONST(SOCK_STREAM);
4658         ADD_CONST(SOCK_DGRAM);
4659         ADD_CONST(SOCK_RAW);
4660         ADD_CONST(SOCK_NONBLOCK);
4661         ADD_CONST(SOCK_CLOEXEC);
4662 #if defined(__linux__)
4663         ADD_CONST(SOCK_PACKET);
4664 #endif
4665 
4666         /**
4667          * @typedef
4668          * @name Message Flags
4669          * @description
4670          * The `MSG_*` flag constants are commonly used in conjunction with the
4671          * {@link module:socket.socket#send|send()} and
4672          * {@link module:socket.socket#recv|recv()} functions.
4673          * @property {number} MSG_CONFIRM - Confirm path validity.
4674          * @property {number} MSG_DONTROUTE - Send without using routing tables.
4675          * @property {number} MSG_DONTWAIT - Enables non-blocking operation.
4676          * @property {number} MSG_EOR - End of record.
4677          * @property {number} MSG_MORE - Sender will send more.
4678          * @property {number} MSG_NOSIGNAL - Do not generate SIGPIPE.
4679          * @property {number} MSG_OOB - Process out-of-band data.
4680          * @property {number} MSG_FASTOPEN - Send data in TCP SYN.
4681          * @property {number} MSG_CMSG_CLOEXEC - Sets the close-on-exec flag on the received file descriptor.
4682          * @property {number} MSG_ERRQUEUE - Receive errors from ICMP.
4683          * @property {number} MSG_PEEK - Peeks at incoming messages.
4684          * @property {number} MSG_TRUNC - Report if datagram truncation occurred.
4685          * @property {number} MSG_WAITALL - Wait for full message.
4686          */
4687         ADD_CONST(MSG_DONTROUTE);
4688         ADD_CONST(MSG_DONTWAIT);
4689         ADD_CONST(MSG_EOR);
4690         ADD_CONST(MSG_NOSIGNAL);
4691         ADD_CONST(MSG_OOB);
4692         ADD_CONST(MSG_PEEK);
4693         ADD_CONST(MSG_TRUNC);
4694         ADD_CONST(MSG_WAITALL);
4695 #if defined(__linux__)
4696         ADD_CONST(MSG_CONFIRM);
4697         ADD_CONST(MSG_MORE);
4698         ADD_CONST(MSG_FASTOPEN);
4699         ADD_CONST(MSG_CMSG_CLOEXEC);
4700         ADD_CONST(MSG_ERRQUEUE);
4701 #endif
4702 
4703         /**
4704          * @typedef
4705          * @name IP Protocol Constants
4706          * @description
4707          * The `IPPROTO_IP` constant specifies the IP protocol number and may be
4708          * passed as third argument to {@link module:socket#create|create()} as well
4709          * as *level* argument value to {@link module:socket.socket#getopt|getopt()}
4710          * and {@link module:socket.socket#setopt|setopt()}.
4711          *
4712          * The `IP_*` constants are option names recognized by
4713          * {@link module:socket.socket#getopt|getopt()}
4714          * and {@link module:socket.socket#setopt|setopt()}, in conjunction with
4715          * the `IPPROTO_IP` socket level.
4716          * @property {number} IPPROTO_IP - Dummy protocol for IP.
4717          * @property {number} IP_ADD_MEMBERSHIP - Add an IP group membership.
4718          * @property {number} IP_ADD_SOURCE_MEMBERSHIP - Add an IP group/source membership.
4719          * @property {number} IP_BIND_ADDRESS_NO_PORT - Bind to the device only.
4720          * @property {number} IP_BLOCK_SOURCE - Block IP group/source.
4721          * @property {number} IP_DROP_MEMBERSHIP - Drop an IP group membership.
4722          * @property {number} IP_DROP_SOURCE_MEMBERSHIP - Drop an IP group/source membership.
4723          * @property {number} IP_FREEBIND - Allow binding to an IP address not assigned to a network interface.
4724          * @property {number} IP_HDRINCL - Header is included with data.
4725          * @property {number} IP_MSFILTER - Filter IP multicast source memberships.
4726          * @property {number} IP_MTU - Path MTU discovery.
4727          * @property {number} IP_MTU_DISCOVER - Control Path MTU discovery.
4728          * @property {number} IP_MULTICAST_ALL - Receive all multicast packets.
4729          * @property {number} IP_MULTICAST_IF - Set outgoing interface for multicast packets.
4730          * @property {number} IP_MULTICAST_LOOP - Control multicast packet looping.
4731          * @property {number} IP_MULTICAST_TTL - Set time-to-live for outgoing multicast packets.
4732          * @property {number} IP_NODEFRAG - Don't fragment IP packets.
4733          * @property {number} IP_OPTIONS - Set/get IP options.
4734          * @property {number} IP_PASSSEC - Pass security information.
4735          * @property {number} IP_PKTINFO - Receive packet information.
4736          * @property {number} IP_RECVERR - Receive all ICMP errors.
4737          * @property {number} IP_RECVOPTS - Receive all IP options.
4738          * @property {number} IP_RECVORIGDSTADDR - Receive original destination address of the socket.
4739          * @property {number} IP_RECVTOS - Receive IP TOS.
4740          * @property {number} IP_RECVTTL - Receive IP TTL.
4741          * @property {number} IP_RETOPTS - Set/get IP options.
4742          * @property {number} IP_ROUTER_ALERT - Receive ICMP msgs generated by router.
4743          * @property {number} IP_TOS - IP type of service and precedence.
4744          * @property {number} IP_TRANSPARENT - Transparent proxy support.
4745          * @property {number} IP_TTL - IP time-to-live.
4746          * @property {number} IP_UNBLOCK_SOURCE - Unblock IP group/source.
4747          */
4748         ADD_CONST(IPPROTO_IP);
4749         ADD_CONST(IP_ADD_MEMBERSHIP);
4750         ADD_CONST(IP_ADD_SOURCE_MEMBERSHIP);
4751         ADD_CONST(IP_BLOCK_SOURCE);
4752         ADD_CONST(IP_DROP_MEMBERSHIP);
4753         ADD_CONST(IP_DROP_SOURCE_MEMBERSHIP);
4754         ADD_CONST(IP_HDRINCL);
4755         ADD_CONST(IP_MSFILTER);
4756         ADD_CONST(IP_MULTICAST_IF);
4757         ADD_CONST(IP_MULTICAST_LOOP);
4758         ADD_CONST(IP_MULTICAST_TTL);
4759         ADD_CONST(IP_OPTIONS);
4760         ADD_CONST(IP_PKTINFO);
4761         ADD_CONST(IP_RECVOPTS);
4762         ADD_CONST(IP_RECVTOS);
4763         ADD_CONST(IP_RECVTTL);
4764         ADD_CONST(IP_RETOPTS);
4765         ADD_CONST(IP_TOS);
4766         ADD_CONST(IP_TTL);
4767         ADD_CONST(IP_UNBLOCK_SOURCE);
4768 #if defined(__linux__)
4769         ADD_CONST(IP_BIND_ADDRESS_NO_PORT);
4770         ADD_CONST(IP_FREEBIND);
4771         ADD_CONST(IP_MTU);
4772         ADD_CONST(IP_MTU_DISCOVER);
4773         ADD_CONST(IP_MULTICAST_ALL);
4774         ADD_CONST(IP_NODEFRAG);
4775         ADD_CONST(IP_PASSSEC);
4776         ADD_CONST(IP_RECVERR);
4777         ADD_CONST(IP_RECVORIGDSTADDR);
4778         ADD_CONST(IP_ROUTER_ALERT);
4779         ADD_CONST(IP_TRANSPARENT);
4780 #endif
4781 
4782         /**
4783          * @typedef {Object} IPv6 Protocol Constants
4784          * @description
4785          * The `IPPROTO_IPV6` constant specifies the IPv6 protocol number and may be
4786          * passed as third argument to {@link module:socket#create|create()} as well
4787          * as *level* argument value to {@link module:socket.socket#getopt|getopt()}
4788          * and {@link module:socket.socket#setopt|setopt()}.
4789          *
4790          * The `IPV6_*` constants are option names recognized by
4791          * {@link module:socket.socket#getopt|getopt()}
4792          * and {@link module:socket.socket#setopt|setopt()}, in conjunction with
4793          * the `IPPROTO_IPV6` socket level.
4794          * @property {number} IPPROTO_IPV6 - The IPv6 protocol.
4795          * @property {number} IPV6_ADDRFORM - Turn an AF_INET6 socket into a socket of a different address family. Only AF_INET is supported.
4796          * @property {number} IPV6_ADDR_PREFERENCES - Specify preferences for address selection.
4797          * @property {number} IPV6_ADD_MEMBERSHIP - Add an IPv6 group membership.
4798          * @property {number} IPV6_AUTHHDR - Set delivery of the authentication header control message for incoming datagrams.
4799          * @property {number} IPV6_AUTOFLOWLABEL - Enable or disable automatic flow labels.
4800          * @property {number} IPV6_DONTFRAG - Control whether the socket allows IPv6 fragmentation.
4801          * @property {number} IPV6_DROP_MEMBERSHIP - Drop an IPv6 group membership.
4802          * @property {number} IPV6_DSTOPTS - Set delivery of the destination options control message for incoming datagrams.
4803          * @property {number} IPV6_FLOWINFO_SEND - Control whether flow information is sent.
4804          * @property {number} IPV6_FLOWINFO - Set delivery of the flow ID control message for incoming datagrams.
4805          * @property {number} IPV6_FLOWLABEL_MGR - Manage flow labels.
4806          * @property {number} IPV6_FREEBIND - Allow binding to an IP address not assigned to a network interface.
4807          * @property {number} IPV6_HOPLIMIT - Set delivery of the hop limit control message for incoming datagrams.
4808          * @property {number} IPV6_HOPOPTS - Set delivery of the hop options control message for incoming datagrams.
4809          * @property {number} IPV6_JOIN_ANYCAST - Join an anycast group.
4810          * @property {number} IPV6_LEAVE_ANYCAST - Leave an anycast group.
4811          * @property {number} IPV6_MINHOPCOUNT - Set the minimum hop count.
4812          * @property {number} IPV6_MTU - Retrieve or set the MTU to be used for the socket.
4813          * @property {number} IPV6_MTU_DISCOVER - Control path-MTU discovery on the socket.
4814          * @property {number} IPV6_MULTICAST_ALL - Control whether the socket receives all multicast packets.
4815          * @property {number} IPV6_MULTICAST_HOPS - Set the multicast hop limit for the socket.
4816          * @property {number} IPV6_MULTICAST_IF - Set the device for outgoing multicast packets on the socket.
4817          * @property {number} IPV6_MULTICAST_LOOP - Control whether the socket sees multicast packets that it has sent itself.
4818          * @property {number} IPV6_PKTINFO - Set delivery of the IPV6_PKTINFO control message on incoming datagrams.
4819          * @property {number} IPV6_RECVDSTOPTS - Control receiving of the destination options control message.
4820          * @property {number} IPV6_RECVERR - Control receiving of asynchronous error options.
4821          * @property {number} IPV6_RECVFRAGSIZE - Control receiving of fragment size.
4822          * @property {number} IPV6_RECVHOPLIMIT - Control receiving of hop limit.
4823          * @property {number} IPV6_RECVHOPOPTS - Control receiving of hop options.
4824          * @property {number} IPV6_RECVORIGDSTADDR - Control receiving of the original destination address.
4825          * @property {number} IPV6_RECVPATHMTU - Control receiving of path MTU.
4826          * @property {number} IPV6_RECVPKTINFO - Control receiving of packet information.
4827          * @property {number} IPV6_RECVRTHDR - Control receiving of routing header.
4828          * @property {number} IPV6_RECVTCLASS - Control receiving of traffic class.
4829          * @property {number} IPV6_ROUTER_ALERT_ISOLATE - Control isolation of router alert messages.
4830          * @property {number} IPV6_ROUTER_ALERT - Pass forwarded packets containing a router alert hop-by-hop option to this socket.
4831          * @property {number} IPV6_RTHDR - Set delivery of the routing header control message for incoming datagrams.
4832          * @property {number} IPV6_RTHDRDSTOPTS - Set delivery of the routing header destination options control message.
4833          * @property {number} IPV6_TCLASS - Set the traffic class.
4834          * @property {number} IPV6_TRANSPARENT - Enable transparent proxy support.
4835          * @property {number} IPV6_UNICAST_HOPS - Set the unicast hop limit for the socket.
4836          * @property {number} IPV6_UNICAST_IF - Set the interface for outgoing unicast packets.
4837          * @property {number} IPV6_V6ONLY - Restrict the socket to sending and receiving IPv6 packets only.
4838          */
4839         ADD_CONST(IPPROTO_IPV6);
4840         ADD_CONST(IPV6_FLOWINFO_SEND);
4841         ADD_CONST(IPV6_FLOWINFO);
4842         ADD_CONST(IPV6_FLOWLABEL_MGR);
4843         ADD_CONST(IPV6_MULTICAST_HOPS);
4844         ADD_CONST(IPV6_MULTICAST_IF);
4845         ADD_CONST(IPV6_MULTICAST_LOOP);
4846         ADD_CONST(IPV6_RECVTCLASS);
4847         ADD_CONST(IPV6_TCLASS);
4848         ADD_CONST(IPV6_UNICAST_HOPS);
4849         ADD_CONST(IPV6_V6ONLY);
4850 #if defined(__linux__)
4851         ADD_CONST(IPV6_ADD_MEMBERSHIP);
4852         ADD_CONST(IPV6_ADDR_PREFERENCES);
4853         ADD_CONST(IPV6_ADDRFORM);
4854         ADD_CONST(IPV6_AUTHHDR);
4855         ADD_CONST(IPV6_AUTOFLOWLABEL);
4856         ADD_CONST(IPV6_DONTFRAG);
4857         ADD_CONST(IPV6_DROP_MEMBERSHIP);
4858         ADD_CONST(IPV6_DSTOPTS);
4859         ADD_CONST(IPV6_FREEBIND);
4860         ADD_CONST(IPV6_HOPLIMIT);
4861         ADD_CONST(IPV6_HOPOPTS);
4862         ADD_CONST(IPV6_JOIN_ANYCAST);
4863         ADD_CONST(IPV6_LEAVE_ANYCAST);
4864         ADD_CONST(IPV6_MINHOPCOUNT);
4865         ADD_CONST(IPV6_MTU_DISCOVER);
4866         ADD_CONST(IPV6_MTU);
4867         ADD_CONST(IPV6_MULTICAST_ALL);
4868         ADD_CONST(IPV6_PKTINFO);
4869         ADD_CONST(IPV6_RECVDSTOPTS);
4870         ADD_CONST(IPV6_RECVERR);
4871         ADD_CONST(IPV6_RECVFRAGSIZE);
4872         ADD_CONST(IPV6_RECVHOPLIMIT);
4873         ADD_CONST(IPV6_RECVHOPOPTS);
4874         ADD_CONST(IPV6_RECVORIGDSTADDR);
4875         ADD_CONST(IPV6_RECVPATHMTU);
4876         ADD_CONST(IPV6_RECVPKTINFO);
4877         ADD_CONST(IPV6_RECVRTHDR);
4878         ADD_CONST(IPV6_ROUTER_ALERT_ISOLATE);
4879         ADD_CONST(IPV6_ROUTER_ALERT);
4880         ADD_CONST(IPV6_RTHDR);
4881         ADD_CONST(IPV6_RTHDRDSTOPTS);
4882         ADD_CONST(IPV6_TRANSPARENT);
4883         ADD_CONST(IPV6_UNICAST_IF);
4884 #endif
4885 
4886         /**
4887          * @typedef
4888          * @name Socket Option Constants
4889          * @description
4890          * The `SOL_SOCKET` constant is passed as *level* argument to the
4891          * {@link module:socket.socket#getopt|getopt()} and
4892          * {@link module:socket.socket#setopt|setopt()} functions in order to set
4893          * or retrieve generic socket option values.
4894          *
4895          * The `SO_*` constants are passed as *option* argument in conjunction with
4896          * the `SOL_SOCKET` level to specify the specific option to get or set on
4897          * the socket.
4898          * @property {number} SOL_SOCKET - Socket options at the socket API level.
4899          * @property {number} SO_ACCEPTCONN - Reports whether socket listening is enabled.
4900          * @property {number} SO_ATTACH_BPF - Attach BPF program to socket.
4901          * @property {number} SO_ATTACH_FILTER - Attach a socket filter.
4902          * @property {number} SO_ATTACH_REUSEPORT_CBPF - Attach BPF program for cgroup and skb program reuseport hook.
4903          * @property {number} SO_ATTACH_REUSEPORT_EBPF - Attach eBPF program for cgroup and skb program reuseport hook.
4904          * @property {number} SO_BINDTODEVICE - Bind socket to a specific interface.
4905          * @property {number} SO_BROADCAST - Allow transmission of broadcast messages.
4906          * @property {number} SO_BUSY_POLL - Enable busy polling.
4907          * @property {number} SO_DEBUG - Enable socket debugging.
4908          * @property {number} SO_DETACH_BPF - Detach BPF program from socket.
4909          * @property {number} SO_DETACH_FILTER - Detach a socket filter.
4910          * @property {number} SO_DOMAIN - Retrieves the domain of the socket.
4911          * @property {number} SO_DONTROUTE - Send packets directly without routing.
4912          * @property {number} SO_ERROR - Retrieves and clears the error status for the socket.
4913          * @property {number} SO_INCOMING_CPU - Retrieves the CPU number on which the last packet was received.
4914          * @property {number} SO_INCOMING_NAPI_ID - Retrieves the NAPI ID of the device.
4915          * @property {number} SO_KEEPALIVE - Enable keep-alive packets.
4916          * @property {number} SO_LINGER - Set linger on close.
4917          * @property {number} SO_LOCK_FILTER - Set or get the socket filter lock state.
4918          * @property {number} SO_MARK - Set the mark for packets sent through the socket.
4919          * @property {number} SO_OOBINLINE - Enables out-of-band data to be received in the normal data stream.
4920          * @property {number} SO_PASSCRED - Enable the receiving of SCM_CREDENTIALS control messages.
4921          * @property {number} SO_PASSSEC - Enable the receiving of security context.
4922          * @property {number} SO_PEEK_OFF - Returns the number of bytes in the receive buffer without removing them.
4923          * @property {number} SO_PEERCRED - Retrieves the credentials of the foreign peer.
4924          * @property {number} SO_PEERSEC - Retrieves the security context of the foreign peer.
4925          * @property {number} SO_PRIORITY - Set the protocol-defined priority for all packets.
4926          * @property {number} SO_PROTOCOL - Retrieves the protocol number.
4927          * @property {number} SO_RCVBUF - Set the receive buffer size.
4928          * @property {number} SO_RCVBUFFORCE - Set the receive buffer size forcefully.
4929          * @property {number} SO_RCVLOWAT - Set the minimum number of bytes to process for input operations.
4930          * @property {number} SO_RCVTIMEO - Set the timeout for receiving data.
4931          * @property {number} SO_REUSEADDR - Allow the socket to be bound to an address that is already in use.
4932          * @property {number} SO_REUSEPORT - Enable duplicate address and port bindings.
4933          * @property {number} SO_RXQ_OVFL - Reports if the receive queue has overflown.
4934          * @property {number} SO_SNDBUF - Set the send buffer size.
4935          * @property {number} SO_SNDBUFFORCE - Set the send buffer size forcefully.
4936          * @property {number} SO_SNDLOWAT - Set the minimum number of bytes to process for output operations.
4937          * @property {number} SO_SNDTIMEO - Set the timeout for sending data.
4938          * @property {number} SO_TIMESTAMP - Enable receiving of timestamps.
4939          * @property {number} SO_TIMESTAMPNS - Enable receiving of nanosecond timestamps.
4940          * @property {number} SO_TYPE - Retrieves the type of the socket (e.g., SOCK_STREAM).
4941          */
4942         ADD_CONST(SOL_SOCKET);
4943         ADD_CONST(SO_ACCEPTCONN);
4944         ADD_CONST(SO_BROADCAST);
4945         ADD_CONST(SO_DEBUG);
4946         ADD_CONST(SO_DONTROUTE);
4947         ADD_CONST(SO_ERROR);
4948         ADD_CONST(SO_KEEPALIVE);
4949         ADD_CONST(SO_LINGER);
4950         ADD_CONST(SO_OOBINLINE);
4951         ADD_CONST(SO_RCVBUF);
4952         ADD_CONST(SO_RCVLOWAT);
4953         ADD_CONST(SO_RCVTIMEO);
4954         ADD_CONST(SO_REUSEADDR);
4955         ADD_CONST(SO_REUSEPORT);
4956         ADD_CONST(SO_SNDBUF);
4957         ADD_CONST(SO_SNDLOWAT);
4958         ADD_CONST(SO_SNDTIMEO);
4959         ADD_CONST(SO_TIMESTAMP);
4960         ADD_CONST(SO_TYPE);
4961 #if defined(__linux__)
4962         ADD_CONST(SO_ATTACH_BPF);
4963         ADD_CONST(SO_ATTACH_FILTER);
4964         ADD_CONST(SO_ATTACH_REUSEPORT_CBPF);
4965         ADD_CONST(SO_ATTACH_REUSEPORT_EBPF);
4966         ADD_CONST(SO_BINDTODEVICE);
4967         ADD_CONST(SO_BUSY_POLL);
4968         ADD_CONST(SO_DETACH_BPF);
4969         ADD_CONST(SO_DETACH_FILTER);
4970         ADD_CONST(SO_DOMAIN);
4971         ADD_CONST(SO_INCOMING_CPU);
4972         ADD_CONST(SO_INCOMING_NAPI_ID);
4973         ADD_CONST(SO_LOCK_FILTER);
4974         ADD_CONST(SO_MARK);
4975         ADD_CONST(SO_PASSCRED);
4976         ADD_CONST(SO_PASSSEC);
4977         ADD_CONST(SO_PEEK_OFF);
4978         ADD_CONST(SO_PEERCRED);
4979         ADD_CONST(SO_PEERSEC);
4980         ADD_CONST(SO_PRIORITY);
4981         ADD_CONST(SO_PROTOCOL);
4982         ADD_CONST(SO_RCVBUFFORCE);
4983         ADD_CONST(SO_RXQ_OVFL);
4984         ADD_CONST(SO_SNDBUFFORCE);
4985         ADD_CONST(SO_TIMESTAMPNS);
4986 
4987         ADD_CONST(SCM_CREDENTIALS);
4988         ADD_CONST(SCM_RIGHTS);
4989 #endif
4990 
4991         /**
4992          * @typedef
4993          * @name TCP Protocol Constants
4994          * @description
4995          * The `IPPROTO_TCP` constant specifies the TCP protocol number and may be
4996          * passed as third argument to {@link module:socket#create|create()} as well
4997          * as *level* argument value to {@link module:socket.socket#getopt|getopt()}
4998          * and {@link module:socket.socket#setopt|setopt()}.
4999          *
5000          * The `TCP_*` constants are *option* argument values recognized by
5001          * {@link module:socket.socket#getopt|getopt()}
5002          * and {@link module:socket.socket#setopt|setopt()}, in conjunction with
5003          * the `IPPROTO_TCP` socket level.
5004          * @property {number} IPPROTO_TCP - TCP protocol.
5005          * @property {number} TCP_CONGESTION - Set the congestion control algorithm.
5006          * @property {number} TCP_CORK - Delay packet transmission until full-sized packets are available.
5007          * @property {number} TCP_DEFER_ACCEPT - Delay accepting incoming connections until data arrives.
5008          * @property {number} TCP_FASTOPEN - Enable TCP Fast Open.
5009          * @property {number} TCP_FASTOPEN_CONNECT - Perform TFO connect.
5010          * @property {number} TCP_INFO - Retrieve TCP statistics.
5011          * @property {number} TCP_KEEPCNT - Number of keepalive probes.
5012          * @property {number} TCP_KEEPIDLE - Time before keepalive probes begin.
5013          * @property {number} TCP_KEEPINTVL - Interval between keepalive probes.
5014          * @property {number} TCP_LINGER2 - Lifetime of orphaned FIN_WAIT2 state sockets.
5015          * @property {number} TCP_MAXSEG - Maximum segment size.
5016          * @property {number} TCP_NODELAY - Disable Nagle's algorithm.
5017          * @property {number} TCP_QUICKACK - Enable quick ACKs.
5018          * @property {number} TCP_SYNCNT - Number of SYN retransmits.
5019          * @property {number} TCP_USER_TIMEOUT - Set the user timeout.
5020          * @property {number} TCP_WINDOW_CLAMP - Set the maximum window.
5021          */
5022         ADD_CONST(IPPROTO_TCP);
5023         ADD_CONST(TCP_FASTOPEN);
5024         ADD_CONST(TCP_KEEPCNT);
5025         ADD_CONST(TCP_KEEPINTVL);
5026         ADD_CONST(TCP_MAXSEG);
5027         ADD_CONST(TCP_NODELAY);
5028 #if defined(__linux__)
5029         ADD_CONST(TCP_CONGESTION);
5030         ADD_CONST(TCP_CORK);
5031         ADD_CONST(TCP_DEFER_ACCEPT);
5032         ADD_CONST(TCP_FASTOPEN_CONNECT);
5033         ADD_CONST(TCP_INFO);
5034         ADD_CONST(TCP_KEEPIDLE);
5035         ADD_CONST(TCP_LINGER2);
5036         ADD_CONST(TCP_QUICKACK);
5037         ADD_CONST(TCP_SYNCNT);
5038         ADD_CONST(TCP_USER_TIMEOUT);
5039         ADD_CONST(TCP_WINDOW_CLAMP);
5040 #endif
5041 
5042         /**
5043          * @typedef
5044          * @name Packet Socket Constants
5045          * @description
5046          * The `SOL_PACKET` constant specifies the packet socket level and may be
5047          * passed as *level* argument value to
5048          * {@link module:socket.socket#getopt|getopt()} and
5049          * {@link module:socket.socket#setopt|setopt()}.
5050          *
5051          * Most `PACKET_*` constants are *option* argument values recognized by
5052          * {@link module:socket.socket#getopt|getopt()}
5053          * and {@link module:socket.socket#setopt|setopt()}, in conjunction with
5054          * the `SOL_PACKET` socket level.
5055          *
5056          * The constants `PACKET_MR_PROMISC`, `PACKET_MR_MULTICAST` and
5057          * `PACKET_MR_ALLMULTI` are used in conjunction with the
5058          * `PACKET_ADD_MEMBERSHIP` and `PACKET_DROP_MEMBERSHIP` options to specify
5059          * the packet socket receive mode.
5060          *
5061          * The constants `PACKET_HOST`, `PACKET_BROADCAST`, `PACKET_MULTICAST`,
5062          * `PACKET_OTHERHOST` and `PACKET_OUTGOING` may be used as *packet_type*
5063          * value in {@link module:socket.socket.SocketAddress|socket address}
5064          * structures.
5065          * @property {number} SOL_PACKET - Socket options at the packet API level.
5066          * @property {number} PACKET_ADD_MEMBERSHIP - Add a multicast group membership.
5067          * @property {number} PACKET_DROP_MEMBERSHIP - Drop a multicast group membership.
5068          * @property {number} PACKET_AUXDATA - Receive auxiliary data (packet info).
5069          * @property {number} PACKET_FANOUT - Configure packet fanout.
5070          * @property {number} PACKET_LOSS - Retrieve the current packet loss statistics.
5071          * @property {number} PACKET_RESERVE - Reserve space for packet headers.
5072          * @property {number} PACKET_RX_RING - Configure a receive ring buffer.
5073          * @property {number} PACKET_STATISTICS - Retrieve packet statistics.
5074          * @property {number} PACKET_TIMESTAMP - Retrieve packet timestamps.
5075          * @property {number} PACKET_TX_RING - Configure a transmit ring buffer.
5076          * @property {number} PACKET_VERSION - Set the packet protocol version.
5077          * @property {number} PACKET_QDISC_BYPASS - Bypass queuing discipline for outgoing packets.
5078          *
5079          * @property {number} PACKET_MR_PROMISC - Enable promiscuous mode.
5080          * @property {number} PACKET_MR_MULTICAST - Receive multicast packets.
5081          * @property {number} PACKET_MR_ALLMULTI - Receive all multicast packets.
5082          *
5083          * @property {number} PACKET_HOST - Receive packets destined for this host.
5084          * @property {number} PACKET_BROADCAST - Receive broadcast packets.
5085          * @property {number} PACKET_MULTICAST - Receive multicast packets.
5086          * @property {number} PACKET_OTHERHOST - Receive packets destined for other hosts.
5087          * @property {number} PACKET_OUTGOING - Transmit packets.
5088          */
5089 #if defined(__linux__)
5090         ADD_CONST(SOL_PACKET);
5091         ADD_CONST(PACKET_ADD_MEMBERSHIP);
5092         ADD_CONST(PACKET_DROP_MEMBERSHIP);
5093         ADD_CONST(PACKET_AUXDATA);
5094         ADD_CONST(PACKET_FANOUT);
5095         ADD_CONST(PACKET_LOSS);
5096         ADD_CONST(PACKET_RESERVE);
5097         ADD_CONST(PACKET_RX_RING);
5098         ADD_CONST(PACKET_STATISTICS);
5099         ADD_CONST(PACKET_TIMESTAMP);
5100         ADD_CONST(PACKET_TX_RING);
5101         ADD_CONST(PACKET_VERSION);
5102         ADD_CONST(PACKET_QDISC_BYPASS);
5103 
5104         ADD_CONST(PACKET_MR_PROMISC);
5105         ADD_CONST(PACKET_MR_MULTICAST);
5106         ADD_CONST(PACKET_MR_ALLMULTI);
5107 
5108         ADD_CONST(PACKET_HOST);
5109         ADD_CONST(PACKET_BROADCAST);
5110         ADD_CONST(PACKET_MULTICAST);
5111         ADD_CONST(PACKET_OTHERHOST);
5112         ADD_CONST(PACKET_OUTGOING);
5113 #endif
5114 
5115         /**
5116          * @typedef
5117          * @name UDP Protocol Constants
5118          * @description
5119          * The `IPPROTO_UDP` constant specifies the UDP protocol number and may be
5120          * passed as third argument to {@link module:socket#create|create()} as well
5121          * as *level* argument value to {@link module:socket.socket#getopt|getopt()}
5122          * and {@link module:socket.socket#setopt|setopt()}.
5123          *
5124          * The `UDP_*` constants are *option* argument values recognized by
5125          * {@link module:socket.socket#getopt|getopt()}
5126          * and {@link module:socket.socket#setopt|setopt()}, in conjunction with
5127          * the `IPPROTO_UDP` socket level.
5128          * @property {number} IPPROTO_UDP - UDP protocol.
5129          * @property {number} UDP_CORK - Cork data until flush.
5130          */
5131         ADD_CONST(IPPROTO_UDP);
5132 #if defined(__linux__)
5133         ADD_CONST(UDP_CORK);
5134 #endif
5135 
5136         /**
5137          * @typedef
5138          * @name Shutdown Constants
5139          * @description
5140          * The `SHUT_*` constants are passed as argument to the
5141          * {@link module:socket.socket#shutdown|shutdown()} function to specify
5142          * which direction of a full duplex connection to shut down.
5143          * @property {number} SHUT_RD - Disallow further receptions.
5144          * @property {number} SHUT_WR - Disallow further transmissions.
5145          * @property {number} SHUT_RDWR - Disallow further receptions and transmissions.
5146          */
5147         ADD_CONST(SHUT_RD);
5148         ADD_CONST(SHUT_WR);
5149         ADD_CONST(SHUT_RDWR);
5150 
5151         /**
5152          * @typedef
5153          * @name Address Info Flags
5154          * @description
5155          * The `AI_*` flags may be passed as bitwise OR-ed number in the *flags*
5156          * property of the *hints* dictionary argument of
5157          * {@link module:socket#addrinfo|addrinfo()}.
5158          * @property {number} AI_ADDRCONFIG - Address configuration flag.
5159          * @property {number} AI_ALL - Return IPv4 and IPv6 socket addresses.
5160          * @property {number} AI_CANONIDN - Canonicalize using the IDNA standard.
5161          * @property {number} AI_CANONNAME - Fill in the canonical name field.
5162          * @property {number} AI_IDN - Enable IDN encoding.
5163          * @property {number} AI_NUMERICHOST - Prevent hostname resolution.
5164          * @property {number} AI_NUMERICSERV - Prevent service name resolution.
5165          * @property {number} AI_PASSIVE - Use passive socket.
5166          * @property {number} AI_V4MAPPED - Map IPv6 addresses to IPv4-mapped format.
5167          */
5168         ADD_CONST(AI_ADDRCONFIG);
5169         ADD_CONST(AI_ALL);
5170         ADD_CONST(AI_CANONIDN);
5171         ADD_CONST(AI_CANONNAME);
5172         ADD_CONST(AI_IDN);
5173         ADD_CONST(AI_NUMERICHOST);
5174         ADD_CONST(AI_NUMERICSERV);
5175         ADD_CONST(AI_PASSIVE);
5176         ADD_CONST(AI_V4MAPPED);
5177 
5178         /**
5179          * @typedef
5180          * @name Name Info Constants
5181          * @description
5182          * The `NI_*` flags may be passed as bitwise OR-ed number via the *flags*
5183          * argument of {@link module:socket#nameinfo|nameinfo()}.
5184          * @property {number} NI_DGRAM - Datagram socket type.
5185          * @property {number} NI_IDN - Enable IDN encoding.
5186          * @property {number} NI_NAMEREQD - Hostname resolution required.
5187          * @property {number} NI_NOFQDN - Do not force fully qualified domain name.
5188          * @property {number} NI_NUMERICHOST - Return numeric form of the hostname.
5189          * @property {number} NI_NUMERICSERV - Return numeric form of the service name.
5190          */
5191         ADD_CONST(NI_DGRAM);
5192         ADD_CONST(NI_IDN);
5193         ADD_CONST(NI_MAXHOST);
5194         ADD_CONST(NI_MAXSERV);
5195         ADD_CONST(NI_NAMEREQD);
5196         ADD_CONST(NI_NOFQDN);
5197         ADD_CONST(NI_NUMERICHOST);
5198         ADD_CONST(NI_NUMERICSERV);
5199 
5200         /**
5201          * @typedef
5202          * @name Poll Event Constants
5203          * @description
5204          * The following constants represent event types for polling operations and
5205          * are set or returned as part of a
5206          * {@link module:socket.PollSpec|PollSpec} tuple by the
5207          * {@link module:socket#poll|poll()} function. When passed via an argument
5208          * PollSpec to `poll()`, they specify the I/O events to watch for on the
5209          * corresponding handle. When appearing in a PollSpec returned by `poll()`,
5210          * they specify the I/O events that occurred on a watched handle.
5211          * @property {number} POLLIN - Data available to read.
5212          * @property {number} POLLPRI - Priority data available to read.
5213          * @property {number} POLLOUT - Writable data available.
5214          * @property {number} POLLERR - Error condition.
5215          * @property {number} POLLHUP - Hang up.
5216          * @property {number} POLLNVAL - Invalid request.
5217          * @property {number} POLLRDHUP - Peer closed or shutdown writing.
5218          */
5219         ADD_CONST(POLLIN);
5220         ADD_CONST(POLLPRI);
5221         ADD_CONST(POLLOUT);
5222         ADD_CONST(POLLERR);
5223         ADD_CONST(POLLHUP);
5224         ADD_CONST(POLLNVAL);
5225 #if defined(__linux__)
5226         ADD_CONST(POLLRDHUP);
5227 #endif
5228 
5229         uc_type_declare(vm, "socket", socket_fns, close_socket);
5230 }
5231 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt