• 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 <sys/types.h>
 59 #include <sys/socket.h>
 60 #include <sys/un.h>
 61 #include <netinet/in.h>
 62 #include <netinet/tcp.h>
 63 #include <netinet/udp.h>
 64 #include <arpa/inet.h>
 65 #include <unistd.h>
 66 #include <fcntl.h>
 67 #include <net/if.h>
 68 #include <netdb.h>
 69 #include <poll.h>
 70 #include <limits.h>
 71 
 72 #include "ucode/module.h"
 73 #include "ucode/platform.h"
 74 
 75 #if defined(__APPLE__)
 76 # include <sys/ucred.h>
 77 
 78 # define SOCK_NONBLOCK (1 << 16)
 79 # define SOCK_CLOEXEC  (1 << 17)
 80 #endif
 81 
 82 #ifndef NI_IDN
 83 # define NI_IDN 0
 84 #endif
 85 
 86 #ifndef AI_IDN
 87 # define AI_IDN 0
 88 #endif
 89 
 90 #ifndef AI_CANONIDN
 91 # define AI_CANONIDN 0
 92 #endif
 93 
 94 #define ok_return(expr) do { set_error(0, NULL); return (expr); } while(0)
 95 #define err_return(err, ...) do { set_error(err, __VA_ARGS__); return NULL; } while(0)
 96 
 97 static struct {
 98         int code;
 99         char *msg;
100 } last_error;
101 
102 __attribute__((format(printf, 2, 3))) static void
103 set_error(int errcode, const char *fmt, ...)
104 {
105         va_list ap;
106 
107         free(last_error.msg);
108 
109         last_error.code = errcode;
110         last_error.msg = NULL;
111 
112         if (fmt) {
113                 va_start(ap, fmt);
114                 xvasprintf(&last_error.msg, fmt, ap);
115                 va_end(ap);
116         }
117 }
118 
119 static char *
120 arg_type_(uc_type_t type)
121 {
122         switch (type) {
123         case UC_INTEGER:  return "an integer value";
124         case UC_BOOLEAN:  return "a boolean value";
125         case UC_STRING:   return "a string value";
126         case UC_DOUBLE:   return "a double value";
127         case UC_ARRAY:    return "an array";
128         case UC_OBJECT:   return "an object";
129         case UC_REGEXP:   return "a regular expression";
130         case UC_CLOSURE:  return "a function";
131         case UC_RESOURCE: return "a resource value";
132         default:          return "the expected type";
133         }
134 }
135 
136 static bool
137 args_get_(uc_vm_t *vm, size_t nargs, int *fdptr, ...)
138 {
139         const char *name, *rtype = NULL;
140         uc_value_t **ptr, *arg;
141         uc_type_t type, t;
142         size_t index = 0;
143         int *sockfd;
144         va_list ap;
145         bool opt;
146 
147         if (fdptr) {
148                 sockfd = uc_fn_this("socket");
149 
150                 if (!sockfd || *sockfd == -1)
151                         err_return(EBADF, "Invalid socket context");
152 
153                 *fdptr = *sockfd;
154         }
155 
156         va_start(ap, fdptr);
157 
158         while (true) {
159                 name = va_arg(ap, const char *);
160 
161                 if (!name)
162                         break;
163 
164                 arg = uc_fn_arg(index++);
165 
166                 type = va_arg(ap, uc_type_t);
167                 opt = va_arg(ap, int);
168                 ptr = va_arg(ap, uc_value_t **);
169 
170                 if (type == UC_RESOURCE) {
171                         rtype = name;
172                         name = strrchr(rtype, '.');
173                         name = name ? name + 1 : rtype;
174 
175                         if (arg && !ucv_resource_dataptr(arg, rtype))
176                                 err_return(EINVAL,
177                                         "Argument %s is not a %s resource", name, rtype);
178                 }
179 
180                 if (!opt && !arg)
181                         err_return(EINVAL,
182                                 "Argument %s is required", name);
183 
184                 t = ucv_type(arg);
185 
186                 if (t == UC_CFUNCTION)
187                         t = UC_CLOSURE;
188 
189                 if (arg && type != UC_NULL && t != type)
190                         err_return(EINVAL,
191                                 "Argument %s is not %s", name, arg_type_(type));
192 
193                 *ptr = arg;
194         }
195 
196         va_end(ap);
197 
198         ok_return(true);
199 }
200 
201 #define args_get(vm, nargs, fdptr, ...) do { \
202         if (!args_get_(vm, nargs, fdptr, ##__VA_ARGS__, NULL)) \
203                 return NULL; \
204 } while(0)
205 
206 static void
207 strbuf_free(uc_stringbuf_t *sb)
208 {
209         printbuf_free(sb);
210 }
211 
212 static bool
213 strbuf_grow(uc_stringbuf_t *sb, size_t size)
214 {
215         if (size > 0) {
216                 if (printbuf_memset(sb, sizeof(uc_string_t) + size - 1, '\0', 1))
217                         err_return(ENOMEM, "Out of memory");
218         }
219 
220         return true;
221 }
222 
223 static char *
224 strbuf_data(uc_stringbuf_t *sb)
225 {
226         return sb->buf + sizeof(uc_string_t);
227 }
228 
229 static size_t
230 strbuf_size(uc_stringbuf_t *sb)
231 {
232         return (size_t)sb->bpos - sizeof(uc_string_t);
233 }
234 
235 static uc_value_t *
236 strbuf_finish(uc_stringbuf_t **sb, size_t final_size)
237 {
238         uc_string_t *us = (uc_string_t *)(*sb)->buf;
239         size_t buffer_size = strbuf_size(*sb);
240 
241         if (final_size > buffer_size)
242                 final_size = buffer_size;
243 
244         free(*sb);
245         *sb = NULL;
246 
247         us = xrealloc(us, sizeof(uc_string_t) + final_size + 1);
248         us->length = final_size;
249         us->str[us->length] = 0;
250 
251         return &us->header;
252 }
253 
254 static uc_stringbuf_t *
255 strbuf_alloc(size_t size)
256 {
257         uc_stringbuf_t *sb = ucv_stringbuf_new();
258 
259         if (!strbuf_grow(sb, size)) {
260                 printbuf_free(sb);
261 
262                 return NULL;
263         }
264 
265         return sb;
266 }
267 
268 static bool
269 sockaddr_to_uv(struct sockaddr_storage *ss, uc_value_t *addrobj)
270 {
271         char *ifname, addrstr[INET6_ADDRSTRLEN];
272         struct sockaddr_in6 *s6;
273         struct sockaddr_in *s4;
274         struct sockaddr_un *su;
275 
276         ucv_object_add(addrobj, "family", ucv_uint64_new(ss->ss_family));
277 
278         switch (ss->ss_family) {
279         case AF_INET6:
280                 s6 = (struct sockaddr_in6 *)ss;
281 
282                 inet_ntop(AF_INET6, &s6->sin6_addr, addrstr, sizeof(addrstr));
283                 ucv_object_add(addrobj, "address",
284                         ucv_string_new(addrstr));
285 
286                 ucv_object_add(addrobj, "port",
287                         ucv_uint64_new(ntohs(s6->sin6_port)));
288 
289                 ucv_object_add(addrobj, "flowinfo",
290                         ucv_uint64_new(ntohl(s6->sin6_flowinfo)));
291 
292                 if (s6->sin6_scope_id) {
293                         ifname = if_indextoname(s6->sin6_scope_id, addrstr);
294 
295                         if (ifname)
296                                 ucv_object_add(addrobj, "interface",
297                                         ucv_string_new(ifname));
298                         else
299                                 ucv_object_add(addrobj, "interface",
300                                         ucv_uint64_new(s6->sin6_scope_id));
301                 }
302 
303                 return true;
304 
305         case AF_INET:
306                 s4 = (struct sockaddr_in *)ss;
307 
308                 inet_ntop(AF_INET, &s4->sin_addr, addrstr, sizeof(addrstr));
309                 ucv_object_add(addrobj, "address",
310                         ucv_string_new(addrstr));
311 
312                 ucv_object_add(addrobj, "port",
313                         ucv_uint64_new(ntohs(s4->sin_port)));
314 
315                 return true;
316 
317         case AF_UNIX:
318                 su = (struct sockaddr_un *)ss;
319 
320                 ucv_object_add(addrobj, "path",
321                         ucv_string_new(su->sun_path));
322 
323                 return true;
324         }
325 
326         return false;
327 }
328 
329 static bool
330 parse_addr(char *addr, struct sockaddr_storage *ss)
331 {
332         bool v6 = (ss->ss_family == 0 || ss->ss_family == AF_INET6);
333         bool v4 = (ss->ss_family == 0 || ss->ss_family == AF_INET);
334         struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)ss;
335         struct sockaddr_in *s4 = (struct sockaddr_in *)ss;
336         unsigned long n;
337         char *scope, *e;
338 
339         if (v6 && (scope = strchr(addr, '%')) != NULL) {
340                 *scope++ = 0;
341                 n = strtoul(scope, &e, 10);
342 
343                 if (e == scope || *e != 0) {
344                         n = if_nametoindex(scope);
345 
346                         if (n == 0)
347                                 err_return(errno, "Unable to resolve interface %s", scope);
348                 }
349 
350                 if (inet_pton(AF_INET6, addr, &s6->sin6_addr) != 1)
351                         err_return(errno, "Invalid IPv6 address");
352 
353                 s6->sin6_family = AF_INET6;
354                 s6->sin6_scope_id = n;
355 
356                 return true;
357         }
358         else if (v6 && inet_pton(AF_INET6, addr, &s6->sin6_addr) == 1) {
359                 s6->sin6_family = AF_INET6;
360 
361                 return true;
362         }
363         else if (v4 && inet_pton(AF_INET, addr, &s4->sin_addr) == 1) {
364                 s4->sin_family = AF_INET;
365 
366                 return true;
367         }
368 
369         err_return(EINVAL, "Unable to parse IP address");
370 }
371 
372 static bool
373 uv_to_sockaddr(uc_value_t *addr, struct sockaddr_storage *ss, socklen_t *slen)
374 {
375         char *s, *p, addrstr[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255%2147483648")];
376         struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)ss;
377         struct sockaddr_in *s4 = (struct sockaddr_in *)ss;
378         struct sockaddr_un *su = (struct sockaddr_un *)ss;
379         uc_value_t *item;
380         unsigned long n;
381         size_t len;
382 
383         memset(ss, 0, sizeof(*ss));
384 
385         if (ucv_type(addr) == UC_STRING) {
386                 s = ucv_string_get(addr);
387                 len = ucv_string_length(addr);
388 
389                 if (memchr(s, '/', len) != NULL) {
390                         *slen = ucv_string_length(addr);
391 
392                         if (*slen >= sizeof(su->sun_path))
393                                 *slen = sizeof(su->sun_path) - 1;
394 
395                         memcpy(su->sun_path, s, *slen);
396                         su->sun_path[(*slen)++] = 0;
397                         su->sun_family = AF_UNIX;
398 
399                         ok_return(true);
400                 }
401 
402                 if (len == 0)
403                         err_return(EINVAL, "Invalid IP address");
404 
405                 if (*s == '[') {
406                         p = memchr(++s, ']', --len);
407 
408                         if (!p || (size_t)(p - s) >= sizeof(addrstr))
409                                 err_return(EINVAL, "Invalid IPv6 address");
410 
411                         memcpy(addrstr, s, p - s);
412                         addrstr[(p - s) + 1] = 0;
413 
414                         ss->ss_family = AF_INET6;
415                         len -= ((p - s) + 1);
416                         s = p + 1;
417                 }
418                 else if ((p = memchr(s, ':', len)) != NULL &&
419                          memchr(p + 1, ':', len - ((p - s) + 1)) == NULL) {
420                         if ((size_t)(p - s) >= sizeof(addrstr))
421                                 err_return(EINVAL, "Invalid IP address");
422 
423                         memcpy(addrstr, s, p - s);
424                         addrstr[p - s + 1] = 0;
425 
426                         ss->ss_family = AF_INET;
427                         len -= (p - s);
428                         s = p;
429                 }
430                 else {
431                         if (len >= sizeof(addrstr))
432                                 err_return(EINVAL, "Invalid IP address");
433 
434                         memcpy(addrstr, s, len);
435                         addrstr[len] = 0;
436 
437                         ss->ss_family = 0;
438                         len = 0;
439                         s = NULL;
440                 }
441 
442                 if (!parse_addr(addrstr, ss))
443                         return NULL;
444 
445                 if (s && *s == ':') {
446                         if (len <= 1)
447                                 err_return(EINVAL, "Invalid port number");
448 
449                         for (s++, len--, n = 0; len > 0; len--, s++) {
450                                 if (*s < '' || *s > '9')
451                                         err_return(EINVAL, "Invalid port number");
452 
453                                 n = n * 10 + (*s - '');
454                         }
455 
456                         if (n > 65535)
457                                 err_return(EINVAL, "Invalid port number");
458 
459                         s6->sin6_port = htons(n);
460                 }
461 
462                 *slen = (ss->ss_family == AF_INET6) ? sizeof(*s6) : sizeof(*s4);
463 
464                 ok_return(true);
465         }
466         else if (ucv_type(addr) == UC_ARRAY) {
467                 if (ucv_array_length(addr) == 16) {
468                         uint8_t *u8 = (uint8_t *)&s6->sin6_addr;
469 
470                         for (size_t i = 0; i < 16; i++) {
471                                 item = ucv_array_get(addr, i);
472                                 n = ucv_uint64_get(item);
473 
474                                 if (ucv_type(item) != UC_INTEGER || errno != 0 || n > 255)
475                                         err_return(EINVAL, "Invalid IP address array");
476 
477                                 u8[i] = n;
478                         }
479 
480                         s6->sin6_family = AF_INET6;
481                         *slen = sizeof(*s6);
482 
483                         ok_return(true);
484                 }
485                 else if (ucv_array_length(addr) == 4) {
486                         s4->sin_addr.s_addr = 0;
487 
488                         for (size_t i = 0; i < 4; i++) {
489                                 item = ucv_array_get(addr, i);
490                                 n = ucv_uint64_get(item);
491 
492                                 if (ucv_type(item) != UC_INTEGER || errno != 0 || n > 255)
493                                         err_return(EINVAL, "Invalid IP address array");
494 
495                                 s4->sin_addr.s_addr = s4->sin_addr.s_addr * 256 + n;
496                         }
497 
498                         s4->sin_addr.s_addr = htonl(s4->sin_addr.s_addr);
499                         s4->sin_family = AF_INET;
500                         *slen = sizeof(*s4);
501 
502                         ok_return(true);
503                 }
504 
505                 err_return(EINVAL, "Invalid IP address array");
506         }
507         else if (ucv_type(addr) == UC_OBJECT) {
508                 n = ucv_to_unsigned(ucv_object_get(addr, "family", NULL));
509 
510                 if (n == 0) {
511                         if (ucv_type(ucv_object_get(addr, "path", NULL)) == UC_STRING) {
512                                 n = AF_UNIX;
513                         }
514                         else {
515                                 item = ucv_object_get(addr, "address", NULL);
516                                 len = ucv_string_length(item);
517                                 s = ucv_string_get(item);
518                                 n = (s && memchr(s, ':', len) != NULL) ? AF_INET6 : AF_INET;
519                         }
520 
521                         if (n == 0)
522                                 err_return(EINVAL, "Invalid address object");
523                 }
524 
525                 switch (n) {
526                 case AF_INET6:
527                         item = ucv_object_get(addr, "flowinfo", NULL);
528                         s6->sin6_flowinfo = htonl(ucv_to_unsigned(item));
529 
530                         item = ucv_object_get(addr, "interface", NULL);
531 
532                         if (ucv_type(item) == UC_STRING) {
533                                 s6->sin6_scope_id = if_nametoindex(ucv_string_get(item));
534 
535                                 if (s6->sin6_scope_id == 0)
536                                         err_return(errno, "Unable to resolve interface %s",
537                                                 ucv_string_get(item));
538                         }
539                         else if (item != NULL) {
540                                 s6->sin6_scope_id = ucv_to_unsigned(item);
541 
542                                 if (errno != 0)
543                                         err_return(errno, "Invalid scope ID");
544                         }
545 
546                         /* fall through */
547 
548                 case AF_INET:
549                         ss->ss_family = n;
550                         *slen = (n == AF_INET6) ? sizeof(*s6) : sizeof(*s4);
551 
552                         item = ucv_object_get(addr, "port", NULL);
553                         n = ucv_to_unsigned(item);
554 
555                         if (errno != 0 || n > 65535)
556                                 err_return(EINVAL, "Invalid port number");
557 
558                         s6->sin6_port = htons(n);
559 
560                         item = ucv_object_get(addr, "address", NULL);
561                         len = ucv_string_length(item);
562                         s = ucv_string_get(item);
563 
564                         if (len >= sizeof(addrstr))
565                                 err_return(EINVAL, "Invalid IP address");
566 
567                         if (len > 0) {
568                                 memcpy(addrstr, s, len);
569                                 addrstr[len] = 0;
570 
571                                 if (!parse_addr(addrstr, ss))
572                                         return NULL;
573                         }
574 
575                         ok_return(true);
576 
577                 case AF_UNIX:
578                         item = ucv_object_get(addr, "path", NULL);
579                         len = ucv_string_length(item);
580 
581                         if (len == 0 || len >= sizeof(su->sun_path))
582                                 err_return(EINVAL, "Invalid path value");
583 
584                         memcpy(su->sun_path, ucv_string_get(item), len);
585                         su->sun_path[len++] = 0;
586                         su->sun_family = AF_UNIX;
587                         *slen = len;
588 
589                         ok_return(true);
590                 }
591         }
592 
593         err_return(EINVAL, "Invalid address value");
594 }
595 
596 static bool
597 uv_to_fileno(uc_vm_t *vm, uc_value_t *val, int *fileno)
598 {
599         uc_value_t *fn;
600         int *fdptr;
601 
602         fdptr = (int *)ucv_resource_dataptr(val, "socket");
603 
604         if (fdptr) {
605                 if (*fdptr < 0)
606                         err_return(EBADF, "Socket is closed");
607 
608                 *fileno = *fdptr;
609 
610                 return true;
611         }
612 
613         fn = ucv_property_get(val, "fileno");
614 
615         if (ucv_is_callable(fn)) {
616                 uc_vm_stack_push(vm, ucv_get(val));
617                 uc_vm_stack_push(vm, ucv_get(fn));
618 
619                 if (uc_vm_call(vm, true, 0) != EXCEPTION_NONE)
620                         return false;
621 
622                 val = uc_vm_stack_pop(vm);
623         }
624         else {
625                 ucv_get(val);
626         }
627 
628         *fileno = ucv_int64_get(val);
629 
630         ucv_put(val);
631 
632         if (errno != 0 || *fileno < 0)
633                 err_return(EBADF, "Invalid file descriptor number");
634 
635         return true;
636 }
637 
638 static bool
639 uv_to_pollfd(uc_vm_t *vm, uc_value_t *val, struct pollfd *pfd)
640 {
641         int64_t flags;
642 
643         if (ucv_type(val) == UC_ARRAY) {
644                 if (!uv_to_fileno(vm, ucv_array_get(val, 0), &pfd->fd))
645                         return false;
646 
647                 flags = ucv_to_integer(ucv_array_get(val, 1));
648 
649                 if (errno != 0 || flags < -32768 || flags > 32767)
650                         err_return(ERANGE, "Flags value out of range");
651 
652                 pfd->events = flags;
653                 pfd->revents = 0;
654         }
655         else {
656                 if (!uv_to_fileno(vm, val, &pfd->fd))
657                         return false;
658 
659                 pfd->events = POLLIN | POLLERR | POLLHUP;
660                 pfd->revents = 0;
661         }
662 
663         return true;
664 }
665 
666 static uc_value_t *
667 ucv_socket_new(uc_vm_t *vm, int fd)
668 {
669         return ucv_resource_new(
670                 ucv_resource_type_lookup(vm, "socket"),
671                 (void *)(intptr_t)fd
672         );
673 }
674 
675 static bool
676 xclose(int *fdptr)
677 {
678         bool rv = true;
679 
680         if (fdptr) {
681                 if (*fdptr >= 0)
682                         rv = (close(*fdptr) == 0);
683 
684                 *fdptr = -1;
685         }
686 
687         return rv;
688 }
689 
690 
691 typedef struct {
692     const char *name;
693     enum { DT_SIGNED, DT_UNSIGNED, DT_IPV4ADDR, DT_CALLBACK } type;
694         union {
695         size_t offset;
696                 bool (*to_c)(void *, uc_value_t *);
697         } u1;
698         union {
699                 size_t size;
700                 uc_value_t *(*to_uv)(void *);
701         } u2;
702 } member_t;
703 
704 typedef struct {
705         size_t size;
706         member_t *members;
707 } struct_t;
708 
709 typedef struct {
710         int level;
711         int option;
712         struct_t *ctype;
713 } sockopt_t;
714 
715 #define STRUCT_MEMBER_NP(struct_name, member_name, data_type)   \
716         { #member_name, data_type,                                                                      \
717           { .offset = offsetof(struct struct_name, member_name) },      \
718           { .size = sizeof(((struct struct_name *)NULL)->member_name) } }
719 
720 #define STRUCT_MEMBER_CB(member_name, to_c_fn, to_uv_fn)                \
721         { #member_name, DT_CALLBACK, { .to_c = to_c_fn }, { .to_uv = to_uv_fn } }
722 
723 #define STRUCT_MEMBER(struct_name, member_prefix, member_name, data_type)                       \
724         { #member_name, data_type,                                                                                                              \
725           { .offset = offsetof(struct struct_name, member_prefix##_##member_name) },    \
726           { .size = sizeof(((struct struct_name *)NULL)->member_prefix##_##member_name) } }
727 
728 static struct_t st_timeval = {
729         .size = sizeof(struct timeval),
730         .members = (member_t []){
731                 STRUCT_MEMBER(timeval, tv, sec, DT_SIGNED),
732                 STRUCT_MEMBER(timeval, tv, usec, DT_SIGNED),
733                 { 0 }
734         }
735 };
736 
737 #if defined(__linux__)
738 static struct_t st_ucred = {
739         .size = sizeof(struct ucred),
740         .members = (member_t []){
741                 STRUCT_MEMBER_NP(ucred, pid, DT_SIGNED),
742                 STRUCT_MEMBER_NP(ucred, uid, DT_SIGNED),
743                 STRUCT_MEMBER_NP(ucred, gid, DT_SIGNED),
744                 { 0 }
745         }
746 };
747 #endif
748 
749 static struct_t st_linger = {
750         .size = sizeof(struct linger),
751         .members = (member_t []){
752                 STRUCT_MEMBER(linger, l, onoff, DT_SIGNED),
753                 STRUCT_MEMBER(linger, l, linger, DT_SIGNED),
754                 { 0 }
755         }
756 };
757 
758 static struct_t st_ip_mreqn = {
759         .size = sizeof(struct ip_mreqn),
760         .members = (member_t []){
761                 STRUCT_MEMBER(ip_mreqn, imr, multiaddr, DT_IPV4ADDR),
762                 STRUCT_MEMBER(ip_mreqn, imr, address, DT_IPV4ADDR),
763                 STRUCT_MEMBER(ip_mreqn, imr, ifindex, DT_SIGNED),
764                 { 0 }
765         }
766 };
767 
768 static struct_t st_ip_mreq_source = {
769         .size = sizeof(struct ip_mreq_source),
770         .members = (member_t []){
771                 STRUCT_MEMBER(ip_mreq_source, imr, multiaddr, DT_IPV4ADDR),
772                 STRUCT_MEMBER(ip_mreq_source, imr, interface, DT_IPV4ADDR),
773                 STRUCT_MEMBER(ip_mreq_source, imr, sourceaddr, DT_IPV4ADDR),
774                 { 0 }
775         }
776 };
777 
778 #if defined(__linux__)
779 static struct_t st_ip_msfilter = {
780         .size = sizeof(struct ip_msfilter),
781         .members = (member_t []){
782                 STRUCT_MEMBER(ip_msfilter, imsf, multiaddr, DT_IPV4ADDR),
783                 STRUCT_MEMBER(ip_msfilter, imsf, interface, DT_IPV4ADDR),
784                 STRUCT_MEMBER(ip_msfilter, imsf, fmode, DT_SIGNED),
785                 STRUCT_MEMBER(ip_msfilter, imsf, numsrc, DT_SIGNED),
786                 STRUCT_MEMBER(ip_msfilter, imsf, slist, DT_SIGNED),
787                 { 0 }
788         }
789 };
790 
791 static uc_value_t *
792 snd_wscale_to_uv(void *st)
793 {
794         return ucv_uint64_new(((struct tcp_info *)st)->tcpi_snd_wscale);
795 }
796 
797 static uc_value_t *
798 rcv_wscale_to_uv(void *st)
799 {
800         return ucv_uint64_new(((struct tcp_info *)st)->tcpi_rcv_wscale);
801 }
802 
803 static bool
804 snd_wscale_to_c(void *st, uc_value_t *uv)
805 {
806         ((struct tcp_info *)st)->tcpi_snd_wscale = ucv_to_unsigned(uv);
807 
808         if (errno)
809                 err_return(errno, "Unable to convert field snd_wscale to unsigned");
810 
811         return true;
812 }
813 
814 static bool
815 rcv_wscale_to_c(void *st, uc_value_t *uv)
816 {
817         ((struct tcp_info *)st)->tcpi_rcv_wscale = ucv_to_unsigned(uv);
818 
819         if (errno)
820                 err_return(errno, "Unable to convert field rcv_wscale to unsigned");
821 
822         return true;
823 }
824 
825 static struct_t st_tcp_info = {
826         .size = sizeof(struct tcp_info),
827         .members = (member_t []){
828                 STRUCT_MEMBER(tcp_info, tcpi, state, DT_UNSIGNED),
829                 STRUCT_MEMBER(tcp_info, tcpi, ca_state, DT_UNSIGNED),
830                 STRUCT_MEMBER(tcp_info, tcpi, retransmits, DT_UNSIGNED),
831                 STRUCT_MEMBER(tcp_info, tcpi, probes, DT_UNSIGNED),
832                 STRUCT_MEMBER(tcp_info, tcpi, backoff, DT_UNSIGNED),
833                 STRUCT_MEMBER(tcp_info, tcpi, options, DT_UNSIGNED),
834                 STRUCT_MEMBER_CB(snd_wscale, snd_wscale_to_c, snd_wscale_to_uv),
835                 STRUCT_MEMBER_CB(rcv_wscale, rcv_wscale_to_c, rcv_wscale_to_uv),
836                 STRUCT_MEMBER(tcp_info, tcpi, rto, DT_UNSIGNED),
837                 STRUCT_MEMBER(tcp_info, tcpi, ato, DT_UNSIGNED),
838                 STRUCT_MEMBER(tcp_info, tcpi, snd_mss, DT_UNSIGNED),
839                 STRUCT_MEMBER(tcp_info, tcpi, rcv_mss, DT_UNSIGNED),
840                 STRUCT_MEMBER(tcp_info, tcpi, unacked, DT_UNSIGNED),
841                 STRUCT_MEMBER(tcp_info, tcpi, sacked, DT_UNSIGNED),
842                 STRUCT_MEMBER(tcp_info, tcpi, lost, DT_UNSIGNED),
843                 STRUCT_MEMBER(tcp_info, tcpi, retrans, DT_UNSIGNED),
844                 STRUCT_MEMBER(tcp_info, tcpi, fackets, DT_UNSIGNED),
845                 STRUCT_MEMBER(tcp_info, tcpi, last_data_sent, DT_UNSIGNED),
846                 STRUCT_MEMBER(tcp_info, tcpi, last_ack_sent, DT_UNSIGNED),
847                 STRUCT_MEMBER(tcp_info, tcpi, last_data_recv, DT_UNSIGNED),
848                 STRUCT_MEMBER(tcp_info, tcpi, last_ack_recv, DT_UNSIGNED),
849                 STRUCT_MEMBER(tcp_info, tcpi, pmtu, DT_UNSIGNED),
850                 STRUCT_MEMBER(tcp_info, tcpi, rcv_ssthresh, DT_UNSIGNED),
851                 STRUCT_MEMBER(tcp_info, tcpi, rtt, DT_UNSIGNED),
852                 STRUCT_MEMBER(tcp_info, tcpi, rttvar, DT_UNSIGNED),
853                 STRUCT_MEMBER(tcp_info, tcpi, snd_ssthresh, DT_UNSIGNED),
854                 STRUCT_MEMBER(tcp_info, tcpi, snd_cwnd, DT_UNSIGNED),
855                 STRUCT_MEMBER(tcp_info, tcpi, advmss, DT_UNSIGNED),
856                 STRUCT_MEMBER(tcp_info, tcpi, reordering, DT_UNSIGNED),
857                 STRUCT_MEMBER(tcp_info, tcpi, rcv_rtt, DT_UNSIGNED),
858                 STRUCT_MEMBER(tcp_info, tcpi, rcv_space, DT_UNSIGNED),
859                 STRUCT_MEMBER(tcp_info, tcpi, total_retrans, DT_UNSIGNED),
860                 { 0 }
861         }
862 };
863 #endif
864 
865 static uc_value_t *
866 ai_addr_to_uv(void *st)
867 {
868         uc_value_t *rv = ucv_object_new(NULL);
869         struct sockaddr_storage ss = { 0 };
870         struct addrinfo *ai = st;
871 
872         memcpy(&ss, ai->ai_addr, ai->ai_addrlen);
873 
874         if (!sockaddr_to_uv(&ss, rv)) {
875                 ucv_put(rv);
876                 return NULL;
877         }
878 
879         return rv;
880 }
881 
882 static uc_value_t *
883 ai_canonname_to_uv(void *st)
884 {
885         struct addrinfo *ai = st;
886         return ai->ai_canonname ? ucv_string_new(ai->ai_canonname) : NULL;
887 }
888 
889 /**
890  * Represents a network address information object returned by
891  * {@link module:socket#addrinfo|`addrinfo()`}.
892  *
893  * @typedef {Object} module:socket.AddressInfo
894  *
895  * @property {module:socket.socket.SocketAddress} addr - A socket address structure.
896  * @property {string} [canonname=null] - The canonical hostname associated with the address.
897  * @property {number} family - The address family (e.g., `2` for `AF_INET`, `10` for `AF_INET6`).
898  * @property {number} flags - Additional flags indicating properties of the address.
899  * @property {number} protocol - The protocol number.
900  * @property {number} socktype - The socket type (e.g., `1` for `SOCK_STREAM`, `2` for `SOCK_DGRAM`).
901  */
902 static struct_t st_addrinfo = {
903         .size = sizeof(struct addrinfo),
904         .members = (member_t []){
905                 STRUCT_MEMBER(addrinfo, ai, flags, DT_SIGNED),
906                 STRUCT_MEMBER(addrinfo, ai, family, DT_SIGNED),
907                 STRUCT_MEMBER(addrinfo, ai, socktype, DT_SIGNED),
908                 STRUCT_MEMBER(addrinfo, ai, protocol, DT_SIGNED),
909                 STRUCT_MEMBER_CB(addr, NULL, ai_addr_to_uv),
910                 STRUCT_MEMBER_CB(canonname, NULL, ai_canonname_to_uv),
911                 { 0 }
912         }
913 };
914 
915 #define SV_VOID   (struct_t *)0
916 #define SV_INT    (struct_t *)1
917 #define SV_INT_RO (struct_t *)2
918 #define SV_BOOL   (struct_t *)3
919 #define SV_STRING (struct_t *)4
920 
921 static sockopt_t sockopts[] = {
922     { SOL_SOCKET, SO_ACCEPTCONN, SV_BOOL },
923     { SOL_SOCKET, SO_BROADCAST, SV_BOOL },
924     { SOL_SOCKET, SO_DEBUG, SV_BOOL },
925     { SOL_SOCKET, SO_ERROR, SV_INT_RO },
926     { SOL_SOCKET, SO_DONTROUTE, SV_BOOL },
927     { SOL_SOCKET, SO_KEEPALIVE, SV_BOOL },
928     { SOL_SOCKET, SO_LINGER, &st_linger },
929     { SOL_SOCKET, SO_OOBINLINE, SV_BOOL },
930     { SOL_SOCKET, SO_RCVBUF, SV_INT },
931     { SOL_SOCKET, SO_RCVLOWAT, SV_INT },
932     { SOL_SOCKET, SO_RCVTIMEO, &st_timeval },
933     { SOL_SOCKET, SO_REUSEADDR, SV_BOOL },
934     { SOL_SOCKET, SO_REUSEPORT, SV_BOOL },
935     { SOL_SOCKET, SO_SNDBUF, SV_INT },
936     { SOL_SOCKET, SO_SNDLOWAT, SV_INT },
937     { SOL_SOCKET, SO_SNDTIMEO, &st_timeval },
938     { SOL_SOCKET, SO_TIMESTAMP, SV_BOOL },
939     { SOL_SOCKET, SO_TYPE, SV_INT },
940 #if defined(__linux__)
941     { SOL_SOCKET, SO_ATTACH_FILTER, SV_STRING },
942     { SOL_SOCKET, SO_ATTACH_BPF, SV_INT },
943     { SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, SV_STRING },
944     { SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, SV_INT },
945     { SOL_SOCKET, SO_BINDTODEVICE, SV_STRING },
946     { SOL_SOCKET, SO_DETACH_FILTER, SV_VOID },
947     { SOL_SOCKET, SO_DETACH_BPF, SV_VOID },
948     { SOL_SOCKET, SO_DOMAIN, SV_INT_RO },
949     { SOL_SOCKET, SO_INCOMING_CPU, SV_INT },
950     { SOL_SOCKET, SO_INCOMING_NAPI_ID, SV_INT_RO },
951     { SOL_SOCKET, SO_LOCK_FILTER, SV_INT },
952     { SOL_SOCKET, SO_MARK, SV_INT },
953     { SOL_SOCKET, SO_PASSCRED, SV_BOOL },
954     { SOL_SOCKET, SO_PASSSEC, SV_BOOL },
955     { SOL_SOCKET, SO_PEEK_OFF, SV_INT },
956     { SOL_SOCKET, SO_PEERCRED, &st_ucred },
957     { SOL_SOCKET, SO_PEERSEC, SV_STRING },
958     { SOL_SOCKET, SO_PRIORITY, SV_INT },
959     { SOL_SOCKET, SO_PROTOCOL, SV_INT },
960     { SOL_SOCKET, SO_RCVBUFFORCE, SV_INT },
961     { SOL_SOCKET, SO_RXQ_OVFL, SV_BOOL },
962     { SOL_SOCKET, SO_SNDBUFFORCE, SV_INT },
963     { SOL_SOCKET, SO_TIMESTAMPNS, SV_BOOL },
964     { SOL_SOCKET, SO_BUSY_POLL, SV_INT },
965 #endif
966 
967     { IPPROTO_IP, IP_ADD_MEMBERSHIP, &st_ip_mreqn },
968     { IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, &st_ip_mreq_source },
969     { IPPROTO_IP, IP_BLOCK_SOURCE, &st_ip_mreq_source },
970     { IPPROTO_IP, IP_DROP_MEMBERSHIP, &st_ip_mreqn },
971     { IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP, &st_ip_mreq_source },
972     { IPPROTO_IP, IP_HDRINCL, SV_BOOL },
973     { IPPROTO_IP, IP_MULTICAST_IF, &st_ip_mreqn },
974     { IPPROTO_IP, IP_MULTICAST_LOOP, SV_BOOL },
975     { IPPROTO_IP, IP_MULTICAST_TTL, SV_INT },
976     { IPPROTO_IP, IP_OPTIONS, SV_STRING },
977     { IPPROTO_IP, IP_PKTINFO, SV_BOOL },
978     { IPPROTO_IP, IP_RECVOPTS, SV_BOOL },
979     { IPPROTO_IP, IP_RECVTOS, SV_BOOL },
980     { IPPROTO_IP, IP_RECVTTL, SV_BOOL },
981     { IPPROTO_IP, IP_RETOPTS, SV_BOOL },
982     { IPPROTO_IP, IP_TOS, SV_INT },
983     { IPPROTO_IP, IP_TTL, SV_INT },
984     { IPPROTO_IP, IP_UNBLOCK_SOURCE, &st_ip_mreq_source },
985 #if defined(__linux__)
986     { IPPROTO_IP, IP_MSFILTER, &st_ip_msfilter },
987     { IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, SV_BOOL },
988     { IPPROTO_IP, IP_FREEBIND, SV_BOOL },
989     { IPPROTO_IP, IP_MTU, SV_INT },
990     { IPPROTO_IP, IP_MTU_DISCOVER, SV_INT },
991     { IPPROTO_IP, IP_MULTICAST_ALL, SV_BOOL },
992     { IPPROTO_IP, IP_NODEFRAG, SV_BOOL },
993     { IPPROTO_IP, IP_PASSSEC, SV_BOOL },
994     { IPPROTO_IP, IP_RECVERR, SV_BOOL },
995     { IPPROTO_IP, IP_RECVORIGDSTADDR, SV_BOOL },
996     { IPPROTO_IP, IP_ROUTER_ALERT, SV_BOOL },
997     { IPPROTO_IP, IP_TRANSPARENT, SV_BOOL },
998 #endif
999 
1000     { IPPROTO_TCP, TCP_KEEPCNT, SV_INT },
1001     { IPPROTO_TCP, TCP_KEEPINTVL, SV_INT },
1002     { IPPROTO_TCP, TCP_MAXSEG, SV_INT },
1003     { IPPROTO_TCP, TCP_NODELAY, SV_BOOL },
1004     { IPPROTO_TCP, TCP_FASTOPEN, SV_INT },
1005 #if defined(__linux__)
1006     { IPPROTO_TCP, TCP_CONGESTION, SV_STRING },
1007     { IPPROTO_TCP, TCP_CORK, SV_BOOL },
1008     { IPPROTO_TCP, TCP_DEFER_ACCEPT, SV_INT },
1009         { IPPROTO_TCP, TCP_INFO, &st_tcp_info },
1010     { IPPROTO_TCP, TCP_KEEPIDLE, SV_INT },
1011     { IPPROTO_TCP, TCP_LINGER2, SV_INT },
1012     { IPPROTO_TCP, TCP_QUICKACK, SV_BOOL },
1013     { IPPROTO_TCP, TCP_SYNCNT, SV_INT },
1014     { IPPROTO_TCP, TCP_USER_TIMEOUT, SV_INT },
1015     { IPPROTO_TCP, TCP_WINDOW_CLAMP, SV_INT },
1016     { IPPROTO_TCP, TCP_FASTOPEN_CONNECT, SV_INT },
1017 #endif
1018 
1019 #if defined(__linux__)
1020     { IPPROTO_UDP, UDP_CORK, SV_BOOL }
1021 #endif
1022 };
1023 
1024 
1025 static char *
1026 uv_to_struct(uc_value_t *uv, struct_t *spec)
1027 {
1028         uc_value_t *fv;
1029         const char *s;
1030         uint64_t u64;
1031         int64_t s64;
1032         member_t *m;
1033         bool found;
1034         char *st;
1035 
1036         union {
1037                 int8_t s8;
1038                 int16_t s16;
1039                 int32_t s32;
1040                 int64_t s64;
1041                 uint8_t u8;
1042                 uint16_t u16;
1043                 uint32_t u32;
1044                 uint64_t u64;
1045         } v;
1046 
1047         st = xalloc(spec->size);
1048 
1049         for (size_t i = 0; spec->members[i].name; i++) {
1050                 m = &spec->members[i];
1051                 fv = ucv_object_get(uv, m->name, &found);
1052 
1053                 if (!found || !fv)
1054                         continue;
1055 
1056                 switch (spec->members[i].type) {
1057                 case DT_UNSIGNED:
1058                         u64 = ucv_to_unsigned(fv);
1059 
1060                         if (errno) {
1061                                 free(st);
1062                                 err_return(errno,
1063                                         "Unable to convert field %s to unsigned",
1064                                         m->name);
1065                         }
1066 
1067                         switch (m->u2.size) {
1068                         case 1:  v.u8 =  (uint8_t)u64; break;
1069                         case 2: v.u16 = (uint16_t)u64; break;
1070                         case 4: v.u32 = (uint32_t)u64; break;
1071                         case 8: v.u64 = (uint64_t)u64; break;
1072                         }
1073 
1074                         memcpy(st + m->u1.offset, &v, m->u2.size);
1075                         break;
1076 
1077                 case DT_SIGNED:
1078                         s64 = ucv_to_integer(fv);
1079 
1080                         if (errno) {
1081                                 free(st);
1082                                 err_return(errno,
1083                                         "Unable to convert field %s to integer", m->name);
1084                         }
1085 
1086                         switch (m->u2.size) {
1087                         case 1:  v.s8 =  (int8_t)s64; break;
1088                         case 2: v.s16 = (int16_t)s64; break;
1089                         case 4: v.s32 = (int32_t)s64; break;
1090                         case 8: v.s64 = (int64_t)s64; break;
1091                         }
1092 
1093                         memcpy(st + m->u1.offset, &v, m->u2.size);
1094                         break;
1095 
1096                 case DT_IPV4ADDR:
1097                         s = ucv_string_get(fv);
1098 
1099                         if (!s || inet_pton(AF_INET, s, st + m->u1.offset) != 1) {
1100                                 free(st);
1101                                 err_return(EINVAL,
1102                                         "Unable to convert field %s to IP address", m->name);
1103                         }
1104 
1105                         break;
1106 
1107                 case DT_CALLBACK:
1108                         if (m->u1.to_c && !m->u1.to_c(st, fv)) {
1109                                 free(st);
1110                                 return NULL;
1111                         }
1112 
1113                         break;
1114                 }
1115         }
1116 
1117         return st;
1118 }
1119 
1120 static uc_value_t *
1121 struct_to_uv(char *st, struct_t *spec)
1122 {
1123         char s[sizeof("255.255.255.255")];
1124         uc_value_t *uv, *fv;
1125         member_t *m;
1126 
1127         uv = ucv_object_new(NULL);
1128 
1129         for (size_t i = 0; spec->members[i].name; i++) {
1130                 m = &spec->members[i];
1131                 fv = NULL;
1132 
1133                 switch (spec->members[i].type) {
1134                 case DT_UNSIGNED:
1135                         switch (spec->members[i].u2.size) {
1136                         case 1:
1137                                 fv = ucv_uint64_new(*(uint8_t *)(st + m->u1.offset));
1138                                 break;
1139 
1140                         case 2:
1141                                 fv = ucv_uint64_new(*(uint16_t *)(st + m->u1.offset));
1142                                 break;
1143 
1144                         case 4:
1145                                 fv = ucv_uint64_new(*(uint32_t *)(st + m->u1.offset));
1146                                 break;
1147 
1148                         case 8:
1149                                 fv = ucv_uint64_new(*(uint64_t *)(st + m->u1.offset));
1150                                 break;
1151                         }
1152 
1153                         break;
1154 
1155                 case DT_SIGNED:
1156                         switch (spec->members[i].u2.size) {
1157                         case 1:
1158                                 fv = ucv_int64_new(*(int8_t *)(st + m->u1.offset));
1159                                 break;
1160 
1161                         case 2:
1162                                 fv = ucv_int64_new(*(int16_t *)(st + m->u1.offset));
1163                                 break;
1164 
1165                         case 4:
1166                                 fv = ucv_int64_new(*(int32_t *)(st + m->u1.offset));
1167                                 break;
1168 
1169                         case 8:
1170                                 fv = ucv_int64_new(*(int64_t *)(st + m->u1.offset));
1171                                 break;
1172                         }
1173 
1174                         break;
1175 
1176                 case DT_IPV4ADDR:
1177                         if (inet_ntop(AF_INET, st + m->u1.offset, s, sizeof(s)))
1178                                 fv = ucv_string_new(s);
1179 
1180                         break;
1181 
1182                 case DT_CALLBACK:
1183                         fv = m->u2.to_uv ? m->u2.to_uv(st) : NULL;
1184                         break;
1185                 }
1186 
1187                 ucv_object_add(uv, m->name, fv);
1188         }
1189 
1190         return uv;
1191 }
1192 
1193 /**
1194  * Sets options on the socket.
1195  *
1196  * Sets the specified option on the socket to the given value.
1197  *
1198  * Returns `true` if the option was successfully set.
1199  *
1200  * Returns `null` if an error occurred.
1201  *
1202  * @function module:socket.socket#setopt
1203  *
1204  * @param {number} level
1205  * The protocol level at which the option resides. This can be a level such as
1206  * `SOL_SOCKET` for the socket API level or a specific protocol level defined
1207  * by the system.
1208  *
1209  * @param {number} option
1210  * The socket option to set. This can be an integer representing the option,
1211  * such as `SO_REUSEADDR`, or a constant defined by the system.
1212  *
1213  * @param {*} value
1214  * The value to set the option to. The type of this argument depends on the
1215  * specific option being set. It can be an integer, a boolean, a string, or a
1216  * dictionary representing the value to set. If a dictionary is provided, it is
1217  * internally translated to the corresponding C struct type required by the
1218  * option.
1219  *
1220  * @returns {?boolean}
1221  */
1222 static uc_value_t *
1223 uc_socket_inst_setopt(uc_vm_t *vm, size_t nargs)
1224 {
1225         int sockfd, solvl, soopt, soval, ret;
1226         uc_value_t *level, *option, *value;
1227         void *valptr = NULL, *st = NULL;
1228         socklen_t vallen = 0;
1229         size_t i;
1230 
1231         args_get(vm, nargs, &sockfd,
1232                 "level", UC_INTEGER, false, &level,
1233                 "option", UC_INTEGER, false, &option,
1234                 "value", UC_NULL, false, &value);
1235 
1236         solvl = ucv_int64_get(level);
1237         soopt = ucv_int64_get(option);
1238 
1239         for (i = 0; i < ARRAY_SIZE(sockopts); i++) {
1240                 if (sockopts[i].level != solvl || sockopts[i].option != soopt)
1241                         continue;
1242 
1243                 switch ((uintptr_t)sockopts[i].ctype) {
1244                 case (uintptr_t)SV_INT_RO:
1245                         err_return(EOPNOTSUPP, "Socket option is read only");
1246 
1247                 case (uintptr_t)SV_VOID:
1248                         valptr = NULL;
1249                         vallen = 0;
1250                         break;
1251 
1252                 case (uintptr_t)SV_INT:
1253                         soval = ucv_to_integer(value);
1254 
1255                         if (errno)
1256                                 err_return(errno, "Unable to convert value to integer");
1257 
1258                         valptr = &soval;
1259                         vallen = sizeof(int);
1260                         break;
1261 
1262                 case (uintptr_t)SV_BOOL:
1263                         soval = ucv_to_unsigned(value) ? 1 : 0;
1264 
1265                         if (errno)
1266                                 err_return(errno, "Unable to convert value to boolean");
1267 
1268                         valptr = &soval;
1269                         vallen = sizeof(int);
1270                         break;
1271 
1272                 case (uintptr_t)SV_STRING:
1273                         valptr = ucv_string_get(value);
1274                         vallen = ucv_string_length(value);
1275                         break;
1276 
1277                 default:
1278                         st = uv_to_struct(value, sockopts[i].ctype);
1279                         valptr = st;
1280                         vallen = sockopts[i].ctype->size;
1281                         break;
1282                 }
1283 
1284                 break;
1285         }
1286 
1287         if (i == ARRAY_SIZE(sockopts))
1288                 err_return(EINVAL, "Unknown socket level or option");
1289 
1290         ret = setsockopt(sockfd, solvl, soopt, valptr, vallen);
1291 
1292         free(st);
1293 
1294         if (ret == -1)
1295                 err_return(errno, "setsockopt()");
1296 
1297         ok_return(ucv_boolean_new(true));
1298 }
1299 
1300 /**
1301  * Gets options from the socket.
1302  *
1303  * Retrieves the value of the specified option from the socket.
1304  *
1305  * Returns the value of the requested option.
1306  *
1307  * Returns `null` if an error occurred or if the option is not supported.
1308  *
1309  * @function module:socket.socket#getopt
1310  *
1311  * @param {number} level
1312  * The protocol level at which the option resides. This can be a level such as
1313  * `SOL_SOCKET` for the socket API level or a specific protocol level defined
1314  * by the system.
1315  *
1316  * @param {number} option
1317  * The socket option to retrieve. This can be an integer representing the
1318  * option, such as `SO_REUSEADDR`, or a constant defined by the system.
1319  *
1320  * @returns {?*}
1321  * The value of the requested option. The type of the returned value depends
1322  * on the specific option being retrieved. It can be an integer, a boolean, a
1323  * string, or a dictionary representing a complex data structure.
1324  */
1325 static uc_value_t *
1326 uc_socket_inst_getopt(uc_vm_t *vm, size_t nargs)
1327 {
1328         uc_value_t *level, *option, *value = NULL;
1329         int sockfd, solvl, soopt, soval, ret;
1330         void *valptr = NULL, *st = NULL;
1331         uc_stringbuf_t *sb = NULL;
1332         socklen_t vallen;
1333         size_t i;
1334 
1335         args_get(vm, nargs, &sockfd,
1336                 "level", UC_INTEGER, false, &level,
1337                 "option", UC_INTEGER, false, &option);
1338 
1339         solvl = ucv_int64_get(level);
1340         soopt = ucv_int64_get(option);
1341         soval = 0;
1342 
1343         for (i = 0; i < ARRAY_SIZE(sockopts); i++) {
1344                 if (sockopts[i].level != solvl || sockopts[i].option != soopt)
1345                         continue;
1346 
1347                 switch ((uintptr_t)sockopts[i].ctype) {
1348                 case (uintptr_t)SV_VOID:
1349                         err_return(EOPNOTSUPP, "Socket option is write only");
1350 
1351                 case (uintptr_t)SV_INT:
1352                 case (uintptr_t)SV_INT_RO:
1353                 case (uintptr_t)SV_BOOL:
1354                         valptr = &soval;
1355                         vallen = sizeof(int);
1356                         break;
1357 
1358                 case (uintptr_t)SV_STRING:
1359                         sb = strbuf_alloc(64);
1360                         valptr = strbuf_data(sb);
1361                         vallen = strbuf_size(sb);
1362                         break;
1363 
1364                 default:
1365                         st = xalloc(sockopts[i].ctype->size);
1366                         valptr = st;
1367                         vallen = sockopts[i].ctype->size;
1368                         break;
1369                 }
1370 
1371                 break;
1372         }
1373 
1374         if (i == ARRAY_SIZE(sockopts))
1375                 err_return(EINVAL, "Unknown socket level or option");
1376 
1377         while (true) {
1378                 ret = getsockopt(sockfd, solvl, soopt, valptr, &vallen);
1379 
1380                 if (sockopts[i].ctype == SV_STRING &&
1381                     (ret == 0 || (ret == -1 && errno == ERANGE)) &&
1382                     vallen > strbuf_size(sb)) {
1383 
1384                         if (!strbuf_grow(sb, vallen))
1385                                 return NULL;
1386 
1387                         valptr = strbuf_data(sb);
1388                         continue;
1389                 }
1390 
1391                 break;
1392         }
1393 
1394         if (ret == 0) {
1395                 switch ((uintptr_t)sockopts[i].ctype) {
1396                 case (uintptr_t)SV_VOID:
1397                         break;
1398 
1399                 case (uintptr_t)SV_INT:
1400                 case (uintptr_t)SV_INT_RO:
1401                         value = ucv_int64_new(soval);
1402                         break;
1403 
1404                 case (uintptr_t)SV_BOOL:
1405                         value = ucv_boolean_new(soval);
1406                         break;
1407 
1408                 case (uintptr_t)SV_STRING:
1409                         value = strbuf_finish(&sb, vallen);
1410                         break;
1411 
1412                 default:
1413                         value = struct_to_uv(st, sockopts[i].ctype);
1414                         break;
1415                 }
1416         }
1417 
1418         strbuf_free(sb);
1419         free(st);
1420 
1421         if (ret == -1)
1422                 err_return(errno, "getsockopt()");
1423 
1424         ok_return(value);
1425 }
1426 
1427 /**
1428  * Returns the UNIX file descriptor number associated with the socket.
1429  *
1430  * Returns the file descriptor number.
1431  *
1432  * Returns `-1` if an error occurred.
1433  *
1434  * @function module:socket.socket#fileno
1435  *
1436  * @returns {number}
1437  */
1438 static uc_value_t *
1439 uc_socket_inst_fileno(uc_vm_t *vm, size_t nargs)
1440 {
1441         int sockfd;
1442 
1443         args_get(vm, nargs, &sockfd);
1444 
1445         ok_return(ucv_int64_new(sockfd));
1446 }
1447 
1448 /**
1449  * Query error information.
1450  *
1451  * Returns a string containing a description of the last occurred error when
1452  * the *numeric* argument is absent or false.
1453  *
1454  * Returns a positive (`errno`) or negative (`EAI_*` constant) error code number
1455  * when the *numeric* argument is `true`.
1456  *
1457  * Returns `null` if there is no error information.
1458  *
1459  * @function module:socket#error
1460  *
1461  * @param {boolean} [numeric]
1462  * Whether to return a numeric error code (`true`) or a human readable error
1463  * message (false).
1464  *
1465  * @returns {?string|?number}
1466  *
1467  * @example
1468  * // Trigger socket error by attempting to bind IPv6 address with IPv4 socket
1469  * socket.create(socket.AF_INET, socket.SOCK_STREAM, 0).bind("::", 8080);
1470  *
1471  * // Print error (should yield "Address family not supported by protocol")
1472  * print(socket.error(), "\n");
1473  *
1474  * // Trigger resolve error
1475  * socket.addrinfo("doesnotexist.org");
1476  *
1477  * // Query error code (should yield -2 for EAI_NONAME)
1478  * print(socket.error(true), "\n");  //
1479  */
1480 static uc_value_t *
1481 uc_socket_error(uc_vm_t *vm, size_t nargs)
1482 {
1483         uc_value_t *numeric = uc_fn_arg(0), *rv;
1484         uc_stringbuf_t *buf;
1485 
1486         if (last_error.code == 0)
1487                 return NULL;
1488 
1489         if (ucv_is_truish(numeric)) {
1490                 rv = ucv_int64_new(last_error.code);
1491         }
1492         else {
1493                 buf = ucv_stringbuf_new();
1494 
1495                 if (last_error.msg)
1496                         ucv_stringbuf_printf(buf, "%s: ", last_error.msg);
1497 
1498                 if (last_error.code >= 0)
1499                         ucv_stringbuf_printf(buf, "%s", strerror(last_error.code));
1500                 else
1501                         ucv_stringbuf_printf(buf, "%s", gai_strerror(last_error.code));
1502 
1503                 rv = ucv_stringbuf_finish(buf);
1504         }
1505 
1506         ok_return(rv);
1507 }
1508 
1509 /**
1510  * @typedef {Object} module:socket.socket.SocketAddress
1511  * @property {number} family
1512  * Address family, one of AF_INET, AF_INET6 or AF_UNIX.
1513  *
1514  * @property {string} address
1515  * IPv4 or IPv6 address string (AF_INET or AF_INET6 only).
1516  *
1517  * @property {number} [port]
1518  * Port number (AF_INET or AF_INET6 only).
1519  *
1520  * @property {number} [flowinfo]
1521  * IPv6 flow information (AF_INET6 only).
1522  *
1523  * @property {string|number} [interface]
1524  * Link local address scope, either a network device name string or a nonzero
1525  * positive integer representing a network interface index (AF_INET6 only).
1526  *
1527  * @property {string} path
1528  * Domain socket filesystem path (AF_UNIX only).
1529  */
1530 
1531 /**
1532  * Parses the provided address value into a socket address representation.
1533  *
1534  * This function parses the given address value into a socket address
1535  * representation required for a number of socket operations. The address value
1536  * can be provided in various formats:
1537  * - For IPv4 addresses, it can be a string representing the IP address,
1538  *   optionally followed by a port number separated by colon, e.g.
1539  *   `192.168.0.1:8080`.
1540  * - For IPv6 addresses, it must be an address string enclosed in square
1541  *   brackets if a port number is specified, otherwise the brackets are
1542  *   optional. The address string may also include a scope ID in the form
1543  *   `%ifname` or `%number`, e.g. `[fe80::1%eth0]:8080` or `fe80::1%15`.
1544  * - Any string value containing a slash is treated as UNIX domain socket path.
1545  * - Alternatively, it can be provided as an array returned by
1546  *   {@link module:core#iptoarr|iptoarr()}, representing the address octets.
1547  * - It can also be an object representing a network address, with properties
1548  *   for `address` (the IP address) and `port` or a single property `path` to
1549  *   denote a UNIX domain socket address.
1550  *
1551  * @function module:socket#sockaddr
1552  *
1553  * @param {string|number[]|module:socket.socket.SocketAddress} address
1554  * The address value to parse.
1555  *
1556  * @returns {?module:socket.socket.SocketAddress}
1557  * A socket address representation of the provided address value, or `null` if
1558  * the address could not be parsed.
1559  *
1560  * @example
1561  * // Parse an IP address string with port
1562  * const address1 = sockaddr('192.168.0.1:8080');
1563  *
1564  * // Parse an IPv6 address string with port and scope identifier
1565  * const address2 = sockaddr('[fe80::1%eth0]:8080');
1566  *
1567  * // Parse an array representing an IP address
1568  * const address3 = sockaddr([192, 168, 0, 1]);
1569  *
1570  * // Parse a network address object
1571  * const address4 = sockaddr({ address: '192.168.0.1', port: 8080 });
1572  *
1573  * // Convert a path value to a UNIX domain socket address
1574  * const address5 = sockaddr('/var/run/daemon.sock');
1575  */
1576 static uc_value_t *
1577 uc_socket_sockaddr(uc_vm_t *vm, size_t nargs)
1578 {
1579         struct sockaddr_storage ss = { 0 };
1580         uc_value_t *addr, *rv;
1581         socklen_t slen;
1582 
1583         args_get(vm, nargs, NULL,
1584                 "address", UC_NULL, false, &addr);
1585 
1586         if (!uv_to_sockaddr(addr, &ss, &slen))
1587                 return NULL;
1588 
1589         rv = ucv_object_new(vm);
1590 
1591         if (!sockaddr_to_uv(&ss, rv)) {
1592                 ucv_put(rv);
1593                 return NULL;
1594         }
1595 
1596         ok_return(rv);
1597 }
1598 
1599 /**
1600  * Resolves the given network address into hostname and service name.
1601  *
1602  * The `nameinfo()` function provides an API for reverse DNS lookup and service
1603  * name resolution. It returns an object containing the following properties:
1604  * - `hostname`: The resolved hostname.
1605  * - `service`: The resolved service name.
1606  *
1607  * Returns an object representing the resolved hostname and service name.
1608  * Return `null` if an error occurred during resolution.
1609  *
1610  * @function module:socket#nameinfo
1611  *
1612  * @param {string|module:socket.socket.SocketAddress} address
1613  * The network address to resolve. It can be specified as:
1614  * - A string representing the IP address.
1615  * - An object representing the address with properties `address` and `port`.
1616  *
1617  * @param {number} [flags]
1618  * Optional flags that provide additional control over the resolution process,
1619  * specified as bitwise OR-ed number of `NI_*` constants.
1620  *
1621  * @returns {?{hostname: string, service: string}}
1622  *
1623  * @see {@link module:socket~"Socket Types"|Socket Types}
1624  * @see {@link module:socket~"Name Info Constants"|AName Info Constants}
1625  *
1626  * @example
1627  * // Resolve a network address into hostname and service name
1628  * const result = network.getnameinfo('192.168.1.1:80');
1629  * print(result); // { "hostname": "example.com", "service": "http" }
1630  */
1631 static uc_value_t *
1632 uc_socket_nameinfo(uc_vm_t *vm, size_t nargs)
1633 {
1634         char host[NI_MAXHOST], serv[NI_MAXSERV];
1635         uc_value_t *addr, *flags, *rv;
1636         struct sockaddr_storage ss;
1637         socklen_t slen;
1638         int ret;
1639 
1640         args_get(vm, nargs, NULL,
1641                 "address", UC_NULL, false, &addr,
1642                 "flags", UC_INTEGER, true, &flags);
1643 
1644         if (!uv_to_sockaddr(addr, &ss, &slen))
1645                 return NULL;
1646 
1647         ret = getnameinfo((struct sockaddr *)&ss, slen,
1648                 host, sizeof(host), serv, sizeof(serv),
1649                 flags ? ucv_int64_get(flags) : 0);
1650 
1651         if (ret != 0)
1652                 err_return((ret == EAI_SYSTEM) ? errno : ret, "getnameinfo()");
1653 
1654         rv = ucv_object_new(vm);
1655 
1656         ucv_object_add(rv, "hostname", ucv_string_new(host));
1657         ucv_object_add(rv, "service", ucv_string_new(serv));
1658 
1659         ok_return(rv);
1660 }
1661 
1662 /**
1663  * Resolves the given hostname and optional service name into a list of network
1664  * addresses, according to the provided hints.
1665  *
1666  * The `addrinfo()` function provides an API for performing DNS and service name
1667  * resolution. It returns an array of objects, each representing a resolved
1668  * address.
1669  *
1670  * Returns an array of resolved addresses.
1671  * Returns `null` if an error occurred during resolution.
1672  *
1673  * @function module:socket#addrinfo
1674  *
1675  * @param {string} hostname
1676  * The hostname to resolve.
1677  *
1678  * @param {string} [service]
1679  * Optional service name to resolve. If not provided, the service field of the
1680  * resulting address information structures is left uninitialized.
1681  *
1682  * @param {Object} [hints]
1683  * Optional hints object that provides additional control over the resolution
1684  * process. It can contain the following properties:
1685  * - `family`: The preferred address family (`AF_INET` or `AF_INET6`).
1686  * - `socktype`: The socket type (`SOCK_STREAM`, `SOCK_DGRAM`, etc.).
1687  * - `protocol`: The protocol of returned addresses.
1688  * - `flags`: Bitwise OR-ed `AI_*` flags to control the resolution behavior.
1689  *
1690  * @returns {?module:socket.AddressInfo[]}
1691  *
1692  * @see {@link module:socket~"Socket Types"|Socket Types}
1693  * @see {@link module:socket~"Address Info Flags"|Address Info Flags}
1694  *
1695  * @example
1696  * // Resolve all addresses
1697  * const addresses = socket.addrinfo('example.org');
1698  *
1699  * // Resolve IPv4 addresses for a given hostname and service
1700  * const ipv4addresses = socket.addrinfo('example.com', 'http', { family: socket.AF_INET });
1701  *
1702  * // Resolve IPv6 addresses without specifying a service
1703  * const ipv6Addresses = socket.addrinfo('example.com', null, { family: socket.AF_INET6 });
1704  */
1705 
1706 static uc_value_t *
1707 uc_socket_addrinfo(uc_vm_t *vm, size_t nargs)
1708 {
1709         struct addrinfo *ai_hints = NULL, *ai_res;
1710         uc_value_t *host, *serv, *hints, *rv;
1711         char *servstr;
1712         int ret;
1713 
1714         args_get(vm, nargs, NULL,
1715                 "hostname", UC_STRING, false, &host,
1716                 "service", UC_NULL, true, &serv,
1717                 "hints", UC_OBJECT, true, &hints);
1718 
1719         if (hints) {
1720                 ai_hints = (struct addrinfo *)uv_to_struct(hints, &st_addrinfo);
1721 
1722                 if (!ai_hints)
1723                         return NULL;
1724         }
1725 
1726         servstr = (ucv_type(serv) != UC_STRING) ? ucv_to_string(vm, serv) : NULL;
1727         ret = getaddrinfo(ucv_string_get(host),
1728                 servstr ? servstr : ucv_string_get(serv),
1729                 ai_hints, &ai_res);
1730 
1731         free(ai_hints);
1732         free(servstr);
1733 
1734         if (ret != 0)
1735                 err_return((ret == EAI_SYSTEM) ? errno : ret, "getaddrinfo()");
1736 
1737         rv = ucv_array_new(vm);
1738 
1739         for (struct addrinfo *ai = ai_res; ai; ai = ai->ai_next) {
1740                 uc_value_t *item = struct_to_uv((char *)ai, &st_addrinfo);
1741 
1742                 if (item)
1743                         ucv_array_push(rv, item);
1744         }
1745 
1746         freeaddrinfo(ai_res);
1747 
1748         ok_return(rv);
1749 }
1750 
1751 /**
1752  * Represents a poll state serving as input parameter and return value type for
1753  * {@link module:socket#poll|`poll()`}.
1754  *
1755  * @typedef {Array} module:socket.PollSpec
1756  * @property {module:socket.socket} 0
1757  * The polled socket instance.
1758  *
1759  * @property {number} 1
1760  * Requested or returned status flags of the polled socket instance.
1761  */
1762 
1763 /**
1764  * Polls a number of sockets for state changes.
1765  *
1766  * Returns an array of socket poll states.
1767  * Returns `null` if an error occurred.
1768  *
1769  * @function module:socket#poll
1770  *
1771  * @param {number} timeout
1772  * Amount of milliseconds to wait for socket activity before aborting the poll
1773  * call. If set to `0`, the poll call will return immediately if none of the
1774  * provided sockets has pending events, if set to a negative value, the poll
1775  * call will wait indefinitely, in all other cases the poll call will wait at
1776  * most for the given amount of milliseconds before returning.
1777  *
1778  * @param {...(module:socket.socket|module:socket.PollSpec)} sockets
1779  * An arbitrary amount of socket arguments. Each argument may be either a plain
1780  * {@link module:socket.socket|socket instance} or a `[socket, flags]` tuple
1781  * specifying the socket and requested poll flags. If a plain socket and not a
1782  * tuple is provided, the requested poll flags default to
1783  * `POLLIN|POLLERR|POLLHUP` for this socket.
1784  *
1785  * @returns {module:socket.PollSpec[]}
1786  */
1787 static uc_value_t *
1788 uc_socket_poll(uc_vm_t *vm, size_t nargs)
1789 {
1790         struct { struct pollfd *entries; size_t count; } pfds = { 0 };
1791         uc_value_t *timeoutarg, *rv, *item;
1792         int64_t timeout;
1793         int ret;
1794 
1795         args_get(vm, nargs, NULL, "timeout", UC_INTEGER, false, &timeoutarg);
1796 
1797         timeout = ucv_to_integer(timeoutarg);
1798 
1799         if (errno != 0 || timeout < (int64_t)INT_MIN || timeout > (int64_t)INT_MAX)
1800                 err_return(ERANGE, "Invalid timeout value");
1801 
1802         rv = ucv_array_new(vm);
1803 
1804         for (size_t i = 1; i < nargs; i++) {
1805                 uc_vector_grow(&pfds);
1806 
1807                 if (uv_to_pollfd(vm, uc_fn_arg(i), &pfds.entries[pfds.count])) {
1808                         item = ucv_array_new_length(vm, 2);
1809                         ucv_array_set(item, 0, ucv_get(uc_fn_arg(i)));
1810                         ucv_array_set(rv, pfds.count++, item);
1811                 }
1812         }
1813 
1814         ret = poll(pfds.entries, pfds.count, timeout);
1815 
1816         if (ret == -1) {
1817                 ucv_put(rv);
1818                 uc_vector_clear(&pfds);
1819                 err_return(errno, "poll()");
1820         }
1821 
1822         for (size_t i = 0; i < pfds.count; i++)
1823                 ucv_array_set(ucv_array_get(rv, i), 1,
1824                         ucv_int64_new(pfds.entries[i].revents));
1825 
1826         uc_vector_clear(&pfds);
1827         ok_return(rv);
1828 }
1829 
1830 /**
1831  * Creates a network socket and connects it to the specified host and service.
1832  *
1833  * This high level function combines the functionality of
1834  * {@link module:socket#create|create()},
1835  * {@link module:socket#addrinfo|addrinfo()} and
1836  * {@link module:socket.socket#connect|connect()} to simplify connection
1837  * establishment with the socket module.
1838  *
1839  * @function module:socket#connect
1840  *
1841  * @param {string|number[]|module:socket.socket.SocketAddress} host
1842  * The host to connect to, can be an IP address, hostname,
1843  * {@link module:socket.socket.SocketAddress|SocketAddress}, or an array value
1844  * returned by {@link module:core#iptoarr|iptoarr()}.
1845  *
1846  * @param {string|number} [service]
1847  * The service to connect to, can be a symbolic service name (such as "http") or
1848  * a port number. Optional if host is specified as
1849  * {@link module:socket.socket.SocketAddress|SocketAddress}.
1850  *
1851  * @param {Object} [hints]
1852  * Optional preferences for the socket. It can contain the following properties:
1853  * - `family`: The preferred address family (`AF_INET` or `AF_INET6`).
1854  * - `socktype`: The socket type (`SOCK_STREAM`, `SOCK_DGRAM`, etc.).
1855  * - `protocol`: The protocol of the created socket.
1856  * - `flags`: Bitwise OR-ed `AI_*` flags to control the resolution behavior.
1857  *
1858  * If no hints are not provided, the default socket type preference is set to
1859  * `SOCK_STREAM`.
1860  *
1861  * @param {number} [timeout=-1]
1862  * The timeout in milliseconds for socket connect operations. If set to a
1863  * negative value, no specifc time limit is imposed and the function will
1864  * block until either a connection was successfull or the underlying operating
1865  * system timeout is reached.
1866  *
1867  * @returns {module:socket.socket}
1868  *
1869  * @example
1870  * // Resolve host, try to connect to both resulting IPv4 and IPv6 addresses
1871  * let conn = socket.connect("example.org", 80);
1872  *
1873  * // Enforce usage of IPv6
1874  * let conn = socket.connect("example.com", 80, { family: socket.AF_INET6 });
1875  *
1876  * // Connect a UDP socket
1877  * let conn = socket.connect("192.168.1.1", 53, { socktype: socket.SOCK_DGRAM });
1878  *
1879  * // Bypass name resolution by specifying a SocketAddress structure
1880  * let conn = socket.connect({ address: "127.0.0.1", port: 9000 });
1881  *
1882  * // Use SocketAddress structure to connect a UNIX domain socket
1883  * let conn = socket.connect({ path: "/var/run/daemon.sock" });
1884  */
1885 static uc_value_t *
1886 uc_socket_connect(uc_vm_t *vm, size_t nargs)
1887 {
1888         struct address {
1889                 struct sockaddr_storage ss;
1890                 struct addrinfo ai;
1891                 int flags;
1892                 int fd;
1893         } *ap;
1894 
1895         struct { struct address *entries; size_t count; } addresses = { 0 };
1896         struct { struct pollfd *entries; size_t count; } pollfds = { 0 };
1897         struct addrinfo *ai_results, *ai_hints, *ai;
1898         uc_value_t *host, *serv, *hints, *timeout;
1899         const char *errmsg = NULL;
1900         struct pollfd *pp = NULL;
1901         size_t slot, connected;
1902         int ret, err;
1903 
1904         args_get(vm, nargs, NULL,
1905                 "host", UC_NULL, false, &host,
1906                 "service", UC_NULL, true, &serv,
1907                 "hints", UC_OBJECT, true, &hints,
1908                 "timeout", UC_INTEGER, true, &timeout);
1909 
1910         ai_hints = hints
1911                 ? (struct addrinfo *)uv_to_struct(hints, &st_addrinfo) : NULL;
1912 
1913         if (ucv_type(host) == UC_STRING) {
1914                 char *servstr = (ucv_type(serv) != UC_STRING)
1915                         ? ucv_to_string(vm, serv) : NULL;
1916 
1917                 ret = getaddrinfo(ucv_string_get(host),
1918                         servstr ? servstr : ucv_string_get(serv),
1919                         ai_hints ? ai_hints : &(struct addrinfo){
1920                                 .ai_socktype = SOCK_STREAM
1921                         }, &ai_results);
1922 
1923                 if (ret != 0) {
1924                         free(servstr);
1925                         free(ai_hints);
1926                         err_return((ret == EAI_SYSTEM) ? errno : ret,
1927                                 "getaddrinfo()");
1928                 }
1929 
1930                 for (ai = ai_results; ai != NULL; ai = ai->ai_next) {
1931                         if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
1932                                 continue;
1933 
1934                         uc_vector_grow(&addresses);
1935                         ap = &addresses.entries[addresses.count++];
1936                         memcpy(&ap->ss, ai->ai_addr, ai->ai_addrlen);
1937                         memcpy(&ap->ai, ai, sizeof(*ai));
1938                         ap->ai.ai_addr = (struct sockaddr *)&ap->ss;
1939                 }
1940 
1941                 freeaddrinfo(ai_results);
1942                 free(servstr);
1943         }
1944         else {
1945                 uc_vector_grow(&addresses);
1946                 ap = &addresses.entries[addresses.count++];
1947 
1948                 if (!uv_to_sockaddr(host, &ap->ss, &ap->ai.ai_addrlen))
1949                         return NULL;
1950 
1951                 if (serv) {
1952                         uint64_t port = ucv_to_unsigned(serv);
1953 
1954                         if (port > 65535)
1955                                 errno = ERANGE;
1956 
1957                         if (errno != 0) {
1958                                 free(ai_hints);
1959                                 uc_vector_clear(&addresses);
1960                                 err_return(errno, "Invalid port number");
1961                         }
1962 
1963                         ((struct sockaddr_in *)&ap->ss)->sin_port = htons(port);
1964                 }
1965 
1966                 ap->ai.ai_addr = (struct sockaddr *)&ap->ss;
1967                 ap->ai.ai_family = ap->ss.ss_family;
1968                 ap->ai.ai_socktype = ai_hints ? ai_hints->ai_socktype : SOCK_STREAM;
1969                 ap->ai.ai_protocol = ai_hints ? ai_hints->ai_protocol : 0;
1970         }
1971 
1972         free(ai_hints);
1973 
1974         for (connected = 0, slot = 0, ap = &addresses.entries[slot];
1975              slot < addresses.count;
1976              slot++, ap = &addresses.entries[slot])
1977         {
1978                 uc_vector_grow(&pollfds);
1979                 pp = &pollfds.entries[pollfds.count++];
1980                 pp->events = POLLIN | POLLOUT | POLLHUP | POLLERR;
1981                 pp->fd = socket(ap->ai.ai_family, ap->ai.ai_socktype, ap->ai.ai_protocol);
1982 
1983                 if (pp->fd == -1)
1984                         continue;
1985 
1986                 if ((ap->flags = fcntl(pp->fd, F_GETFL, 0)) == -1) {
1987                         xclose(&pp->fd);
1988                         continue;
1989                 }
1990 
1991                 if (fcntl(pp->fd, F_SETFL, ap->flags | O_NONBLOCK) == -1) {
1992                         xclose(&pp->fd);
1993                         continue;
1994                 }
1995 
1996                 ret = connect(pp->fd, ap->ai.ai_addr, ap->ai.ai_addrlen);
1997 
1998                 if (ret == -1 && errno != EINPROGRESS) {
1999                         xclose(&pp->fd);
2000                         continue;
2001                 }
2002 
2003                 connected++;
2004         }
2005 
2006         if (connected == 0) {
2007                 err = EAI_NONAME;
2008                 errmsg = "Could not connect to any host address";
2009                 goto out;
2010         }
2011 
2012         ret = poll(pollfds.entries, pollfds.count,
2013                 timeout ? ucv_int64_get(timeout) : -1);
2014 
2015         if (ret == -1) {
2016                 err = errno;
2017                 errmsg = "poll()";
2018                 goto out;
2019         }
2020 
2021         for (slot = 0, ap = NULL, pp = NULL; slot < pollfds.count; slot++) {
2022                 if (pollfds.entries[slot].revents & (POLLIN|POLLOUT)) {
2023                         ap = &addresses.entries[slot];
2024                         pp = &pollfds.entries[slot];
2025                         break;
2026                 }
2027         }
2028 
2029         if (!ap) {
2030                 err = ETIMEDOUT;
2031                 errmsg = "Connection timed out";
2032                 goto out;
2033         }
2034 
2035         if (fcntl(pp->fd, F_SETFL, ap->flags) == -1) {
2036                 err = errno;
2037                 errmsg = "fcntl(F_SETFL)";
2038                 goto out;
2039         }
2040 
2041 out:
2042         for (slot = 0, ret = -1; slot < pollfds.count; slot++) {
2043                 if (pp == &pollfds.entries[slot])
2044                         ret = pollfds.entries[slot].fd;
2045                 else
2046                         xclose(&pollfds.entries[slot].fd);
2047         }
2048 
2049         uc_vector_clear(&addresses);
2050         uc_vector_clear(&pollfds);
2051 
2052         if (errmsg)
2053                 err_return(err, "%s", errmsg);
2054 
2055         ok_return(ucv_socket_new(vm, ret));
2056 }
2057 
2058 /**
2059  * Binds a listening network socket to the specified host and service.
2060  *
2061  * This high-level function combines the functionality of
2062  * {@link module:socket#create|create()},
2063  * {@link module:socket#addrinfo|addrinfo()},
2064  * {@link module:socket.socket#bind|bind()}, and
2065  * {@link module:socket.socket#listen|listen()} to simplify setting up a
2066  * listening socket with the socket module.
2067  *
2068  * @function module:socket#listen
2069  *
2070  * @param {string|number[]|module:socket.socket.SocketAddress} host
2071  * The host to bind to, can be an IP address, hostname,
2072  * {@link module:socket.socket.SocketAddress|SocketAddress}, or an array value
2073  * returned by {@link module:core#iptoarr|iptoarr()}.
2074  *
2075  * @param {string|number} [service]
2076  * The service to listen on, can be a symbolic service name (such as "http") or
2077  * a port number. Optional if host is specified as
2078  * {@link module:socket.socket.SocketAddress|SocketAddress}.
2079  *
2080  * @param {Object} [hints]
2081  * Optional preferences for the socket. It can contain the following properties:
2082  * - `family`: The preferred address family (`AF_INET` or `AF_INET6`).
2083  * - `socktype`: The socket type (`SOCK_STREAM`, `SOCK_DGRAM`, etc.).
2084  * - `protocol`: The protocol of the created socket.
2085  * - `flags`: Bitwise OR-ed `AI_*` flags to control the resolution behavior.
2086  *
2087  * If no hints are provided, the default socket type preference is set to
2088  * `SOCK_STREAM`.
2089  *
2090  * @param {number} [backlog=128]
2091  * The maximum length of the queue of pending connections.
2092  *
2093  * @returns {module:socket.socket}
2094  *
2095  * @example
2096  * // Listen for incoming TCP connections on port 80
2097  * let server = socket.listen("localhost", 80);
2098  *
2099  * // Listen on IPv6 address only
2100  * let server = socket.listen("machine.local", 8080, { family: socket.AF_INET6 });
2101  *
2102  * // Listen on a UNIX domain socket
2103  * let server = socket.listen({ path: "/var/run/server.sock" });
2104  */
2105 static uc_value_t *
2106 uc_socket_listen(uc_vm_t *vm, size_t nargs)
2107 {
2108         int ret, fd, curr_weight, prev_weight, socktype = 0, protocol = 0;
2109         struct addrinfo *ai_results, *ai_hints, *ai;
2110         uc_value_t *host, *serv, *hints, *backlog;
2111         struct sockaddr_storage ss = { 0 };
2112         bool v6, lo, ll;
2113         socklen_t slen;
2114 
2115         args_get(vm, nargs, NULL,
2116                 "host", UC_NULL, true, &host,
2117                 "service", UC_NULL, true, &serv,
2118                 "hints", UC_OBJECT, true, &hints,
2119                 "backlog", UC_INTEGER, true, &backlog);
2120 
2121         ai_hints = hints
2122                 ? (struct addrinfo *)uv_to_struct(hints, &st_addrinfo) : NULL;
2123 
2124         if (host == NULL || ucv_type(host) == UC_STRING) {
2125                 char *servstr = (ucv_type(serv) != UC_STRING)
2126                         ? ucv_to_string(vm, serv) : NULL;
2127 
2128                 ret = getaddrinfo(ucv_string_get(host),
2129                         servstr ? servstr : ucv_string_get(serv),
2130                         ai_hints ? ai_hints : &(struct addrinfo){
2131                                 .ai_flags = AI_PASSIVE | AI_ADDRCONFIG,
2132                                 .ai_socktype = SOCK_STREAM
2133                         }, &ai_results);
2134 
2135                 free(servstr);
2136 
2137                 if (ret != 0) {
2138                         free(ai_hints);
2139                         err_return((ret == EAI_SYSTEM) ? errno : ret,
2140                                 "getaddrinfo()");
2141                 }
2142 
2143                 for (ai = ai_results, prev_weight = -1; ai != NULL; ai = ai->ai_next) {
2144                         struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)ai->ai_addr;
2145                         struct sockaddr_in *s4 = (struct sockaddr_in *)ai->ai_addr;
2146 
2147                         v6 = (s6->sin6_family == AF_INET6);
2148                         ll = v6
2149                                 ? IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr)
2150                                 : ((ntohl(s4->sin_addr.s_addr) & 0xffff0000) == 0xa9fe0000);
2151                         lo = v6
2152                                 ? IN6_IS_ADDR_LOOPBACK(&s6->sin6_addr)
2153                                 : ((ntohl(s4->sin_addr.s_addr) & 0xff000000) == 0x7f000000);
2154 
2155                         curr_weight = (!lo << 2) | (v6 << 1) | (!ll << 0);
2156 
2157                         if (curr_weight > prev_weight) {
2158                                 prev_weight = curr_weight;
2159                                 socktype = ai->ai_socktype;
2160                                 protocol = ai->ai_protocol;
2161                                 slen     = ai->ai_addrlen;
2162                                 memcpy(&ss, ai->ai_addr, slen);
2163                         }
2164                 }
2165 
2166                 freeaddrinfo(ai_results);
2167         }
2168         else {
2169                 if (!uv_to_sockaddr(host, &ss, &slen)) {
2170                         free(ai_hints);
2171                         return NULL;
2172                 }
2173 
2174                 if (serv) {
2175                         uint64_t port = ucv_to_unsigned(serv);
2176 
2177                         if (port > 65535)
2178                                 errno = ERANGE;
2179 
2180                         if (errno != 0) {
2181                                 free(ai_hints);
2182                                 err_return(errno, "Invalid port number");
2183                         }
2184 
2185                         ((struct sockaddr_in *)&ss)->sin_port = htons(port);
2186                 }
2187 
2188                 socktype = ai_hints ? ai_hints->ai_socktype : SOCK_STREAM;
2189                 protocol = ai_hints ? ai_hints->ai_protocol : 0;
2190         }
2191 
2192         free(ai_hints);
2193 
2194         if (ss.ss_family == AF_UNSPEC)
2195                 err_return(EAI_NONAME, "Could not resolve host address");
2196 
2197         fd = socket(ss.ss_family, socktype, protocol);
2198 
2199         if (fd == -1)
2200                 err_return(errno, "socket()");
2201 
2202         ret = bind(fd, (struct sockaddr *)&ss, slen);
2203 
2204         if (ret == -1) {
2205                 close(fd);
2206                 err_return(errno, "bind()");
2207         }
2208 
2209         ret = listen(fd, backlog ? ucv_to_unsigned(backlog) : 128);
2210 
2211         if (ret == -1) {
2212                 close(fd);
2213                 err_return(errno, "listen()");
2214         }
2215 
2216         ok_return(ucv_socket_new(vm, fd));
2217 }
2218 
2219 /**
2220  * Represents a socket handle.
2221  *
2222  * @class module:socket.socket
2223  * @hideconstructor
2224  *
2225  * @borrows module:socket#error as module:socket.socket#error
2226  *
2227  * @see {@link module:socket#create|create()}
2228  *
2229  * @example
2230  *
2231  * const sock = create(…);
2232  *
2233  * sock.getopt(…);
2234  * sock.setopt(…);
2235  *
2236  * sock.connect(…);
2237  * sock.listen(…);
2238  * sock.accept(…);
2239  * sock.bind(…);
2240  *
2241  * sock.send(…);
2242  * sock.recv(…);
2243  *
2244  * sock.shutdown(…);
2245  *
2246  * sock.fileno();
2247  * sock.peername();
2248  * sock.sockname();
2249  *
2250  * sock.close();
2251  *
2252  * sock.error();
2253  */
2254 
2255 /**
2256  * Creates a socket and returns its hand
2257  *
2258  * The handle will be connected to the process stdin or stdout, depending on the
2259  * value of the mode argument.
2260  *
2261  * The mode argument may be either "r" to open the process for reading (connect
2262  * to its stdin) or "w" to open the process for writing (connect to its stdout).
2263  *
2264  * The mode character "r" or "w" may be optionally followed by "e" to apply the
2265  * FD_CLOEXEC flag onto the open descriptor.
2266  *
2267  * Returns a process handle referring to the executed process.
2268  *
2269  * Returns `null` if an error occurred.
2270  *
2271  * @function module:fs#popen
2272  *
2273  * @param {string} command
2274  * The command to be executed.
2275  *
2276  * @param {string} [mode="r"]
2277  * The open mode of the process handle.
2278  *
2279  * @returns {?module:fs.proc}
2280  *
2281  * @example
2282  * // Open a process
2283  * const process = popen('command', 'r');
2284  */
2285 
2286 /**
2287  * Creates a network socket instance.
2288  *
2289  * This function creates a new network socket with the specified domain and
2290  * type, determined by one of the modules `AF_*` and `SOCK_*` constants
2291  * respectively, and returns the resulting socket instance for use in subsequent
2292  * socket operations.
2293  *
2294  * The domain argument specifies the protocol family, such as AF_INET or
2295  * AF_INET6, and defaults to AF_INET if not provided.
2296  *
2297  * The type argument specifies the socket type, such as SOCK_STREAM or
2298  * SOCK_DGRAM, and defaults to SOCK_STREAM if not provided. It may also
2299  * be bitwise OR-ed with SOCK_NONBLOCK to enable non-blocking mode or
2300  * SOCK_CLOEXEC to enable close-on-exec semantics.
2301  *
2302  * The protocol argument may be used to indicate a particular protocol
2303  * to be used with the socket, and it defaults to 0 (automatically
2304  * determined protocol) if not provided.
2305  *
2306  * Returns a socket descriptor representing the newly created socket.
2307  *
2308  * Returns `null` if an error occurred during socket creation.
2309  *
2310  * @function module:socket#create
2311  *
2312  * @param {number} [domain=AF_INET]
2313  * The communication domain for the socket, e.g., AF_INET or AF_INET6.
2314  *
2315  * @param {number} [type=SOCK_STREAM]
2316  * The socket type, e.g., SOCK_STREAM or SOCK_DGRAM. It may also be
2317  * bitwise OR-ed with SOCK_NONBLOCK or SOCK_CLOEXEC.
2318  *
2319  * @param {number} [protocol=0]
2320  * The protocol to be used with the socket.
2321  *
2322  * @returns {?module:socket.socket}
2323  * A socket instance representing the newly created socket.
2324  *
2325  * @example
2326  * // Create a TCP socket
2327  * const tcp_socket = create(AF_INET, SOCK_STREAM);
2328  *
2329  * // Create a nonblocking IPv6 UDP socket
2330  * const udp_socket = create(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK);
2331  */
2332 static uc_value_t *
2333 uc_socket_create(uc_vm_t *vm, size_t nargs)
2334 {
2335         uc_value_t *domain, *type, *protocol;
2336         int sockfd, socktype;
2337 
2338         args_get(vm, nargs, NULL,
2339                 "domain", UC_INTEGER, true, &domain,
2340                 "type", UC_INTEGER, true, &type,
2341                 "protocol", UC_INTEGER, true, &protocol);
2342 
2343         socktype = type ? (int)ucv_int64_get(type) : SOCK_STREAM;
2344 
2345         sockfd = socket(
2346                 domain ? (int)ucv_int64_get(domain) : AF_INET,
2347 #if defined(__APPLE__)
2348                 socktype & ~(SOCK_NONBLOCK|SOCK_CLOEXEC),
2349 #else
2350                 socktype,
2351 #endif
2352                 protocol ? (int)ucv_int64_get(protocol) : 0);
2353 
2354         if (sockfd == -1)
2355                 err_return(errno, "socket()");
2356 
2357 #if defined(__APPLE__)
2358         if (socktype & SOCK_NONBLOCK) {
2359                 int flags = fcntl(sockfd, F_GETFL);
2360 
2361                 if (flags == -1) {
2362                         close(sockfd);
2363                         err_return(errno, "fcntl(F_GETFL)");
2364                 }
2365 
2366                 if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) == -1) {
2367                         close(sockfd);
2368                         err_return(errno, "fcntl(F_SETFL)");
2369                 }
2370         }
2371 
2372         if (socktype & SOCK_CLOEXEC) {
2373                 if (fcntl(sockfd, F_SETFD, FD_CLOEXEC) == -1) {
2374                         close(sockfd);
2375                         err_return(errno, "fcntl(F_SETFD)");
2376                 }
2377         }
2378 #endif
2379 
2380         ok_return(ucv_socket_new(vm, sockfd));
2381 }
2382 
2383 /**
2384  * Connects the socket to a remote address.
2385  *
2386  * Attempts to establish a connection to the specified remote address.
2387  *
2388  * Returns `true` if the connection is successfully established.
2389  * Returns `null` if an error occurred during the connection attempt.
2390  *
2391  * @function module:socket.socket#connect
2392  *
2393  * @param {string|module:socket.socket.SocketAddress} address
2394  * The address of the remote endpoint to connect to.
2395  *
2396  * @param {number} port
2397  * The port number of the remote endpoint to connect to.
2398  *
2399  * @returns {?boolean}
2400  */
2401 static uc_value_t *
2402 uc_socket_inst_connect(uc_vm_t *vm, size_t nargs)
2403 {
2404         struct sockaddr_storage ss;
2405         uc_value_t *addr;
2406         int ret, sockfd;
2407         socklen_t slen;
2408 
2409         args_get(vm, nargs, &sockfd,
2410                 "address", UC_NULL, false, &addr);
2411 
2412         if (!uv_to_sockaddr(addr, &ss, &slen))
2413                 return NULL;
2414 
2415         ret = connect(sockfd, (struct sockaddr *)&ss, slen);
2416 
2417         if (ret == -1)
2418                 err_return(errno, "connect()");
2419 
2420         ok_return(ucv_boolean_new(true));
2421 }
2422 
2423 /**
2424  * Sends data through the socket.
2425  *
2426  * Sends the provided data through the socket handle to the specified remote
2427  * address, if provided.
2428  *
2429  * Returns the number of bytes sent.
2430  * Returns `null` if an error occurred during the send operation.
2431  *
2432  * @function module:socket.socket#send
2433  *
2434  * @param {*} data
2435  * The data to be sent through the socket. String data is sent as-is, any other
2436  * type is implicitly converted to a string first before being sent on the
2437  * socket.
2438  *
2439  * @param {number} [flags]
2440  * Optional flags that modify the behavior of the send operation.
2441  *
2442  * @param {module:socket.socket.SocketAddress|number[]|string} [address]
2443  * The address of the remote endpoint to send the data to. It can be either an
2444  * IP address string, an array returned by {@link module:core#iptoarr|iptoarr()},
2445  * or an object representing a network address. If not provided, the data is
2446  * sent to the remote endpoint the socket is connected to.
2447  *
2448  * @returns {?number}
2449  *
2450  * @see {@link module:socket#sockaddr|sockaddr()}
2451  *
2452  * @example
2453  * // Send to connected socket
2454  * let tcp_sock = socket.create(socket.AF_INET, socket.SOCK_STREAM);
2455  * tcp_sock.connect("192.168.1.1", 80);
2456  * tcp_sock.send("GET / HTTP/1.0\r\n\r\n");
2457  *
2458  * // Send a datagram on unconnected socket
2459  * let udp_sock = socket.create(socket.AF_INET, socket.SOCK_DGRAM);
2460  * udp_sock.send("Hello there!", 0, "255.255.255.255:9000");
2461  * udp_sock.send("Hello there!", 0, {
2462  *   family: socket.AF_INET,      // optional
2463  *   address: "255.255.255.255",
2464  *   port: 9000
2465  * });
2466  */
2467 static uc_value_t *
2468 uc_socket_inst_send(uc_vm_t *vm, size_t nargs)
2469 {
2470         uc_value_t *data, *flags, *addr;
2471         struct sockaddr_storage ss = { 0 };
2472         struct sockaddr *sa = NULL;
2473         socklen_t salen = 0;
2474         char *buf = NULL;
2475         ssize_t ret;
2476         int sockfd;
2477 
2478         args_get(vm, nargs, &sockfd,
2479                 "data", UC_NULL, false, &data,
2480                 "flags", UC_INTEGER, true, &flags,
2481                 "address", UC_NULL, true, &addr);
2482 
2483         if (addr) {
2484                 if (!uv_to_sockaddr(addr, &ss, &salen))
2485                         return NULL;
2486 
2487                 sa = (struct sockaddr *)&ss;
2488         }
2489 
2490         if (ucv_type(data) != UC_STRING)
2491                 buf = ucv_to_string(vm, data);
2492 
2493         ret = sendto(sockfd,
2494                 buf ? buf : ucv_string_get(data),
2495                 buf ? strlen(buf) : ucv_string_length(data),
2496                 (flags ? ucv_int64_get(flags) : 0) | MSG_NOSIGNAL, sa, salen);
2497 
2498         free(buf);
2499 
2500         if (ret == -1)
2501                 err_return(errno, "send()");
2502 
2503         ok_return(ucv_int64_new(ret));
2504 }
2505 
2506 /**
2507  * Receives data from the socket.
2508  *
2509  * Receives data from the socket handle, optionally specifying the maximum
2510  * length of data to receive, flags to modify the receive behavior, and an
2511  * optional address dictionary where the function will place the address from
2512  * which the data was received (for unconnected sockets).
2513  *
2514  * Returns a string containing the received data.
2515  * Returns an empty string if the remote side closed the socket.
2516  * Returns `null` if an error occurred during the receive operation.
2517  *
2518  * @function module:socket.socket#recv
2519  *
2520  * @param {number} [length=4096]
2521  * The maximum number of bytes to receive.
2522  *
2523  * @param {number} [flags]
2524  * Optional flags that modify the behavior of the receive operation.
2525  *
2526  * @param {Object} [address]
2527  * An object where the function will store the address from which the data was
2528  * received. If provided, it will be filled with the details obtained from the
2529  * sockaddr argument of the underlying `recvfrom()` syscall. See the type
2530  * definition of {@link module:socket.socket.SocketAddress|SocketAddress} for
2531  * details on the format.
2532  *
2533  * @returns {?string}
2534  */
2535 static uc_value_t *
2536 uc_socket_inst_recv(uc_vm_t *vm, size_t nargs)
2537 {
2538         uc_value_t *length, *flags, *addrobj;
2539         struct sockaddr_storage ss = { 0 };
2540         uc_stringbuf_t *buf;
2541         ssize_t len, ret;
2542         socklen_t sslen;
2543         int sockfd;
2544 
2545         args_get(vm, nargs, &sockfd,
2546                 "length", UC_INTEGER, true, &length,
2547                 "flags", UC_INTEGER, true, &flags,
2548                 "address", UC_OBJECT, true, &addrobj);
2549 
2550         len = length ? ucv_to_integer(length) : 4096;
2551 
2552         if (errno || len <= 0)
2553                 err_return(errno, "Invalid length argument");
2554 
2555         buf = strbuf_alloc(len);
2556 
2557         if (!buf)
2558                 return NULL;
2559 
2560         do {
2561                 sslen = sizeof(ss);
2562                 ret = recvfrom(sockfd, strbuf_data(buf), len,
2563                         flags ? ucv_int64_get(flags) : 0, (struct sockaddr *)&ss, &sslen);
2564         } while (ret == -1 && errno == EINTR);
2565 
2566         if (ret == -1) {
2567                 strbuf_free(buf);
2568                 err_return(errno, "recv()");
2569         }
2570 
2571         if (addrobj)
2572                 sockaddr_to_uv(&ss, addrobj);
2573 
2574         ok_return(strbuf_finish(&buf, ret));
2575 }
2576 
2577 /**
2578  * Binds a socket to a specific address.
2579  *
2580  * This function binds the socket to the specified address.
2581  *
2582  * Returns `true` if the socket is successfully bound.
2583  *
2584  * Returns `null` on error, e.g. when the address is in use.
2585  *
2586  * @function module:socket.socket#bind
2587  *
2588  * @param {string|module:socket.socket.SocketAddress} address
2589  * The IP address to bind the socket to.
2590  *
2591  * @returns {?boolean}
2592  *
2593  * @example
2594  * const sock = socket.create(…);
2595  * const success = sock.bind("192.168.0.1:80");
2596  *
2597  * if (success)
2598  *     print(`Socket bound successfully!\n`);
2599  * else
2600  *     print(`Failed to bind socket: ${sock.error()}.\n`);
2601  */
2602 static uc_value_t *
2603 uc_socket_inst_bind(uc_vm_t *vm, size_t nargs)
2604 {
2605         struct sockaddr_storage ss = { 0 };
2606         uc_value_t *addr;
2607         socklen_t slen;
2608         int sockfd;
2609 
2610         args_get(vm, nargs, &sockfd,
2611                 "address", UC_NULL, true, &addr);
2612 
2613         if (addr) {
2614                 if (!uv_to_sockaddr(addr, &ss, &slen))
2615                         return NULL;
2616 
2617                 if (bind(sockfd, (struct sockaddr *)&ss, slen) == -1)
2618                         err_return(errno, "bind()");
2619         }
2620         else {
2621 #if defined(__linux__)
2622                 int sval = 0;
2623                 slen = sizeof(sval);
2624 
2625                 if (getsockopt(sockfd, SOL_SOCKET, SO_DOMAIN, &sval, &slen) == -1)
2626                         err_return(errno, "getsockopt()");
2627 
2628                 switch (sval) {
2629                 case AF_INET6:
2630                         ss.ss_family = AF_INET6;
2631                         slen = sizeof(struct sockaddr_in6);
2632                         break;
2633 
2634                 case AF_INET:
2635                         ss.ss_family = AF_INET;
2636                         slen = sizeof(struct sockaddr_in);
2637                         break;
2638 
2639                 default:
2640                         err_return(EAFNOSUPPORT, "Unsupported socket address family");
2641                 }
2642 
2643                 if (bind(sockfd, (struct sockaddr *)&ss, slen) == -1)
2644                         err_return(errno, "bind()");
2645 #else
2646                 ss.ss_family = AF_INET6;
2647                 slen = sizeof(struct sockaddr_in6);
2648 
2649                 if (bind(sockfd, (struct sockaddr *)&ss, slen) == -1) {
2650                         if (errno != EAFNOSUPPORT)
2651                                 err_return(errno, "bind()");
2652 
2653                         ss.ss_family = AF_INET;
2654                         slen = sizeof(struct sockaddr_in);
2655 
2656                         if (bind(sockfd, (struct sockaddr *)&ss, slen) == -1)
2657                                 err_return(errno, "bind()");
2658                 }
2659 #endif
2660         }
2661 
2662         ok_return(ucv_boolean_new(true));
2663 }
2664 
2665 /**
2666  * Listen for connections on a socket.
2667  *
2668  * This function marks the socket as a passive socket, that is, as a socket that
2669  * will be used to accept incoming connection requests using `accept()`.
2670  *
2671  * The `backlog` parameter specifies the maximum length to which the queue of
2672  * pending connections may grow. If a connection request arrives when the queue
2673  * is full, the client connection might get refused.
2674  *
2675  * If `backlog` is not provided, it defaults to 128.
2676  *
2677  * Returns `true` if the socket is successfully marked as passive.
2678  * Returns `null` if an error occurred, e.g. when the requested port is in use.
2679  *
2680  * @function module:socket.socket#listen
2681  *
2682  * @param {number} [backlog=128]
2683  * The maximum length of the queue of pending connections.
2684  *
2685  * @returns {?boolean}
2686  *
2687  * @see {@link module:socket.socket#accept|accept()}
2688  *
2689  * @example
2690  * const sock = socket.create(…);
2691  * sock.bind(…);
2692  *
2693  * const success = sock.listen(10);
2694  * if (success)
2695  *     print(`Socket is listening for incoming connections!\n`);
2696  * else
2697  *     print(`Failed to listen on socket: ${sock.error()}\n`);
2698  */
2699 static uc_value_t *
2700 uc_socket_inst_listen(uc_vm_t *vm, size_t nargs)
2701 {
2702         uc_value_t *backlog;
2703         int ret, sockfd;
2704 
2705         args_get(vm, nargs, &sockfd,
2706                 "backlog", UC_INTEGER, true, &backlog);
2707 
2708         ret = listen(sockfd, backlog ? ucv_to_unsigned(backlog) : 128);
2709 
2710         if (ret == -1)
2711                 err_return(errno, "listen()");
2712 
2713         ok_return(ucv_boolean_new(true));
2714 }
2715 
2716 /**
2717  * Accept a connection on a socket.
2718  *
2719  * This function accepts a connection on the socket. It extracts the first
2720  * connection request on the queue of pending connections, creates a new
2721  * connected socket, and returns a new socket handle referring to that socket.
2722  * The newly created socket is not in listening state and has no backlog.
2723  *
2724  * When a optional `address` dictionary is provided, it is populated with the
2725  * remote address details of the peer socket.
2726  *
2727  * The optional `flags` parameter is a bitwise-or-ed number of flags to modify
2728  * the behavior of accepted peer socket. Possible values are:
2729  * - `SOCK_CLOEXEC`: Enable close-on-exec semantics for the new socket.
2730  * - `SOCK_NONBLOCK`: Enable nonblocking mode for the new socket.
2731  *
2732  * Returns a socket handle representing the newly created peer socket of the
2733  * accepted connection.
2734  *
2735  * Returns `null` if an error occurred.
2736  *
2737  * @function module:socket.socket#accept
2738  *
2739  * @param {object} [address]
2740  * An optional dictionary to receive the address details of the peer socket.
2741  * See {@link module:socket.socket.SocketAddress|SocketAddress} for details.
2742  *
2743  * @param {number} [flags]
2744  * Optional flags to modify the behavior of the peer socket.
2745  *
2746  * @returns {?module:socket.socket}
2747  *
2748  * @example
2749  * const sock = socket.create(…);
2750  * sock.bind(…);
2751  * sock.listen();
2752  *
2753  * const peerAddress = {};
2754  * const newSocket = sock.accept(peerAddress, socket.SOCK_CLOEXEC);
2755  * if (newSocket)
2756  *     print(`Accepted connection from: ${peerAddress}\n`);
2757  * else
2758  *     print(`Failed to accept connection: ${sock.error()}\n`);
2759  */
2760 static uc_value_t *
2761 uc_socket_inst_accept(uc_vm_t *vm, size_t nargs)
2762 {
2763         struct sockaddr_storage ss = { 0 };
2764         int peerfd, sockfd, sockflags;
2765         uc_value_t *addrobj, *flags;
2766         socklen_t slen;
2767 
2768         args_get(vm, nargs, &sockfd,
2769                 "address", UC_OBJECT, true, &addrobj,
2770                 "flags", UC_INTEGER, true, &flags);
2771 
2772         slen = sizeof(ss);
2773         sockflags = flags ? ucv_to_integer(flags) : 0;
2774 
2775 #ifdef __APPLE__
2776         peerfd = accept(sockfd, (struct sockaddr *)&ss, &slen);
2777 
2778         if (peerfd == -1)
2779                 err_return(errno, "accept()");
2780 
2781         if (sockflags & SOCK_CLOEXEC) {
2782                 if (fcntl(peerfd, F_SETFD, FD_CLOEXEC) == -1) {
2783                         close(peerfd);
2784                         err_return(errno, "fcntl(F_SETFD)");
2785                 }
2786         }
2787 
2788         if (sockflags & SOCK_NONBLOCK) {
2789                 sockflags = fcntl(peerfd, F_GETFL);
2790 
2791                 if (sockflags == -1) {
2792                         close(peerfd);
2793                         err_return(errno, "fcntl(F_GETFL)");
2794                 }
2795 
2796                 if (fcntl(peerfd, F_SETFL, sockflags | O_NONBLOCK) == -1) {
2797                         close(peerfd);
2798                         err_return(errno, "fcntl(F_SETFL)");
2799                 }
2800         }
2801 #else
2802         peerfd = accept4(sockfd, (struct sockaddr *)&ss, &slen, sockflags);
2803 
2804         if (peerfd == -1)
2805                 err_return(errno, "accept4()");
2806 #endif
2807 
2808         if (addrobj)
2809                 sockaddr_to_uv(&ss, addrobj);
2810 
2811         ok_return(ucv_socket_new(vm, peerfd));
2812 }
2813 
2814 /**
2815  * Shutdown part of a full-duplex connection.
2816  *
2817  * This function shuts down part of the full-duplex connection associated with
2818  * the socket handle. The `how` parameter specifies which half of the connection
2819  * to shut down. It can take one of the following constant values:
2820  *
2821  * - `SHUT_RD`: Disables further receive operations.
2822  * - `SHUT_WR`: Disables further send operations.
2823  * - `SHUT_RDWR`: Disables further send and receive operations.
2824  *
2825  * Returns `true` if the shutdown operation is successful.
2826  * Returns `null` if an error occurred.
2827  *
2828  * @function module:socket.socket#shutdown
2829  *
2830  * @param {number} how
2831  * Specifies which half of the connection to shut down.
2832  * It can be one of the following constant values: `SHUT_RD`, `SHUT_WR`,
2833  * or `SHUT_RDWR`.
2834  *
2835  * @returns {?boolean}
2836  *
2837  * @example
2838  * const sock = socket.create(…);
2839  * sock.connect(…);
2840  * // Perform data exchange…
2841  *
2842  * const success = sock.shutdown(socket.SHUT_WR);
2843  * if (success)
2844  *     print(`Send operations on socket shut down successfully.\n`);
2845  * else
2846  *     print(`Failed to shut down send operations: ${sock.error()}\n`);
2847  */
2848 static uc_value_t *
2849 uc_socket_inst_shutdown(uc_vm_t *vm, size_t nargs)
2850 {
2851         uc_value_t *how;
2852         int sockfd, ret;
2853 
2854         args_get(vm, nargs, &sockfd,
2855                 "how", UC_INTEGER, true, &how);
2856 
2857         ret = shutdown(sockfd, ucv_int64_get(how));
2858 
2859         if (ret == -1)
2860                 err_return(errno, "shutdown()");
2861 
2862         ok_return(ucv_boolean_new(true));
2863 }
2864 
2865 /**
2866  * Represents a credentials information object returned by
2867  * {@link module:socket.socket#peercred|`peercred()`}.
2868  *
2869  * @typedef {Object} module:socket.socket.PeerCredentials
2870  * @property {number} uid
2871  * The effective user ID the remote socket endpoint.
2872  *
2873  * @property {number} gid
2874  * The effective group ID the remote socket endpoint.
2875  *
2876  * @property {number} pid
2877  * The ID of the process the remote socket endpoint belongs to.
2878  */
2879 
2880 /**
2881  * Retrieves the peer credentials.
2882  *
2883  * This function retrieves the remote uid, gid and pid of a connected UNIX
2884  * domain socket.
2885  *
2886  * Returns the remote credentials if the operation is successful.
2887  * Returns `null` on error.
2888  *
2889  * @function module:socket.socket#peercred
2890  *
2891  * @returns {?module:socket.socket.PeerCredentials}
2892  *
2893  * @example
2894  * const sock = socket.create(socket.AF_UNIX, …);
2895  * sock.connect(…);
2896  *
2897  * const peerCredentials = sock.peercred();
2898  * if (peerCredentials)
2899  *     print(`Peer credentials: ${peerCredentials}\n`);
2900  * else
2901  *     print(`Failed to retrieve peer credentials: ${sock.error()}\n`);
2902  */
2903 static uc_value_t *
2904 uc_socket_inst_peercred(uc_vm_t *vm, size_t nargs)
2905 {
2906         uc_value_t *rv = NULL;
2907         socklen_t optlen;
2908         int ret, sockfd;
2909 
2910         args_get(vm, nargs, &sockfd);
2911 
2912 #if defined(__linux__)
2913         struct ucred cred;
2914 
2915         optlen = sizeof(cred);
2916         ret = getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &cred, &optlen);
2917 
2918         if (ret == -1)
2919                 err_return(errno, "getsockopt()");
2920 
2921         if (optlen != sizeof(cred))
2922                 err_return(EINVAL, "Invalid credentials received");
2923 
2924         rv = ucv_object_new(vm);
2925 
2926         ucv_object_add(rv, "uid", ucv_uint64_new(cred.uid));
2927         ucv_object_add(rv, "gid", ucv_uint64_new(cred.gid));
2928         ucv_object_add(rv, "pid", ucv_int64_new(cred.pid));
2929 #elif defined(__APPLE__)
2930         struct xucred cred;
2931         pid_t pid;
2932 
2933         optlen = sizeof(cred);
2934         ret = getsockopt(sockfd, SOL_LOCAL, LOCAL_PEERCRED, &cred, &optlen);
2935 
2936         if (ret == -1)
2937                 err_return(errno, "getsockopt(LOCAL_PEERCRED)");
2938 
2939         if (optlen != sizeof(cred) || cred.cr_version != XUCRED_VERSION)
2940                 err_return(EINVAL, "Invalid credentials received");
2941 
2942         rv = ucv_object_new(vm);
2943 
2944         ucv_object_add(rv, "uid", ucv_uint64_new(cred.cr_uid));
2945         ucv_object_add(rv, "gid", ucv_uint64_new(cred.cr_gid));
2946 
2947         optlen = sizeof(pid);
2948         ret = getsockopt(sockfd, SOL_LOCAL, LOCAL_PEERPID, &pid, &optlen);
2949 
2950         if (ret == -1) {
2951                 ucv_put(rv);
2952                 err_return(errno, "getsockopt(LOCAL_PEERPID)");
2953         }
2954 
2955         ucv_object_add(rv, "pid", ucv_int64_new(pid));
2956 #else
2957         err_return(ENOSYS, "Operation not supported on this system");
2958 #endif
2959 
2960         return rv;
2961 }
2962 
2963 /**
2964  * Retrieves the remote address.
2965  *
2966  * This function retrieves the remote address of a connected socket.
2967  *
2968  * Returns the remote address if the operation is successful.
2969  * Returns `null` on error.
2970  *
2971  * @function module:socket.socket#peername
2972  *
2973  * @returns {?module:socket.socket.SocketAddress}
2974  *
2975  * @see {@link module:socket.socket#sockname|sockname()}
2976  *
2977  * @example
2978  * const sock = socket.create(…);
2979  * sock.connect(…);
2980  *
2981  * const peerAddress = sock.peername();
2982  * if (peerAddress)
2983  *     print(`Connected to ${peerAddress}\n`);
2984  * else
2985  *     print(`Failed to retrieve peer address: ${sock.error()}\n`);
2986  */
2987 static uc_value_t *
2988 uc_socket_inst_peername(uc_vm_t *vm, size_t nargs)
2989 {
2990         struct sockaddr_storage ss = { 0 };
2991         uc_value_t *addr;
2992         socklen_t sslen;
2993         int sockfd, ret;
2994 
2995         args_get(vm, nargs, &sockfd);
2996 
2997         sslen = sizeof(ss);
2998         ret = getpeername(sockfd, (struct sockaddr *)&ss, &sslen);
2999 
3000         if (ret == -1)
3001                 err_return(errno, "getpeername()");
3002 
3003         addr = ucv_object_new(vm);
3004         sockaddr_to_uv(&ss, addr);
3005 
3006         ok_return(addr);
3007 }
3008 
3009 /**
3010  * Retrieves the local address.
3011  *
3012  * This function retrieves the local address of a bound or connected socket.
3013  *
3014  * Returns the local address if the operation is successful.
3015  * Returns `null` on error.
3016  *
3017  * @function module:socket.socket#sockname
3018  *
3019  * @returns {?module:socket.socket.SocketAddress}
3020  *
3021  * @see {@link module:socket.socket#peername|peername()}
3022  *
3023  * @example
3024  * const sock = socket.create(…);
3025  * sock.connect(…);
3026  *
3027  * const myAddress = sock.sockname();
3028  * if (myAddress)
3029  *     print(`My source IP address is ${myAddress}\n`);
3030  * else
3031  *     print(`Failed to retrieve peer address: ${sock.error()}\n`);
3032  */
3033 static uc_value_t *
3034 uc_socket_inst_sockname(uc_vm_t *vm, size_t nargs)
3035 {
3036         struct sockaddr_storage ss = { 0 };
3037         uc_value_t *addr;
3038         socklen_t sslen;
3039         int sockfd, ret;
3040 
3041         args_get(vm, nargs, &sockfd);
3042 
3043         sslen = sizeof(ss);
3044         ret = getsockname(sockfd, (struct sockaddr *)&ss, &sslen);
3045 
3046         if (ret == -1)
3047                 err_return(errno, "getsockname()");
3048 
3049         addr = ucv_object_new(vm);
3050         sockaddr_to_uv(&ss, addr);
3051 
3052         ok_return(addr);
3053 }
3054 
3055 /**
3056  * Closes the socket.
3057  *
3058  * This function closes the socket, releasing its resources and terminating its
3059  * associated connections.
3060  *
3061  * Returns `true` if the socket was successfully closed.
3062  * Returns `null` on error.
3063  *
3064  * @function module:socket.socket#close
3065  *
3066  * @returns {?boolean}
3067  *
3068  * @example
3069  * const sock = socket.create(…);
3070  * sock.connect(…);
3071  * // Perform operations with the socket…
3072  * sock.close();
3073  */
3074 static uc_value_t *
3075 uc_socket_inst_close(uc_vm_t *vm, size_t nargs)
3076 {
3077         int *sockfd = uc_fn_this("socket");
3078 
3079         if (!sockfd || *sockfd == -1)
3080                 err_return(EBADF, "Invalid socket context");
3081 
3082         if (!xclose(sockfd))
3083                 err_return(errno, "close()");
3084 
3085         ok_return(ucv_boolean_new(true));
3086 }
3087 
3088 static void
3089 close_socket(void *ud)
3090 {
3091         int fd = (intptr_t)ud;
3092 
3093         if (fd != -1)
3094                 close(fd);
3095 }
3096 
3097 static const uc_function_list_t socket_fns[] = {
3098         { "connect",    uc_socket_inst_connect },
3099         { "bind",               uc_socket_inst_bind },
3100         { "listen",             uc_socket_inst_listen },
3101         { "accept",             uc_socket_inst_accept },
3102         { "send",               uc_socket_inst_send },
3103         { "recv",           uc_socket_inst_recv },
3104         { "setopt",             uc_socket_inst_setopt },
3105         { "getopt",             uc_socket_inst_getopt },
3106         { "fileno",             uc_socket_inst_fileno },
3107         { "shutdown",   uc_socket_inst_shutdown },
3108         { "peercred",   uc_socket_inst_peercred },
3109         { "peername",   uc_socket_inst_peername },
3110         { "sockname",   uc_socket_inst_sockname },
3111         { "close",              uc_socket_inst_close },
3112         { "error",              uc_socket_error },
3113 };
3114 
3115 static const uc_function_list_t global_fns[] = {
3116         { "sockaddr",   uc_socket_sockaddr },
3117         { "create",             uc_socket_create },
3118         { "nameinfo",   uc_socket_nameinfo },
3119         { "addrinfo",   uc_socket_addrinfo },
3120         { "poll",               uc_socket_poll },
3121         { "connect",    uc_socket_connect },
3122         { "listen",             uc_socket_listen },
3123         { "error",              uc_socket_error },
3124 };
3125 
3126 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
3127 {
3128         uc_function_list_register(scope, global_fns);
3129 
3130 #define ADD_CONST(x) ucv_object_add(scope, #x, ucv_int64_new(x))
3131 
3132         /**
3133          * @typedef
3134          * @name Address Families
3135          * @description Constants representing address families and socket domains.
3136          * @property {number} AF_UNSPEC - Unspecified address family.
3137          * @property {number} AF_UNIX - UNIX domain sockets.
3138          * @property {number} AF_INET - IPv4 Internet protocols.
3139          * @property {number} AF_INET6 - IPv6 Internet protocols.
3140          * @property {number} AF_PACKET - Low-level packet interface.
3141          */
3142         ADD_CONST(AF_UNSPEC);
3143         ADD_CONST(AF_UNIX);
3144         ADD_CONST(AF_INET);
3145         ADD_CONST(AF_INET6);
3146 #if defined(__linux__)
3147         ADD_CONST(AF_PACKET);
3148 #endif
3149 
3150         /**
3151          * @typedef
3152          * @name Socket Types
3153          * @description
3154          * The `SOCK_*` type and flag constants are used by
3155          * {@link module:socket#create|create()} to specify the type of socket to
3156          * open. The {@link module:socket.socket#accept|accept()} function
3157          * recognizes the `SOCK_NONBLOCK` and `SOCK_CLOEXEC` flags and applies them
3158          * to accepted peer sockets.
3159          * @property {number} SOCK_STREAM - Provides sequenced, reliable, two-way, connection-based byte streams.
3160          * @property {number} SOCK_DGRAM - Supports datagrams (connectionless, unreliable messages of a fixed maximum length).
3161          * @property {number} SOCK_RAW - Provides raw network protocol access.
3162          * @property {number} SOCK_PACKET - Obsolete and should not be used.
3163          * @property {number} SOCK_NONBLOCK - Enables non-blocking operation.
3164          * @property {number} SOCK_CLOEXEC - Sets the close-on-exec flag on the new file descriptor.
3165          */
3166         ADD_CONST(SOCK_STREAM);
3167         ADD_CONST(SOCK_DGRAM);
3168         ADD_CONST(SOCK_RAW);
3169         ADD_CONST(SOCK_NONBLOCK);
3170         ADD_CONST(SOCK_CLOEXEC);
3171 #if defined(__linux__)
3172         ADD_CONST(SOCK_PACKET);
3173 #endif
3174 
3175         /**
3176          * @typedef
3177          * @name Message Flags
3178          * @description
3179          * The `MSG_*` flag constants are commonly used in conjunction with the
3180          * {@link module:socket.socket#send|send()} and
3181          * {@link module:socket.socket#recv|recv()} functions.
3182          * @property {number} MSG_CONFIRM - Confirm path validity.
3183          * @property {number} MSG_DONTROUTE - Send without using routing tables.
3184          * @property {number} MSG_DONTWAIT - Enables non-blocking operation.
3185          * @property {number} MSG_EOR - End of record.
3186          * @property {number} MSG_MORE - Sender will send more.
3187          * @property {number} MSG_NOSIGNAL - Do not generate SIGPIPE.
3188          * @property {number} MSG_OOB - Process out-of-band data.
3189          * @property {number} MSG_FASTOPEN - Send data in TCP SYN.
3190          * @property {number} MSG_CMSG_CLOEXEC - Sets the close-on-exec flag on the received file descriptor.
3191          * @property {number} MSG_ERRQUEUE - Receive errors from ICMP.
3192          * @property {number} MSG_PEEK - Peeks at incoming messages.
3193          * @property {number} MSG_TRUNC - Report if datagram truncation occurred.
3194          * @property {number} MSG_WAITALL - Wait for full message.
3195          */
3196         ADD_CONST(MSG_DONTROUTE);
3197         ADD_CONST(MSG_DONTWAIT);
3198         ADD_CONST(MSG_EOR);
3199         ADD_CONST(MSG_NOSIGNAL);
3200         ADD_CONST(MSG_OOB);
3201         ADD_CONST(MSG_PEEK);
3202         ADD_CONST(MSG_TRUNC);
3203         ADD_CONST(MSG_WAITALL);
3204 #if defined(__linux__)
3205         ADD_CONST(MSG_CONFIRM);
3206         ADD_CONST(MSG_MORE);
3207         ADD_CONST(MSG_FASTOPEN);
3208         ADD_CONST(MSG_CMSG_CLOEXEC);
3209         ADD_CONST(MSG_ERRQUEUE);
3210 #endif
3211 
3212         /**
3213          * @typedef
3214          * @name IP Protocol Constants
3215          * @description
3216          * The `IPPROTO_IP` constant specifies the IP protocol number and may be
3217          * passed as third argument to {@link module:socket#create|create()} as well
3218          * as *level* argument value to {@link module:socket.socket#getopt|getopt()}
3219          * and {@link module:socket.socket#setopt|setopt()}.
3220          *
3221          * The `IP_*` constants are option names recognized by
3222          * {@link module:socket.socket#getopt|getopt()}
3223          * and {@link module:socket.socket#setopt|setopt()}, in conjunction with
3224          * the `IPPROTO_IP` socket level.
3225          * @property {number} IPPROTO_IP - Dummy protocol for IP.
3226          * @property {number} IP_ADD_MEMBERSHIP - Add an IP group membership.
3227          * @property {number} IP_ADD_SOURCE_MEMBERSHIP - Add an IP group/source membership.
3228          * @property {number} IP_BIND_ADDRESS_NO_PORT - Bind to the device only.
3229          * @property {number} IP_BLOCK_SOURCE - Block IP group/source.
3230          * @property {number} IP_DROP_MEMBERSHIP - Drop an IP group membership.
3231          * @property {number} IP_DROP_SOURCE_MEMBERSHIP - Drop an IP group/source membership.
3232          * @property {number} IP_FREEBIND - Allow binding to an IP address not assigned to a network interface.
3233          * @property {number} IP_HDRINCL - Header is included with data.
3234          * @property {number} IP_MSFILTER - Filter IP multicast source memberships.
3235          * @property {number} IP_MTU - Path MTU discovery.
3236          * @property {number} IP_MTU_DISCOVER - Control Path MTU discovery.
3237          * @property {number} IP_MULTICAST_ALL - Receive all multicast packets.
3238          * @property {number} IP_MULTICAST_IF - Set outgoing interface for multicast packets.
3239          * @property {number} IP_MULTICAST_LOOP - Control multicast packet looping.
3240          * @property {number} IP_MULTICAST_TTL - Set time-to-live for outgoing multicast packets.
3241          * @property {number} IP_NODEFRAG - Don't fragment IP packets.
3242          * @property {number} IP_OPTIONS - Set/get IP options.
3243          * @property {number} IP_PASSSEC - Pass security information.
3244          * @property {number} IP_PKTINFO - Receive packet information.
3245          * @property {number} IP_RECVERR - Receive all ICMP errors.
3246          * @property {number} IP_RECVOPTS - Receive all IP options.
3247          * @property {number} IP_RECVORIGDSTADDR - Receive original destination address of the socket.
3248          * @property {number} IP_RECVTOS - Receive IP TOS.
3249          * @property {number} IP_RECVTTL - Receive IP TTL.
3250          * @property {number} IP_RETOPTS - Set/get IP options.
3251          * @property {number} IP_ROUTER_ALERT - Receive ICMP msgs generated by router.
3252          * @property {number} IP_TOS - IP type of service and precedence.
3253          * @property {number} IP_TRANSPARENT - Transparent proxy support.
3254          * @property {number} IP_TTL - IP time-to-live.
3255          * @property {number} IP_UNBLOCK_SOURCE - Unblock IP group/source.
3256          */
3257         ADD_CONST(IPPROTO_IP);
3258         ADD_CONST(IP_ADD_MEMBERSHIP);
3259         ADD_CONST(IP_ADD_SOURCE_MEMBERSHIP);
3260         ADD_CONST(IP_BLOCK_SOURCE);
3261         ADD_CONST(IP_DROP_MEMBERSHIP);
3262         ADD_CONST(IP_DROP_SOURCE_MEMBERSHIP);
3263         ADD_CONST(IP_HDRINCL);
3264         ADD_CONST(IP_MSFILTER);
3265         ADD_CONST(IP_MULTICAST_IF);
3266         ADD_CONST(IP_MULTICAST_LOOP);
3267         ADD_CONST(IP_MULTICAST_TTL);
3268         ADD_CONST(IP_OPTIONS);
3269         ADD_CONST(IP_PKTINFO);
3270         ADD_CONST(IP_RECVOPTS);
3271         ADD_CONST(IP_RECVTOS);
3272         ADD_CONST(IP_RECVTTL);
3273         ADD_CONST(IP_RETOPTS);
3274         ADD_CONST(IP_TOS);
3275         ADD_CONST(IP_TTL);
3276         ADD_CONST(IP_UNBLOCK_SOURCE);
3277 #if defined(__linux__)
3278         ADD_CONST(IP_BIND_ADDRESS_NO_PORT);
3279         ADD_CONST(IP_FREEBIND);
3280         ADD_CONST(IP_MTU);
3281         ADD_CONST(IP_MTU_DISCOVER);
3282         ADD_CONST(IP_MULTICAST_ALL);
3283         ADD_CONST(IP_NODEFRAG);
3284         ADD_CONST(IP_PASSSEC);
3285         ADD_CONST(IP_RECVERR);
3286         ADD_CONST(IP_RECVORIGDSTADDR);
3287         ADD_CONST(IP_ROUTER_ALERT);
3288         ADD_CONST(IP_TRANSPARENT);
3289 #endif
3290 
3291         /**
3292          * @typedef
3293          * @name Socket Option Constants
3294          * @description
3295          * The `SOL_SOCKET` constant is passed as *level* argument to the
3296          * {@link module:socket.socket#getopt|getopt()} and
3297          * {@link module:socket.socket#setopt|setopt()} functions in order to set
3298          * or retrieve generic socket option values.
3299          *
3300          * The `SO_*` constants are passed as *option* argument in conjunction with
3301          * the `SOL_SOCKET` level to specify the specific option to get or set on
3302          * the socket.
3303          * @property {number} SOL_SOCKET - Socket options at the socket API level.
3304          * @property {number} SO_ACCEPTCONN - Reports whether socket listening is enabled.
3305          * @property {number} SO_ATTACH_BPF - Attach BPF program to socket.
3306          * @property {number} SO_ATTACH_FILTER - Attach a socket filter.
3307          * @property {number} SO_ATTACH_REUSEPORT_CBPF - Attach BPF program for cgroup and skb program reuseport hook.
3308          * @property {number} SO_ATTACH_REUSEPORT_EBPF - Attach eBPF program for cgroup and skb program reuseport hook.
3309          * @property {number} SO_BINDTODEVICE - Bind socket to a specific interface.
3310          * @property {number} SO_BROADCAST - Allow transmission of broadcast messages.
3311          * @property {number} SO_BUSY_POLL - Enable busy polling.
3312          * @property {number} SO_DEBUG - Enable socket debugging.
3313          * @property {number} SO_DETACH_BPF - Detach BPF program from socket.
3314          * @property {number} SO_DETACH_FILTER - Detach a socket filter.
3315          * @property {number} SO_DOMAIN - Retrieves the domain of the socket.
3316          * @property {number} SO_DONTROUTE - Send packets directly without routing.
3317          * @property {number} SO_ERROR - Retrieves and clears the error status for the socket.
3318          * @property {number} SO_INCOMING_CPU - Retrieves the CPU number on which the last packet was received.
3319          * @property {number} SO_INCOMING_NAPI_ID - Retrieves the NAPI ID of the device.
3320          * @property {number} SO_KEEPALIVE - Enable keep-alive packets.
3321          * @property {number} SO_LINGER - Set linger on close.
3322          * @property {number} SO_LOCK_FILTER - Set or get the socket filter lock state.
3323          * @property {number} SO_MARK - Set the mark for packets sent through the socket.
3324          * @property {number} SO_OOBINLINE - Enables out-of-band data to be received in the normal data stream.
3325          * @property {number} SO_PASSCRED - Enable the receiving of SCM_CREDENTIALS control messages.
3326          * @property {number} SO_PASSSEC - Enable the receiving of security context.
3327          * @property {number} SO_PEEK_OFF - Returns the number of bytes in the receive buffer without removing them.
3328          * @property {number} SO_PEERCRED - Retrieves the credentials of the foreign peer.
3329          * @property {number} SO_PEERSEC - Retrieves the security context of the foreign peer.
3330          * @property {number} SO_PRIORITY - Set the protocol-defined priority for all packets.
3331          * @property {number} SO_PROTOCOL - Retrieves the protocol number.
3332          * @property {number} SO_RCVBUF - Set the receive buffer size.
3333          * @property {number} SO_RCVBUFFORCE - Set the receive buffer size forcefully.
3334          * @property {number} SO_RCVLOWAT - Set the minimum number of bytes to process for input operations.
3335          * @property {number} SO_RCVTIMEO - Set the timeout for receiving data.
3336          * @property {number} SO_REUSEADDR - Allow the socket to be bound to an address that is already in use.
3337          * @property {number} SO_REUSEPORT - Enable duplicate address and port bindings.
3338          * @property {number} SO_RXQ_OVFL - Reports if the receive queue has overflown.
3339          * @property {number} SO_SNDBUF - Set the send buffer size.
3340          * @property {number} SO_SNDBUFFORCE - Set the send buffer size forcefully.
3341          * @property {number} SO_SNDLOWAT - Set the minimum number of bytes to process for output operations.
3342          * @property {number} SO_SNDTIMEO - Set the timeout for sending data.
3343          * @property {number} SO_TIMESTAMP - Enable receiving of timestamps.
3344          * @property {number} SO_TIMESTAMPNS - Enable receiving of nanosecond timestamps.
3345          * @property {number} SO_TYPE - Retrieves the type of the socket (e.g., SOCK_STREAM).
3346          */
3347         ADD_CONST(SOL_SOCKET);
3348         ADD_CONST(SO_ACCEPTCONN);
3349         ADD_CONST(SO_BROADCAST);
3350         ADD_CONST(SO_DEBUG);
3351         ADD_CONST(SO_DONTROUTE);
3352         ADD_CONST(SO_ERROR);
3353         ADD_CONST(SO_KEEPALIVE);
3354         ADD_CONST(SO_LINGER);
3355         ADD_CONST(SO_OOBINLINE);
3356         ADD_CONST(SO_RCVBUF);
3357         ADD_CONST(SO_RCVLOWAT);
3358         ADD_CONST(SO_RCVTIMEO);
3359         ADD_CONST(SO_REUSEADDR);
3360         ADD_CONST(SO_REUSEPORT);
3361         ADD_CONST(SO_SNDBUF);
3362         ADD_CONST(SO_SNDLOWAT);
3363         ADD_CONST(SO_SNDTIMEO);
3364         ADD_CONST(SO_TIMESTAMP);
3365         ADD_CONST(SO_TYPE);
3366 #if defined(__linux__)
3367         ADD_CONST(SO_ATTACH_BPF);
3368         ADD_CONST(SO_ATTACH_FILTER);
3369         ADD_CONST(SO_ATTACH_REUSEPORT_CBPF);
3370         ADD_CONST(SO_ATTACH_REUSEPORT_EBPF);
3371         ADD_CONST(SO_BINDTODEVICE);
3372         ADD_CONST(SO_BUSY_POLL);
3373         ADD_CONST(SO_DETACH_BPF);
3374         ADD_CONST(SO_DETACH_FILTER);
3375         ADD_CONST(SO_DOMAIN);
3376         ADD_CONST(SO_INCOMING_CPU);
3377         ADD_CONST(SO_INCOMING_NAPI_ID);
3378         ADD_CONST(SO_LOCK_FILTER);
3379         ADD_CONST(SO_MARK);
3380         ADD_CONST(SO_PASSCRED);
3381         ADD_CONST(SO_PASSSEC);
3382         ADD_CONST(SO_PEEK_OFF);
3383         ADD_CONST(SO_PEERCRED);
3384         ADD_CONST(SO_PEERSEC);
3385         ADD_CONST(SO_PRIORITY);
3386         ADD_CONST(SO_PROTOCOL);
3387         ADD_CONST(SO_RCVBUFFORCE);
3388         ADD_CONST(SO_RXQ_OVFL);
3389         ADD_CONST(SO_SNDBUFFORCE);
3390         ADD_CONST(SO_TIMESTAMPNS);
3391 #endif
3392 
3393         /**
3394          * @typedef
3395          * @name TCP Protocol Constants
3396          * @description
3397          * The `IPPROTO_TCP` constant specifies the TCP protocol number and may be
3398          * passed as third argument to {@link module:socket#create|create()} as well
3399          * as *level* argument value to {@link module:socket.socket#getopt|getopt()}
3400          * and {@link module:socket.socket#setopt|setopt()}.
3401          *
3402          * The `TCP_*` constants are *option* argument values recognized by
3403          * {@link module:socket.socket#getopt|getopt()}
3404          * and {@link module:socket.socket#setopt|setopt()}, in conjunction with
3405          * the `IPPROTO_TCP` socket level.
3406          * @property {number} IPPROTO_TCP - TCP protocol.
3407          * @property {number} TCP_CONGESTION - Set the congestion control algorithm.
3408          * @property {number} TCP_CORK - Delay packet transmission until full-sized packets are available.
3409          * @property {number} TCP_DEFER_ACCEPT - Delay accepting incoming connections until data arrives.
3410          * @property {number} TCP_FASTOPEN - Enable TCP Fast Open.
3411          * @property {number} TCP_FASTOPEN_CONNECT - Perform TFO connect.
3412          * @property {number} TCP_INFO - Retrieve TCP statistics.
3413          * @property {number} TCP_KEEPCNT - Number of keepalive probes.
3414          * @property {number} TCP_KEEPIDLE - Time before keepalive probes begin.
3415          * @property {number} TCP_KEEPINTVL - Interval between keepalive probes.
3416          * @property {number} TCP_LINGER2 - Lifetime of orphaned FIN_WAIT2 state sockets.
3417          * @property {number} TCP_MAXSEG - Maximum segment size.
3418          * @property {number} TCP_NODELAY - Disable Nagle's algorithm.
3419          * @property {number} TCP_QUICKACK - Enable quick ACKs.
3420          * @property {number} TCP_SYNCNT - Number of SYN retransmits.
3421          * @property {number} TCP_USER_TIMEOUT - Set the user timeout.
3422          * @property {number} TCP_WINDOW_CLAMP - Set the maximum window.
3423          */
3424         ADD_CONST(IPPROTO_TCP);
3425         ADD_CONST(TCP_FASTOPEN);
3426         ADD_CONST(TCP_KEEPCNT);
3427         ADD_CONST(TCP_KEEPINTVL);
3428         ADD_CONST(TCP_MAXSEG);
3429         ADD_CONST(TCP_NODELAY);
3430 #if defined(__linux__)
3431         ADD_CONST(TCP_CONGESTION);
3432         ADD_CONST(TCP_CORK);
3433         ADD_CONST(TCP_DEFER_ACCEPT);
3434         ADD_CONST(TCP_FASTOPEN_CONNECT);
3435         ADD_CONST(TCP_INFO);
3436         ADD_CONST(TCP_KEEPIDLE);
3437         ADD_CONST(TCP_LINGER2);
3438         ADD_CONST(TCP_QUICKACK);
3439         ADD_CONST(TCP_SYNCNT);
3440         ADD_CONST(TCP_USER_TIMEOUT);
3441         ADD_CONST(TCP_WINDOW_CLAMP);
3442 #endif
3443 
3444         /**
3445          * @typedef
3446          * @name UDP Protocol Constants
3447          * @description
3448          * The `IPPROTO_UDP` constant specifies the UDP protocol number and may be
3449          * passed as third argument to {@link module:socket#create|create()} as well
3450          * as *level* argument value to {@link module:socket.socket#getopt|getopt()}
3451          * and {@link module:socket.socket#setopt|setopt()}.
3452          *
3453          * The `UDP_*` constants are *option* argument values recognized by
3454          * {@link module:socket.socket#getopt|getopt()}
3455          * and {@link module:socket.socket#setopt|setopt()}, in conjunction with
3456          * the `IPPROTO_UDP` socket level.
3457          * @property {number} IPPROTO_UDP - UDP protocol.
3458          * @property {number} UDP_CORK - Cork data until flush.
3459          */
3460         ADD_CONST(IPPROTO_UDP);
3461 #if defined(__linux__)
3462         ADD_CONST(UDP_CORK);
3463 #endif
3464 
3465         /**
3466          * @typedef
3467          * @name Shutdown Constants
3468          * @description
3469          * The `SHUT_*` constants are passed as argument to the
3470          * {@link module:socket.socket#shutdown|shutdown()} function to specify
3471          * which direction of a full duplex connection to shut down.
3472          * @property {number} SHUT_RD - Disallow further receptions.
3473          * @property {number} SHUT_WR - Disallow further transmissions.
3474          * @property {number} SHUT_RDWR - Disallow further receptions and transmissions.
3475          */
3476         ADD_CONST(SHUT_RD);
3477         ADD_CONST(SHUT_WR);
3478         ADD_CONST(SHUT_RDWR);
3479 
3480         /**
3481          * @typedef
3482          * @name Address Info Flags
3483          * @description
3484          * The `AI_*` flags may be passed as bitwise OR-ed number in the *flags*
3485          * property of the *hints* dictionary argument of
3486          * {@link module:socket#addrinfo|addrinfo()}.
3487          * @property {number} AI_ADDRCONFIG - Address configuration flag.
3488          * @property {number} AI_ALL - Return IPv4 and IPv6 socket addresses.
3489          * @property {number} AI_CANONIDN - Canonicalize using the IDNA standard.
3490          * @property {number} AI_CANONNAME - Fill in the canonical name field.
3491          * @property {number} AI_IDN - Enable IDN encoding.
3492          * @property {number} AI_NUMERICHOST - Prevent hostname resolution.
3493          * @property {number} AI_NUMERICSERV - Prevent service name resolution.
3494          * @property {number} AI_PASSIVE - Use passive socket.
3495          * @property {number} AI_V4MAPPED - Map IPv6 addresses to IPv4-mapped format.
3496          */
3497         ADD_CONST(AI_ADDRCONFIG);
3498         ADD_CONST(AI_ALL);
3499         ADD_CONST(AI_CANONIDN);
3500         ADD_CONST(AI_CANONNAME);
3501         ADD_CONST(AI_IDN);
3502         ADD_CONST(AI_NUMERICHOST);
3503         ADD_CONST(AI_NUMERICSERV);
3504         ADD_CONST(AI_PASSIVE);
3505         ADD_CONST(AI_V4MAPPED);
3506 
3507         /**
3508          * @typedef
3509          * @name Name Info Constants
3510          * @description
3511          * The `NI_*` flags may be passed as bitwise OR-ed number via the *flags*
3512          * argument of {@link module:socket#nameinfo|nameinfo()}.
3513          * @property {number} NI_DGRAM - Datagram socket type.
3514          * @property {number} NI_IDN - Enable IDN encoding.
3515          * @property {number} NI_NAMEREQD - Hostname resolution required.
3516          * @property {number} NI_NOFQDN - Do not force fully qualified domain name.
3517          * @property {number} NI_NUMERICHOST - Return numeric form of the hostname.
3518          * @property {number} NI_NUMERICSERV - Return numeric form of the service name.
3519          */
3520         ADD_CONST(NI_DGRAM);
3521         ADD_CONST(NI_IDN);
3522         ADD_CONST(NI_MAXHOST);
3523         ADD_CONST(NI_MAXSERV);
3524         ADD_CONST(NI_NAMEREQD);
3525         ADD_CONST(NI_NOFQDN);
3526         ADD_CONST(NI_NUMERICHOST);
3527         ADD_CONST(NI_NUMERICSERV);
3528 
3529         /**
3530          * @typedef
3531          * @name Poll Event Constants
3532          * @description
3533          * The following constants represent event types for polling operations and
3534          * are set or returned as part of a
3535          * {@link module:socket.PollSpec|PollSpec} tuple by the
3536          * {@link module:socket#poll|poll()} function. When passed via an argument
3537          * PollSpec to `poll()`, they specify the I/O events to watch for on the
3538          * corresponding handle. When appearing in a PollSpec returned by `poll()`,
3539          * they specify the I/O events that occurred on a watched handle.
3540          * @property {number} POLLIN - Data available to read.
3541          * @property {number} POLLPRI - Priority data available to read.
3542          * @property {number} POLLOUT - Writable data available.
3543          * @property {number} POLLERR - Error condition.
3544          * @property {number} POLLHUP - Hang up.
3545          * @property {number} POLLNVAL - Invalid request.
3546          * @property {number} POLLRDHUP - Peer closed or shutdown writing.
3547          */
3548         ADD_CONST(POLLIN);
3549         ADD_CONST(POLLPRI);
3550         ADD_CONST(POLLOUT);
3551         ADD_CONST(POLLERR);
3552         ADD_CONST(POLLHUP);
3553         ADD_CONST(POLLNVAL);
3554 #if defined(__linux__)
3555         ADD_CONST(POLLRDHUP);
3556 #endif
3557 
3558         uc_type_declare(vm, "socket", socket_fns, close_socket);
3559 }
3560 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt