From: Paul Hänsch Date: Wed, 11 Mar 2026 19:43:01 +0000 (+0100) Subject: unix sockets, getters for address lookup, some code reformatting X-Git-Url: https://git.plutz.net/?a=commitdiff_plain;h=fa8102177af323a374ef0a8fdd5fc2bbce3b090d;p=quickjs_net unix sockets, getters for address lookup, some code reformatting --- diff --git a/socket.c b/socket.c index 12ea884..f774fd1 100644 --- a/socket.c +++ b/socket.c @@ -13,11 +13,16 @@ #define libc_error(ctx) ( JS_ThrowInternalError(ctx, "%s", strerror(errno)) ) // use struct addrinfo in socket functions -#define ai_socket(a) ( socket( (a)->ai_family, (a)->ai_socktype, (a)->ai_protocol ) ) -#define ai_bind(s, a) ( bind((s), (a)->ai_addr, (a)->ai_addrlen) ) +#define ai_socket(a) ( socket( (a)->ai_family, \ + (a)->ai_socktype, \ + (a)->ai_protocol \ + ) ) +#define ai_bind(s, a) ( bind((s), (a)->ai_addr, (a)->ai_addrlen) ) #define ai_connect(s, a) ( connect((s), (a)->ai_addr, (a)->ai_addrlen) ) -static JSValue js_os_socket( JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv) { +static JSValue js_os_socket( + JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv +) { int family, type, protocol, fd; if ( argc >= 3 @@ -32,7 +37,9 @@ static JSValue js_os_socket( JSContext *ctx, JSValueConst this, int argc, JSValu } } -static JSValue js_os_bind( JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv, int con) { +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; @@ -68,7 +75,9 @@ static JSValue js_os_bind( JSContext *ctx, JSValueConst this, int argc, JSValu } } -static JSValue js_os_listen( JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv) { +static JSValue js_os_listen( + JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv +) { int fd, backlog = 1; if (argc < 1 ) return JS_EXCEPTION; @@ -81,7 +90,9 @@ static JSValue js_os_listen( JSContext *ctx, JSValueConst this, int argc, JSValu else return libc_error(ctx); } -static JSValue js_os_accept( JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv) { +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; @@ -127,7 +138,9 @@ typedef struct { struct sockaddr peer; } SocketData; -static JSValue sock_accept(JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv) { +static JSValue sock_accept( + JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv +) { SocketData * data = JS_GetOpaque2(ctx, this, socket_cid); SocketData * newdata = js_mallocz(ctx, sizeof(SocketData)); JSValue new = JS_NewObjectClass(ctx, socket_cid); @@ -146,10 +159,13 @@ static JSValue sock_accept(JSContext *ctx, JSValueConst this, int argc, JSValueC return new; } -static JSValue sock_send( JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv) { +static JSValue sock_send( + JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv +) { SocketData * data = JS_GetOpaque2(ctx, this, socket_cid); uint8_t *buf; size_t len; + // FIXME: how portable is MSG_NOSIGNAL ? (POSIX.1-2008) if ( (buf = JS_GetArrayBuffer(ctx, &len, argv[0])) ) { len = send(data->fd, buf, len, MSG_NOSIGNAL); } else if ( (buf = (uint8_t*) JS_ToCStringLen(ctx, &len, argv[0])) ) { @@ -163,7 +179,9 @@ static JSValue sock_send( JSContext *ctx, JSValueConst this, int argc, JSValueC else return JS_NewInt32(ctx, len); } -static JSValue sock_recv( JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv, int str) { +static JSValue sock_recv( + JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv, int str +) { SocketData * data = JS_GetOpaque2(ctx, this, socket_cid); uint8_t *buf; int len; JSValue jbuf; @@ -189,7 +207,37 @@ static JSValue sock_recv( JSContext *ctx, JSValueConst this, int argc, JSValueC return libc_error(ctx); } } -static JSValue sock_close( JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv) { + +static JSValue sock_get_addr( JSContext *ctx, JSValueConst this, int magic ) { + SocketData * data = JS_GetOpaque2(ctx, this, socket_cid); + struct sockaddr * addr = (magic&2) ? &(data->peer) : &(data->bind); + char host[40]; + + if ( addr == NULL) return JS_UNDEFINED; + + if (magic&1){ + if (addr->sa_family == AF_INET) + return JS_NewInt32(ctx, ntohs(((struct sockaddr_in *) addr)->sin_port)); + else if (addr->sa_family == AF_INET6) + return JS_NewInt32(ctx, ntohs(((struct sockaddr_in6 *) addr)->sin6_port)); + else return JS_UNDEFINED; + } + + if (addr->sa_family == AF_UNIX) { + return JS_NewString(ctx, ((struct sockaddr_un *) addr)->sun_path); + + } else if (addr->sa_family == AF_INET || addr->sa_family == AF_INET6) { + if (!getnameinfo(addr, sizeof(struct sockaddr), + host, 40, NULL, 0, + NI_NUMERICHOST | NI_NUMERICSERV) + ) { return JS_NewString(ctx, host); + } else return libc_error(ctx); + } else return JS_UNDEFINED; +} + +static JSValue sock_close( + JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv +) { SocketData *data = JS_GetOpaque2(ctx, this, socket_cid); if ( close(data->fd) ) return libc_error(ctx); @@ -207,13 +255,19 @@ static const JSCFunctionListEntry socket_ptype[] = { JS_CFUNC_DEF("close", 0, sock_close), JS_CFUNC_MAGIC_DEF("recv", 0, sock_recv, 0), JS_CFUNC_MAGIC_DEF("recvString", 0, sock_recv, 1), + JS_CGETSET_MAGIC_DEF("localName", sock_get_addr, NULL, 0), + JS_CGETSET_MAGIC_DEF("localPort", sock_get_addr, NULL, 1), + JS_CGETSET_MAGIC_DEF("peerName", sock_get_addr, NULL, 2), + JS_CGETSET_MAGIC_DEF("peerPort", sock_get_addr, NULL, 3), }; static JSClassDef socket_class = { "Socket", .finalizer = sock_destroy }; -static JSValue ip_listen( JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv, int magic) { +static JSValue ip_listen( + JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv, int magic +) { struct addrinfo hints = { .ai_family = (magic&2) ? AF_INET6 : AF_INET, .ai_socktype = (magic&1) ? SOCK_STREAM : SOCK_DGRAM, @@ -235,45 +289,109 @@ static JSValue ip_listen( JSContext *ctx, JSValueConst this, int argc, JSValueCo memcpy(&(data->bind), self->ai_addr, sizeof(struct sockaddr)); JS_SetOpaque(new, data); } else { - JS_FreeValue(ctx, new); - if (data->fd >= 0) close(data->fd); - js_free(ctx, data); - if (!gai_err) new = libc_error(ctx); + if (data && data->fd >= 0) close(data->fd); + if (!data) new = JS_EXCEPTION; + else if (!gai_err) new = libc_error(ctx); else new = JS_ThrowInternalError(ctx, "%s", gai_strerror(gai_err)); + js_free(ctx, data); + JS_FreeValue(ctx, new); } freeaddrinfo(self); JS_FreeCString(ctx, host); JS_FreeCString(ctx, port); return new; } -static JSValue unix_listen(JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv) { - return JS_UNDEFINED; -} -static JSValue udp_connect( JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv) { - return JS_UNDEFINED; -} -static JSValue tcp_connect( JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv) { - return JS_UNDEFINED; +static JSValue ip_connect( + JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv, int magic +) { + struct addrinfo hints = { + .ai_family = (magic&2) ? AF_INET6 : AF_INET, + .ai_socktype = (magic&1) ? SOCK_STREAM : SOCK_DGRAM, + }; + struct addrinfo * peer = NULL; + const char *host, *port; int gai_err; + SocketData *data = js_mallocz(ctx, sizeof(*data)); + JSValue new = JS_NewObjectClass(ctx, socket_cid); + + host = JS_ToCString(ctx, argv[0]); + port = JS_ToCString(ctx, argv[1]); + + if ( data && (data->fd = -1) + && !(gai_err = getaddrinfo( host, port, &hints, &peer)) + && (data->fd = ai_socket(peer)) >= 0 + && !ai_connect(data->fd, peer) + ) { + memcpy(&(data->peer), peer->ai_addr, sizeof(struct sockaddr)); + JS_SetOpaque(new, data); + } else { + if (data && data->fd >= 0) close(data->fd); + if (!data) new = JS_EXCEPTION; + else if (!gai_err) new = libc_error(ctx); + else new = JS_ThrowInternalError(ctx, "%s", gai_strerror(gai_err)); + js_free(ctx, data); + JS_FreeValue(ctx, new); + } + freeaddrinfo(peer); + JS_FreeCString(ctx, host); JS_FreeCString(ctx, port); + + return new; } -static JSValue unix_connect(JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv) { - return JS_UNDEFINED; + +static JSValue unix_bind( + JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv, int c +) { + SocketData *data = js_mallocz(ctx, sizeof(*data)); + JSValue new = JS_NewObjectClass(ctx, socket_cid); + const char *path; size_t plen; + struct sockaddr_un * addr; + + path = JS_ToCStringLen(ctx, &plen, argv[0]); + + if ( data && (data->fd = -1) + && (addr = (struct sockaddr_un *) (c ? &(data->peer) : &(data->bind))) + && (addr->sun_family = AF_UNIX) + && (plen < sizeof(addr->sun_path)) + && strncpy(addr->sun_path, path, plen) + && (data->fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0 + && !( c ? connect(data->fd, &(data->peer), sizeof(*addr)) + : bind(data->fd, &(data->bind), sizeof(*addr)) + ) && (c || !listen(data->fd, 1)) + ) { + JS_SetOpaque(new, data); + } else { + if (data && data->fd >= 0) close(data->fd); + if (!data) { + new = JS_EXCEPTION; + } else if (plen < sizeof(addr->sun_path)) { + new = libc_error(ctx); + } else new = JS_ThrowRangeError( + ctx, "pathname too long (>= %lu bytes)", sizeof(addr->sun_path) + ); + js_free(ctx, data); + JS_FreeValue(ctx, new); + } + JS_FreeCString(ctx, path); + + return new; } static const JSCFunctionListEntry net_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_MAGIC_DEF("udpListen", 2, ip_listen, 0), - JS_CFUNC_MAGIC_DEF("tcpListen", 2, ip_listen, 1), - JS_CFUNC_MAGIC_DEF("udp6Listen", 2, ip_listen, 2), - JS_CFUNC_MAGIC_DEF("tcp6Listen", 2, ip_listen, 3), - JS_CFUNC_DEF("unixListen", 1, unix_listen), - JS_CFUNC_DEF("udpConnect", 2, udp_connect), - JS_CFUNC_DEF("tcpConnect", 2, tcp_connect), - JS_CFUNC_DEF("unixConnect", 1, unix_connect), + 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_MAGIC_DEF("udpListen", 2, ip_listen, 0), + JS_CFUNC_MAGIC_DEF("tcpListen", 2, ip_listen, 1), + JS_CFUNC_MAGIC_DEF("udp6Listen", 2, ip_listen, 2), + JS_CFUNC_MAGIC_DEF("tcp6Listen", 2, ip_listen, 3), + JS_CFUNC_MAGIC_DEF("udpConnect", 2, ip_connect, 0), + JS_CFUNC_MAGIC_DEF("tcpConnect", 2, ip_connect, 1), + JS_CFUNC_MAGIC_DEF("udp6Connect", 2, ip_connect, 2), + JS_CFUNC_MAGIC_DEF("tcp6Connect", 2, ip_connect, 3), + JS_CFUNC_MAGIC_DEF("unixListen", 1, unix_bind, 0), + JS_CFUNC_MAGIC_DEF("unixConnect", 1, unix_bind, 1), }; // static const JSCFunctionListEntry net_obj[] = { @@ -294,15 +412,16 @@ static int sock_modinit(JSContext *ctx, JSModuleDef *mod ) { } JSModuleDef *js_init_module(JSContext *ctx, const char *name) { + JSModuleDef *mod = JS_NewCModule(ctx, name, sock_modinit); JSValue global = JS_GetGlobalObject(ctx); JSValue os = JS_GetPropertyStr(ctx, global, "os"); - JSModuleDef *mod = JS_NewCModule(ctx, name, sock_modinit); if (mod) { JS_AddModuleExportList(ctx, mod, net_funcs, countof(net_funcs)); // JS_SetPropertyFunctionList(ctx, global, net_obj, countof(net_obj)); if ( !JS_IsUndefined(os) ) - JS_SetPropertyFunctionList(ctx, os, os_socket_funcs, countof(os_socket_funcs)); + JS_SetPropertyFunctionList(ctx, os, os_socket_funcs, + countof(os_socket_funcs)); } JS_FreeValue(ctx, global); JS_FreeValue(ctx, os);