Newer
Older
/* Synchronet JavaScript "Socket" Object */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright Rob Swindell - http://www.synchro.net/copyright.html *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
#include "js_request.h"
#include "ssl.h"
#define TLS_ERROR_LEVEL LOG_WARNING // It'd be nice if this was configurable
// TODO: All log output (lprintf calls) go to the terminal server's lprintf (!)
static void dbprintf(bool error, js_socket_private_t* p, char* fmt, ...);
static bool do_CryptFlush(js_socket_private_t *p);
static int do_cryptAttribute(const CRYPT_CONTEXT session, CRYPT_ATTRIBUTE_TYPE attr, int val);
static int do_cryptAttributeString(const CRYPT_CONTEXT session, CRYPT_ATTRIBUTE_TYPE attr, void *val, int len);
static void do_js_close(JSContext *cx, js_socket_private_t *p, bool finalize);
static bool js_DefineSocketOptionsArray(JSContext *cx, JSObject *obj, int type);
static JSBool js_accept(JSContext *cx, uintN argc, jsval *arglist);
static JSBool js_bind(JSContext *cx, uintN argc, jsval *arglist);
static JSBool js_close(JSContext *cx, uintN argc, jsval *arglist);
static JSBool js_connect(JSContext *cx, uintN argc, jsval *arglist);
static void js_finalize_socket(JSContext *cx, JSObject *obj);
static JSBool js_ioctlsocket(JSContext *cx, uintN argc, jsval *arglist);
static JSBool js_listen(JSContext *cx, uintN argc, jsval *arglist);
static js_callback_t * js_get_callback(JSContext *cx);
static JSBool js_getsockopt(JSContext *cx, uintN argc, jsval *arglist);
static JSBool js_peek(JSContext *cx, uintN argc, jsval *arglist);
static JSBool js_poll(JSContext *cx, uintN argc, jsval *arglist);
static ushort js_port(JSContext* cx, jsval val, int type);
static JSBool js_recv(JSContext *cx, uintN argc, jsval *arglist);
static JSBool js_recvbin(JSContext *cx, uintN argc, jsval *arglist);
static JSBool js_recvfrom(JSContext *cx, uintN argc, jsval *arglist);
static JSBool js_recvline(JSContext *cx, uintN argc, jsval *arglist);
static JSBool js_send(JSContext *cx, uintN argc, jsval *arglist);
static JSBool js_sendbin(JSContext *cx, uintN argc, jsval *arglist);
static JSBool js_sendfile(JSContext *cx, uintN argc, jsval *arglist);
static JSBool js_sendline(JSContext *cx, uintN argc, jsval *arglist);
static JSBool js_sendto(JSContext *cx, uintN argc, jsval *arglist);
static JSBool js_setsockopt(JSContext *cx, uintN argc, jsval *arglist);
static int js_sock_read_check(js_socket_private_t *p, time_t start, int32 timeout, int i);
static JSBool js_socket_constructor(JSContext *cx, uintN argc, jsval *arglist);
static JSBool js_socket_enumerate(JSContext *cx, JSObject *obj);
static bool js_socket_peek_byte(JSContext *cx, js_socket_private_t *p);
static JSBool js_socket_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
static ptrdiff_t js_socket_recv(JSContext *cx, js_socket_private_t *p, void *buf, size_t len, int flags, int timeout);
static JSBool js_socket_resolve(JSContext *cx, JSObject *obj, jsid id);
static off_t js_socket_sendfilesocket(js_socket_private_t *p, int file);
static ptrdiff_t js_socket_sendsocket(js_socket_private_t *p, const void *msg, size_t len, int flush);
static JSBool js_socket_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp);
static JSBool js_install_event(JSContext *cx, uintN argc, jsval *arglist, bool once);
static JSBool js_on(JSContext *cx, uintN argc, jsval *arglist);
static JSBool js_once(JSContext *cx, uintN argc, jsval *arglist);

