Newer
Older
else {
if(p->session != -1) {
cryptDestroySession(p->session);
p->session=-1;
ioctlsocket(p->sock,FIONBIO,(ulong*)&(p->nonblocking));
do_js_close(p, false);
static JSBool js_socket_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
jsval idval;
ulong cnt;
BOOL rd;
BOOL wr;
socklen_t len=sizeof(addr);
// Protoype access
return(JS_TRUE);
JS_IdToValue(cx, id, &idval);
tiny = JSVAL_TO_INT(idval);
rc=JS_SUSPENDREQUEST(cx);
*vp = INT_TO_JSVAL(p->last_error);
case SOCK_PROP_ERROR_STR:
if((js_str=JS_NewStringCopyZ(cx, socket_strerror(p->last_error, str, sizeof(str))))==NULL)
return JS_FALSE;
*vp = STRING_TO_JSVAL(js_str);
break;
if(!p->is_connected)
*vp = JSVAL_FALSE;
else
*vp = BOOLEAN_TO_JSVAL(socket_check(p->sock,NULL,NULL,0));
case SOCK_PROP_IS_WRITEABLE:
if(p->sock==INVALID_SOCKET && p->set)
wr = FALSE;
else
socket_check(p->sock,NULL,&wr,0);
*vp = BOOLEAN_TO_JSVAL(wr);
break;
case SOCK_PROP_DATA_WAITING:
else {
if (p->peeked)
rd = TRUE;
else if (p->session != -1)
rd = js_socket_peek_byte(p);
else
socket_check(p->sock,&rd,NULL,0);
}
*vp = BOOLEAN_TO_JSVAL(rd);
break;
case SOCK_PROP_NREAD:
if(p->sock==INVALID_SOCKET && p->set) {
*vp = JSVAL_ZERO;
break;
}
if (p->session != -1) {
if (js_socket_peek_byte(p))
*vp=DOUBLE_TO_JSVAL((double)1);
else
*vp = JSVAL_ZERO;
}
else if(ioctlsocket(p->sock, FIONREAD, &cnt)==0) {
*vp=DOUBLE_TO_JSVAL((double)cnt);
*vp = INT_TO_JSVAL(p->debug);
*vp = INT_TO_JSVAL(p->sock);
case SOCK_PROP_NONBLOCKING:
*vp = BOOLEAN_TO_JSVAL(p->nonblocking);
break;
case SOCK_PROP_LOCAL_IP:
if(p->sock != INVALID_SOCKET) {
if(getsockname(p->sock, (struct sockaddr *)&addr,&len)!=0)
return(JS_FALSE);
JS_RESUMEREQUEST(cx, rc);
inet_addrtop(&addr, str, sizeof(str));
if((js_str=JS_NewStringCopyZ(cx,str))==NULL)
return(JS_FALSE);
*vp = STRING_TO_JSVAL(js_str);
rc=JS_SUSPENDREQUEST(cx);
else
*vp=JSVAL_VOID;
break;
case SOCK_PROP_LOCAL_PORT:
if(p->local_port != 0) {
*vp = INT_TO_JSVAL(p->local_port);
} else if(p->sock != INVALID_SOCKET) {
JS_RESUMEREQUEST(cx, rc);
rc=JS_SUSPENDREQUEST(cx);
else
*vp=JSVAL_VOID;
break;
case SOCK_PROP_REMOTE_IP:
if(p->is_connected) {
JS_RESUMEREQUEST(cx, rc);
inet_addrtop(&p->remote_addr, str, sizeof(str));
if((js_str=JS_NewStringCopyZ(cx,str))==NULL)
return(JS_FALSE);
*vp = STRING_TO_JSVAL(js_str);
rc=JS_SUSPENDREQUEST(cx);
else
*vp=JSVAL_VOID;
break;
case SOCK_PROP_REMOTE_PORT:
if(p->is_connected)
else
*vp=JSVAL_VOID;
case SOCK_PROP_TYPE:
*vp = INT_TO_JSVAL(p->type);
break;
case SOCK_PROP_FAMILY:
if(p->sock != INVALID_SOCKET) {
if(getsockname(p->sock, &addr.addr, &len)!=0)
return(JS_FALSE);
JS_RESUMEREQUEST(cx, rc);
*vp = INT_TO_JSVAL(addr.addr.sa_family);
rc=JS_SUSPENDREQUEST(cx);
}
else
*vp=JSVAL_VOID;
break;
case SOCK_PROP_NETWORK_ORDER:
*vp = BOOLEAN_TO_JSVAL(p->network_byte_order);
case SOCK_PROP_SSL_SESSION:
*vp = BOOLEAN_TO_JSVAL(p->session != -1);
break;
*vp = BOOLEAN_TO_JSVAL(p->session != -1 && p->tls_server);
JS_RESUMEREQUEST(cx, rc);
#define SOCK_PROP_FLAGS JSPROP_ENUMERATE|JSPROP_READONLY
static jsSyncPropertySpec js_socket_properties[] = {
/* name ,tinyid ,flags, ver */
{ "error" ,SOCK_PROP_LAST_ERROR ,SOCK_PROP_FLAGS, 311 },
{ "last_error" ,SOCK_PROP_LAST_ERROR ,JSPROP_READONLY, 310 }, /* alias */
{ "error_str" ,SOCK_PROP_ERROR_STR ,SOCK_PROP_FLAGS, 318 },
{ "is_connected" ,SOCK_PROP_IS_CONNECTED ,SOCK_PROP_FLAGS, 310 },
{ "is_writeable" ,SOCK_PROP_IS_WRITEABLE ,JSPROP_ENUMERATE, 311 },
{ "is_writable" ,SOCK_PROP_IS_WRITEABLE ,JSPROP_ENUMERATE, 312 }, /* alias */
{ "data_waiting" ,SOCK_PROP_DATA_WAITING ,SOCK_PROP_FLAGS, 310 },
{ "nread" ,SOCK_PROP_NREAD ,SOCK_PROP_FLAGS, 310 },
{ "debug" ,SOCK_PROP_DEBUG ,JSPROP_ENUMERATE, 310 },
{ "descriptor" ,SOCK_PROP_DESCRIPTOR ,JSPROP_ENUMERATE, 310 },
{ "nonblocking" ,SOCK_PROP_NONBLOCKING ,JSPROP_ENUMERATE, 310 },
{ "local_ip_address" ,SOCK_PROP_LOCAL_IP ,SOCK_PROP_FLAGS, 310 },
{ "local_port" ,SOCK_PROP_LOCAL_PORT ,SOCK_PROP_FLAGS, 310 },
{ "remote_ip_address" ,SOCK_PROP_REMOTE_IP ,SOCK_PROP_FLAGS, 310 },
{ "remote_port" ,SOCK_PROP_REMOTE_PORT ,SOCK_PROP_FLAGS, 310 },
{ "type" ,SOCK_PROP_TYPE ,SOCK_PROP_FLAGS, 310 },
{ "family" ,SOCK_PROP_FAMILY ,SOCK_PROP_FLAGS, 318 },
{ "network_byte_order",SOCK_PROP_NETWORK_ORDER,JSPROP_ENUMERATE, 311 },
{ "ssl_session" ,SOCK_PROP_SSL_SESSION ,JSPROP_ENUMERATE, 316 },
{ "ssl_server" ,SOCK_PROP_SSL_SERVER ,JSPROP_ENUMERATE, 316 },
static jsSyncMethodSpec js_socket_functions[] = {
,JSDOCSTR("close (shutdown) the socket immediately")
,310
},
{"bind", js_bind, 0, JSTYPE_BOOLEAN, JSDOCSTR("[port] [,ip_address]")
,JSDOCSTR("bind socket to a TCP or UDP <i>port</i> (number or service name), "
"optionally specifying a network interface (via <i>ip_address</i>)")
,311
},
{"connect", js_connect, 2, JSTYPE_BOOLEAN, JSDOCSTR("host, port [,timeout=<tt>10.0</tt>]")
,JSDOCSTR("connect to a remote port (number or service name) on the specified host (IP address or host name)"
", default <i>timeout</i> value is <i>10.0</i> (seconds)")
,311
},
{"listen", js_listen, 0, JSTYPE_BOOLEAN, JSDOCSTR("")
,JSDOCSTR("place socket in a state to listen for incoming connections (use before an accept)")
,310
},
{"accept", js_accept, 0, JSTYPE_OBJECT, JSDOCSTR("")
,JSDOCSTR("accept an incoming connection, returns a new <i>Socket</i> object representing the new connection")
,310
},
{"send", js_send, 1, JSTYPE_NUMBER, JSDOCSTR("data")
,JSDOCSTR("send a string (AKA write). Returns the number of bytes sent or undefined if an error occured. "
"Versions before 3.17 returned a bool true if all bytes were sent and false otherwise.")
,310
},
{"writeln", js_sendline, 1, JSTYPE_ALIAS },
{"sendline", js_sendline, 1, JSTYPE_BOOLEAN, JSDOCSTR("data")
,JSDOCSTR("send a string (AKA write) with a carriage return line feed appended")
,317
},
{"sendto", js_sendto, 3, JSTYPE_BOOLEAN, JSDOCSTR("data, address, port")
,JSDOCSTR("send data to a specific host (IP address or host name) and port (number or service name), for UDP sockets")
,310
},
{"sendfile", js_sendfile, 1, JSTYPE_BOOLEAN, JSDOCSTR("path/filename")
,JSDOCSTR("send an entire file over the socket")
,310
},
{"writeBin", js_sendbin, 1, JSTYPE_ALIAS },
{"sendBin", js_sendbin, 1, JSTYPE_BOOLEAN, JSDOCSTR("value [,bytes=<tt>4</tt>]")
,JSDOCSTR("send a binary integer over the socket, default number of bytes is 4 (32-bits)")
,311
{"recv", js_recv, 1, JSTYPE_STRING, JSDOCSTR("[maxlen=<tt>512</tt>, [timeout_sec=<tt>120</tt>]]")
,JSDOCSTR("receive a string, default maxlen is 512 characters (AKA read)")
,310
},
{"peek", js_peek, 0, JSTYPE_STRING, JSDOCSTR("[maxlen=<tt>512</tt>]")
,JSDOCSTR("receive a string, default maxlen is 512 characters, leaves string in receive buffer (TLS sockets will never return more than one byte)")
,310
},
{"readline", js_recvline, 0, JSTYPE_ALIAS },
{"readln", js_recvline, 0, JSTYPE_ALIAS },
{"recvline", js_recvline, 0, JSTYPE_STRING, JSDOCSTR("[maxlen=<tt>512</tt>] [,timeout=<tt>30.0</tt>]")
,JSDOCSTR("receive a line-feed terminated string, default maxlen is 512 characters, default timeout is 30 seconds (AKA readline and readln)")
,310
{"recvfrom", js_recvfrom, 0, JSTYPE_OBJECT, JSDOCSTR("[binary=<tt>false</tt>] [,maxlen=<tt>512</tt> or int_size=<tt>4</tt>]")
,JSDOCSTR("receive data (string or integer) from a socket (typically UDP)"
"<p>returns object with <i>ip_address</i> and <i>port</i> of sender along with <i>data</i> properties"
"<p><i>binary</i> defaults to <i>false</i>, <i>maxlen</i> defaults to 512 chars, <i>int_size</i> defaults to 4 bytes (32-bits)")
,311
},
{"readBin", js_recvbin, 0, JSTYPE_ALIAS },
{"recvBin", js_recvbin, 0, JSTYPE_NUMBER, JSDOCSTR("[bytes=<tt>4</tt>]")
,JSDOCSTR("receive a binary integer from the socket, default number of bytes is 4 (32-bits)")
,311
},
{"getoption", js_getsockopt, 1, JSTYPE_NUMBER, JSDOCSTR("option")
,JSDOCSTR("get socket option value, option may be socket option name "
"(see <tt>sockopts</tt> in <tt>sockdefs.js</tt>) or number")
,310
},
{"setoption", js_setsockopt, 2, JSTYPE_BOOLEAN, JSDOCSTR("option, value")
,JSDOCSTR("set socket option value, option may be socket option name "
"(see <tt>sockopts</tt> in <tt>sockdefs.js</tt>) or number")
,310
},
{"ioctl", js_ioctlsocket, 1, JSTYPE_NUMBER, JSDOCSTR("command [,argument=<tt>0</tt>]")
,JSDOCSTR("send socket IOCTL (advanced)")
,310
},
{"poll", js_poll, 1, JSTYPE_NUMBER, JSDOCSTR("[timeout=<tt>0</tt>] [,write=<tt>false</tt>]")
,JSDOCSTR("poll socket for read or write ability (default is <i>read</i>), "
"default timeout value is 0.0 seconds (immediate timeout)")
,310
},
static JSBool js_socket_resolve(JSContext *cx, JSObject *obj, jsid id)
if(id != JSID_VOID && id != JSID_EMPTY) {
jsval idval;
if(JSVAL_IS_STRING(idval)) {
JSSTRING_TO_MSTRING(cx, JSVAL_TO_STRING(idval), name, NULL);
HANDLE_PENDING(cx, name);
ret=js_SyncResolve(cx, obj, name, js_socket_properties, js_socket_functions, NULL, 0);
if(name)
free(name);
return ret;
}
static JSBool js_socket_enumerate(JSContext *cx, JSObject *obj)
{
JSClass js_socket_class = {
"Socket" /* name */
,JSCLASS_HAS_PRIVATE /* flags */
,JS_PropertyStub /* addProperty */
,JS_PropertyStub /* delProperty */
,js_socket_get /* getProperty */
,js_socket_set /* setProperty */
,js_socket_enumerate /* enumerate */
,js_socket_resolve /* resolve */
,JS_ConvertStub /* convert */
,js_finalize_socket /* finalize */
};
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
JSClass js_connected_socket_class = {
"ConnectedSocket" /* name */
,JSCLASS_HAS_PRIVATE /* flags */
,JS_PropertyStub /* addProperty */
,JS_PropertyStub /* delProperty */
,js_socket_get /* getProperty */
,js_socket_set /* setProperty */
,js_socket_enumerate /* enumerate */
,js_socket_resolve /* resolve */
,JS_ConvertStub /* convert */
,js_finalize_socket /* finalize */
};
JSClass js_listening_socket_class = {
"ListeningSocket" /* name */
,JSCLASS_HAS_PRIVATE /* flags */
,JS_PropertyStub /* addProperty */
,JS_PropertyStub /* delProperty */
,js_socket_get /* getProperty */
,js_socket_set /* setProperty */
,js_socket_enumerate /* enumerate */
,js_socket_resolve /* resolve */
,JS_ConvertStub /* convert */
,js_finalize_socket /* finalize */
};
static BOOL js_DefineSocketOptionsArray(JSContext *cx, JSObject *obj, int type)
{
size_t i;
size_t count=0;
jsval val;
JSObject* array;
socket_option_t* options;
if((options=getSocketOptionList())==NULL)
return(FALSE);
if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)
return(FALSE);
if(!JS_DefineProperty(cx, obj, "option_list", OBJECT_TO_JSVAL(array)
, NULL, NULL, JSPROP_ENUMERATE))
return(FALSE);
for(i=0; options[i].name!=NULL; i++) {
if(options[i].type && options[i].type!=type)
continue;
val=STRING_TO_JSVAL(JS_NewStringCopyZ(cx,options[i].name));
JS_SetElement(cx, array, count++, &val);
}
return(TRUE);
}
/* Socket Constructor (creates socket descriptor) */
JSObject* DLLCALL js_CreateSocketObjectWithoutParent(JSContext* cx, SOCKET sock, CRYPT_CONTEXT session)
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
{
JSObject* obj;
js_socket_private_t* p;
int type=SOCK_STREAM;
socklen_t len;
obj=JS_NewObject(cx, &js_socket_class, NULL, NULL);
if(obj==NULL)
return(NULL);
len = sizeof(type);
getsockopt(sock,SOL_SOCKET,SO_TYPE,(void*)&type,&len);
if(!js_DefineSocketOptionsArray(cx, obj, type))
return(NULL);
if((p=(js_socket_private_t*)malloc(sizeof(js_socket_private_t)))==NULL)
return(NULL);
memset(p,0,sizeof(js_socket_private_t));
p->sock = sock;
p->external = TRUE;
p->network_byte_order = TRUE;
p->session=session;
if (p->sock != INVALID_SOCKET) {
len=sizeof(p->remote_addr);
if(getpeername(p->sock, &p->remote_addr.addr,&len)==0)
p->is_connected=TRUE;
}
if(!JS_SetPrivate(cx, obj, p)) {
dbprintf(TRUE, p, "JS_SetPrivate failed");
return(NULL);
}
dbprintf(FALSE, p, "object created");
return(obj);
}
handle_addrs(char *host, struct sockaddr_in *addr4, socklen_t *addr4len, struct sockaddr_in6 *addr6, socklen_t *addr6len)
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
{
in_addr_t ia;
union xp_sockaddr ia6;
char *p, *p2;
struct addrinfo hints;
struct addrinfo *res=NULL;
struct addrinfo *cur;
int i;
// First, clean up for host:port style...
p = strrchr(host, ':');
/*
* If there isn't a [, and the first and last colons aren't the same
* it's assumed to be an IPv6 address
*/
if(host[0] != '[' && p != NULL && strchr(host, ':') != p)
p=NULL;
if(host[0]=='[') {
host++;
p2=strrchr(host,']');
if(p2)
*p2=0;
if(p2 > p)
p=NULL;
}
if(p!=NULL)
*p=0;
ia = inet_addr(host);
if (ia != INADDR_NONE) {
addr4->sin_addr.s_addr = ia;
*addr4len = sizeof(struct sockaddr_in);
}
return TRUE;
}
if (inet_ptoaddr(host, &ia6, sizeof(ia6)) != NULL) {
addr6->sin6_addr = ia6.in6.sin6_addr;
*addr6len = sizeof(struct sockaddr_in6);
}
return TRUE;
}
// So it's a hostname... resolve it into addr4 and addr6 if possible.
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE;
if((i = getaddrinfo(host, NULL, &hints, &res)) != 0)
return FALSE;
for(cur=res; cur && (*addr4len == 0 || *addr6len == 0); cur=cur->ai_next) {
switch (cur->ai_family) {
case AF_INET:
addr4->sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
*addr4len = sizeof(struct sockaddr_in);
}
break;
case AF_INET6:
addr6->sin6_addr = ((struct sockaddr_in6 *)cur->ai_addr)->sin6_addr;
*addr6len = sizeof(struct sockaddr_in6);
}
break;
}
}
freeaddrinfo(res);
return TRUE;
}
static JSBool
js_connected_socket_constructor(JSContext *cx, uintN argc, jsval *arglist)
{
JSObject *obj;
jsval *argv=JS_ARGV(cx, arglist);
int32 type=SOCK_STREAM; /* default = TCP */
int32 domain = PF_UNSPEC; /* default = IPvAny */
int32 proto = IPPROTO_IP;
char* protocol=NULL;
uintN i;
js_socket_private_t* p = NULL;
jsval v;
struct addrinfo hints;
struct addrinfo *res=NULL;
struct addrinfo *cur;
char *host = NULL;
uint16_t port;
char pstr[6];
jsrefcount rc;
int nonblock;
scfg_t *scfg;
char error[256];
uint16_t bindport = 0;
struct sockaddr_in addr4;
struct sockaddr_in6 addr6;
socklen_t addr4len;
socklen_t addr6len;
BOOL sockbind = FALSE;
jsuint count;
int32 timeout = 10;
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
scfg = JS_GetRuntimePrivate(JS_GetRuntime(cx));
if (scfg == NULL) {
JS_ReportError(cx, "Unable to get private runtime");
return JS_FALSE;
}
if (argc < 2) {
JS_ReportError(cx, "At least two arguments required (hostname and port)");
return JS_FALSE;
}
// Optional arguments in an object...
if (argc > 2) {
if (!JS_ValueToObject(cx, argv[2], &obj)) {
JS_ReportError(cx, "Invalid third argument");
return JS_FALSE;
}
if (JS_GetProperty(cx, obj, "domain", &v) && !JSVAL_IS_VOID(v)) {
if (!JS_ValueToInt32(cx, v, &domain)) {
JS_ReportError(cx, "Invalid domain property");
return JS_FALSE;
}
}
if (JS_GetProperty(cx, obj, "type", &v) && !JSVAL_IS_VOID(v)) {
if (!JS_ValueToInt32(cx, v, &type)) {
JS_ReportError(cx, "Invalid type property");
return JS_FALSE;
}
}
if (JS_GetProperty(cx, obj, "proto", &v) && !JSVAL_IS_VOID(v)) {
if (!JS_ValueToInt32(cx, v, &proto)) {
JS_ReportError(cx, "Invalid proto property");
return JS_FALSE;
}
}
if (JS_GetProperty(cx, obj, "timeout", &v) && !JSVAL_IS_VOID(v)) {
if (!JS_ValueToInt32(cx, v, &timeout)) {
JS_ReportError(cx, "Invalid timeout property");
return JS_FALSE;
}
}
if (JS_GetProperty(cx, obj, "protocol", &v) && !JSVAL_IS_VOID(v)) {
JSVALUE_TO_MSTRING(cx, v, protocol, NULL);
HANDLE_PENDING(cx, protocol);
}
if (JS_GetProperty(cx, obj, "bindport", &v) && !JSVAL_IS_VOID(v)) {
bindport = js_port(cx, v, type);
memset(&addr4, 0, sizeof(addr4));
addr4.sin_family = AF_INET;
addr4.sin_addr.s_addr = INADDR_ANY;
addr4.sin_port = htons(bindport);
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
addr6.sin6_addr = in6addr_any;
addr6.sin6_port = htons(bindport);
sockbind = TRUE;
}
if (JS_GetProperty(cx, obj, "bindaddrs", &v) && !JSVAL_IS_VOID(v)) {
if (!sockbind) {
memset(&addr4, 0, sizeof(addr4));
addr4.sin_family = AF_INET;
addr4.sin_addr.s_addr = INADDR_ANY;
addr4.sin_port = htons(bindport);
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
addr6.sin6_addr = in6addr_any;
addr6.sin6_port = htons(bindport);
sockbind = TRUE;
}
if (JSVAL_IS_OBJECT(v)) {
ao = JSVAL_TO_OBJECT(v);
if (ao == NULL || !JS_IsArrayObject(cx, ao)) {
JS_ReportError(cx, "Invalid bindaddrs list");
goto fail;
}
if (!JS_GetArrayLength(cx, ao, &count)) {
JS_ReportError(cx, "Unable to get bindaddrs length");
goto fail;
}
for (i = 0; i < count; i++) {
if (!JS_GetElement(cx, ao, i, &v)) {
JS_ReportError(cx, "Invalid bindaddrs entry");
goto fail;
}
JSVALUE_TO_MSTRING(cx, v, host, NULL);
HANDLE_PENDING(cx, host);
rc = JS_SUSPENDREQUEST(cx);
if (!handle_addrs(host, &addr4, &addr4len, &addr6, &addr6len)) {
JS_RESUMEREQUEST(cx, rc);
JS_ReportError(cx, "Unparsable bindaddrs entry");
goto fail;
}
FREE_AND_NULL(host);
JS_RESUMEREQUEST(cx, rc);
}
}
else {
JSVALUE_TO_MSTRING(cx, v, host, NULL);
HANDLE_PENDING(cx, host);
rc = JS_SUSPENDREQUEST(cx);
if (!handle_addrs(host, &addr4, &addr4len, &addr6, &addr6len)) {
JS_RESUMEREQUEST(cx, rc);
JS_ReportError(cx, "Unparsable bindaddrs entry");
goto fail;
}
FREE_AND_NULL(host);
JS_RESUMEREQUEST(cx, rc);
}
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
}
JSVALUE_TO_MSTRING(cx, argv[0], host, NULL);
HANDLE_PENDING(cx, host);
port = js_port(cx, argv[1], type);
if (port == 0) {
JS_ReportError(cx, "Invalid port");
goto fail;
}
obj=JS_NewObject(cx, &js_socket_class, NULL, NULL);
JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(obj));
if((p=(js_socket_private_t*)malloc(sizeof(js_socket_private_t)))==NULL) {
JS_ReportError(cx,"malloc failed");
goto fail;
}
memset(p,0,sizeof(js_socket_private_t));
rc = JS_SUSPENDREQUEST(cx);
sprintf(pstr, "%hu", port);
memset(&hints, 0, sizeof(hints));
hints.ai_family = domain;
hints.ai_socktype = type;
hints.ai_protocol = proto;
hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
if((i = getaddrinfo(host, pstr, &hints, &res)) != 0) {
JS_RESUMEREQUEST(cx, rc);
JS_ReportError(cx, gai_strerror(i));
goto fail;
}
p->sock = INVALID_SOCKET;
for(cur=res; cur && p->sock == INVALID_SOCKET; cur=cur->ai_next) {
if(p->sock==INVALID_SOCKET) {
p->sock=socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
if(p->sock==INVALID_SOCKET)
continue;
if (set_socket_options(scfg, p->sock, protocol, error, sizeof(error))) {
freeaddrinfo(res);
JS_RESUMEREQUEST(cx, rc);
JS_ReportError(cx, error);
goto fail;
}
if (sockbind) {
addr = NULL;
switch(cur->ai_family) {
case PF_INET:
addr = (struct sockaddr *)&addr4;
break;
case PF_INET6:
addr = (struct sockaddr *)&addr6;
if (addr == NULL)
continue;
if (bind(p->sock, addr, *addrlen) != 0) {
lprintf(LOG_WARNING, "Unable to bind to local address");
closesocket(p->sock);
p->sock = INVALID_SOCKET;
continue;
}
}
/* Set to non-blocking for the connect */
nonblock=-1;
ioctlsocket(p->sock, FIONBIO, &nonblock);
}
if(connect(p->sock, cur->ai_addr, cur->ai_addrlen)) {
switch(ERROR_VALUE) {
case EINPROGRESS:
case EINTR:
case EAGAIN:
#if (EAGAIN!=EWOULDBLOCK)
case EWOULDBLOCK:
#endif
for(;p->sock!=INVALID_SOCKET;) {
// TODO: Do clever timeout stuff here.
if (socket_writable(p->sock, timeout * 1000)) {
if(!socket_recvdone(p->sock, 0))
goto connected;
closesocket(p->sock);
p->sock=INVALID_SOCKET;
continue;
}
else {
closesocket(p->sock);
p->sock=INVALID_SOCKET;
continue;
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
}
}
connected:
memcpy(&p->remote_addr, cur->ai_addr, cur->ai_addrlen);
break;
default:
closesocket(p->sock);
p->sock=INVALID_SOCKET;
continue;
}
}
}
freeaddrinfo(res);
JS_RESUMEREQUEST(cx, rc);
if (p->sock == INVALID_SOCKET) {
JS_ReportError(cx, "Unable to connect");
goto fail;
}
ioctlsocket(p->sock,FIONBIO,(ulong*)&(p->nonblocking));
call_socket_open_callback(TRUE);
if(protocol)
free(protocol);
p->hostname = host;
p->type = type;
p->network_byte_order = TRUE;
p->session=-1;
p->unflushed = 0;
p->is_connected = TRUE;
if(!JS_SetPrivate(cx, obj, p)) {
JS_ReportError(cx,"JS_SetPrivate failed");
free(p);
return(JS_FALSE);
}
#ifdef BUILD_JSDOCS
js_DescribeSyncObject(cx,obj,"Class used for outgoing TCP/IP socket communications",317);
js_DescribeSyncConstructor(cx,obj,"To create a new ConnectedSocket object: "
"<tt>load('sockdefs.js'); var s = new ConnectedSocket(<i>hostname</i>, <i>port</i>, {domain:<i>domain</i>, proto:<i>proto</i> ,type:<i>type</i>, protocol:<i>protocol</i>, timeout:<i>timeout</i>, bindport:<i>port</i>, bindaddrs:<i>bindaddrs</i>})</tt><br>"
"where <i>domain</i> (optional) = <tt>PF_UNSPEC</tt> (default) for IPv4 or IPv6, <tt>PF_INET</tt> for IPv4, or <tt>PF_INET6</tt> for IPv6<br>"
"<i>proto</i> (optional) = <tt>IPPROTO_IP</tt> (default)<br>"
"<i>type</i> (optional) = <tt>SOCK_STREAM</tt> for TCP (default) or <tt>SOCK_DGRAM</tt> for UDP<br>"
"<i>protocol</i> (optional) = the name of the protocol or service the socket is to be used for<br>"
"<i>timeout</i> (optional) = 10 (default) the number of seconds to wait for each connect() call to complete.<br>"
"<i>bindport</i> (optional) = the name or number of the source port to bind to<br>"
"<i>bindaddrs</i> (optional) = the name or number of the source addresses to bind to. The first of each IPv4 or IPv6 type is used for that family.<br>"
);
JS_DefineProperty(cx,obj,"_dont_document",JSVAL_TRUE,NULL,NULL,JSPROP_READONLY);
#endif
if(!js_DefineSocketOptionsArray(cx, obj, type))
return(JS_FALSE);
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
dbprintf(FALSE, p, "object constructed");
return(JS_TRUE);
fail:
if (p)
free(p);
if (protocol)
free(protocol);
if (host)
free(host);
return JS_FALSE;
}
struct ls_cb_data {
char *protocol;
scfg_t *scfg;
};
static void
ls_cb(SOCKET sock, void *cbptr)
{
struct ls_cb_data *cb = cbptr;
char error[256];
call_socket_open_callback(TRUE);
if(set_socket_options(cb->scfg, sock, cb->protocol, error, sizeof(error)))
lprintf(LOG_ERR,"%04d !ERROR %s", sock, error);
}
static JSBool
js_listening_socket_constructor(JSContext *cx, uintN argc, jsval *arglist)
{
JSObject *obj;
jsval *argv=JS_ARGV(cx, arglist);
int32 type=SOCK_STREAM; /* default = TCP */
int32 domain = PF_UNSPEC; /* default = IPvAny */
int32 proto = IPPROTO_IP;
int retry_count = 0;
int retry_delay = 15;
char* protocol=NULL;
char *interface=NULL;
js_socket_private_t* p = NULL;
jsval v;
uint16_t port;
jsrefcount rc;
scfg_t *scfg;
struct ls_cb_data cb;
jsuint count;
int i;
scfg = JS_GetRuntimePrivate(JS_GetRuntime(cx));
if (scfg == NULL) {
JS_ReportError(cx, "Unable to get private runtime");
goto fail;
}
cb.scfg = scfg;
if (argc < 3) {
JS_ReportError(cx, "At least three arguments required (interfaces, port, and protocol)");
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
goto fail;
}
if (argc > 3) {
if (!JS_ValueToObject(cx, argv[3], &obj)) {
JS_ReportError(cx, "Invalid fourth argument");
goto fail;
}
if (JS_GetProperty(cx, obj, "domain", &v) && !JSVAL_IS_VOID(v)) {
if (!JS_ValueToInt32(cx, v, &domain)) {
JS_ReportError(cx, "Invalid domain property");
goto fail;
}
}
if (JS_GetProperty(cx, obj, "type", &v) && !JSVAL_IS_VOID(v)) {
if (!JS_ValueToInt32(cx, v, &type)) {
JS_ReportError(cx, "Invalid type property");
goto fail;
}
}
if (JS_GetProperty(cx, obj, "proto", &v) && !JSVAL_IS_VOID(v)) {
if (!JS_ValueToInt32(cx, v, &proto)) {
JS_ReportError(cx, "Invalid proto property");
goto fail;
}
}
if (JS_GetProperty(cx, obj, "retry_count", &v) && !JSVAL_IS_VOID(v)) {
if (!JS_ValueToInt32(cx, v, &retry_count)) {
JS_ReportError(cx, "Invalid retry_count property");
goto fail;
}
}
if (JS_GetProperty(cx, obj, "retry_delay", &v) && !JSVAL_IS_VOID(v)) {
if (!JS_ValueToInt32(cx, v, &retry_delay)) {
JS_ReportError(cx, "Invalid retry_delay property");
goto fail;
}
}
}
obj = NULL;
port = js_port(cx, argv[1], type);
JSVALUE_TO_MSTRING(cx, argv[2], protocol, NULL);
HANDLE_PENDING(cx, protocol);
cb.protocol = protocol;
if (JSVAL_IS_OBJECT(argv[0])) {
obj = JSVAL_TO_OBJECT(argv[0]);
if (obj == NULL || !JS_IsArrayObject(cx, obj)) {
JS_ReportError(cx, "Invalid interface list");
goto fail;
}
}
set = xpms_create(retry_count, retry_delay, lprintf);
if (set == NULL) {
JS_ReportError(cx, "Unable to create socket set");
goto fail;
}
if (obj == NULL) {
JSVALUE_TO_MSTRING(cx, argv[0], interface, NULL);
HANDLE_PENDING(cx, interface);
rc = JS_SUSPENDREQUEST(cx);
if (!xpms_add(set, domain, type, proto, interface, port, protocol, ls_cb, NULL, &cb)) {
JS_RESUMEREQUEST(cx, rc);
JS_ReportError(cx, "Unable to add host to socket set");
goto fail;
}
JS_RESUMEREQUEST(cx, rc);
}
else {
if (!JS_GetArrayLength(cx, obj, &count)) {
free(protocol);
return JS_FALSE;
}
for (i = 0; (jsuint)i < count; i++) {
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
if (!JS_GetElement(cx, obj, i, &v)) {
lprintf(LOG_WARNING, "Unable to get element %d from interface array", i);
continue;
}
JSVALUE_TO_MSTRING(cx, v, interface, NULL);
HANDLE_PENDING(cx, interface);
rc = JS_SUSPENDREQUEST(cx);
if (!xpms_add(set, domain, type, proto, interface, port, protocol, ls_cb, NULL, &cb)) {
free(interface);
JS_RESUMEREQUEST(cx, rc);
JS_ReportError(cx, "Unable to add host to socket set");
goto fail;
}
free(interface);
JS_RESUMEREQUEST(cx, rc);
}
}
obj=JS_NewObject(cx, &js_socket_class, NULL, NULL);
JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(obj));
if((p=(js_socket_private_t*)malloc(sizeof(js_socket_private_t)))==NULL) {
JS_ReportError(cx,"malloc failed");
return(JS_FALSE);
}
memset(p,0,sizeof(js_socket_private_t));
p->type = type;
p->set = set;
p->sock = INVALID_SOCKET;
p->external = TRUE;
p->network_byte_order = TRUE;
p->session=-1;
p->unflushed = 0;
p->local_port = port;
if(!JS_SetPrivate(cx, obj, p)) {
JS_ReportError(cx,"JS_SetPrivate failed");
free(p);
return(JS_FALSE);
}
if(!js_DefineSocketOptionsArray(cx, obj, type)) {
free(p);
free(set);
return(JS_FALSE);
#ifdef BUILD_JSDOCS
js_DescribeSyncObject(cx,obj,"Class used for incoming TCP/IP socket communications",317);
js_DescribeSyncConstructor(cx,obj,"To create a new ListeningSocket object: "
"<tt>load('sockdefs.js'); var s = new ListeningSocket(<i>interface</i>, <i>port</i> ,<i>protocol</i>, {domain:<i>domain</i>, type:<i>type</i>, proto:<i>proto</i>, retry_count:<i>retry_count</i>, retry_delay:<i>retry_delay</i>})</tt><br>"
"where <i>interface</i> = A array or strings or a single string of hostnames or address optionally including a :port suffix<br>"
"<i>port</i> = a port to use when the interface doesn't specify one<br>"
"<i>protocol</i> = protocol name, used for socket options and logging.<br>"
"<i>domain</i> (optional) = <tt>PF_UNSPEC</tt> (default) for IPv4 or IPv6, <tt>AF_INET</tt> for IPv4, or <tt>AF_INET6</tt> for IPv6<br>"
"<i>proto</i> (optional) = <tt>IPPROTO_IP</tt> (default)<br>"
"<i>type</i> (optional) = <tt>SOCK_STREAM</tt> for TCP (default) or <tt>SOCK_DGRAM</tt> for UDP<br>"
"<i>retry_count</i> (optional) = <tt>0</tt> (default) number of times to retry binding<br>"
"and <i>retry_delay</i> (optional) = <tt>15</tt> (default) seconds to wait before a retry<br>"
);
JS_DefineProperty(cx,obj,"_dont_document",JSVAL_TRUE,NULL,NULL,JSPROP_READONLY);