From 2f12ad69e4bfe278a5103efe138a0410ecf13e6c Mon Sep 17 00:00:00 2001
From: Jo-Philipp Wich <jo@mein.io>
Date: Fri, 19 Apr 2024 09:19:12 +0200
Subject: [PATCH] lib: introduce socket library

Introduce a new socket module which provides bindings for the BSD sockets
API to ucode scripts.

Example usage:

    import * as socket from 'socket';

    let sk = socket.create(socket.AF_INET, socket.SOCK_STREAM);
    sk.connect("192.168.1.1", 80);
    sk.send("GET / HTTP/1.0\r\n\r\n");
    print(sk.recv(4096));
    sk.close();

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
---
 CMakeLists.txt |    8 +
 lib/socket.c   | 2087 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 2095 insertions(+)
 create mode 100644 lib/socket.c

diff --git a/CMakeLists.txt b/CMakeLists.txt
index ade8aac4..084c4417 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -60,6 +60,7 @@ option(RESOLV_SUPPORT "NS resolve plugin support" ON)
 option(STRUCT_SUPPORT "Struct plugin support" ON)
 option(ULOOP_SUPPORT "Uloop plugin support" ${DEFAULT_ULOOP_SUPPORT})
 option(LOG_SUPPORT "Log plugin support" ON)
+option(SOCKET_SUPPORT "Socket plugin support" ON)
 
 set(LIB_SEARCH_PATH "${CMAKE_INSTALL_PREFIX}/lib/ucode/*.so:${CMAKE_INSTALL_PREFIX}/share/ucode/*.uc:./*.so:./*.uc" CACHE STRING "Default library search path")
 string(REPLACE ":" "\", \"" LIB_SEARCH_DEFINE "${LIB_SEARCH_PATH}")
@@ -266,6 +267,13 @@ if(LOG_SUPPORT)
   endif()
 endif()
 
+if(SOCKET_SUPPORT)
+  set(LIBRARIES ${LIBRARIES} socket_lib)
+  add_library(socket_lib MODULE lib/socket.c)
+  set_target_properties(socket_lib PROPERTIES OUTPUT_NAME socket PREFIX "")
+  target_link_options(socket_lib PRIVATE ${UCODE_MODULE_LINK_OPTIONS})
+endif()
+
 if(UNIT_TESTING)
   enable_testing()
   add_definitions(-DUNIT_TESTING)