Rob Swindell
committed
static void store_socket_error(js_socket_private_t* p, int error_num, const char* error_str)
{
p->last_error = error_num;

Rob Swindell
committed
strlcpy(p->last_error_str, error_str, sizeof p->last_error_str);
else
socket_strerror(error_num, p->last_error_str, sizeof p->last_error_str);
}
static int do_cryptAttribute(const CRYPT_CONTEXT session, CRYPT_ATTRIBUTE_TYPE attr, int val)
{
case CRYPT_OPTION_NET_READTIMEOUT:
if (val < 0)
val = 0;
if (val > 300)
val = 300;
break;
default:
break;
}
ret = cryptSetAttribute(session, attr, val);
if (ret != CRYPT_OK) {
sprintf(action, "setting attribute %d", attr);
get_crypt_error_string(ret, session, &estr, action, &level);
if (level < TLS_ERROR_LEVEL)
level = TLS_ERROR_LEVEL;
lprintf(level, "TLS %s", estr);
free_crypt_attrstr(estr);
return ret;
}
static int do_cryptAttributeString(const CRYPT_CONTEXT session, CRYPT_ATTRIBUTE_TYPE attr, void *val, int len)
{
int ret = cryptSetAttributeString(session, attr, val, len);
if (ret != CRYPT_OK) {
sprintf(action, "setting attribute string %d", attr);
get_crypt_error_string(ret, session, &estr, "setting attribute string", &level);
if (estr) {
if (level < TLS_ERROR_LEVEL)
level = TLS_ERROR_LEVEL;
lprintf(level, "TLS %s", estr);
free_crypt_attrstr(estr);
#define GCES(status, pdata, estr, action) do { \
int GCES_level; \
get_crypt_error_string(status, pdata->session, &estr, action, &GCES_level); \
if (estr) { \
if (GCES_level < TLS_ERROR_LEVEL) \
GCES_level = TLS_ERROR_LEVEL; \
lprintf(GCES_level, "%04d TLS %s", p->sock, estr); \
free_crypt_attrstr(estr); \
} \
} while (0)
#define GCESH(status, socket, handle, estr, action) do { \
int GCESH_level; \
get_crypt_error_string(status, handle, &estr, action, &GCESH_level); \
if (estr) { \
if (GCESH_level < TLS_ERROR_LEVEL) \
GCESH_level = TLS_ERROR_LEVEL; \
lprintf(GCESH_level, "%04d TLS %s", socket, estr); \
free_crypt_attrstr(estr); \
} \
} while (0)
static bool do_CryptFlush(js_socket_private_t *p)
int ret;
char *estr;
if (ret == CRYPT_OK) {
if (ret != CRYPT_ERROR_COMPLETE)
GCES(ret, p, estr, "flushing data");
static void
remove_js_socket_event(JSContext *cx, js_callback_t *cb, SOCKET sock)
struct js_event_list *ev;
struct js_event_list *nev;
if (!cb->events_supported) {
return;
}
for (ev = cb->events; ev; ev = nev) {
nev = ev->next;
if (ev->type == JS_EVENT_SOCKET_READABLE || ev->type == JS_EVENT_SOCKET_READABLE_ONCE
|| ev->type == JS_EVENT_SOCKET_WRITABLE || ev->type == JS_EVENT_SOCKET_WRITABLE_ONCE) {
if (ev->data.sock == sock) {
if (ev->next)
ev->next->prev = ev->prev;
if (ev->prev)
ev->prev->next = ev->next;
else
cb->events = ev->next;
JS_RemoveObjectRoot(cx, &ev->cx);
free(ev);
}
}
else if (ev->type == JS_EVENT_SOCKET_CONNECT) {
if (ev->data.connect.sock == sock) {
if (ev->next)
ev->next->prev = ev->prev;
if (ev->prev)
ev->prev->next = ev->next;
else
cb->events = ev->next;
closesocket(ev->data.connect.sv[0]);
JS_RemoveObjectRoot(cx, &ev->cx);
free(ev);
}
}
}
}
static void do_js_close(JSContext *cx, js_socket_private_t *p, bool finalize)
{
size_t i;
if (p->session != -1) {
if (p->tls_server)
destroy_session(lprintf, p->session);
else
cryptDestroySession(p->session);
// Delete any event handlers for the socket
if (p->js_cb) {
if (p->set) {
for (i = 0; i < p->set->sock_count; i++) {
if (p->set->socks[i].sock != INVALID_SOCKET)
remove_js_socket_event(cx, p->js_cb, p->set->socks[i].sock);
}
}
else {
if (p->sock != INVALID_SOCKET)
remove_js_socket_event(cx, p->js_cb, p->sock);
}
}
if (p->sock == INVALID_SOCKET) {
if (p->external == false) {
store_socket_error(p, SOCKET_ERRNO, NULL);
else {
if (!finalize)
shutdown(p->sock, SHUT_RDWR);
}
// This is a lie for external sockets... don't tell anyone.
static bool js_socket_peek_byte(JSContext *cx, js_socket_private_t *p)
{
if (do_cryptAttribute(p->session, CRYPT_OPTION_NET_READTIMEOUT, 0) != CRYPT_OK)
if (p->peeked)
return TRUE;
if (js_socket_recv(cx, p, &p->peeked_byte, 1, 0, 0) == 1) {
p->peeked = TRUE;
return TRUE;
}
}
/* Returns > 0 upon successful data received (even if there was an error or disconnection) */
/* Returns -1 upon error (and no data received) */
/* Returns 0 upon timeout or disconnection (and no data received) */
static ptrdiff_t js_socket_recv(JSContext *cx, js_socket_private_t *p, void *buf, size_t len, int flags, int timeout)
ptrdiff_t total = 0;
int copied, ret;
char * estr;
time_t now = time(NULL);
int status;
if (p->session != -1) {
if (flags & MSG_PEEK)
if (p->peeked) {
*(uint8_t *)buf = p->peeked_byte;
buf = ((uint8_t *)buf) + 1;
if (!(flags & MSG_PEEK))
total++;
len--;
if (len == 0)
return total;
}
if (flags & MSG_PEEK)
return total;
if (do_cryptAttribute(p->session, CRYPT_OPTION_NET_READTIMEOUT, p->nonblocking?0:timeout) != CRYPT_OK)
return -1;
}
if (p->session == -1) {
if (p->sock == INVALID_SOCKET)
ret = -1;
else {
ret = recv(p->sock, buf, len, flags);
}
status = cryptPopData(p->session, buf, len, &copied);
if (cryptStatusOK(status))
ret = copied;
else {
ret = -1;
if (status == CRYPT_ERROR_TIMEOUT)
ret = 0;
GCES(ret, p, estr, "popping data");
}
}
if (ret == -1) {
if (total > 0)
return total;
return ret;
}
if ((!(flags & MSG_WAITALL)) || p->nonblocking)
return total;
if (ret >= (ptrdiff_t)len)
return total;
len -= ret;
buf = ((uint8_t *)buf) + ret;
if (!socket_check(p->sock, NULL, NULL, 0)) {
if (now + timeout > time(NULL))
return total;
return total;
static ptrdiff_t js_socket_sendsocket(js_socket_private_t *p, const void *msg, size_t len, int flush)
ptrdiff_t total = 0;
int copied = 0, ret;
char * estr;
// If we don't limit this, we occasionally get errors on large sends...
if ((ret = cryptPushData(p->session, msg, len > 0x2000 ? 0x2000 : len, &copied)) == CRYPT_OK) {
if (copied >= (ptrdiff_t)len)
msg = ((uint8_t *)msg) + copied;
if (ret != CRYPT_ERROR_COMPLETE)
GCES(ret, p, estr, "pushing data");
if (!socket_check(p->sock, NULL, NULL, 0))
break;
} while (len);
if (flush)
return total;
static off_t js_socket_sendfilesocket(js_socket_private_t *p, int file)
for (;;) {
ssize_t rd = read(file, buf, sizeof(buf));
if (rd < 0) {
if (p->session != -1)
do_CryptFlush(p);
ssize_t sent = 0;
while (sent < rd) {
ptrdiff_t wr = js_socket_sendsocket(p, buf + sent, rd - sent, false);
if (wr > 0) {
sent += wr;
}
else if (wr == SOCKET_ERROR && SOCKET_ERRNO == EWOULDBLOCK) {
else {
if (p->session != -1)
do_CryptFlush(p);
if (sent != rd) {
if (p->session != -1)
do_CryptFlush(p);
if (p->session != -1)
do_CryptFlush(p);
static void dbprintf(bool error, js_socket_private_t* p, char* fmt, ...)
if (p == NULL || (!p->debug /*&& !error */))
va_start(argptr, fmt);
vsnprintf(sbuf, sizeof(sbuf), fmt, argptr);
sbuf[sizeof(sbuf) - 1] = 0;
va_end(argptr);
lprintf(LOG_DEBUG, "%04d Socket %s%s", p->sock, error ? "ERROR: ":"", sbuf);
/* Socket Destructor */
static void js_finalize_socket(JSContext *cx, JSObject *obj)
{
if ((p = (js_socket_private_t*)JS_GetPrivate(cx, obj)) == NULL)
if (p->tls_psk)
JS_RemoveObjectRoot(cx, &p->tls_psk);
free(p->set);
free(p->hostname);
free(p);
JS_SetPrivate(cx, obj, NULL);
extern JSClass js_socket_class;
js_close(JSContext *cx, uintN argc, jsval *arglist)
JSObject * obj = JS_THIS_OBJECT(cx, arglist);
js_socket_private_t* p;
jsrefcount rc;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
if ((p = (js_socket_private_t*)js_GetClassPrivate(cx, obj, &js_socket_class)) == NULL) {
rc = JS_SUSPENDREQUEST(cx);
JS_RESUMEREQUEST(cx, rc);
static ushort js_port(JSContext* cx, jsval val, int type)
{
char* cp;
JSString* str;
int32 i = 0;
struct servent* serv;
jsrefcount rc;
if (JSVAL_IS_NUMBER(val)) {
JS_ValueToInt32(cx, val, &i);
}
if (JSVAL_IS_STRING(val)) {
str = JS_ValueToString(cx, val);
if (cp != NULL) {
if (IS_DIGIT(*cp))
return (ushort)strtol(cp, NULL, 0);
rc = JS_SUSPENDREQUEST(cx);
serv = getservbyname(cp, type == SOCK_STREAM ? "tcp":"udp");
return htons(serv->s_port);
}
}
SOCKET js_socket(JSContext *cx, jsval val)
void* vp;
JSClass* cl;
SOCKET sock = INVALID_SOCKET;
if (JSVAL_IS_OBJECT(val) && !JSVAL_IS_NULL(val) && (cl = JS_GetClass(cx, JSVAL_TO_OBJECT(val))) != NULL) {
if (cl->flags & JSCLASS_HAS_PRIVATE)
if ((vp = JS_GetInstancePrivate(cx, JSVAL_TO_OBJECT(val), &js_socket_class, NULL)) != NULL)
sock = *(SOCKET*)vp;
} else if (val != JSVAL_VOID) {
int32 i;
if (JS_ValueToInt32(cx, val, &i))
sock = i;
}
size_t js_socket_numsocks(JSContext *cx, jsval val)
js_socket_private_t *p;
JSClass* cl;
SOCKET sock = INVALID_SOCKET;
size_t i;
int32_t intval;
size_t ret = 0;
if (JSVAL_IS_OBJECT(val) && !JSVAL_IS_NULL(val) && (cl = JS_GetClass(cx, JSVAL_TO_OBJECT(val))) != NULL) {
if (cl->flags & JSCLASS_HAS_PRIVATE) {
if ((p = (js_socket_private_t *)JS_GetInstancePrivate(cx, JSVAL_TO_OBJECT(val), &js_socket_class, NULL)) != NULL) {
if (p->set) {
for (i = 0; i < p->set->sock_count; i++) {
if (p->set->socks[i].sock == INVALID_SOCKET)
if (sock != INVALID_SOCKET)
} else if (val != JSVAL_VOID) {
if (JS_ValueToInt32(cx, val, &intval)) {
if (intval != INVALID_SOCKET)
ret = 1;
size_t js_socket_add(JSContext *cx, jsval val, struct pollfd *fds, short events)
js_socket_private_t *p;
JSClass* cl;
SOCKET sock = INVALID_SOCKET;
size_t i;
int32_t intval;
size_t ret = 0;
if (JSVAL_IS_OBJECT(val) && !JSVAL_IS_NULL(val) && (cl = JS_GetClass(cx, JSVAL_TO_OBJECT(val))) != NULL) {
if (cl->flags & JSCLASS_HAS_PRIVATE) {
if ((p = (js_socket_private_t *)JS_GetInstancePrivate(cx, JSVAL_TO_OBJECT(val), &js_socket_class, NULL)) != NULL) {
if (p->set) {
for (i = 0; i < p->set->sock_count; i++) {
if (p->set->socks[i].sock == INVALID_SOCKET)
fds[ret].events = events;
fds[ret++].fd = p->set->socks[i].sock;
if (sock != INVALID_SOCKET) {
fds[ret].events = events;
fds[ret++].fd = sock;
} else if (val != JSVAL_VOID) {
if (JS_ValueToInt32(cx, val, &intval)) {
if (sock != INVALID_SOCKET) {
fds[ret].events = events;
fds[ret++].fd = sock;
}
SOCKET js_socket_add(JSContext *cx, jsval val, fd_set *fds)
js_socket_private_t *p;
JSClass* cl;
SOCKET sock = INVALID_SOCKET;
size_t i;
int32_t intval;
if (JSVAL_IS_OBJECT(val) && !JSVAL_IS_NULL(val) && (cl = JS_GetClass(cx, JSVAL_TO_OBJECT(val))) != NULL) {
if (cl->flags & JSCLASS_HAS_PRIVATE) {
if ((p = (js_socket_private_t *)JS_GetInstancePrivate(cx, JSVAL_TO_OBJECT(val), &js_socket_class, NULL)) != NULL) {
if (p->set) {
for (i = 0; i < p->set->sock_count; i++) {
if (p->set->socks[i].sock == INVALID_SOCKET)
FD_SET(p->set->socks[i].sock, fds);
if (p->set->socks[i].sock > sock)
sock = p->set->socks[i].sock;
if (sock != INVALID_SOCKET)
} else if (val != JSVAL_VOID) {
if (JS_ValueToInt32(cx, val, &intval)) {
sock = intval;
FD_SET(sock, fds);
bool js_socket_isset(JSContext *cx, jsval val, fd_set *fds)
js_socket_private_t *p;
JSClass* cl;
size_t i;
int intval;
if (JSVAL_IS_OBJECT(val) && !JSVAL_IS_NULL(val) && (cl = JS_GetClass(cx, JSVAL_TO_OBJECT(val))) != NULL) {
if (cl->flags & JSCLASS_HAS_PRIVATE) {
if ((p = (js_socket_private_t *)JS_GetInstancePrivate(cx, JSVAL_TO_OBJECT(val), &js_socket_class, NULL)) != NULL) {
if (p->set) {
for (i = 0; i < p->set->sock_count; i++) {
if (p->set->socks[i].sock == INVALID_SOCKET)
if (FD_ISSET(p->set->socks[i].sock, fds))
if (p->sock == INVALID_SOCKET)
return TRUE;
else {
if (FD_ISSET(p->sock, fds))
} else if (val != JSVAL_VOID) {
if (JS_ValueToInt32(cx, val, &intval)) {
if (FD_ISSET(intval, fds))
void js_timeval(JSContext* cx, jsval val, struct timeval* tv)
if (JSVAL_IS_INT(val))
tv->tv_sec = JSVAL_TO_INT(val);
else if (JSVAL_IS_DOUBLE(val)) {
if (JS_ValueToNumber(cx, val, &jsd)) {
tv->tv_usec = (int)(jsd * 1000000.0) % 1000000;
int js_polltimeout(JSContext* cx, jsval val)
if (JSVAL_IS_INT(val))
if (JSVAL_IS_DOUBLE(val)) {
if (JS_ValueToNumber(cx, val, &jsd))
return (int)(jsd * 1000);
js_bind(JSContext *cx, uintN argc, jsval *arglist)
JSObject * obj = JS_THIS_OBJECT(cx, arglist);
jsval * argv = JS_ARGV(cx, arglist);
js_socket_private_t* p;
ushort port = 0;
union xp_sockaddr addr;
jsrefcount rc;
char * cstr = NULL;
char portstr[6];
struct addrinfo hints, *res, *tres;
int ret;
if ((p = (js_socket_private_t*)js_GetClassPrivate(cx, obj, &js_socket_class)) == NULL) {
memset(&addr, 0, sizeof(addr));
if (argc)
port = js_port(cx, argv[0], p->type);
if (argc > 1 && argv[1] != JSVAL_VOID) {
JSVALUE_TO_ASTRING(cx, argv[1], cstr, INET6_ADDRSTRLEN, NULL);
}
hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST | AI_NUMERICSERV | AI_PASSIVE;
hints.ai_socktype = p->type;
/* We need servname to be non-NULL so we can use a NULL hostname */
sprintf(portstr, "%hu", port);
rc = JS_SUSPENDREQUEST(cx);
if ((ret = getaddrinfo(cstr, portstr, &hints, &res)) != 0) {
JS_RESUMEREQUEST(cx, rc);
dbprintf(TRUE, p, "getaddrinfo(%s, %s) failed with error %d", cstr, portstr, ret);

Rob Swindell
committed
store_socket_error(p, ret, gai_strerror(ret));
for (tres = res; tres; tres = tres->ai_next) {
if (bind(p->sock, tres->ai_addr, tres->ai_addrlen) != 0) {
store_socket_error(p, SOCKET_ERRNO, NULL);
dbprintf(TRUE, p, "bind failed with error %d", SOCKET_ERRNO);
else
break;
dbprintf(false, p, "bound to port %u", port);
JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
JS_RESUMEREQUEST(cx, rc);
static JSBool
js_listen(JSContext *cx, uintN argc, jsval *arglist)
JSObject * obj = JS_THIS_OBJECT(cx, arglist);
jsval * argv = JS_ARGV(cx, arglist);
js_socket_private_t* p;
int32 backlog = 1;
jsrefcount rc;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
if ((p = (js_socket_private_t*)js_GetClassPrivate(cx, obj, &js_socket_class)) == NULL) {
if (argc && argv[0] != JSVAL_VOID)
backlog = JS_ValueToInt32(cx, argv[0], &backlog);
rc = JS_SUSPENDREQUEST(cx);
if (listen(p->sock, backlog) != 0) {
store_socket_error(p, SOCKET_ERRNO, NULL);
dbprintf(TRUE, p, "listen failed with error %d", SOCKET_ERRNO);
JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
JS_RESUMEREQUEST(cx, rc);
dbprintf(false, p, "listening, backlog=%d", backlog);
JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
JS_RESUMEREQUEST(cx, rc);
}
static JSBool
js_accept(JSContext *cx, uintN argc, jsval *arglist)
JSObject * obj = JS_THIS_OBJECT(cx, arglist);
js_socket_private_t* p;
js_socket_private_t* new_p;
JSObject* sockobj;
SOCKET new_socket;
socklen_t addrlen;
jsrefcount rc;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
if ((p = (js_socket_private_t*)js_GetClassPrivate(cx, obj, &js_socket_class)) == NULL) {
addrlen = sizeof(p->remote_addr);
rc = JS_SUSPENDREQUEST(cx);
if (p->set) {
if ((new_socket = xpms_accept(p->set, &(p->remote_addr), &addrlen, XPMS_FOREVER, XPMS_FLAGS_NONE, NULL)) == INVALID_SOCKET) {
store_socket_error(p, SOCKET_ERRNO, NULL);
dbprintf(TRUE, p, "accept failed with error %d", SOCKET_ERRNO);
call_socket_open_callback(TRUE);
if ((new_socket = accept_socket(p->sock, &(p->remote_addr), &addrlen)) == INVALID_SOCKET) {
store_socket_error(p, SOCKET_ERRNO, NULL);
dbprintf(TRUE, p, "accept failed with error %d", SOCKET_ERRNO);
if ((sockobj = js_CreateSocketObject(cx, obj, "new_socket", new_socket, -1)) == NULL) {
closesocket(new_socket);
JS_RESUMEREQUEST(cx, rc);
JS_ReportError(cx, "Error creating new socket object");
return JS_FALSE;
if ((new_p = (js_socket_private_t*)JS_GetPrivate(cx, sockobj)) == NULL) {
JS_RESUMEREQUEST(cx, rc);
new_p->type = p->type;
new_p->debug = p->debug;
new_p->nonblocking = p->nonblocking;
new_p->external = false; /* let destructor close socket */
new_p->is_connected = TRUE;
dbprintf(false, p, "accepted connection");
JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(sockobj));
JS_RESUMEREQUEST(cx, rc);
struct js_connect_event_args {
SOCKET sv[2];
SOCKET sock;
int socktype;
char *host;
ushort port;
};
static void
js_connect_event_thread(void *args)
{
struct js_connect_event_args *a = args;
struct addrinfo hints, *res = NULL, *cur;
int result;
ulong val;
char sresult;
SetThreadName("sbbs/jsConnect");
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = a->socktype;
hints.ai_flags = AI_ADDRCONFIG;
result = getaddrinfo(a->host, NULL, &hints, &res);
goto done;
/* always set to blocking here */
val = 0;
ioctlsocket(a->sock, FIONBIO, &val);
result = SOCKET_ERROR;
for (cur = res; cur != NULL; cur = cur->ai_next) {
inet_setaddrport((void *)cur->ai_addr, a->port);
result = connect(a->sock, cur->ai_addr, cur->ai_addrlen);
break;
}
sresult = result;
/* Restore original setting here */
ioctlsocket(a->sock, FIONBIO, (ulong*)&(a->nonblocking));
send(a->sv[1], &sresult, 1, 0);
done:
closesocket(a->sv[1]);
free(a);
}
static JSBool
js_connect_event(JSContext *cx, uintN argc, jsval *arglist, js_socket_private_t *p, ushort port, JSObject *obj)
{
SOCKET sv[2];
struct js_event_list * ev;
JSFunction * ecb;
js_callback_t * cb = js_get_callback(cx);
jsval * argv = JS_ARGV(cx, arglist);
JS_ReportError(cx, "invalid socket");
return JS_FALSE;
}
if (cb == NULL) {
return JS_FALSE;
}
if (!cb->events_supported) {
JS_ReportError(cx, "events not supported");
return JS_FALSE;
}
ecb = JS_ValueToFunction(cx, argv[2]);
if (ecb == NULL) {
return JS_FALSE;
}
// Create socket pair...
#ifdef _WIN32
if (socketpair(AF_INET, SOCK_STREAM, 0, sv) == -1) {
#else
if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) {
#endif
JS_ReportError(cx, "Error %d creating socket pair", SOCKET_ERRNO);
return JS_FALSE;
}
// Create event
ev = malloc(sizeof(*ev));
if (ev == NULL) {
JS_ReportError(cx, "error allocating %lu bytes", sizeof(*ev));
closesocket(sv[0]);
closesocket(sv[1]);
return JS_FALSE;
}
ev->prev = NULL;
ev->next = cb->events;
if (ev->next)
ev->next->prev = ev;
ev->type = JS_EVENT_SOCKET_CONNECT;
ev->cx = obj;
JS_AddObjectRoot(cx, &ev->cx);
ev->cb = ecb;
ev->data.connect.sv[0] = sv[0];
ev->data.connect.sv[1] = sv[1];
ev->id = cb->next_eid++;
p->js_cb = cb;
cb->events = ev;
// Start thread
args = malloc(sizeof(*args));
if (args == NULL) {
JS_ReportError(cx, "error allocating %lu bytes", sizeof(*args));