]> git.plutz.net Git - quickjs_net/commitdiff
use sockaddr_storage in data structure, set peer address when receiving udp messages... master
authorPaul Hänsch <paul@plutz.net>
Thu, 12 Mar 2026 03:11:11 +0000 (04:11 +0100)
committerPaul Hänsch <paul@plutz.net>
Thu, 12 Mar 2026 03:11:11 +0000 (04:11 +0100)
socket.c

index f774fd173f1804c46101a8fb00d3e8e6c5fe92ec..80d9cd9e69c8ac86f7f7ca4997a08555c2ab9d8a 100644 (file)
--- 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[] = {