#include "cell.h" #ifdef _WIN32 #include #include #include #pragma comment(lib, "ws2_32.lib") #define close closesocket #define SHUT_RD SD_RECEIVE #define SHUT_WR SD_SEND #define SHUT_RDWR SD_BOTH #ifndef AF_UNIX #define AF_UNIX 1 #endif #else #include #include #include #include #include #include #endif #include #include #include #include // Helper to convert JS value to file descriptor static int js2fd(JSContext *ctx, JSValueConst val) { int fd; if (JS_ToInt32(ctx, &fd, val) < 0) { JS_ThrowTypeError(ctx, "Expected file descriptor number"); return -1; } return fd; } // SOCKET FUNCTIONS JSC_CCALL(socket_getaddrinfo, const char *node = NULL; const char *service = NULL; struct addrinfo hints, *res; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if (!JS_IsNull(argv[0]) && !JS_IsNull(argv[0])) node = JS_ToCString(js, argv[0]); if (!JS_IsNull(argv[1]) && !JS_IsNull(argv[1])) service = JS_ToCString(js, argv[1]); // Parse optional hints object if (argc > 2 && JS_IsObject(argv[2])) { JSValue val; val = JS_GetPropertyStr(js, argv[2], "family"); if (!JS_IsNull(val)) { const char *family = JS_ToCString(js, val); if (strcmp(family, "AF_INET") == 0) hints.ai_family = AF_INET; else if (strcmp(family, "AF_INET6") == 0) hints.ai_family = AF_INET6; JS_FreeCString(js, family); } JS_FreeValue(js, val); val = JS_GetPropertyStr(js, argv[2], "socktype"); if (!JS_IsNull(val)) { const char *socktype = JS_ToCString(js, val); if (strcmp(socktype, "SOCK_STREAM") == 0) hints.ai_socktype = SOCK_STREAM; else if (strcmp(socktype, "SOCK_DGRAM") == 0) hints.ai_socktype = SOCK_DGRAM; JS_FreeCString(js, socktype); } JS_FreeValue(js, val); val = JS_GetPropertyStr(js, argv[2], "flags"); if (!JS_IsNull(val)) { hints.ai_flags = js2number(js, val); } JS_FreeValue(js, val); val = JS_GetPropertyStr(js, argv[2], "passive"); if (JS_ToBool(js, val)) { hints.ai_flags |= AI_PASSIVE; } JS_FreeValue(js, val); } int status = getaddrinfo(node, service, &hints, &res); if (node) JS_FreeCString(js, node); if (service) JS_FreeCString(js, service); if (status != 0) { return JS_ThrowReferenceError(js, "getaddrinfo error: %s", gai_strerror(status)); } // Convert linked list to JS array ret = JS_NewArray(js); int idx = 0; for (struct addrinfo *p = res; p != NULL; p = p->ai_next) { JSValue info = JS_NewObject(js); JS_SetPropertyStr(js, info, "family", JS_NewInt32(js, p->ai_family)); JS_SetPropertyStr(js, info, "socktype", JS_NewInt32(js, p->ai_socktype)); JS_SetPropertyStr(js, info, "protocol", JS_NewInt32(js, p->ai_protocol)); // Convert address to string char ipstr[INET6_ADDRSTRLEN]; void *addr; if (p->ai_family == AF_INET) { struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; addr = &(ipv4->sin_addr); } else { struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; addr = &(ipv6->sin6_addr); } inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr); JS_SetPropertyStr(js, info, "address", JS_NewString(js, ipstr)); // Store the addrinfo for later use struct addrinfo *copy = malloc(sizeof(struct addrinfo)); memcpy(copy, p, sizeof(struct addrinfo)); copy->ai_addr = malloc(p->ai_addrlen); memcpy(copy->ai_addr, p->ai_addr, p->ai_addrlen); copy->ai_next = NULL; if (p->ai_canonname) { copy->ai_canonname = strdup(p->ai_canonname); } // Store the addrinfo pointer as an internal property // We'll need to handle this differently since we can't wrap it // For now, we'll skip storing the raw addrinfo JS_SetPropertyUint32(js, ret, idx++, info); } freeaddrinfo(res); ) JSC_CCALL(socket_socket, int domain = AF_INET; int type = SOCK_STREAM; int protocol = 0; // Parse domain if (JS_IsText(argv[0])) { const char *domain_str = JS_ToCString(js, argv[0]); if (strcmp(domain_str, "AF_INET") == 0) domain = AF_INET; else if (strcmp(domain_str, "AF_INET6") == 0) domain = AF_INET6; else if (strcmp(domain_str, "AF_UNIX") == 0) domain = AF_UNIX; JS_FreeCString(js, domain_str); } else if (JS_IsNumber(argv[0])) { domain = js2number(js, argv[0]); } // Parse type if (argc > 1) { if (JS_IsText(argv[1])) { const char *type_str = JS_ToCString(js, argv[1]); if (strcmp(type_str, "SOCK_STREAM") == 0) type = SOCK_STREAM; else if (strcmp(type_str, "SOCK_DGRAM") == 0) type = SOCK_DGRAM; JS_FreeCString(js, type_str); } else if (JS_IsNumber(argv[1])) { type = js2number(js, argv[1]); } } // Parse protocol if (argc > 2) { protocol = js2number(js, argv[2]); } int sockfd = socket(domain, type, protocol); if (sockfd < 0) { return JS_ThrowReferenceError(js, "socket failed: %s", strerror(errno)); } return JS_NewInt32(js, sockfd); ) JSC_CCALL(socket_bind, int sockfd = js2fd(js, argv[0]); if (sockfd < 0) return JS_EXCEPTION; // For now, we'll only support manual address parsing { // Try to parse address and port manually const char *addr_str = JS_ToCString(js, JS_GetPropertyStr(js, argv[1], "address")); int port = js2number(js, JS_GetPropertyStr(js, argv[1], "port")); struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); if (inet_pton(AF_INET, addr_str, &addr.sin_addr) <= 0) { JS_FreeCString(js, addr_str); return JS_ThrowReferenceError(js, "Invalid address"); } JS_FreeCString(js, addr_str); if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { return JS_ThrowReferenceError(js, "bind failed: %s", strerror(errno)); } } return JS_NULL; ) JSC_CCALL(socket_connect, int sockfd = js2fd(js, argv[0]); if (sockfd < 0) return JS_EXCEPTION; // For now, we'll only support manual address parsing { // Try to parse address and port manually const char *addr_str = JS_ToCString(js, JS_GetPropertyStr(js, argv[1], "address")); int port = js2number(js, JS_GetPropertyStr(js, argv[1], "port")); struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); if (inet_pton(AF_INET, addr_str, &addr.sin_addr) <= 0) { JS_FreeCString(js, addr_str); return JS_ThrowReferenceError(js, "Invalid address"); } JS_FreeCString(js, addr_str); if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { return JS_ThrowReferenceError(js, "connect failed: %s", strerror(errno)); } } return JS_NULL; ) JSC_CCALL(socket_listen, int sockfd = js2fd(js, argv[0]); if (sockfd < 0) return JS_EXCEPTION; int backlog = 10; if (argc > 1) { backlog = js2number(js, argv[1]); } if (listen(sockfd, backlog) < 0) { return JS_ThrowReferenceError(js, "listen failed: %s", strerror(errno)); } return JS_NULL; ) JSC_CCALL(socket_accept, int sockfd = js2fd(js, argv[0]); if (sockfd < 0) return JS_EXCEPTION; struct sockaddr_storage their_addr; socklen_t addr_size = sizeof their_addr; int new_sockfd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_size); if (new_sockfd < 0) { return JS_ThrowReferenceError(js, "accept failed: %s", strerror(errno)); } ret = JS_NewObject(js); JS_SetPropertyStr(js, ret, "socket", JS_NewInt32(js, new_sockfd)); // Get peer address info char ipstr[INET6_ADDRSTRLEN]; int port; if (their_addr.ss_family == AF_INET) { struct sockaddr_in *s = (struct sockaddr_in *)&their_addr; inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr); port = ntohs(s->sin_port); } else { struct sockaddr_in6 *s = (struct sockaddr_in6 *)&their_addr; inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr); port = ntohs(s->sin6_port); } JSValue addr_info = JS_NewObject(js); JS_SetPropertyStr(js, addr_info, "address", JS_NewString(js, ipstr)); JS_SetPropertyStr(js, addr_info, "port", JS_NewInt32(js, port)); JS_SetPropertyStr(js, ret, "address", addr_info); ) JSC_CCALL(socket_send, int sockfd = js2fd(js, argv[0]); if (sockfd < 0) return JS_EXCEPTION; size_t len; ssize_t sent; int flags = 0; if (argc > 2) { flags = js2number(js, argv[2]); } if (JS_IsText(argv[1])) { const char *data = JS_ToCStringLen(js, &len, argv[1]); sent = send(sockfd, data, len, flags); JS_FreeCString(js, data); } else { unsigned char *data = js_get_blob_data(js, &len, argv[1]); if (data == -1) return JS_EXCEPTION; if (len == 0) return JS_ThrowReferenceError(js, "No data to send"); sent = send(sockfd, (const char *)data, len, flags); } if (sent < 0) { return JS_ThrowReferenceError(js, "send failed: %s", strerror(errno)); } return JS_NewInt64(js, sent); ) JSC_CCALL(socket_recv, int sockfd = js2fd(js, argv[0]); if (sockfd < 0) return JS_EXCEPTION; size_t len = 4096; if (argc > 1) { len = js2number(js, argv[1]); } int flags = 0; if (argc > 2) { flags = js2number(js, argv[2]); } void *buf = malloc(len); if (!buf) { return JS_ThrowReferenceError(js, "malloc failed"); } ssize_t received = recv(sockfd, buf, len, flags); if (received < 0) { free(buf); return JS_ThrowReferenceError(js, "recv failed: %s", strerror(errno)); } ret = js_new_blob_stoned_copy(js, buf, received); free(buf); return ret; ) JSC_CCALL(socket_sendto, int sockfd = js2fd(js, argv[0]); if (sockfd < 0) return JS_EXCEPTION; size_t len; ssize_t sent; int flags = 0; if (argc > 3) { flags = js2number(js, argv[3]); } // Get destination address struct sockaddr *to_addr; socklen_t to_len; // For now, we'll only support manual address parsing { // Try to parse address and port manually const char *addr_str = JS_ToCString(js, JS_GetPropertyStr(js, argv[2], "address")); int port = js2number(js, JS_GetPropertyStr(js, argv[2], "port")); static struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); if (inet_pton(AF_INET, addr_str, &addr.sin_addr) <= 0) { JS_FreeCString(js, addr_str); return JS_ThrowReferenceError(js, "Invalid address"); } JS_FreeCString(js, addr_str); to_addr = (struct sockaddr *)&addr; to_len = sizeof(addr); } if (JS_IsText(argv[1])) { const char *data = JS_ToCStringLen(js, &len, argv[1]); sent = sendto(sockfd, data, len, flags, to_addr, to_len); JS_FreeCString(js, data); } else { unsigned char *data = js_get_blob_data(js, &len, argv[1]); if (data == (unsigned char *)-1) { return JS_EXCEPTION; } if (len == 0) { return JS_ThrowReferenceError(js, "No data to send"); } sent = sendto(sockfd, (const char *)data, len, flags, to_addr, to_len); } if (sent < 0) { return JS_ThrowReferenceError(js, "sendto failed: %s", strerror(errno)); } return JS_NewInt64(js, sent); ) JSC_CCALL(socket_recvfrom, int sockfd = js2fd(js, argv[0]); if (sockfd < 0) return JS_EXCEPTION; size_t len = 4096; if (argc > 1) { len = js2number(js, argv[1]); } int flags = 0; if (argc > 2) { flags = js2number(js, argv[2]); } void *buf = malloc(len); if (!buf) { return JS_ThrowReferenceError(js, "malloc failed"); } struct sockaddr_storage from_addr; socklen_t from_len = sizeof from_addr; ssize_t received = recvfrom(sockfd, buf, len, flags, (struct sockaddr *)&from_addr, &from_len); if (received < 0) { free(buf); return JS_ThrowReferenceError(js, "recvfrom failed: %s", strerror(errno)); } ret = JS_NewObject(js); JS_SetPropertyStr(js, ret, "data", js_new_blob_stoned_copy(js, buf, received)); free(buf); // Get source address info char ipstr[INET6_ADDRSTRLEN]; int port; if (from_addr.ss_family == AF_INET) { struct sockaddr_in *s = (struct sockaddr_in *)&from_addr; inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr); port = ntohs(s->sin_port); } else { struct sockaddr_in6 *s = (struct sockaddr_in6 *)&from_addr; inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr); port = ntohs(s->sin6_port); } JSValue addr_info = JS_NewObject(js); JS_SetPropertyStr(js, addr_info, "address", JS_NewString(js, ipstr)); JS_SetPropertyStr(js, addr_info, "port", JS_NewInt32(js, port)); JS_SetPropertyStr(js, ret, "address", addr_info); ) JSC_CCALL(socket_shutdown, int sockfd = js2fd(js, argv[0]); if (sockfd < 0) return JS_EXCEPTION; int how = SHUT_RDWR; if (argc > 1) { how = js2number(js, argv[1]); } if (shutdown(sockfd, how) < 0) { return JS_ThrowReferenceError(js, "shutdown failed: %s", strerror(errno)); } return JS_NULL; ) JSC_CCALL(socket_getpeername, int sockfd = js2fd(js, argv[0]); if (sockfd < 0) return JS_EXCEPTION; struct sockaddr_storage addr; socklen_t len = sizeof addr; if (getpeername(sockfd, (struct sockaddr *)&addr, &len) < 0) { return JS_ThrowReferenceError(js, "getpeername failed: %s", strerror(errno)); } char ipstr[INET6_ADDRSTRLEN]; int port; if (addr.ss_family == AF_INET) { struct sockaddr_in *s = (struct sockaddr_in *)&addr; inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr); port = ntohs(s->sin_port); } else { struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr; inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr); port = ntohs(s->sin6_port); } ret = JS_NewObject(js); JS_SetPropertyStr(js, ret, "address", JS_NewString(js, ipstr)); JS_SetPropertyStr(js, ret, "port", JS_NewInt32(js, port)); ) JSC_CCALL(socket_gethostname, char hostname[256]; if (gethostname(hostname, sizeof(hostname)) < 0) { return JS_ThrowReferenceError(js, "gethostname failed: %s", strerror(errno)); } return JS_NewString(js, hostname); ) JSC_CCALL(socket_gai_strerror, int errcode = js2number(js, argv[0]); return JS_NewString(js, gai_strerror(errcode)); ) JSC_CCALL(socket_setsockopt, int sockfd = js2fd(js, argv[0]); if (sockfd < 0) return JS_EXCEPTION; int level = SOL_SOCKET; int optname = 0; // Parse level if (JS_IsText(argv[1])) { const char *level_str = JS_ToCString(js, argv[1]); if (strcmp(level_str, "SOL_SOCKET") == 0) level = SOL_SOCKET; else if (strcmp(level_str, "IPPROTO_TCP") == 0) level = IPPROTO_TCP; else if (strcmp(level_str, "IPPROTO_IP") == 0) level = IPPROTO_IP; else if (strcmp(level_str, "IPPROTO_IPV6") == 0) level = IPPROTO_IPV6; JS_FreeCString(js, level_str); } else { level = js2number(js, argv[1]); } // Parse option name if (JS_IsText(argv[2])) { const char *opt_str = JS_ToCString(js, argv[2]); if (strcmp(opt_str, "SO_REUSEADDR") == 0) optname = SO_REUSEADDR; else if (strcmp(opt_str, "SO_KEEPALIVE") == 0) optname = SO_KEEPALIVE; else if (strcmp(opt_str, "SO_BROADCAST") == 0) optname = SO_BROADCAST; JS_FreeCString(js, opt_str); } else { optname = js2number(js, argv[2]); } // Parse option value if (JS_IsBool(argv[3])) { int optval = JS_ToBool(js, argv[3]); if (setsockopt(sockfd, level, optname, &optval, sizeof(optval)) < 0) { return JS_ThrowReferenceError(js, "setsockopt failed: %s", strerror(errno)); } } else if (JS_IsNumber(argv[3])) { int optval = js2number(js, argv[3]); if (setsockopt(sockfd, level, optname, &optval, sizeof(optval)) < 0) { return JS_ThrowReferenceError(js, "setsockopt failed: %s", strerror(errno)); } } else { return JS_ThrowTypeError(js, "Invalid option value"); } return JS_NULL; ) JSC_CCALL(socket_close, int sockfd = js2fd(js, argv[0]); if (sockfd < 0) return JS_EXCEPTION; if (close(sockfd) != 0) return JS_ThrowReferenceError(js, "close failed: %s", strerror(errno)); return JS_NULL; ) static const JSCFunctionListEntry js_socket_funcs[] = { MIST_FUNC_DEF(socket, getaddrinfo, 3), MIST_FUNC_DEF(socket, socket, 3), MIST_FUNC_DEF(socket, bind, 2), MIST_FUNC_DEF(socket, connect, 2), MIST_FUNC_DEF(socket, listen, 2), MIST_FUNC_DEF(socket, accept, 1), MIST_FUNC_DEF(socket, send, 3), MIST_FUNC_DEF(socket, recv, 3), MIST_FUNC_DEF(socket, sendto, 4), MIST_FUNC_DEF(socket, recvfrom, 3), MIST_FUNC_DEF(socket, shutdown, 2), MIST_FUNC_DEF(socket, getpeername, 1), MIST_FUNC_DEF(socket, gethostname, 0), MIST_FUNC_DEF(socket, gai_strerror, 1), MIST_FUNC_DEF(socket, setsockopt, 4), MIST_FUNC_DEF(socket, close, 1), }; JSValue js_socket_use(JSContext *js) { JSValue mod = JS_NewObject(js); JS_SetPropertyFunctionList(js, mod, js_socket_funcs, countof(js_socket_funcs)); // Add constants JS_SetPropertyStr(js, mod, "AF_UNSPEC", JS_NewInt32(js, AF_UNSPEC)); JS_SetPropertyStr(js, mod, "AF_INET", JS_NewInt32(js, AF_INET)); JS_SetPropertyStr(js, mod, "AF_INET6", JS_NewInt32(js, AF_INET6)); JS_SetPropertyStr(js, mod, "AF_UNIX", JS_NewInt32(js, AF_UNIX)); JS_SetPropertyStr(js, mod, "SOCK_STREAM", JS_NewInt32(js, SOCK_STREAM)); JS_SetPropertyStr(js, mod, "SOCK_DGRAM", JS_NewInt32(js, SOCK_DGRAM)); JS_SetPropertyStr(js, mod, "AI_PASSIVE", JS_NewInt32(js, AI_PASSIVE)); JS_SetPropertyStr(js, mod, "SHUT_RD", JS_NewInt32(js, SHUT_RD)); JS_SetPropertyStr(js, mod, "SHUT_WR", JS_NewInt32(js, SHUT_WR)); JS_SetPropertyStr(js, mod, "SHUT_RDWR", JS_NewInt32(js, SHUT_RDWR)); JS_SetPropertyStr(js, mod, "SOL_SOCKET", JS_NewInt32(js, SOL_SOCKET)); JS_SetPropertyStr(js, mod, "SO_REUSEADDR", JS_NewInt32(js, SO_REUSEADDR)); return mod; }