diff --git a/lib/socket.c b/lib/socket.c
new file mode 100644
index 00000000..a6bd40e7
--- /dev/null
+++ b/lib/socket.c
@@ -0,0 +1,2087 @@
+/*
+ * Copyright (C) 2024 Jo-Philipp Wich <jo@mein.io>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * # Socket Module
+ *
+ * The `socket` module provides functions for interacting with sockets.
+ *
+ * Functions can be individually imported and directly accessed using the
+ * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#named_import named import}
+ * syntax:
+ *
+ *   ```javascript
+ *   import { AF_INET, SOCK_STREAM, create as socket } from 'socket';
+ *
+ *   let sock = socket(AF_INET, SOCK_STREAM, 0);
+ *   sock.connect('192.168.1.1', 80);
+ *   sock.send(…);
+ *   sock.recv(…);
+ *   sock.close();
+ *   ```
+ *
+ * Alternatively, the module namespace can be imported
+ * using a wildcard import statement:
+ *
+ *   ```javascript
+ *   import * as socket from 'socket';
+ *
+ *   let sock = socket.create(socket.AF_INET, socket.SOCK_STREAM, 0);
+ *   sock.connect('192.168.1.1', 80);
+ *   sock.send(…);
+ *   sock.recv(…);
+ *   sock.close();
+ *   ```
+ *
+ * Additionally, the socket module namespace may also be imported by invoking
+ * the `ucode` interpreter with the `-lsocket` switch.
+ *
+ * @module socket
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <net/if.h>
+#include <ctype.h>
+
+#include "ucode/module.h"
+#include "ucode/platform.h"
+
+#if defined(__APPLE__)
+# define SOCK_NONBLOCK (1 << 16)
+# define SOCK_CLOEXEC  (1 << 17)
+#endif
+
+#define ok_return(expr) do { set_error(0, NULL); return (expr); } while(0)
+#define err_return(err, ...) do { set_error(err, __VA_ARGS__); return NULL; } while(0)
+
+static struct {
+	int code;
+	char *msg;
+} last_error;
+
+__attribute__((format(printf, 2, 3))) static void
+set_error(int errcode, const char *fmt, ...)
+{
+	va_list ap;
+
+	free(last_error.msg);
+
+	last_error.code = errcode;
+	last_error.msg = NULL;
+
+	if (fmt) {
+		va_start(ap, fmt);
+		xvasprintf(&last_error.msg, fmt, ap);
+		va_end(ap);
+	}
+}
+
+static char *
+arg_type_(uc_type_t type)
+{
+	switch (type) {
+	case UC_INTEGER:  return "an integer value";
+	case UC_BOOLEAN:  return "a boolean value";
+	case UC_STRING:   return "a string value";
+	case UC_DOUBLE:   return "a double value";
+	case UC_ARRAY:    return "an array";
+	case UC_OBJECT:   return "an object";
+	case UC_REGEXP:   return "a regular expression";
+	case UC_CLOSURE:  return "a function";
+	case UC_RESOURCE: return "a resource value";
+	default:          return "the expected type";
+	}
+}
+
+static bool
+args_get_(uc_vm_t *vm, size_t nargs, int *fdptr, ...)
+{
+	const char *name, *rtype = NULL;
+	uc_value_t **ptr, *arg;
+	uc_type_t type, t;
+	size_t index = 0;
+	int *sockfd;
+	va_list ap;
+	bool opt;
+
+	if (fdptr) {
+		sockfd = uc_fn_this("socket");
+
+		if (!sockfd || *sockfd == -1)
+			err_return(EBADF, "Invalid socket context");
+
+		*fdptr = *sockfd;
+	}
+
+	va_start(ap, fdptr);
+
+	while (true) {
+		name = va_arg(ap, const char *);
+
+		if (!name)
+			break;
+
+		arg = uc_fn_arg(index++);
+
+		type = va_arg(ap, uc_type_t);
+		opt = va_arg(ap, int);
+		ptr = va_arg(ap, uc_value_t **);
+
+		if (type == UC_RESOURCE) {
+			rtype = name;
+			name = strrchr(rtype, '.');
+			name = name ? name + 1 : rtype;
+
+			if (arg && !ucv_resource_dataptr(arg, rtype))
+				err_return(EINVAL,
+					"Argument %s is not a %s resource", name, rtype);
+		}
+
+		if (!opt && !arg)
+			err_return(EINVAL,
+				"Argument %s is required", name);
+
+		t = ucv_type(arg);
+
+		if (t == UC_CFUNCTION)
+			t = UC_CLOSURE;
+
+		if (arg && type != UC_NULL && t != type)
+			err_return(EINVAL,
+				"Argument %s is not %s", name, arg_type_(type));
+
+		*ptr = arg;
+	}
+
+	va_end(ap);
+
+	ok_return(true);
+}
+
+#define args_get(vm, nargs, fdptr, ...) do { \
+	if (!args_get_(vm, nargs, fdptr, ##__VA_ARGS__, NULL)) \
+		return NULL; \
+} while(0)
+
+static int
+addr_get(uc_value_t *addr, struct sockaddr_storage *ss)
+{
+	struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)ss;
+	struct sockaddr_in *s4 = (struct sockaddr_in *)ss;
+
+	memset(ss, 0, sizeof(*ss));
+
+	if (inet_pton(AF_INET6, ucv_string_get(addr), &s6->sin6_addr) == 1)
+		return (s6->sin6_family = AF_INET6);
+
+	if (inet_pton(AF_INET, ucv_string_get(addr), &s4->sin_addr) == 1)
+		return (s4->sin_family = AF_INET);
+
+	return 0;
+}
+
+static void
+strbuf_free(uc_stringbuf_t *sb)
+{
+	printbuf_free(sb);
+}
+
+static bool
+strbuf_grow(uc_stringbuf_t *sb, size_t size)
+{
+	if (size > 0) {
+		if (printbuf_memset(sb, sizeof(uc_string_t) + size - 1, '\0', 1))
+			err_return(ENOMEM, "Out of memory");
+	}
+
+	return true;
+}
+
+static char *
+strbuf_data(uc_stringbuf_t *sb)
+{
+	return sb->buf + sizeof(uc_string_t);
+}
+
+static size_t
+strbuf_size(uc_stringbuf_t *sb)
+{
+	return (size_t)sb->bpos - sizeof(uc_string_t);
+}
+
+static uc_value_t *
+strbuf_finish(uc_stringbuf_t **sb, size_t final_size)
+{
+	uc_string_t *us = (uc_string_t *)(*sb)->buf;
+	size_t buffer_size = strbuf_size(*sb);
+
+	if (final_size > buffer_size)
+		final_size = buffer_size;
+
+	free(*sb);
+	*sb = NULL;
+
+	us = xrealloc(us, sizeof(uc_string_t) + final_size + 1);
+	us->length = final_size;
+	us->str[us->length] = 0;
+
+	return &us->header;
+}
+
+static uc_stringbuf_t *
+strbuf_alloc(size_t size)
+{
+	uc_stringbuf_t *sb = ucv_stringbuf_new();
+
+	if (!strbuf_grow(sb, size)) {
+		printbuf_free(sb);
+
+		return NULL;
+	}
+
+	return sb;
+}
+
+static bool
+sockaddr_to_uv(struct sockaddr_storage *ss, uc_value_t *addrobj)
+{
+	char *ifname, addrstr[INET6_ADDRSTRLEN];
+	struct sockaddr_in6 *s6;
+	struct sockaddr_in *s4;
+	struct sockaddr_un *su;
+
+	ucv_object_add(addrobj, "family", ucv_uint64_new(ss->ss_family));
+
+	switch (ss->ss_family) {
+	case AF_INET6:
+		s6 = (struct sockaddr_in6 *)ss;
+
+		inet_ntop(AF_INET6, &s6->sin6_addr, addrstr, sizeof(addrstr));
+		ucv_object_add(addrobj, "address",
+			ucv_string_new(addrstr));
+
+		ucv_object_add(addrobj, "port",
+			ucv_uint64_new(ntohs(s6->sin6_port)));
+
+		ucv_object_add(addrobj, "flowinfo",
+			ucv_uint64_new(ntohl(s6->sin6_flowinfo)));
+
+		if (s6->sin6_scope_id) {
+			ifname = if_indextoname(s6->sin6_scope_id, addrstr);
+
+			if (ifname)
+				ucv_object_add(addrobj, "interface",
+					ucv_string_new(ifname));
+			else
+				ucv_object_add(addrobj, "interface",
+					ucv_uint64_new(s6->sin6_scope_id));
+		}
+
+		return true;
+
+	case AF_INET:
+		s4 = (struct sockaddr_in *)ss;
+
+		inet_ntop(AF_INET, &s4->sin_addr, addrstr, sizeof(addrstr));
+		ucv_object_add(addrobj, "address",
+			ucv_string_new(addrstr));
+
+		ucv_object_add(addrobj, "port",
+			ucv_uint64_new(ntohs(s4->sin_port)));
+
+		return true;
+
+	case AF_UNIX:
+		su = (struct sockaddr_un *)ss;
+
+		ucv_object_add(addrobj, "path",
+			ucv_string_new(su->sun_path));
+
+		return true;
+	}
+
+	return false;
+}
+
+static bool
+parse_addr(char *addr, struct sockaddr_storage *ss)
+{
+	bool v6 = (ss->ss_family == 0 || ss->ss_family == AF_INET6);
+	bool v4 = (ss->ss_family == 0 || ss->ss_family == AF_INET);
+	struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)ss;
+	struct sockaddr_in *s4 = (struct sockaddr_in *)ss;
+	unsigned long n;
+	char *scope, *e;
+
+	if (v6 && (scope = strchr(addr, '%')) != NULL) {
+		*scope++ = 0;
+		n = strtoul(scope, &e, 10);
+
+		if (e == scope || *e != 0) {
+			n = if_nametoindex(scope);
+
+			if (n == 0)
+				err_return(errno, "Unable to resolve interface %s", scope);
+		}
+
+		if (inet_pton(AF_INET6, addr, &s6->sin6_addr) != 1)
+			err_return(errno, "Invalid IPv6 address");
+
+		s6->sin6_family = AF_INET6;
+		s6->sin6_scope_id = n;
+
+		return true;
+	}
+	else if (v6 && inet_pton(AF_INET6, addr, &s6->sin6_addr) == 1) {
+		s6->sin6_family = AF_INET6;
+
+		return true;
+	}
+	else if (v4 && inet_pton(AF_INET, addr, &s4->sin_addr) == 1) {
+		s4->sin_family = AF_INET;
+
+		return true;
+	}
+
+	err_return(EINVAL, "Unable to parse IP address");
+}
+
+static bool
+uv_to_sockaddr(uc_value_t *addr, struct sockaddr_storage *ss, socklen_t *slen)
+{
+	char *s, *p, addrstr[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255%2147483648")];
+	struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)ss;
+	struct sockaddr_in *s4 = (struct sockaddr_in *)ss;
+	struct sockaddr_un *su = (struct sockaddr_un *)ss;
+	uc_value_t *item;
+	unsigned long n;
+	size_t len;
+
+	memset(ss, 0, sizeof(*ss));
+
+	if (ucv_type(addr) == UC_STRING) {
+		s = ucv_string_get(addr);
+		len = ucv_string_length(addr);
+
+		if (memchr(s, '/', len) != NULL) {
+			*slen = ucv_string_length(addr);
+
+			if (*slen >= sizeof(su->sun_path))
+				*slen = sizeof(su->sun_path) - 1;
+
+			memcpy(su->sun_path, s, *slen);
+			su->sun_path[(*slen)++] = 0;
+			su->sun_family = AF_UNIX;
+
+			ok_return(true);
+		}
+
+		while (isspace(*s) && len > 0) {
+			len--;
+			s++;
+		}
+
+		if (len == 0)
+			err_return(EINVAL, "Invalid IP address");
+
+		if (*s == '[') {
+			p = memchr(++s, ']', --len);
+
+			if (!p || (size_t)(p - s) >= sizeof(addrstr))
+				err_return(EINVAL, "Invalid IPv6 address");
+
+			memcpy(addrstr, s, p - s);
+			addrstr[(p - s) + 1] = 0;
+
+			ss->ss_family = AF_INET6;
+			len -= ((p - s) + 1);
+			s = p + 1;
+		}
+		else if ((p = memchr(s, ':', len)) != NULL &&
+		         memchr(p + 1, ':', len - ((p - s) + 1)) == NULL) {
+			if ((size_t)(p - s) >= sizeof(addrstr))
+				err_return(EINVAL, "Invalid IP address");
+
+			memcpy(addrstr, s, p - s);
+			addrstr[p - s + 1] = 0;
+
+			ss->ss_family = AF_INET;
+			len -= (p - s);
+			s = p;
+		}
+		else {
+			if (len >= sizeof(addrstr))
+				err_return(EINVAL, "Invalid IP address");
+
+			memcpy(addrstr, s, len);
+			addrstr[len] = 0;
+
+			ss->ss_family = 0;
+			len = 0;
+			s = NULL;
+		}
+
+		if (!parse_addr(addrstr, ss))
+			return NULL;
+
+		if (s && *s == ':') {
+			if (len <= 1)
+				err_return(EINVAL, "Invalid port number");
+
+			for (s++, len--, n = 0; len > 0; len--, s++) {
+				if (*s < '0' || *s > '9')
+					err_return(EINVAL, "Invalid port number");
+
+				n = n * 10 + (*s - '0');
+			}
+
+			if (n > 65535)
+				err_return(EINVAL, "Invalid port number");
+
+			s6->sin6_port = htons(n);
+		}
+
+		*slen = (ss->ss_family == AF_INET6) ? sizeof(*s6) : sizeof(*s4);
+
+		ok_return(true);
+	}
+	else if (ucv_type(addr) == UC_ARRAY) {
+		if (ucv_array_length(addr) == 16) {
+			uint8_t *u8 = (uint8_t *)&s6->sin6_addr;
+
+			for (size_t i = 0; i < 16; i++) {
+				item = ucv_array_get(addr, i);
+				n = ucv_uint64_get(item);
+
+				if (ucv_type(item) != UC_INTEGER || errno != 0 || n > 255)
+					err_return(EINVAL, "Invalid IP address array");
+
+				u8[i] = n;
+			}
+
+			s6->sin6_family = AF_INET6;
+			*slen = sizeof(*s6);
+
+			ok_return(true);
+		}
+		else if (ucv_array_length(addr) == 4) {
+			s4->sin_addr.s_addr = 0;
+
+			for (size_t i = 0; i < 4; i++) {
+				item = ucv_array_get(addr, i);
+				n = ucv_uint64_get(item);
+
+				if (ucv_type(item) != UC_INTEGER || errno != 0 || n > 255)
+					err_return(EINVAL, "Invalid IP address array");
+
+				s4->sin_addr.s_addr = s4->sin_addr.s_addr * 256 + n;
+			}
+
+			s4->sin_addr.s_addr = htonl(s4->sin_addr.s_addr);
+			s4->sin_family = AF_INET;
+			*slen = sizeof(*s4);
+
+			ok_return(true);
+		}
+
+		err_return(EINVAL, "Invalid IP address array");
+	}
+	else if (ucv_type(addr) == UC_OBJECT) {
+		n = ucv_to_unsigned(ucv_object_get(addr, "family", NULL));
+
+		if (n == 0) {
+			if (ucv_type(ucv_object_get(addr, "path", NULL)) == UC_STRING) {
+				n = AF_UNIX;
+			}
+			else {
+				item = ucv_object_get(addr, "address", NULL);
+				len = ucv_string_length(item);
+				s = ucv_string_get(item);
+				n = (s && memchr(s, ':', len) != NULL) ? AF_INET6 : AF_INET;
+			}
+
+			if (n == 0)
+				err_return(EINVAL, "Invalid address object");
+		}
+
+		switch (n) {
+		case AF_INET6:
+			item = ucv_object_get(addr, "flowinfo", NULL);
+			s6->sin6_flowinfo = htonl(ucv_to_unsigned(item));
+
+			item = ucv_object_get(addr, "interface", NULL);
+
+			if (ucv_type(item) == UC_STRING) {
+				s6->sin6_scope_id = if_nametoindex(ucv_string_get(item));
+
+				if (s6->sin6_scope_id == 0)
+					err_return(errno, "Unable to resolve interface %s",
+						ucv_string_get(item));
+			}
+			else if (item != NULL) {
+				s6->sin6_scope_id = ucv_to_unsigned(item);
+
+				if (errno != 0)
+					err_return(errno, "Invalid scope ID");
+			}
+
+			/* fall through */
+
+		case AF_INET:
+			ss->ss_family = n;
+
+			item = ucv_object_get(addr, "port", NULL);
+			n = ucv_to_unsigned(item);
+
+			if (errno != 0 || n > 65535)
+				err_return(EINVAL, "Invalid port number");
+
+			s6->sin6_port = htons(n);
+
+			item = ucv_object_get(addr, "address", NULL);
+			len = ucv_string_length(item);
+			s = ucv_string_get(item);
+
+			if (len >= sizeof(addrstr))
+				err_return(EINVAL, "Invalid IP address");
+
+			memcpy(addrstr, s, len);
+			addrstr[len] = 0;
+
+			*slen = (n == AF_INET6) ? sizeof(*s6) : sizeof(*s4);
+
+			if (!parse_addr(addrstr, ss))
+				return NULL;
+
+			ok_return(true);
+
+		case AF_UNIX:
+			item = ucv_object_get(addr, "path", NULL);
+			len = ucv_string_length(item);
+
+			if (len == 0 || len >= sizeof(su->sun_path))
+				err_return(EINVAL, "Invalid path value");
+
+			memcpy(su->sun_path, ucv_string_get(item), len);
+			su->sun_path[len++] = 0;
+			su->sun_family = AF_UNIX;
+			*slen = len;
+
+			ok_return(true);
+		}
+	}
+
+	err_return(EINVAL, "Invalid address value");
+}
+
+
+typedef struct {
+    const char *name;
+    enum { DT_SIGNED, DT_UNSIGNED, DT_IPV4ADDR, DT_CALLBACK } type;
+	union {
+    	size_t offset;
+		bool (*to_c)(void *, uc_value_t *);
+	} u1;
+	union {
+		size_t size;
+		uc_value_t *(*to_uv)(void *);
+	} u2;
+} member_t;
+
+typedef struct {
+	size_t size;
+	member_t *members;
+} struct_t;
+
+typedef struct {
+	int level;
+	int option;
+	struct_t *ctype;
+} sockopt_t;
+
+#define STRUCT_MEMBER_NP(struct_name, member_name, data_type)	\
+	{ #member_name, data_type,									\
+	  { .offset = offsetof(struct struct_name, member_name) },	\
+	  { .size = sizeof(((struct struct_name *)NULL)->member_name) } }
+
+#define STRUCT_MEMBER_CB(member_name, to_c_fn, to_uv_fn)		\
+	{ #member_name, DT_CALLBACK, { .to_c = to_c_fn }, { .to_uv = to_uv_fn } }
+
+#define STRUCT_MEMBER(struct_name, member_prefix, member_name, data_type)			\
+	{ #member_name, data_type, 														\
+	  { .offset = offsetof(struct struct_name, member_prefix##_##member_name) },	\
+	  { .size = sizeof(((struct struct_name *)NULL)->member_prefix##_##member_name) } }
+
+static struct_t st_timeval = {
+	.size = sizeof(struct timeval),
+	.members = (member_t []){
+		STRUCT_MEMBER(timeval, tv, sec, DT_SIGNED),
+		STRUCT_MEMBER(timeval, tv, usec, DT_SIGNED),
+		{ 0 }
+	}
+};
+
+static struct_t st_ucred = {
+	.size = sizeof(struct ucred),
+	.members = (member_t []){
+		STRUCT_MEMBER_NP(ucred, pid, DT_SIGNED),
+		STRUCT_MEMBER_NP(ucred, uid, DT_SIGNED),
+		STRUCT_MEMBER_NP(ucred, gid, DT_SIGNED),
+		{ 0 }
+	}
+};
+
+static struct_t st_linger = {
+	.size = sizeof(struct linger),
+	.members = (member_t []){
+		STRUCT_MEMBER(linger, l, onoff, DT_SIGNED),
+		STRUCT_MEMBER(linger, l, linger, DT_SIGNED),
+		{ 0 }
+	}
+};
+
+static struct_t st_ip_mreqn = {
+	.size = sizeof(struct ip_mreqn),
+	.members = (member_t []){
+		STRUCT_MEMBER(ip_mreqn, imr, multiaddr, DT_IPV4ADDR),
+		STRUCT_MEMBER(ip_mreqn, imr, address, DT_IPV4ADDR),
+		STRUCT_MEMBER(ip_mreqn, imr, ifindex, DT_SIGNED),
+		{ 0 }
+	}
+};
+
+static struct_t st_ip_mreq_source = {
+	.size = sizeof(struct ip_mreq_source),
+	.members = (member_t []){
+		STRUCT_MEMBER(ip_mreq_source, imr, multiaddr, DT_IPV4ADDR),
+		STRUCT_MEMBER(ip_mreq_source, imr, interface, DT_IPV4ADDR),
+		STRUCT_MEMBER(ip_mreq_source, imr, sourceaddr, DT_IPV4ADDR),
+		{ 0 }
+	}
+};
+
+static struct_t st_ip_msfilter = {
+	.size = sizeof(struct ip_msfilter),
+	.members = (member_t []){
+		STRUCT_MEMBER(ip_msfilter, imsf, multiaddr, DT_IPV4ADDR),
+		STRUCT_MEMBER(ip_msfilter, imsf, interface, DT_IPV4ADDR),
+		STRUCT_MEMBER(ip_msfilter, imsf, fmode, DT_SIGNED),
+		STRUCT_MEMBER(ip_msfilter, imsf, numsrc, DT_SIGNED),
+		STRUCT_MEMBER(ip_msfilter, imsf, slist, DT_SIGNED),
+		{ 0 }
+	}
+};
+
+static uc_value_t *
+snd_wscale_to_uv(void *st)
+{
+	return ucv_uint64_new(((struct tcp_info *)st)->tcpi_snd_wscale);
+}
+
+static uc_value_t *
+rcv_wscale_to_uv(void *st)
+{
+	return ucv_uint64_new(((struct tcp_info *)st)->tcpi_rcv_wscale);
+}
+
+static bool
+snd_wscale_to_c(void *st, uc_value_t *uv)
+{
+	((struct tcp_info *)st)->tcpi_snd_wscale = ucv_to_unsigned(uv);
+
+	if (errno)
+		err_return(errno, "Unable to convert field snd_wscale to unsigned");
+
+	return true;
+}
+
+static bool
+rcv_wscale_to_c(void *st, uc_value_t *uv)
+{
+	((struct tcp_info *)st)->tcpi_rcv_wscale = ucv_to_unsigned(uv);
+
+	if (errno)
+		err_return(errno, "Unable to convert field rcv_wscale to unsigned");
+
+	return true;
+}
+
+static struct_t st_tcp_info = {
+	.size = sizeof(struct tcp_info),
+	.members = (member_t []){
+		STRUCT_MEMBER(tcp_info, tcpi, state, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, ca_state, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, retransmits, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, probes, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, backoff, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, options, DT_UNSIGNED),
+		STRUCT_MEMBER_CB(snd_wscale, snd_wscale_to_c, snd_wscale_to_uv),
+		STRUCT_MEMBER_CB(rcv_wscale, rcv_wscale_to_c, rcv_wscale_to_uv),
+		STRUCT_MEMBER(tcp_info, tcpi, rto, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, ato, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, snd_mss, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, rcv_mss, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, unacked, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, sacked, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, lost, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, retrans, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, fackets, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, last_data_sent, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, last_ack_sent, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, last_data_recv, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, last_ack_recv, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, pmtu, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, rcv_ssthresh, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, rtt, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, rttvar, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, snd_ssthresh, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, snd_cwnd, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, advmss, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, reordering, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, rcv_rtt, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, rcv_space, DT_UNSIGNED),
+		STRUCT_MEMBER(tcp_info, tcpi, total_retrans, DT_UNSIGNED),
+		{ 0 }
+	}
+};
+
+#define SV_VOID   (struct_t *)0
+#define SV_INT    (struct_t *)1
+#define SV_INT_RO (struct_t *)2
+#define SV_BOOL   (struct_t *)3
+#define SV_STRING (struct_t *)4
+
+static sockopt_t sockopts[] = {
+    { SOL_SOCKET, SO_ACCEPTCONN, SV_BOOL },
+    { SOL_SOCKET, SO_ATTACH_FILTER, SV_STRING },
+    { SOL_SOCKET, SO_ATTACH_BPF, SV_INT },
+    { SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, SV_STRING },
+    { SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, SV_INT },
+    { SOL_SOCKET, SO_BINDTODEVICE, SV_STRING },
+    { SOL_SOCKET, SO_BROADCAST, SV_BOOL },
+    { SOL_SOCKET, SO_DEBUG, SV_BOOL },
+    { SOL_SOCKET, SO_DETACH_FILTER, SV_VOID },
+    { SOL_SOCKET, SO_DETACH_BPF, SV_VOID },
+    { SOL_SOCKET, SO_DOMAIN, SV_INT_RO },
+    { SOL_SOCKET, SO_ERROR, SV_INT_RO },
+    { SOL_SOCKET, SO_DONTROUTE, SV_BOOL },
+    { SOL_SOCKET, SO_INCOMING_CPU, SV_INT },
+    { SOL_SOCKET, SO_INCOMING_NAPI_ID, SV_INT_RO },
+    { SOL_SOCKET, SO_KEEPALIVE, SV_BOOL },
+    { SOL_SOCKET, SO_LINGER, &st_linger },
+    { SOL_SOCKET, SO_LOCK_FILTER, SV_INT },
+    { SOL_SOCKET, SO_MARK, SV_INT },
+    { SOL_SOCKET, SO_OOBINLINE, SV_BOOL },
+    { SOL_SOCKET, SO_PASSCRED, SV_BOOL },
+    { SOL_SOCKET, SO_PASSSEC, SV_BOOL },
+    { SOL_SOCKET, SO_PEEK_OFF, SV_INT },
+    { SOL_SOCKET, SO_PEERCRED, &st_ucred },
+    { SOL_SOCKET, SO_PEERSEC, SV_STRING },
+    { SOL_SOCKET, SO_PRIORITY, SV_INT },
+    { SOL_SOCKET, SO_PROTOCOL, SV_INT },
+    { SOL_SOCKET, SO_RCVBUF, SV_INT },
+    { SOL_SOCKET, SO_RCVBUFFORCE, SV_INT },
+    { SOL_SOCKET, SO_RCVLOWAT, SV_INT },
+    { SOL_SOCKET, SO_RCVTIMEO, &st_timeval },
+    { SOL_SOCKET, SO_REUSEADDR, SV_BOOL },
+    { SOL_SOCKET, SO_REUSEPORT, SV_BOOL },
+    { SOL_SOCKET, SO_RXQ_OVFL, SV_BOOL },
+    { SOL_SOCKET, SO_SNDBUF, SV_INT },
+    { SOL_SOCKET, SO_SNDBUFFORCE, SV_INT },
+    { SOL_SOCKET, SO_SNDLOWAT, SV_INT },
+    { SOL_SOCKET, SO_SNDTIMEO, &st_timeval },
+    { SOL_SOCKET, SO_TIMESTAMP, SV_BOOL },
+    { SOL_SOCKET, SO_TIMESTAMPNS, SV_BOOL },
+    { SOL_SOCKET, SO_TYPE, SV_INT },
+    { SOL_SOCKET, SO_BUSY_POLL, SV_INT },
+
+    { IPPROTO_IP, IP_ADD_MEMBERSHIP, &st_ip_mreqn },
+    { IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, &st_ip_mreq_source },
+    { IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, SV_BOOL },
+    { IPPROTO_IP, IP_BLOCK_SOURCE, &st_ip_mreq_source },
+    { IPPROTO_IP, IP_DROP_MEMBERSHIP, &st_ip_mreqn },
+    { IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP, &st_ip_mreq_source },
+    { IPPROTO_IP, IP_FREEBIND, SV_BOOL },
+    { IPPROTO_IP, IP_HDRINCL, SV_BOOL },
+    { IPPROTO_IP, IP_MSFILTER, &st_ip_msfilter },
+    { IPPROTO_IP, IP_MTU, SV_INT },
+    { IPPROTO_IP, IP_MTU_DISCOVER, SV_INT },
+    { IPPROTO_IP, IP_MULTICAST_ALL, SV_BOOL },
+    { IPPROTO_IP, IP_MULTICAST_IF, &st_ip_mreqn },
+    { IPPROTO_IP, IP_MULTICAST_LOOP, SV_BOOL },
+    { IPPROTO_IP, IP_MULTICAST_TTL, SV_INT },
+    { IPPROTO_IP, IP_NODEFRAG, SV_BOOL },
+    { IPPROTO_IP, IP_OPTIONS, SV_STRING },
+    { IPPROTO_IP, IP_PASSSEC, SV_BOOL },
+    { IPPROTO_IP, IP_PKTINFO, SV_BOOL },
+    { IPPROTO_IP, IP_RECVERR, SV_BOOL },
+    { IPPROTO_IP, IP_RECVOPTS, SV_BOOL },
+    { IPPROTO_IP, IP_RECVORIGDSTADDR, SV_BOOL },
+    { IPPROTO_IP, IP_RECVTOS, SV_BOOL },
+    { IPPROTO_IP, IP_RECVTTL, SV_BOOL },
+    { IPPROTO_IP, IP_RETOPTS, SV_BOOL },
+    { IPPROTO_IP, IP_ROUTER_ALERT, SV_BOOL },
+    { IPPROTO_IP, IP_TOS, SV_INT },
+    { IPPROTO_IP, IP_TRANSPARENT, SV_BOOL },
+    { IPPROTO_IP, IP_TTL, SV_INT },
+    { IPPROTO_IP, IP_UNBLOCK_SOURCE, &st_ip_mreq_source },
+
+    { IPPROTO_TCP, TCP_CONGESTION, SV_STRING },
+    { IPPROTO_TCP, TCP_CORK, SV_BOOL },
+    { IPPROTO_TCP, TCP_DEFER_ACCEPT, SV_INT },
+    { IPPROTO_TCP, TCP_INFO, &st_tcp_info },
+    { IPPROTO_TCP, TCP_KEEPCNT, SV_INT },
+    { IPPROTO_TCP, TCP_KEEPIDLE, SV_INT },
+    { IPPROTO_TCP, TCP_KEEPINTVL, SV_INT },
+    { IPPROTO_TCP, TCP_LINGER2, SV_INT },
+    { IPPROTO_TCP, TCP_MAXSEG, SV_INT },
+    { IPPROTO_TCP, TCP_NODELAY, SV_BOOL },
+    { IPPROTO_TCP, TCP_QUICKACK, SV_BOOL },
+    { IPPROTO_TCP, TCP_SYNCNT, SV_INT },
+    { IPPROTO_TCP, TCP_USER_TIMEOUT, SV_INT },
+    { IPPROTO_TCP, TCP_WINDOW_CLAMP, SV_INT },
+    { IPPROTO_TCP, TCP_FASTOPEN, SV_INT },
+    { IPPROTO_TCP, TCP_FASTOPEN_CONNECT, SV_INT },
+
+    { IPPROTO_UDP, UDP_CORK, SV_BOOL }
+};
+
+
+static char *
+uv_to_struct(uc_value_t *uv, struct_t *spec)
+{
+	uc_value_t *fv;
+	const char *s;
+	uint64_t u64;
+	int64_t s64;
+	member_t *m;
+	bool found;
+	char *st;
+
+	union {
+		int8_t s8;
+		int16_t s16;
+		int32_t s32;
+		int64_t s64;
+		uint8_t u8;
+		uint16_t u16;
+		uint32_t u32;
+		uint64_t u64;
+	} v;
+
+	st = xalloc(spec->size);
+
+	for (size_t i = 0; spec->members[i].name; i++) {
+		m = &spec->members[i];
+		fv = ucv_object_get(uv, m->name, &found);
+
+		if (!found || !fv)
+			continue;
+
+		switch (spec->members[i].type) {
+		case DT_UNSIGNED:
+			u64 = ucv_to_unsigned(fv);
+
+			if (errno) {
+				free(st);
+				err_return(errno,
+					"Unable to convert field %s to unsigned",
+					m->name);
+			}
+
+			switch (m->u2.size) {
+			case 1:  v.u8 =  (uint8_t)u64; break;
+			case 2: v.u16 = (uint16_t)u64; break;
+			case 4: v.u32 = (uint32_t)u64; break;
+			case 8: v.u64 = (uint64_t)u64; break;
+			}
+
+			memcpy(st + m->u1.offset, &v, m->u2.size);
+			break;
+
+		case DT_SIGNED:
+			s64 = ucv_to_integer(fv);
+
+			if (errno) {
+				free(st);
+				err_return(errno,
+					"Unable to convert field %s to integer", m->name);
+			}
+
+			switch (m->u2.size) {
+			case 1:  v.s8 =  (int8_t)s64; break;
+			case 2: v.s16 = (int16_t)s64; break;
+			case 4: v.s32 = (int32_t)s64; break;
+			case 8: v.s64 = (int64_t)s64; break;
+			}
+
+			memcpy(st + m->u1.offset, &v, m->u2.size);
+			break;
+
+		case DT_IPV4ADDR:
+			s = ucv_string_get(fv);
+
+			if (!s || inet_pton(AF_INET, s, st + m->u1.offset) != 1) {
+				free(st);
+				err_return(EINVAL,
+					"Unable to convert field %s to IP address", m->name);
+			}
+
+			break;
+
+		case DT_CALLBACK:
+			if (!m->u1.to_c(st, fv)) {
+				free(st);
+				return NULL;
+			}
+
+			break;
+		}
+	}
+
+	return st;
+}
+
+static uc_value_t *
+struct_to_uv(char *st, struct_t *spec)
+{
+	char s[sizeof("255.255.255.255")];
+	uc_value_t *uv, *fv;
+	member_t *m;
+
+	uv = ucv_object_new(NULL);
+
+	for (size_t i = 0; spec->members[i].name; i++) {
+		m = &spec->members[i];
+		fv = NULL;
+
+		switch (spec->members[i].type) {
+		case DT_UNSIGNED:
+			switch (spec->members[i].u2.size) {
+			case 1:
+				fv = ucv_uint64_new(*(uint8_t *)(st + m->u1.offset));
+				break;
+
+			case 2:
+				fv = ucv_uint64_new(*(uint16_t *)(st + m->u1.offset));
+				break;
+
+			case 4:
+				fv = ucv_uint64_new(*(uint32_t *)(st + m->u1.offset));
+				break;
+
+			case 8:
+				fv = ucv_uint64_new(*(uint64_t *)(st + m->u1.offset));
+				break;
+			}
+
+			break;
+
+		case DT_SIGNED:
+			switch (spec[i].size) {
+			case 1:
+				fv = ucv_int64_new(*(int8_t *)(st + m->u1.offset));
+				break;
+
+			case 2:
+				fv = ucv_int64_new(*(int16_t *)(st + m->u1.offset));
+				break;
+
+			case 4:
+				fv = ucv_int64_new(*(int32_t *)(st + m->u1.offset));
+				break;
+
+			case 8:
+				fv = ucv_int64_new(*(int64_t *)(st + m->u1.offset));
+				break;
+			}
+
+			break;
+
+		case DT_IPV4ADDR:
+			if (inet_ntop(AF_INET, st + m->u1.offset, s, sizeof(s)))
+				fv = ucv_string_new(s);
+
+			break;
+
+		case DT_CALLBACK:
+			fv = m->u2.to_uv(st);
+			break;
+		}
+
+		ucv_object_add(uv, m->name, fv);
+	}
+
+	return uv;
+}
+
+/**
+ * Sets options on the socket.
+ *
+ * Sets the specified option on the socket to the given value.
+ *
+ * Returns `true` if the option was successfully set.
+ *
+ * Returns `null` if an error occurred.
+ *
+ * @function module:socket.socket#setopt
+ *
+ * @param {number} level
+ * The protocol level at which the option resides. This can be a level such as
+ * `SOL_SOCKET` for the socket API level or a specific protocol level defined
+ * by the system.
+ *
+ * @param {number} option
+ * The socket option to set. This can be an integer representing the option,
+ * such as `SO_REUSEADDR`, or a constant defined by the system.
+ *
+ * @param {*} value
+ * The value to set the option to. The type of this argument depends on the
+ * specific option being set. It can be an integer, a boolean, a string, or a
+ * dictionary representing the value to set. If a dictionary is provided, it is
+ * internally translated to the corresponding C struct type required by the
+ * option.
+ *
+ * @returns {?boolean}
+ */
+static uc_value_t *
+uc_socket_setopt(uc_vm_t *vm, size_t nargs)
+{
+	int sockfd, solvl, soopt, soval, ret;
+	uc_value_t *level, *option, *value;
+	void *valptr = NULL, *st = NULL;
+	socklen_t vallen = 0;
+	size_t i;
+
+	args_get(vm, nargs, &sockfd,
+		"level", UC_INTEGER, false, &level,
+		"option", UC_INTEGER, false, &option,
+		"value", UC_NULL, false, &value);
+
+	solvl = ucv_int64_get(level);
+	soopt = ucv_int64_get(option);
+
+	for (i = 0; i < ARRAY_SIZE(sockopts); i++) {
+		if (sockopts[i].level != solvl || sockopts[i].option != soopt)
+			continue;
+
+		switch ((uintptr_t)sockopts[i].ctype) {
+		case (uintptr_t)SV_INT_RO:
+			err_return(EOPNOTSUPP, "Socket option is read only");
+
+		case (uintptr_t)SV_VOID:
+			valptr = NULL;
+			vallen = 0;
+			break;
+
+		case (uintptr_t)SV_INT:
+			soval = ucv_to_integer(value);
+
+			if (errno)
+				err_return(errno, "Unable to convert value to integer");
+
+			valptr = &soval;
+			vallen = sizeof(int);
+			break;
+
+		case (uintptr_t)SV_BOOL:
+			soval = ucv_to_unsigned(value) ? 1 : 0;
+
+			if (errno)
+				err_return(errno, "Unable to convert value to boolean");
+
+			valptr = &soval;
+			vallen = sizeof(int);
+			break;
+
+		case (uintptr_t)SV_STRING:
+			valptr = ucv_string_get(value);
+			vallen = ucv_string_length(value);
+			break;
+
+		default:
+			st = uv_to_struct(value, sockopts[i].ctype);
+			valptr = st;
+			vallen = sockopts[i].ctype->size;
+			break;
+		}
+
+		break;
+	}
+
+	if (i == ARRAY_SIZE(sockopts))
+		err_return(EINVAL, "Unknown socket level or option");
+
+	ret = setsockopt(sockfd, solvl, soopt, valptr, vallen);
+
+	free(st);
+
+	if (ret == -1)
+		err_return(errno, "setsockopt() failed");
+
+	return ucv_boolean_new(true);
+}
+
+/**
+ * Gets options from the socket.
+ *
+ * Retrieves the value of the specified option from the socket.
+ *
+ * Returns the value of the requested option.
+ *
+ * Returns `null` if an error occurred or if the option is not supported.
+ *
+ * @function module:socket.socket#getopt
+ *
+ * @param {number} level
+ * The protocol level at which the option resides. This can be a level such as
+ * `SOL_SOCKET` for the socket API level or a specific protocol level defined
+ * by the system.
+ *
+ * @param {number} option
+ * The socket option to retrieve. This can be an integer representing the
+ * option, such as `SO_REUSEADDR`, or a constant defined by the system.
+ *
+ * @returns {?*}
+ * The value of the requested option. The type of the returned value depends
+ * on the specific option being retrieved. It can be an integer, a boolean, a
+ * string, or a dictionary representing a complex data structure.
+ */
+static uc_value_t *
+uc_socket_getopt(uc_vm_t *vm, size_t nargs)
+{
+	uc_value_t *level, *option, *value = NULL;
+	int sockfd, solvl, soopt, soval, ret;
+	void *valptr = NULL, *st = NULL;
+	uc_stringbuf_t *sb = NULL;
+	socklen_t vallen;
+	size_t i;
+
+	args_get(vm, nargs, &sockfd,
+		"level", UC_INTEGER, false, &level,
+		"option", UC_INTEGER, false, &option);
+
+	solvl = ucv_int64_get(level);
+	soopt = ucv_int64_get(option);
+
+	for (i = 0; i < ARRAY_SIZE(sockopts); i++) {
+		if (sockopts[i].level != solvl || sockopts[i].option != soopt)
+			continue;
+
+		switch ((uintptr_t)sockopts[i].ctype) {
+		case (uintptr_t)SV_VOID:
+			err_return(EOPNOTSUPP, "Socket option is write only");
+
+		case (uintptr_t)SV_INT:
+		case (uintptr_t)SV_INT_RO:
+		case (uintptr_t)SV_BOOL:
+			valptr = &soval;
+			vallen = sizeof(int);
+			break;
+
+		case (uintptr_t)SV_STRING:
+			sb = strbuf_alloc(64);
+			valptr = strbuf_data(sb);
+			vallen = strbuf_size(sb);
+			break;
+
+		default:
+			st = xalloc(sockopts[i].ctype->size);
+			valptr = st;
+			vallen = sockopts[i].ctype->size;
+			break;
+		}
+
+		break;
+	}
+
+	if (i == ARRAY_SIZE(sockopts))
+		err_return(EINVAL, "Unknown socket level or option");
+
+	while (true) {
+		ret = getsockopt(sockfd, solvl, soopt, valptr, &vallen);
+
+		if (sockopts[i].ctype == SV_STRING &&
+		    (ret == 0 || (ret == -1 && errno == ERANGE)) &&
+		    vallen > strbuf_size(sb)) {
+
+			if (!strbuf_grow(sb, vallen))
+				return NULL;
+
+			valptr = strbuf_data(sb);
+			continue;
+		}
+
+		break;
+	}
+
+	if (ret == 0) {
+		switch ((uintptr_t)sockopts[i].ctype) {
+		case (uintptr_t)SV_VOID:
+			break;
+
+		case (uintptr_t)SV_INT:
+		case (uintptr_t)SV_INT_RO:
+			value = ucv_int64_new(soval);
+			break;
+
+		case (uintptr_t)SV_BOOL:
+			value = ucv_boolean_new(soval);
+			break;
+
+		case (uintptr_t)SV_STRING:
+			value = strbuf_finish(&sb, vallen);
+			break;
+
+		default:
+			value = struct_to_uv(st, sockopts[i].ctype);
+			break;
+		}
+	}
+
+	strbuf_free(sb);
+	free(st);
+
+	if (ret == -1)
+		err_return(errno, "getsockopt() failed");
+
+	return value;
+}
+
+/**
+ * Returns the UNIX file descriptor number associated with the socket.
+ *
+ * Returns the file descriptor number.
+ *
+ * Returns `-1` if an error occurred.
+ *
+ * @function module:socket.socket#fileno
+ *
+ * @returns {number}
+ */
+static uc_value_t *
+uc_socket_fileno(uc_vm_t *vm, size_t nargs)
+{
+	int sockfd;
+
+	args_get(vm, nargs, &sockfd);
+
+	return ucv_int64_new(sockfd);
+}
+
+/**
+ * Query error information.
+ *
+ * Returns a string containing a description of the last occurred error or
+ * `null` if there is no error information.
+ *
+ * @function module:socket#error
+ *
+ *
+ * @returns {?string}
+ *
+ * @example
+ * // Trigger file system error
+ * unlink('/path/does/not/exist');
+ *
+ * // Print error (should yield "No such file or directory")
+ * print(error(), "\n");
+ */
+static uc_value_t *
+uc_socket_error(uc_vm_t *vm, size_t nargs)
+{
+	uc_value_t *numeric = uc_fn_arg(0), *rv;
+	uc_stringbuf_t *buf;
+
+	if (last_error.code == 0)
+		return NULL;
+
+	if (ucv_is_truish(numeric)) {
+		rv = ucv_int64_new(last_error.code);
+	}
+	else {
+		buf = ucv_stringbuf_new();
+
+		if (last_error.code == 0 && last_error.msg) {
+			ucv_stringbuf_addstr(buf, last_error.msg, strlen(last_error.msg));
+		}
+		else {
+			ucv_stringbuf_printf(buf, "%s", strerror(last_error.code));
+
+			if (last_error.msg)
+				ucv_stringbuf_printf(buf, ": %s", last_error.msg);
+		}
+
+		rv = ucv_stringbuf_finish(buf);
+	}
+
+	set_error(0, NULL);
+
+	return rv;
+}
+
+/**
+ * @typedef {Object} module:socket.socket.UnixSocketAddress
+ * @property {number} family - Address family, set to `socket.AF_UNIX`.
+ * @property {string} path - Domain socket filesystem path.
+ */
+
+/**
+ * @typedef {Object} module:socket.socket.IPv4SocketAddress
+ * @property {number} family - Address family, set to `socket.AF_INET`.
+ * @property {string} address - IPv4 address in dotted quad notation.
+ * @property {number} [port] - Port number.
+ */
+
+/**
+ * @typedef {Object} module:socket.socket.IPv6SocketAddress
+ * @property {number} family - Address family, set to `socket.AF_INET6`.
+ * @property {string} address - IPv6 address.
+ * @property {number} [port] - Port number.
+ * @property {number} [flowinfo] - IPv6 flow information.
+ * @property {string|number} [interface] - Link local address scope, either a
+ *     string containing a network device name or a nonzero positive integer
+ *     representing a network interface index.
+ */
+
+static uc_value_t *
+uc_socket_sockaddr(uc_vm_t *vm, size_t nargs)
+{
+	struct sockaddr_storage ss;
+	uc_value_t *addr, *rv;
+	socklen_t slen;
+
+	args_get(vm, nargs, NULL,
+		"address", UC_NULL, false, &addr);
+
+	if (!uv_to_sockaddr(addr, &ss, &slen))
+		return NULL;
+
+	rv = ucv_object_new(vm);
+
+	if (!sockaddr_to_uv(&ss, rv)) {
+		ucv_put(rv);
+		return NULL;
+	}
+
+	return rv;
+}
+
+/**
+ * Represents a socket handle.
+ *
+ * @class module:socket.socket
+ * @hideconstructor
+ *
+ * @borrows module:socket#error as module:socket.socket#error
+ *
+ * @see {@link module:socket#create|create()}
+ *
+ * @example
+ *
+ * const sock = create(…);
+ *
+ * sock.getopt(…);
+ * sock.setopt(…);
+ *
+ * sock.connect(…);
+ * sock.listen(…);
+ * sock.accept(…);
+ * sock.bind(…);
+ *
+ * sock.send(…);
+ * sock.recv(…);
+ *
+ * sock.shutdown(…);
+ *
+ * sock.fileno();
+ * sock.peername();
+ * sock.sockname();
+ *
+ * sock.close();
+ *
+ * sock.error();
+ */
+
+/**
+ * Creates a socket and returns its hand
+ *
+ * The handle will be connected to the process stdin or stdout, depending on the
+ * value of the mode argument.
+ *
+ * The mode argument may be either "r" to open the process for reading (connect
+ * to its stdin) or "w" to open the process for writing (connect to its stdout).
+ *
+ * The mode character "r" or "w" may be optionally followed by "e" to apply the
+ * FD_CLOEXEC flag onto the open descriptor.
+ *
+ * Returns a process handle referring to the executed process.
+ *
+ * Returns `null` if an error occurred.
+ *
+ * @function module:fs#popen
+ *
+ * @param {string} command
+ * The command to be executed.
+ *
+ * @param {string} [mode="r"]
+ * The open mode of the process handle.
+ *
+ * @returns {?module:fs.proc}
+ *
+ * @example
+ * // Open a process
+ * const process = popen('command', 'r');
+ */
+
+/**
+ * Creates a network socket instance.
+ *
+ * This function creates a new network socket with the specified domain and
+ * type, determined by one of the modules `AF_*` and `SOCK_*` constants
+ * respectively, and returns the resulting socket instance for use in subsequent
+ * socket operations.
+ *
+ * The domain argument specifies the protocol family, such as AF_INET or
+ * AF_INET6, and defaults to AF_INET if not provided.
+ *
+ * The type argument specifies the socket type, such as SOCK_STREAM or
+ * SOCK_DGRAM, and defaults to SOCK_STREAM if not provided. It may also
+ * be bitwise OR-ed with SOCK_NONBLOCK to enable non-blocking mode or
+ * SOCK_CLOEXEC to enable close-on-exec semantics.
+ *
+ * The protocol argument may be used to indicate a particular protocol
+ * to be used with the socket, and it defaults to 0 (automatically
+ * determined protocol) if not provided.
+ *
+ * Returns a socket descriptor representing the newly created socket.
+ *
+ * Returns `null` if an error occurred during socket creation.
+ *
+ * @function module:socket#create
+ *
+ * @param {number} [domain=AF_INET]
+ * The communication domain for the socket, e.g., AF_INET or AF_INET6.
+ *
+ * @param {number} [type=SOCK_STREAM]
+ * The socket type, e.g., SOCK_STREAM or SOCK_DGRAM. It may also be
+ * bitwise OR-ed with SOCK_NONBLOCK or SOCK_CLOEXEC.
+ *
+ * @param {number} [protocol=0]
+ * The protocol to be used with the socket.
+ *
+ * @returns {?module:socket.socket}
+ * A socket instance representing the newly created socket.
+ *
+ * @example
+ * // Create a TCP socket
+ * const tcp_socket = create(AF_INET, SOCK_STREAM);
+ *
+ * // Create a nonblocking IPv6 UDP socket
+ * const udp_socket = create(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK);
+ */
+static uc_value_t *
+uc_socket_create(uc_vm_t *vm, size_t nargs)
+{
+	uc_value_t *domain, *type, *protocol;
+	int sockfd, socktype;
+
+	args_get(vm, nargs, NULL,
+		"domain", UC_INTEGER, true, &domain,
+		"type", UC_INTEGER, true, &type,
+		"protocol", UC_INTEGER, true, &protocol);
+
+	socktype = type ? (int)ucv_int64_get(type) : SOCK_STREAM;
+
+	sockfd = socket(
+		domain ? (int)ucv_int64_get(domain) : AF_INET,
+#if defined(__APPLE__)
+		socktype & ~(SOCK_NONBLOCK|SOCK_CLOEXEC),
+#else
+		socktype,
+#endif
+		protocol ? (int)ucv_int64_get(protocol) : 0);
+
+	if (sockfd == -1)
+		err_return(errno, "socket() failed");
+
+#if defined(__APPLE__)
+	if (socktype & SOCK_NONBLOCK) {
+		int flags = fcntl(sockfd, F_GETFL);
+
+		if (flags == -1) {
+			close(sockfd);
+			err_return(errno, "fcntl(F_GETFL) failed");
+		}
+
+		if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) == -1) {
+			close(sockfd);
+			err_return(errno, "fcntl(F_SETFL) failed");
+		}
+	}
+
+	if (socktype & SOCK_CLOEXEC) {
+		if (fcntl(sockfd, F_SETFD, FD_CLOEXEC) == -1) {
+			close(sockfd);
+			err_return(errno, "fcntl(F_SETFD) failed");
+		}
+	}
+#endif
+
+	ok_return(ucv_resource_new(
+		ucv_resource_type_lookup(vm, "socket"),
+		(void *)(intptr_t)sockfd));
+}
+
+/**
+ * Connects the socket to a remote address and port.
+ *
+ * Attempts to establish a connection to the specified remote address and port.
+ *
+ * Returns `true` if the connection is successfully established.
+ * Returns `null` if an error occurred during the connection attempt.
+ *
+ * @function module:socket.socket#connect
+ *
+ * @param {string} address
+ * The IP address or hostname of the remote endpoint to connect to.
+ *
+ * @param {number} port
+ * The port number of the remote endpoint to connect to.
+ *
+ * @returns {?boolean}
+ */
+static uc_value_t *
+uc_socket_connect(uc_vm_t *vm, size_t nargs)
+{
+	struct sockaddr_storage ss;
+	uc_value_t *addr, *port;
+	int ret, sockfd;
+
+	args_get(vm, nargs, &sockfd,
+		"address", UC_STRING, false, &addr,
+		"port", UC_INTEGER, false, &port);
+
+	switch (addr_get(addr, &ss)) {
+	case AF_INET6:
+		((struct sockaddr_in6 *)&ss)->sin6_port = htons(ucv_uint64_get(port));
+		break;
+
+	case AF_INET:
+		((struct sockaddr_in *)&ss)->sin_port = htons(ucv_uint64_get(port));
+		break;
+
+	default:
+		err_return(EINVAL, "Invalid address specified");
+	}
+
+	ret = connect(sockfd, (struct sockaddr *)&ss, sizeof(ss));
+
+	if (ret == -1)
+		err_return(errno, "connect() failed");
+
+	ok_return(ucv_boolean_new(true));
+}
+
+/**
+ * Sends data through the socket.
+ *
+ * Sends the provided data through the socket handle to the specified remote
+ * address, if provided.
+ *
+ * Returns the number of bytes sent.
+ * Returns `null` if an error occurred during the send operation.
+ *
+ * @function module:socket.socket#send
+ *
+ * @param {string} data
+ * The data to be sent through the socket.
+ *
+ * @param {number} [flags]
+ * Optional flags that modify the behavior of the send operation.
+ *
+ * @param {module:module:socket.socket.IPv4SocketAddress|module:socket.socket.IPv6SocketAddress|socket.socket.UnixSocketAddress|number[]|string} [address]
+ * The address of the remote endpoint to send the data to. It can be either an
+ * IP address string, an array returned by {@link module:core#iptoarr|iptoarr()},
+ * or an object representing a network address. If not provided, the data is
+ * sent to the remote endpoint the socket is connected to.
+ *
+ * @returns {?number}
+ */
+static uc_value_t *
+uc_socket_send(uc_vm_t *vm, size_t nargs)
+{
+	uc_value_t *data, *flags, *addr;
+	struct sockaddr_storage ss = { 0 };
+	struct sockaddr *sa = NULL;
+	socklen_t salen = 0;
+	ssize_t ret;
+	int sockfd;
+
+	args_get(vm, nargs, &sockfd,
+		"data", UC_STRING, false, &data,
+		"flags", UC_INTEGER, true, &flags,
+		"address", UC_NULL, true, &addr);
+
+	if (addr) {
+		if (!uv_to_sockaddr(addr, &ss, &salen))
+			return NULL;
+
+		sa = (struct sockaddr *)&ss;
+	}
+
+	ret = sendto(sockfd,
+		ucv_string_get(data), ucv_string_length(data),
+		flags ? ucv_int64_get(flags) : 0, sa, salen);
+
+	if (ret == -1)
+		err_return(errno, "send() failed");
+
+	ok_return(ucv_int64_new(ret));
+}
+
+/**
+ * Receives data from the socket.
+ *
+ * Receives data from the socket handle, optionally specifying the maximum
+ * length of data to receive, flags to modify the receive behavior, and an
+ * optional address dictionary where the function will place the address from
+ * which the data was received (for unconnected sockets).
+ *
+ * Returns a string containing the received data.
+ * Returns an empty string if the remote side closed the socket.
+ * Returns `null` if an error occurred during the receive operation.
+ *
+ * @function module:socket.socket#recv
+ *
+ * @param {number} [length=4096]
+ * The maximum number of bytes to receive.
+ *
+ * @param {number} [flags]
+ * Optional flags that modify the behavior of the receive operation.
+ *
+ * @param {Object} [address]
+ * An object where the function will store the address from which the data was
+ * received. If provided, it will be filled with the details obtained from the
+ * sockaddr argument of the underlying `recvfrom()` syscall.
+ *
+ * @returns {?string}
+ */
+static uc_value_t *
+uc_socket_recv(uc_vm_t *vm, size_t nargs)
+{
+	uc_value_t *length, *flags, *addrobj;
+	struct sockaddr_storage ss = { 0 };
+	uc_stringbuf_t *buf;
+	ssize_t len, ret;
+	socklen_t sslen;
+	int sockfd;
+
+	args_get(vm, nargs, &sockfd,
+		"length", UC_INTEGER, true, &length,
+		"flags", UC_INTEGER, true, &flags,
+		"address", UC_OBJECT, true, &addrobj);
+
+	len = length ? ucv_to_integer(length) : 4096;
+
+	if (errno || len <= 0)
+		err_return(errno, "Invalid length argument");
+
+	buf = strbuf_alloc(len);
+
+	if (!buf)
+		return NULL;
+
+	do {
+		sslen = sizeof(ss);
+		ret = recvfrom(sockfd, strbuf_data(buf), len,
+			flags ? ucv_int64_get(flags) : 0, (struct sockaddr *)&ss, &sslen);
+	} while (ret == -1 && errno == EINTR);
+
+	if (ret == -1) {
+		strbuf_free(buf);
+		err_return(errno, "recv() failed");
+	}
+
+	if (addrobj)
+		sockaddr_to_uv(&ss, addrobj);
+
+	ok_return(strbuf_finish(&buf, ret));
+}
+
+static uc_value_t *
+uc_socket_bind(uc_vm_t *vm, size_t nargs)
+{
+	struct sockaddr_storage ss;
+	uc_value_t *addr, *port;
+	int ret, sockfd;
+
+	args_get(vm, nargs, &sockfd,
+		"address", UC_STRING, false, &addr,
+		"port", UC_INTEGER, false, &port);
+
+	switch (addr_get(addr, &ss)) {
+	case AF_INET6:
+		((struct sockaddr_in6 *)&ss)->sin6_port = htons(ucv_uint64_get(port));
+		break;
+
+	case AF_INET:
+		((struct sockaddr_in *)&ss)->sin_port = htons(ucv_uint64_get(port));
+		break;
+
+	default:
+		err_return(EINVAL, "Invalid address specified");
+	}
+
+	ret = bind(sockfd, (struct sockaddr *)&ss, sizeof(ss));
+
+	if (ret == -1)
+		err_return(errno, "bind() failed");
+
+	ok_return(ucv_boolean_new(true));
+}
+
+static uc_value_t *
+uc_socket_listen(uc_vm_t *vm, size_t nargs)
+{
+	uc_value_t *backlog;
+	int ret, sockfd;
+
+	args_get(vm, nargs, &sockfd,
+		"backlog", UC_INTEGER, true, &backlog);
+
+	ret = listen(sockfd, backlog ? ucv_to_unsigned(backlog) : 128);
+
+	if (ret == -1)
+		err_return(errno, "listen() failed");
+
+	ok_return(ucv_boolean_new(true));
+}
+
+static uc_value_t *
+uc_socket_accept(uc_vm_t *vm, size_t nargs)
+{
+	int peerfd, sockfd, sockflags;
+	uc_value_t *addrobj, *flags;
+	struct sockaddr_storage ss;
+	socklen_t sslen;
+
+	args_get(vm, nargs, &sockfd,
+		"address", UC_OBJECT, true, &addrobj,
+		"flags", UC_INTEGER, true, &flags);
+
+	sslen = sizeof(ss);
+	sockflags = flags ? ucv_to_integer(flags) : 0;
+
+#ifdef __APPLE__
+	peerfd = accept(sockfd, (struct sockaddr *)&ss, &sslen);
+
+	if (peerfd == -1)
+		err_return(errno, "accept() failed");
+
+	if (sockflags & SOCK_CLOEXEC) {
+		if (fcntl(peerfd, F_SETFD, FD_CLOEXEC) == -1) {
+			close(peerfd);
+			err_return("fcntl(F_SETFD) failed");
+		}
+	}
+
+	if (sockflags & SOCK_NONBLOCK) {
+		sockflags = fcntl(peerfd, F_GETFL);
+
+		if (sockflags == -1) {
+			close(peerfd);
+			err_return("fcntl(F_GETFL) failed");
+		}
+
+		if (fcntl(peerfd, F_SETFL, sockflags | O_NONBLOCK) == -1) {
+			close(peerfd);
+			err_return("fcntl(F_SETFL) failed");
+		}
+	}
+#else
+	peerfd = accept4(sockfd, (struct sockaddr *)&ss, &sslen, sockflags);
+
+	if (peerfd == -1)
+		err_return(errno, "accept4() failed");
+#endif
+
+	if (addrobj)
+		sockaddr_to_uv(&ss, addrobj);
+
+	ok_return(ucv_resource_new(
+		ucv_resource_type_lookup(vm, "socket"),
+		(void *)(intptr_t)peerfd));
+}
+
+static uc_value_t *
+uc_socket_shutdown(uc_vm_t *vm, size_t nargs)
+{
+	uc_value_t *how;
+	int sockfd, ret;
+
+	args_get(vm, nargs, &sockfd,
+		"how", UC_INTEGER, true, &how);
+
+	ret = shutdown(sockfd, ucv_int64_get(how));
+
+	if (ret == -1)
+		err_return(errno, "shutdown() failed");
+
+	ok_return(ucv_boolean_new(true));
+}
+
+static uc_value_t *
+uc_socket_peername(uc_vm_t *vm, size_t nargs)
+{
+	struct sockaddr_storage ss = { 0 };
+	uc_value_t *addr;
+	socklen_t sslen;
+	int sockfd, ret;
+
+	args_get(vm, nargs, &sockfd);
+
+	sslen = sizeof(ss);
+	ret = getpeername(sockfd, (struct sockaddr *)&ss, &sslen);
+
+	if (ret == -1)
+		err_return(errno, "getpeername() failed");
+
+	addr = ucv_object_new(vm);
+	sockaddr_to_uv(&ss, addr);
+
+	ok_return(addr);
+}
+
+static uc_value_t *
+uc_socket_sockname(uc_vm_t *vm, size_t nargs)
+{
+	struct sockaddr_storage ss = { 0 };
+	uc_value_t *addr;
+	socklen_t sslen;
+	int sockfd, ret;
+
+	args_get(vm, nargs, &sockfd);
+
+	sslen = sizeof(ss);
+	ret = getsockname(sockfd, (struct sockaddr *)&ss, &sslen);
+
+	if (ret == -1)
+		err_return(errno, "getsockname() failed");
+
+	addr = ucv_object_new(vm);
+	sockaddr_to_uv(&ss, addr);
+
+	ok_return(addr);
+}
+
+static uc_value_t *
+uc_socket_close(uc_vm_t *vm, size_t nargs)
+{
+	int *sockfd = uc_fn_this("socket");
+	int ret;
+
+	if (!sockfd || *sockfd == -1)
+		err_return(EBADF, "Invalid socket context");
+
+	ret = close(*sockfd);
+
+	if (ret == -1)
+		err_return(errno, "close() failed");
+
+	*sockfd = -1;
+
+	ok_return(ucv_boolean_new(true));
+}
+
+static void
+close_socket(void *ud)
+{
+	int fd = (intptr_t)ud;
+
+	if (fd != -1)
+		close(fd);
+}
+
+static const uc_function_list_t socket_fns[] = {
+	{ "connect",	uc_socket_connect },
+	{ "bind",		uc_socket_bind },
+	{ "listen",		uc_socket_listen },
+	{ "accept",		uc_socket_accept },
+	{ "send",		uc_socket_send },
+	{ "recv",	    uc_socket_recv },
+	{ "setopt",		uc_socket_setopt },
+	{ "getopt",		uc_socket_getopt },
+	{ "fileno",		uc_socket_fileno },
+	{ "shutdown",	uc_socket_shutdown },
+	{ "peername",	uc_socket_peername },
+	{ "sockname",	uc_socket_sockname },
+	{ "close",		uc_socket_close },
+	{ "error",		uc_socket_error },
+};
+
+static const uc_function_list_t global_fns[] = {
+	{ "sockaddr",	uc_socket_sockaddr },
+	{ "create",		uc_socket_create },
+	{ "error",		uc_socket_error },
+};
+
+void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
+{
+	uc_function_list_register(scope, global_fns);
+
+#define ADD_CONST(x) ucv_object_add(scope, #x, ucv_int64_new(x))
+	ADD_CONST(AF_UNSPEC);
+	ADD_CONST(AF_UNIX);
+	ADD_CONST(AF_INET);
+	ADD_CONST(AF_INET6);
+	ADD_CONST(AF_PACKET);
+
+	ADD_CONST(SOCK_STREAM);
+	ADD_CONST(SOCK_DGRAM);
+	ADD_CONST(SOCK_RAW);
+	ADD_CONST(SOCK_PACKET);
+	ADD_CONST(SOCK_NONBLOCK);
+	ADD_CONST(SOCK_CLOEXEC);
+
+	ADD_CONST(MSG_CONFIRM);
+	ADD_CONST(MSG_DONTROUTE);
+	ADD_CONST(MSG_DONTWAIT);
+	ADD_CONST(MSG_EOR);
+	ADD_CONST(MSG_MORE);
+	ADD_CONST(MSG_NOSIGNAL);
+	ADD_CONST(MSG_OOB);
+	ADD_CONST(MSG_FASTOPEN);
+	ADD_CONST(MSG_CMSG_CLOEXEC);
+	ADD_CONST(MSG_ERRQUEUE);
+	ADD_CONST(MSG_PEEK);
+	ADD_CONST(MSG_TRUNC);
+	ADD_CONST(MSG_WAITALL);
+
+	ADD_CONST(IPPROTO_IP);
+	ADD_CONST(IP_ADD_MEMBERSHIP);
+	ADD_CONST(IP_ADD_SOURCE_MEMBERSHIP);
+	ADD_CONST(IP_BIND_ADDRESS_NO_PORT);
+	ADD_CONST(IP_BLOCK_SOURCE);
+	ADD_CONST(IP_DROP_MEMBERSHIP);
+	ADD_CONST(IP_DROP_SOURCE_MEMBERSHIP);
+	ADD_CONST(IP_FREEBIND);
+	ADD_CONST(IP_HDRINCL);
+	ADD_CONST(IP_MSFILTER);
+	ADD_CONST(IP_MTU);
+	ADD_CONST(IP_MTU_DISCOVER);
+	ADD_CONST(IP_MULTICAST_ALL);
+	ADD_CONST(IP_MULTICAST_IF);
+	ADD_CONST(IP_MULTICAST_LOOP);
+	ADD_CONST(IP_MULTICAST_TTL);
+	ADD_CONST(IP_NODEFRAG);
+	ADD_CONST(IP_OPTIONS);
+	ADD_CONST(IP_PASSSEC);
+	ADD_CONST(IP_PKTINFO);
+	ADD_CONST(IP_RECVERR);
+	ADD_CONST(IP_RECVOPTS);
+	ADD_CONST(IP_RECVORIGDSTADDR);
+	ADD_CONST(IP_RECVTOS);
+	ADD_CONST(IP_RECVTTL);
+	ADD_CONST(IP_RETOPTS);
+	ADD_CONST(IP_ROUTER_ALERT);
+	ADD_CONST(IP_TOS);
+	ADD_CONST(IP_TRANSPARENT);
+	ADD_CONST(IP_TTL);
+	ADD_CONST(IP_UNBLOCK_SOURCE);
+
+	ADD_CONST(SOL_SOCKET);
+	ADD_CONST(SO_ACCEPTCONN);
+	ADD_CONST(SO_ATTACH_BPF);
+	ADD_CONST(SO_ATTACH_FILTER);
+	ADD_CONST(SO_ATTACH_REUSEPORT_CBPF);
+	ADD_CONST(SO_ATTACH_REUSEPORT_EBPF);
+	ADD_CONST(SO_BINDTODEVICE);
+	ADD_CONST(SO_BROADCAST);
+	ADD_CONST(SO_BUSY_POLL);
+	ADD_CONST(SO_DEBUG);
+	ADD_CONST(SO_DETACH_BPF);
+	ADD_CONST(SO_DETACH_FILTER);
+	ADD_CONST(SO_DOMAIN);
+	ADD_CONST(SO_DONTROUTE);
+	ADD_CONST(SO_ERROR);
+	ADD_CONST(SO_INCOMING_CPU);
+	ADD_CONST(SO_INCOMING_NAPI_ID);
+	ADD_CONST(SO_KEEPALIVE);
+	ADD_CONST(SO_LINGER);
+	ADD_CONST(SO_LOCK_FILTER);
+	ADD_CONST(SO_MARK);
+	ADD_CONST(SO_OOBINLINE);
+	ADD_CONST(SO_PASSCRED);
+	ADD_CONST(SO_PASSSEC);
+	ADD_CONST(SO_PEEK_OFF);
+	ADD_CONST(SO_PEERCRED);
+	ADD_CONST(SO_PEERSEC);
+	ADD_CONST(SO_PRIORITY);
+	ADD_CONST(SO_PROTOCOL);
+	ADD_CONST(SO_RCVBUF);
+	ADD_CONST(SO_RCVBUFFORCE);
+	ADD_CONST(SO_RCVLOWAT);
+	ADD_CONST(SO_RCVTIMEO);
+	ADD_CONST(SO_REUSEADDR);
+	ADD_CONST(SO_REUSEPORT);
+	ADD_CONST(SO_RXQ_OVFL);
+	ADD_CONST(SO_SNDBUF);
+	ADD_CONST(SO_SNDBUFFORCE);
+	ADD_CONST(SO_SNDLOWAT);
+	ADD_CONST(SO_SNDTIMEO);
+	ADD_CONST(SO_TIMESTAMP);
+	ADD_CONST(SO_TIMESTAMPNS);
+	ADD_CONST(SO_TYPE);
+
+	ADD_CONST(IPPROTO_TCP);
+	ADD_CONST(TCP_CONGESTION);
+	ADD_CONST(TCP_CORK);
+	ADD_CONST(TCP_DEFER_ACCEPT);
+	ADD_CONST(TCP_FASTOPEN);
+	ADD_CONST(TCP_FASTOPEN_CONNECT);
+	ADD_CONST(TCP_INFO);
+	ADD_CONST(TCP_KEEPCNT);
+	ADD_CONST(TCP_KEEPIDLE);
+	ADD_CONST(TCP_KEEPINTVL);
+	ADD_CONST(TCP_LINGER2);
+	ADD_CONST(TCP_MAXSEG);
+	ADD_CONST(TCP_NODELAY);
+	ADD_CONST(TCP_QUICKACK);
+	ADD_CONST(TCP_SYNCNT);
+	ADD_CONST(TCP_USER_TIMEOUT);
+	ADD_CONST(TCP_WINDOW_CLAMP);
+
+	ADD_CONST(IPPROTO_UDP);
+	ADD_CONST(UDP_CORK);
+
+	ADD_CONST(SHUT_RD);
+	ADD_CONST(SHUT_WR);
+	ADD_CONST(SHUT_RDWR);
+
+	uc_type_declare(vm, "socket", socket_fns, close_socket);
+}