From: Paul Hänsch Date: Thu, 12 Mar 2026 03:11:11 +0000 (+0100) Subject: use sockaddr_storage in data structure, set peer address when receiving udp messages... X-Git-Url: https://git.plutz.net/?a=commitdiff_plain;h=HEAD;p=quickjs_net use sockaddr_storage in data structure, set peer address when receiving udp messages, optimize buffer in recv --- diff --git a/socket.c b/socket.c index f774fd1..80d9cd9 100644 --- a/socket.c +++ b/socket.c @@ -93,24 +93,23 @@ static JSValue js_os_listen( static JSValue js_os_accept( JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv ) { - struct sockaddr * peer; socklen_t ps = sizeof(struct sockaddr); + 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 ) { - peer = malloc(ps); - 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); + 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)); } } - free(peer); } else { new = accept(fd, NULL, 0); } @@ -134,8 +133,8 @@ static JSClassID socket_cid; typedef struct { int fd; - struct sockaddr bind; - struct sockaddr peer; + struct sockaddr_storage bind; + struct sockaddr_storage peer; } SocketData; static JSValue sock_accept( @@ -144,12 +143,14 @@ static JSValue sock_accept( SocketData * data = JS_GetOpaque2(ctx, this, socket_cid); SocketData * newdata = js_mallocz(ctx, sizeof(SocketData)); JSValue new = JS_NewObjectClass(ctx, socket_cid); - socklen_t ps = sizeof(struct sockaddr); + socklen_t ps = sizeof(data->bind); if ( newdata - && (newdata->fd = accept(data->fd, &(newdata->peer), &ps)) >= 0 + && (newdata->fd = accept( + data->fd, (struct sockaddr *) &(newdata->peer), &ps + )) >= 0 ) { - memcpy(&(newdata->bind), &(data->bind), sizeof(struct sockaddr)); + memcpy(&(newdata->bind), &(data->bind), sizeof(data->bind)); JS_SetOpaque(new, newdata); } else { new = libc_error(ctx); @@ -159,13 +160,13 @@ static JSValue sock_accept( return new; } -static JSValue sock_send( +static JSValue sock_send( // TODO: optional dstHost, dstPort for UDP 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) + // XXX: 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])) ) { @@ -179,13 +180,19 @@ static JSValue sock_send( else return JS_NewInt32(ctx, len); } +// like in quickjs.c +static void js_array_buffer_free(JSRuntime *rt, void *junk, void *buf) { + js_free_rt(rt, buf); +} + 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; + socklen_t ps = sizeof(data->peer); - // FIXME: how portable is ioctl/FIONREAD ? + // XXX: how portable is ioctl/FIONREAD ? if ( (JS_ToInt32(ctx, &len, argv[0]) || len <= 0) && ioctl(data->fd, FIONREAD, &len) ) return libc_error(ctx); @@ -195,12 +202,18 @@ static JSValue sock_recv( else if (len == 0) return JS_NewArrayBufferCopy(ctx, NULL, 0); else if ( len > 0 - && (buf = malloc(len)) - && ((len = recv(data->fd, buf, len, 0)) >= 0) + && (buf = js_malloc(ctx, len)) + && ((len = recvfrom(data->fd, buf, len, 0, + (struct sockaddr *) &(data->peer), &ps)) >= 0) ) { + // make sure the buffer is resized after a short read + // avoid realloc(p, 0) as recommended by glibc + if (len) js_realloc(ctx, buf, len); + else { js_free(ctx, buf); buf = NULL; } + + // XXX: what does the opaque field in NewArryBuffer do? Is it important? if (str) jbuf = JS_NewStringLen(ctx, (char*) buf, len); - else jbuf = JS_NewArrayBufferCopy(ctx, buf, len); - free(buf); + else jbuf = JS_NewArrayBuffer(ctx, buf, len, js_array_buffer_free, NULL, 0); return jbuf; } else { free(buf); @@ -210,24 +223,24 @@ static JSValue sock_recv( 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); + struct sockaddr_storage * 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) + if (addr->ss_family == AF_INET) return JS_NewInt32(ctx, ntohs(((struct sockaddr_in *) addr)->sin_port)); - else if (addr->sa_family == AF_INET6) + else if (addr->ss_family == AF_INET6) return JS_NewInt32(ctx, ntohs(((struct sockaddr_in6 *) addr)->sin6_port)); else return JS_UNDEFINED; } - if (addr->sa_family == AF_UNIX) { + if (addr->ss_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), + } else if (addr->ss_family == AF_INET || addr->ss_family == AF_INET6) { + if (!getnameinfo((struct sockaddr *) addr, sizeof(*addr), host, 40, NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV) ) { return JS_NewString(ctx, host); @@ -259,6 +272,8 @@ static const JSCFunctionListEntry socket_ptype[] = { 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), + // JS_CGETSET_DEF("timeout", ) // TODO: send / receive timeout + // JS_CFUNC_DEF("reconnect", ) // TODO }; static JSClassDef socket_class = { @@ -354,8 +369,8 @@ static JSValue unix_bind( && (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 ?connect(data->fd,(struct sockaddr *) &(data->peer),sizeof(*addr)) + : bind(data->fd,(struct sockaddr *) &(data->bind),sizeof(*addr)) ) && (c || !listen(data->fd, 1)) ) { JS_SetOpaque(new, data); @@ -392,6 +407,7 @@ static const JSCFunctionListEntry net_funcs[] = { 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), + // JS_CGETSET_DEF("timeout", ) // TODO: connect timeout }; // static const JSCFunctionListEntry net_obj[] = {