]> git.plutz.net Git - quickjs_net/commitdiff
allow selection destination for udp send
authorPaul Hänsch <paul@plutz.net>
Thu, 12 Mar 2026 14:12:52 +0000 (15:12 +0100)
committerPaul Hänsch <paul@plutz.net>
Thu, 12 Mar 2026 14:12:52 +0000 (15:12 +0100)
socket.c

index 80d9cd9e69c8ac86f7f7ca4997a08555c2ab9d8a..2b5c9ff9e0e79c31efe74b295228a9058aee2c71 100644 (file)
--- a/socket.c
+++ b/socket.c
@@ -133,6 +133,7 @@ static JSClassID socket_cid;
 
 typedef struct {
   int fd;
+  int type;
   struct sockaddr_storage bind;
   struct sockaddr_storage peer;
 } SocketData;
@@ -160,18 +161,43 @@ static JSValue sock_accept(
 
   return new;
 }
-static JSValue sock_send(  // TODO: optional dstHost, dstPort for UDP
+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;
+  const char *host, *port; int gai_err;
+  struct addrinfo hints = { .ai_family = data->bind.ss_family, .ai_socktype = data->type };
+  struct addrinfo * dest; struct sockaddr * to;
+
+  // destHost and destPort may be given for UDP sockets
+  if (data->type == SOCK_DGRAM && argc >= 3) {
+    host = JS_ToCString(ctx, argv[1]);
+    port = JS_ToCString(ctx, argv[2]);
+    if (! (gai_err = getaddrinfo(host, port, &hints, &dest)))
+      memcpy(&(data->peer), dest->ai_addr, dest->ai_addrlen);
+    JS_FreeCString(ctx, host); JS_FreeCString(ctx, port);
+    freeaddrinfo(dest);
+    if (gai_err) return libc_error(ctx);
+    to = (struct sockaddr *) &(data->peer);
+
+  // otherwise use default peer address
+  } else if (data->type == SOCK_DGRAM) {
+    to = (struct sockaddr *) &(data->peer);
 
-  // XXX: how portable is MSG_NOSIGNAL ? (POSIX.1-2008)
+  // destination should not be given for SOCK_STREAM sockets
+  } else to = NULL;
+    
+
+  // Read Argument either as ArrayBuffer or String
   if ( (buf = JS_GetArrayBuffer(ctx, &len, argv[0])) ) {
-    len = send(data->fd, buf, len, MSG_NOSIGNAL);
+    // XXX: how portable is MSG_NOSIGNAL ? (POSIX.1-2008)
+    len = sendto(data->fd, buf, len, MSG_NOSIGNAL, to, to ? sizeof(*to) : 0);
+
   } else if ( (buf = (uint8_t*) JS_ToCStringLen(ctx, &len, argv[0])) ) {
-    len = send(data->fd, buf, len, MSG_NOSIGNAL);
+    len = sendto(data->fd, buf, len, MSG_NOSIGNAL, to, to ? sizeof(*to) : 0);
     JS_FreeCString(ctx, (char *) buf);
+
   } else return JS_ThrowTypeError(ctx,
     "expected ArrayBuffer or String"
   );
@@ -180,7 +206,7 @@ static JSValue sock_send(  // TODO: optional dstHost, dstPort for UDP
   else return JS_NewInt32(ctx, len);
 }
 
-// like in quickjs.c
+// like in quickjs.c, needed for direct ArrayBuffer in sock_recv()
 static void js_array_buffer_free(JSRuntime *rt, void *junk, void *buf) {
   js_free_rt(rt, buf);
 }
@@ -192,15 +218,18 @@ static JSValue sock_recv(
   uint8_t *buf; int len; JSValue jbuf;
   socklen_t ps = sizeof(data->peer);
 
+  // see how much data is queued up if no length argument was given
   // XXX: how portable is ioctl/FIONREAD ?
   if ( (JS_ToInt32(ctx, &len, argv[0]) || len <= 0)
     && ioctl(data->fd, FIONREAD, &len)
   ) return libc_error(ctx);
 
+  // return immediately if 0 data was requested (and queue is empty)
   if (len == 0 && str)
     return JS_NewStringLen(ctx, NULL, 0);
   else if (len == 0)
     return JS_NewArrayBufferCopy(ctx, NULL, 0);
+
   else if ( len > 0
     && (buf = js_malloc(ctx, len))
     && ((len = recvfrom(data->fd, buf, len, 0,
@@ -215,8 +244,9 @@ static JSValue sock_recv(
     if (str) jbuf = JS_NewStringLen(ctx, (char*) buf, len);
     else jbuf = JS_NewArrayBuffer(ctx, buf, len, js_array_buffer_free, NULL, 0);
     return jbuf;
+
   } else {
-    free(buf);
+    js_free(ctx, buf);
     return libc_error(ctx);
   }
 }
@@ -301,6 +331,7 @@ static JSValue ip_listen(
     && !ai_bind(data->fd, self)
     && ( hints.ai_socktype == SOCK_DGRAM || !listen(data->fd, 1))
   ) {
+    data->type = self->ai_socktype;
     memcpy(&(data->bind), self->ai_addr, sizeof(struct sockaddr));
     JS_SetOpaque(new, data);
   } else {
@@ -337,6 +368,7 @@ static JSValue ip_connect(
     && (data->fd = ai_socket(peer)) >= 0
     && !ai_connect(data->fd, peer)
   ) {
+    data->type = peer->ai_socktype;
     memcpy(&(data->peer), peer->ai_addr, sizeof(struct sockaddr));
     JS_SetOpaque(new, data);
   } else {
@@ -373,6 +405,7 @@ static JSValue unix_bind(
             :   bind(data->fd,(struct sockaddr *) &(data->bind),sizeof(*addr))
     ) && (c || !listen(data->fd, 1))
   ) {
+    data->type = SOCK_STREAM;
     JS_SetOpaque(new, data);
   } else {
     if (data && data->fd >= 0) close(data->fd);