--- /dev/null
+#include <quickjs/quickjs.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <sys/ioctl.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#define countof(x) ( sizeof(x) / sizeof(x[0]) )
+#define libc_error(ctx) ( JS_ThrowInternalError(ctx, "%s", strerror(errno)) )
+
+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;
+
+ 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);
+ 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);
+ }
+ if (new != -1)
+ return JS_NewInt32(ctx, new);
+ else return JS_EXCEPTION;
+}
+
+static const JSCFunctionListEntry os_socket_funcs[] = {
+ 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_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 JSClassID socket_cid;
+
+typedef struct {
+ int fd;
+ struct sockaddr bind;
+ struct sockaddr peer;
+} SocketData;
+
+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);
+ socklen_t ps = sizeof(struct sockaddr);
+
+ if ( newdata
+ && (newdata->fd = accept(data->fd, &(newdata->peer), &ps)) >= 0
+ ) {
+ memcpy(&(newdata->bind), &(data->bind), sizeof(struct sockaddr));
+ JS_SetOpaque(new, newdata);
+ } else {
+ new = libc_error(ctx);
+ js_free(ctx, newdata);
+ JS_FreeValue(ctx, new);
+ }
+
+ return new;
+}
+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;
+
+ if ( ! data->peer.sa_family ) {
+ return JS_ThrowInternalError(ctx, "Socket not connected");
+ } else if ( (buf = JS_GetArrayBuffer(ctx, &len, argv[0])) ) {
+ len = send(data->fd, buf, len, 0);
+ } else if ( (buf = (uint8_t*) JS_ToCStringLen(ctx, &len, argv[0])) ) {
+ len = send(data->fd, buf, len, 0);
+ JS_FreeCString(ctx, (char *) buf);
+ } else return JS_ThrowTypeError(ctx,
+ "expected ArrayBuffer or String"
+ );
+
+ if ((int)len < 0) return libc_error(ctx);
+ else return JS_NewInt32(ctx, len);
+}
+
+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;
+
+ if ( (JS_ToInt32(ctx, &len, argv[0]) || len <= 0)
+ && ioctl(data->fd, FIONREAD, &len)
+ ) return libc_error(ctx);
+
+ 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 = malloc(len))
+ && ((len = recv(data->fd, buf, len, 0)) >= 0)
+ ) {
+ if (str) jbuf = JS_NewStringLen(ctx, (char*) buf, len);
+ else jbuf = JS_NewArrayBufferCopy(ctx, buf, len);
+ free(buf);
+ return jbuf;
+ } else {
+ free(buf);
+ return libc_error(ctx);
+ }
+}
+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);
+ else return JS_UNDEFINED;
+}
+static void sock_destroy(JSRuntime *rt, JSValue this){
+ SocketData *data = JS_GetOpaque(this, socket_cid);
+ // close(data->fd); // do not do what a c programmer wouldn't do
+ js_free_rt(rt, data);
+};
+
+static const JSCFunctionListEntry socket_ptype[] = {
+ JS_CFUNC_DEF("accept", 0, sock_accept),
+ JS_CFUNC_DEF("send", 1, sock_send),
+ 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),
+};
+
+static JSClassDef socket_class = {
+ "Socket", .finalizer = sock_destroy
+};
+
+
+static int ai_socket(struct addrinfo *address) {
+ return socket(address->ai_family,
+ address->ai_socktype,
+ address->ai_protocol
+ );
+}
+
+static int ai_bind(int sock, struct addrinfo *address) {
+ return bind(sock,
+ address->ai_addr,
+ address->ai_addrlen
+ );
+}
+
+static int ai_connect(int sock, struct addrinfo *address) {
+ return connect(sock,
+ address->ai_addr,
+ address->ai_addrlen
+ );
+}
+
+static JSValue udp_listen( JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv) {
+ return JS_UNDEFINED;
+}
+static JSValue tcp_listen( JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv, int family) {
+ struct addrinfo hints = { .ai_family = family, .ai_socktype = SOCK_STREAM };
+ struct addrinfo * self;
+ const char *host, *port;
+ 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)
+ && !getaddrinfo( host, port, &hints, &self)
+ && (data->fd = ai_socket(self)) >= 0
+ && !ai_bind(data->fd, self)
+ && !listen(data->fd, 1)
+ ) {
+ 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);
+ new = libc_error(ctx);
+ }
+ 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 unix_connect(JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv) {
+ return JS_UNDEFINED;
+}
+
+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_PROP_INT32_DEF("SOCK_NONBLOCK", SOCK_NONBLOCK, JS_PROP_CONFIGURABLE),
+ JS_CFUNC_DEF("udpListen", 2, udp_listen),
+ JS_CFUNC_MAGIC_DEF("tcpListen", 2, tcp_listen, AF_INET),
+ JS_CFUNC_MAGIC_DEF("tcp6Listen", 2, tcp_listen, AF_INET6),
+ 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),
+};
+
+// static const JSCFunctionListEntry net_obj[] = {
+// JS_OBJECT_DEF("net", net_funcs, countof(net_funcs), JS_PROP_CONFIGURABLE)
+// };
+
+static int sock_modinit(JSContext *ctx, JSModuleDef *mod ) {
+ JSValue proto = JS_NewObject(ctx);
+
+ JS_NewClassID(&socket_cid);
+ JS_NewClass(JS_GetRuntime(ctx), socket_cid, &socket_class);
+
+ JS_SetPropertyFunctionList(ctx, proto, socket_ptype, countof(socket_ptype));
+ JS_SetClassProto(ctx, socket_cid, proto);
+
+ JS_SetModuleExportList(ctx, mod, net_funcs, countof(net_funcs));
+ return 0;
+}
+
+JSModuleDef *js_init_module(JSContext *ctx, const char *name) {
+ 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_FreeValue(ctx, global); JS_FreeValue(ctx, os);
+
+ return mod;
+}