#define libc_error(ctx) ( JS_ThrowInternalError(ctx, "%s", strerror(errno)) )
// use struct addrinfo in socket functions
-#define ai_socket(a) ( socket( (a)->ai_family, (a)->ai_socktype, (a)->ai_protocol ) )
-#define ai_bind(s, a) ( bind((s), (a)->ai_addr, (a)->ai_addrlen) )
+#define ai_socket(a) ( socket( (a)->ai_family, \
+ (a)->ai_socktype, \
+ (a)->ai_protocol \
+ ) )
+#define ai_bind(s, a) ( bind((s), (a)->ai_addr, (a)->ai_addrlen) )
#define ai_connect(s, a) ( connect((s), (a)->ai_addr, (a)->ai_addrlen) )
-static JSValue js_os_socket( JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv) {
+static JSValue js_os_socket(
+ JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv
+) {
int family, type, protocol, fd;
if ( argc >= 3
}
}
-static JSValue js_os_bind( JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv, int con) {
+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;
}
}
-static JSValue js_os_listen( JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv) {
+static JSValue js_os_listen(
+ JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv
+) {
int fd, backlog = 1;
if (argc < 1 ) return JS_EXCEPTION;
else return libc_error(ctx);
}
-static JSValue js_os_accept( JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv) {
+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;
struct sockaddr peer;
} SocketData;
-static JSValue sock_accept(JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv) {
+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);
return new;
}
-static JSValue sock_send( JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv) {
+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;
+ // FIXME: 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])) ) {
else return JS_NewInt32(ctx, len);
}
-static JSValue sock_recv( JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv, int str) {
+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;
return libc_error(ctx);
}
}
-static JSValue sock_close( JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv) {
+
+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);
+ char host[40];
+
+ if ( addr == NULL) return JS_UNDEFINED;
+
+ if (magic&1){
+ if (addr->sa_family == AF_INET)
+ return JS_NewInt32(ctx, ntohs(((struct sockaddr_in *) addr)->sin_port));
+ else if (addr->sa_family == AF_INET6)
+ return JS_NewInt32(ctx, ntohs(((struct sockaddr_in6 *) addr)->sin6_port));
+ else return JS_UNDEFINED;
+ }
+
+ if (addr->sa_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),
+ host, 40, NULL, 0,
+ NI_NUMERICHOST | NI_NUMERICSERV)
+ ) { return JS_NewString(ctx, host);
+ } else return libc_error(ctx);
+ } else return JS_UNDEFINED;
+}
+
+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);
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),
+ JS_CGETSET_MAGIC_DEF("localName", sock_get_addr, NULL, 0),
+ 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),
};
static JSClassDef socket_class = {
"Socket", .finalizer = sock_destroy
};
-static JSValue ip_listen( JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv, int magic) {
+static JSValue ip_listen(
+ JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv, int magic
+) {
struct addrinfo hints = {
.ai_family = (magic&2) ? AF_INET6 : AF_INET,
.ai_socktype = (magic&1) ? SOCK_STREAM : SOCK_DGRAM,
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);
- if (!gai_err) new = libc_error(ctx);
+ if (data && data->fd >= 0) close(data->fd);
+ if (!data) new = JS_EXCEPTION;
+ else if (!gai_err) new = libc_error(ctx);
else new = JS_ThrowInternalError(ctx, "%s", gai_strerror(gai_err));
+ js_free(ctx, data);
+ JS_FreeValue(ctx, new);
}
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 ip_connect(
+ JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv, int magic
+) {
+ struct addrinfo hints = {
+ .ai_family = (magic&2) ? AF_INET6 : AF_INET,
+ .ai_socktype = (magic&1) ? SOCK_STREAM : SOCK_DGRAM,
+ };
+ struct addrinfo * peer = NULL;
+ const char *host, *port; int gai_err;
+ 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)
+ && !(gai_err = getaddrinfo( host, port, &hints, &peer))
+ && (data->fd = ai_socket(peer)) >= 0
+ && !ai_connect(data->fd, peer)
+ ) {
+ memcpy(&(data->peer), peer->ai_addr, sizeof(struct sockaddr));
+ JS_SetOpaque(new, data);
+ } else {
+ if (data && data->fd >= 0) close(data->fd);
+ if (!data) new = JS_EXCEPTION;
+ else if (!gai_err) new = libc_error(ctx);
+ else new = JS_ThrowInternalError(ctx, "%s", gai_strerror(gai_err));
+ js_free(ctx, data);
+ JS_FreeValue(ctx, new);
+ }
+ freeaddrinfo(peer);
+ JS_FreeCString(ctx, host); JS_FreeCString(ctx, port);
+
+ return new;
}
-static JSValue unix_connect(JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv) {
- return JS_UNDEFINED;
+
+static JSValue unix_bind(
+ JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv, int c
+) {
+ SocketData *data = js_mallocz(ctx, sizeof(*data));
+ JSValue new = JS_NewObjectClass(ctx, socket_cid);
+ const char *path; size_t plen;
+ struct sockaddr_un * addr;
+
+ path = JS_ToCStringLen(ctx, &plen, argv[0]);
+
+ if ( data && (data->fd = -1)
+ && (addr = (struct sockaddr_un *) (c ? &(data->peer) : &(data->bind)))
+ && (addr->sun_family = AF_UNIX)
+ && (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 || !listen(data->fd, 1))
+ ) {
+ JS_SetOpaque(new, data);
+ } else {
+ if (data && data->fd >= 0) close(data->fd);
+ if (!data) {
+ new = JS_EXCEPTION;
+ } else if (plen < sizeof(addr->sun_path)) {
+ new = libc_error(ctx);
+ } else new = JS_ThrowRangeError(
+ ctx, "pathname too long (>= %lu bytes)", sizeof(addr->sun_path)
+ );
+ js_free(ctx, data);
+ JS_FreeValue(ctx, new);
+ }
+ JS_FreeCString(ctx, path);
+
+ return new;
}
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_CFUNC_MAGIC_DEF("udpListen", 2, ip_listen, 0),
- JS_CFUNC_MAGIC_DEF("tcpListen", 2, ip_listen, 1),
- JS_CFUNC_MAGIC_DEF("udp6Listen", 2, ip_listen, 2),
- JS_CFUNC_MAGIC_DEF("tcp6Listen", 2, ip_listen, 3),
- 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),
+ 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_CFUNC_MAGIC_DEF("udpListen", 2, ip_listen, 0),
+ JS_CFUNC_MAGIC_DEF("tcpListen", 2, ip_listen, 1),
+ JS_CFUNC_MAGIC_DEF("udp6Listen", 2, ip_listen, 2),
+ JS_CFUNC_MAGIC_DEF("tcp6Listen", 2, ip_listen, 3),
+ JS_CFUNC_MAGIC_DEF("udpConnect", 2, ip_connect, 0),
+ JS_CFUNC_MAGIC_DEF("tcpConnect", 2, ip_connect, 1),
+ JS_CFUNC_MAGIC_DEF("udp6Connect", 2, ip_connect, 2),
+ 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),
};
// static const JSCFunctionListEntry net_obj[] = {
}
JSModuleDef *js_init_module(JSContext *ctx, const char *name) {
+ JSModuleDef *mod = JS_NewCModule(ctx, name, sock_modinit);
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_SetPropertyFunctionList(ctx, os, os_socket_funcs,
+ countof(os_socket_funcs));
}
JS_FreeValue(ctx, global); JS_FreeValue(ctx, os);