From: Paul Hänsch Date: Wed, 18 Mar 2026 17:57:33 +0000 (+0100) Subject: split low level os socket functions into separate module X-Git-Url: https://git.plutz.net/?a=commitdiff_plain;h=1df81f5cfb67854de2445f457780235afb0f80b0;p=quickjs_net split low level os socket functions into separate module --- diff --git a/os-net.c b/os-net.c new file mode 100644 index 0000000..cc217a2 --- /dev/null +++ b/os-net.c @@ -0,0 +1,150 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +}