]> git.plutz.net Git - quickjs_net/commitdiff
split low level os socket functions into separate module
authorPaul Hänsch <paul@plutz.net>
Wed, 18 Mar 2026 17:57:33 +0000 (18:57 +0100)
committerPaul Hänsch <paul@plutz.net>
Wed, 18 Mar 2026 17:57:33 +0000 (18:57 +0100)
os-net.c [new file with mode: 0644]

diff --git a/os-net.c b/os-net.c
new file mode 100644 (file)
index 0000000..cc217a2
--- /dev/null
+++ b/os-net.c
@@ -0,0 +1,150 @@
+#include <quickjs/quickjs.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <sys/ioctl.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+
+#define countof(x) ( sizeof(x) / sizeof(x[0]) )
+#define libc_error(ctx) ( JS_ThrowInternalError(ctx, "%s", strerror(errno)) )
+
+static JSValue js_os_fork(
+  JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv
+) {
+  return JS_NewInt32(ctx, fork());
+}
+
+static JSValue js_os_socket(
+  JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv
+) {
+  int family, type, protocol, fd;
+
+  if ( argc >= 3
+    && ! JS_ToInt32(ctx, &family,   argv[0])
+    && ! JS_ToInt32(ctx, &type,     argv[1])
+    && ! JS_ToInt32(ctx, &protocol, argv[2])
+    && ( fd = socket(family, type, protocol))
+  ) {
+    return JS_NewInt32(ctx, fd);
+  } else {
+    return JS_EXCEPTION;
+  }
+}
+
+static JSValue js_os_bind(
+  JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv, int con
+) {
+  struct addrinfo *addr, *waddr;
+  struct sockaddr_un addr_un = { .sun_family = AF_UNIX };
+  int fd, b = -1; const char *host, *port, *path;
+
+  if ( argc < 2 ) return JS_EXCEPTION;
+  if ( JS_ToInt32(ctx, &fd, argv[0]) ) return JS_EXCEPTION;
+
+  if        ( argc == 2 ) {
+    path = JS_ToCString(ctx, argv[1]);
+    if (strlen(path) < sizeof(addr_un.sun_path)) {
+      strcpy((char *) &(addr_un.sun_path), path);
+      if (!con) b = bind(fd, (struct sockaddr *) &addr_un, sizeof(addr_un));
+      else   b = connect(fd, (struct sockaddr *) &addr_un, sizeof(addr_un));
+    }
+    JS_FreeCString(ctx, path);
+    if (b) return libc_error(ctx); else return JS_UNDEFINED;
+
+  } else {  // argc >= 3
+    host = JS_ToCString(ctx, argv[1]);
+    port = JS_ToCString(ctx, argv[2]);
+    if (! getaddrinfo(host, port, NULL, &addr))
+      waddr = addr;
+    JS_FreeCString(ctx, host); JS_FreeCString(ctx, port);
+
+    while (waddr) {
+      if (!con) b = bind(fd, addr->ai_addr, addr->ai_addrlen);
+      else   b = connect(fd, addr->ai_addr, addr->ai_addrlen);
+      if (!b) break;
+      waddr = waddr->ai_next;
+    }
+    freeaddrinfo(addr);
+    if (b) return libc_error(ctx); else return JS_UNDEFINED;
+  }
+}
+
+static JSValue js_os_listen(
+  JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv
+) {
+  int fd, backlog = 1;
+
+  if (argc < 1 ) return JS_EXCEPTION;
+  if ( JS_ToInt32(ctx, &fd, argv[0]) )
+    return JS_EXCEPTION;
+  if ( argc >= 2 && JS_ToInt32(ctx, &backlog, argv[1]) )
+    return JS_EXCEPTION;
+
+  if (! listen(fd, backlog)) return JS_UNDEFINED;
+  else return libc_error(ctx);
+}
+
+static JSValue js_os_accept(
+  JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv
+) {
+  struct sockaddr peer; socklen_t ps = sizeof(struct sockaddr);
+  char host[40], port[6];
+  int fd, new;
+
+  if ( argc < 1 ) return JS_EXCEPTION;
+  if ( JS_ToInt32(ctx, &fd, argv[0]) ) return JS_EXCEPTION;
+
+  // FIXME: use numeric port number (int instead of string)
+  if ( argc >= 2 ) {
+    if ((new = accept(fd, &peer, &ps)) != -1) {
+      JS_SetPropertyStr(ctx, argv[1], "protocol", JS_NewInt32(ctx, peer.sa_family));
+      if ( peer.sa_family != AF_UNIX ) {
+        getnameinfo(&peer, ps, host, 40, port, 6, NI_NUMERICHOST | NI_NUMERICSERV);
+        JS_SetPropertyStr(ctx, argv[1], "host", JS_NewString(ctx, host));
+        JS_SetPropertyStr(ctx, argv[1], "port", JS_NewString(ctx, port));
+      }
+    }
+  } else {
+    new = accept(fd, NULL, 0);
+  }
+  if (new != -1)
+    return JS_NewInt32(ctx, new);
+  else return JS_EXCEPTION;
+}
+
+static const JSCFunctionListEntry os_socket_funcs[] = {
+  JS_PROP_INT32_DEF("AF_UNIX",     AF_UNIX,     JS_PROP_CONFIGURABLE),
+  JS_PROP_INT32_DEF("AF_INET",     AF_INET,     JS_PROP_CONFIGURABLE),
+  JS_PROP_INT32_DEF("AF_INET6",    AF_INET6,    JS_PROP_CONFIGURABLE),
+  JS_PROP_INT32_DEF("SOCK_STREAM", SOCK_STREAM, JS_PROP_CONFIGURABLE),
+  JS_PROP_INT32_DEF("SOCK_DGRAM",  SOCK_DGRAM,  JS_PROP_CONFIGURABLE),
+  JS_CFUNC_DEF("socket",  3, js_os_socket),
+  JS_CFUNC_DEF("listen",  1, js_os_listen),
+  JS_CFUNC_DEF("accept",  1, js_os_accept),
+  JS_CFUNC_DEF("fork",    0, js_os_fork),
+  JS_CFUNC_MAGIC_DEF("bind",    2, js_os_bind, 0),
+  JS_CFUNC_MAGIC_DEF("connect", 2, js_os_bind, 1),
+  // JS_CFUNC_DEF("send",  2, js_os_send),   // would be same as os.write
+  // JS_CFUNC_DEF("recv",  2, js_os_recv),   // would be same as os.read
+  // JS_CFUNC_DEF("close", 1, js_os_close),  // would be same as os.close
+};
+
+static int sock_modinit(JSContext *ctx, JSModuleDef *mod ) {
+  return JS_SetModuleExportList(
+    ctx, mod, os_socket_funcs , countof(os_socket_funcs)
+  );
+}
+
+JSModuleDef *js_init_module(JSContext *ctx, const char *name) {
+  JSModuleDef *mod = JS_NewCModule(ctx, name, sock_modinit);
+  if (mod) JS_AddModuleExportList(
+    ctx, mod, os_socket_funcs, countof(os_socket_funcs)
+  );
+
+  return mod;
+}