Newer
Older
JS_ReportError(cx, __FUNCTION__ ": Error allocating %d elements of %lu bytes at %s:%d"
, nfds, sizeof(*fds), getfname(__FILE__), __LINE__);
return JS_FALSE;
}
nfds = js_socket_add(cx, objval, fds, poll_for_write ? POLLOUT : POLLIN);
result = poll(fds, nfds, timeout);
free(fds);
}
#else
FD_ZERO(&socket_set);
if(p->set) {
for(i=0; i<p->set->sock_count; i++) {
FD_SET(p->set->socks[i].sock,&socket_set);
if(p->set->socks[i].sock > high)
high = p->set->socks[i].sock;
}
}
else {
high=p->sock;
FD_SET(p->sock,&socket_set);
}
if(poll_for_write)
wr_set=&socket_set;
else
rd_set=&socket_set;
if (p->peeked && !poll_for_write)
result = 1;
else
result = select(high+1,rd_set,wr_set,NULL,&tv);
p->last_error=ERROR_VALUE;
dbprintf(FALSE, p, "poll: select/poll returned %d (errno %d)"
,result,p->last_error);
JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(result));
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
}
static js_callback_t *
js_get_callback(JSContext *cx)
{
JSObject* scope = JS_GetScopeChain(cx);
JSObject* pscope;
jsval val = JSVAL_NULL;
JSObject *pjs_obj;
pscope = scope;
while ((!JS_LookupProperty(cx, pscope, "js", &val) || val==JSVAL_VOID || !JSVAL_IS_OBJECT(val)) && pscope != NULL) {
pscope = JS_GetParent(cx, pscope);
if (pscope == NULL) {
JS_ReportError(cx, __FUNCTION__ ": Walked to global, no js object!");
return NULL;
}
}
pjs_obj = JSVAL_TO_OBJECT(val);
return JS_GetPrivate(cx, pjs_obj);
}
static void
js_install_one_socket_event(JSContext *cx, JSObject *obj, JSFunction *ecb, js_callback_t *cb, js_socket_private_t *p, SOCKET sock, enum js_event_type et)
{
struct js_event_list *ev;
ev = malloc(sizeof(*ev));
if (ev == NULL) {
JS_ReportError(cx, __FUNCTION__ ": error allocating %lu bytes", sizeof(*ev));
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
return;
}
ev->prev = NULL;
ev->next = cb->events;
if (ev->next)
ev->next->prev = ev;
ev->type = et;
ev->cx = obj;
JS_AddObjectRoot(cx, &ev->cx);
ev->cb = ecb;
ev->data.sock = sock;
ev->id = cb->next_eid;
p->js_cb = cb;
cb->events = ev;
}
static JSBool
js_install_event(JSContext *cx, uintN argc, jsval *arglist, BOOL once)
{
jsval *argv=JS_ARGV(cx, arglist);
js_callback_t* cb;
JSObject *obj=JS_THIS_OBJECT(cx, arglist);
JSFunction *ecb;
js_socket_private_t* p;
size_t i;
char operation[16];
enum js_event_type et;
size_t slen;
if((p=(js_socket_private_t*)js_GetClassPrivate(cx, obj, &js_socket_class))==NULL) {
return(JS_FALSE);
}
/*
* NOTE: If you allow a thisObj here, you'll need to deal with js_GetClassPrivate
* in js_internal.c where the object is assumed to be a socket.
*/
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
if (argc != 2) {
JS_ReportError(cx, "js.on() and js.once() require exactly two parameters");
return JS_FALSE;
}
ecb = JS_ValueToFunction(cx, argv[1]);
if (ecb == NULL) {
return JS_FALSE;
}
JSVALUE_TO_STRBUF(cx, argv[0], operation, sizeof(operation), &slen);
HANDLE_PENDING(cx, NULL);
if (strcmp(operation, "read") == 0) {
if (once)
et = JS_EVENT_SOCKET_READABLE_ONCE;
else
et = JS_EVENT_SOCKET_READABLE;
}
else if (strcmp(operation, "write") == 0) {
if (once)
et = JS_EVENT_SOCKET_WRITABLE_ONCE;
else
et = JS_EVENT_SOCKET_WRITABLE;
}
else {
JS_ReportError(cx, "event parameter must be 'read' or 'write'");
return JS_FALSE;
}
cb = js_get_callback(cx);
if (cb == NULL) {
return JS_FALSE;
}
if (!cb->events_supported) {
JS_ReportError(cx, "events not supported");
return JS_FALSE;
}
if (p->set) {
for (i = 0; i < p->set->sock_count; i++) {
if (p->set->socks[i].sock != INVALID_SOCKET)
js_install_one_socket_event(cx, obj, ecb, cb, p, p->set->socks[i].sock, et);
}
}
else {
if (p->sock != INVALID_SOCKET)
js_install_one_socket_event(cx, obj, ecb, cb, p, p->sock, et);
}
JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(cb->next_eid));
cb->next_eid++;
if (JS_IsExceptionPending(cx))
return JS_FALSE;
return JS_TRUE;
}
static JSBool
js_clear_socket_event(JSContext *cx, uintN argc, jsval *arglist, BOOL once)
{
jsval *argv=JS_ARGV(cx, arglist);
enum js_event_type et;
char operation[16];
size_t slen;
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
if (argc != 2) {
JS_ReportError(cx, "js.clearOn() and js.clearOnce() require exactly two parameters");
return JS_FALSE;
}
JSVALUE_TO_STRBUF(cx, argv[0], operation, sizeof(operation), &slen);
HANDLE_PENDING(cx, NULL);
if (strcmp(operation, "read") == 0) {
if (once)
et = JS_EVENT_SOCKET_READABLE_ONCE;
else
et = JS_EVENT_SOCKET_READABLE;
}
else if (strcmp(operation, "write") == 0) {
if (once)
et = JS_EVENT_SOCKET_WRITABLE_ONCE;
else
et = JS_EVENT_SOCKET_WRITABLE;
}
else {
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
return JS_TRUE;
}
cb = js_get_callback(cx);
if (cb == NULL) {
return JS_FALSE;
}
return js_clear_event(cx, arglist, cb, et, 1);
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
}
static JSBool
js_once(JSContext *cx, uintN argc, jsval *arglist)
{
return js_install_event(cx, argc, arglist, TRUE);
}
static JSBool
js_clearOnce(JSContext *cx, uintN argc, jsval *arglist)
{
return js_clear_socket_event(cx, argc, arglist, TRUE);
}
static JSBool
js_on(JSContext *cx, uintN argc, jsval *arglist)
{
return js_install_event(cx, argc, arglist, FALSE);
}
static JSBool
js_clearOn(JSContext *cx, uintN argc, jsval *arglist)
{
return js_clear_socket_event(cx, argc, arglist, FALSE);
/* Socket Object Properties */
,SOCK_PROP_IS_WRITEABLE
,SOCK_PROP_DATA_WAITING
,SOCK_PROP_NREAD
,SOCK_PROP_LOCAL_IP
,SOCK_PROP_LOCAL_PORT
,SOCK_PROP_REMOTE_IP
,SOCK_PROP_REMOTE_PORT
,SOCK_PROP_TYPE
,SOCK_PROP_FAMILY
,SOCK_PROP_NETWORK_ORDER
#ifdef BUILD_JSDOCS
static char* socket_prop_desc[] = {
"Error status for the last socket operation that failed - <small>READ ONLY</small>"
,"Error description for the last socket operation that failed - <small>READ ONLY</small>"
,"<tt>true</tt> if socket is in a connected state - <small>READ ONLY</small>"
,"<tt>true</tt> if socket can accept written data - Setting to false will shutdown the write end of the socket."
,"<tt>true</tt> if data is waiting to be read from socket - <small>READ ONLY</small>"
,"Number of bytes waiting to be read - TLS sockets will never return more than 1 - <small>READ ONLY</small>"
,"Enable debug logging"
,"Socket descriptor (advanced uses only)"
,"Use non-blocking operation (default is <tt>false</tt>)"
,"Local IP address (string in dotted-decimal format)"
,"Local TCP or UDP port number"
,"Remote IP address (string in dotted-decimal format)"
,"Remote TCP or UDP port number"
,"Socket type, <tt>SOCK_STREAM</tt> (TCP) or <tt>SOCK_DGRAM</tt> (UDP)"
,"Socket protocol family, <tt>PF_INET</tt> (IPv4) or <tt>PF_INET6</tt> (IPv6)"
,"<tt>true</tt> if binary data is to be sent in Network Byte Order (big end first), default is <tt>true</tt>"
,"Set to <tt>true</tt> to enable SSL as a client on the socket"
,"Set to <tt>true</tt> to enable SSL as a server on the socket"
/* statically-defined properties: */
,"Array of socket option names supported by the current platform"
static JSBool js_socket_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
jsval idval;
BOOL b;
int32 i;
scfg_t *scfg;
// Prototype access
return(JS_TRUE);
JS_IdToValue(cx, id, &idval);
tiny = JSVAL_TO_INT(idval);
rc=JS_SUSPENDREQUEST(cx);
dbprintf(FALSE, p, "setting property %d",tiny);
JS_RESUMEREQUEST(cx, rc);
switch(tiny) {
case SOCK_PROP_DEBUG:
JS_ValueToBoolean(cx,*vp,&(p->debug));
if(p->session != -1) {
cryptDestroySession(p->session);
p->session=-1;
}
if(JS_ValueToInt32(cx,*vp,&i))
p->sock = i;
p->is_connected=TRUE;
break;
case SOCK_PROP_LAST_ERROR:
if(JS_ValueToInt32(cx,*vp,&i))
p->last_error = i;
case SOCK_PROP_NONBLOCKING:
JS_ValueToBoolean(cx,*vp,&(p->nonblocking));
rc=JS_SUSPENDREQUEST(cx);
ioctlsocket(p->sock,FIONBIO,(ulong*)&(p->nonblocking));
JS_RESUMEREQUEST(cx, rc);
case SOCK_PROP_NETWORK_ORDER:
JS_ValueToBoolean(cx,*vp,&(p->network_byte_order));
break;
case SOCK_PROP_IS_WRITEABLE:
JS_ValueToBoolean(cx,*vp,&b);
if(!b)
shutdown(p->sock,SHUT_WR);
break;
case SOCK_PROP_SSL_SESSION:
JS_ValueToBoolean(cx,*vp,&b);
rc=JS_SUSPENDREQUEST(cx);
if(b) {
if(p->session==-1) {
if((ret=cryptCreateSession(&p->session, CRYPT_UNUSED, tiny == SOCK_PROP_SSL_SESSION ? CRYPT_SESSION_SSL: CRYPT_SESSION_SSL_SERVER))==CRYPT_OK) {
ulong nb=0;
ioctlsocket(p->sock,FIONBIO,&nb);
nb=1;
setsockopt(p->sock,IPPROTO_TCP,TCP_NODELAY,(char*)&nb,sizeof(nb));
if((ret=do_cryptAttribute(p->session, CRYPT_SESSINFO_NETWORKSOCKET, p->sock))==CRYPT_OK) {
// Reduced compliance checking... required for acme-staging-v02.api.letsencrypt.org
do_cryptAttribute(p->session, CRYPT_OPTION_CERT_COMPLIANCELEVEL, CRYPT_COMPLIANCELEVEL_REDUCED);
// TODO: Make this configurable
do_cryptAttribute(p->session, CRYPT_SESSINFO_SSL_OPTIONS, CRYPT_SSLOPTION_DISABLE_NAMEVERIFY);
ret=do_cryptAttributeString(p->session, CRYPT_SESSINFO_SERVER_NAME, p->hostname, strlen(p->hostname));
p->tls_server = FALSE;
scfg = JS_GetRuntimePrivate(JS_GetRuntime(cx));
if (scfg == NULL) {
ret = CRYPT_ERROR_NOTAVAIL;
}
if (get_ssl_cert(scfg, &estr, &level) == -1) {
if (estr) {
lprintf(level, "%04d %s", p->sock, estr);
free_crypt_attrstr(estr);
if (scfg->tls_certificate == -1)
ret = CRYPT_ERROR_NOTAVAIL;
else {
lock_ssl_cert();
ret = cryptSetAttribute(p->session, CRYPT_SESSINFO_PRIVATEKEY, scfg->tls_certificate);
if (ret != CRYPT_OK) {
unlock_ssl_cert();
GCES(ret, p, estr, "setting private key");
if((ret=do_cryptAttribute(p->session, CRYPT_SESSINFO_ACTIVE, 1))!=CRYPT_OK) {
GCES(ret, p, estr, "setting session active");
if (tiny == SOCK_PROP_SSL_SERVER)
unlock_ssl_cert();
else
GCESH(ret, p->sock, CRYPT_UNUSED, estr, "creating session");
}
if (ret != CRYPT_OK) {
if (p->session != -1)
cryptDestroySession(p->session);
p->session=-1;
ioctlsocket(p->sock,FIONBIO,(ulong*)&(p->nonblocking));
}
}
}
else {
if(p->session != -1) {
cryptDestroySession(p->session);
p->session=-1;
ioctlsocket(p->sock,FIONBIO,(ulong*)&(p->nonblocking));
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)
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) {
*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) {
JS_RESUMEREQUEST(cx, rc);
if(getsockname(p->sock, &addr.addr, &len)!=0)
return(JS_FALSE);
*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[] = {
,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=10.0]")
,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")
{"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")
,310
},
{"writeBin", js_sendbin, 1, JSTYPE_ALIAS },
{"sendBin", js_sendbin, 1, JSTYPE_BOOLEAN, JSDOCSTR("value [,bytes=4]")
,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=512, [timeout_sec=120]]")
,JSDOCSTR("Receive a string, default maxlen is 512 characters (AKA read)")
,310
},
{"peek", js_peek, 0, JSTYPE_STRING, JSDOCSTR("[maxlen=512]")
,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=512] [,timeout=30]")
,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=false] [,maxlen=512 or int_size=4]")
,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 <tt>false</tt>, <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=4]")
,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=0]")
,310
},
{"poll", js_poll, 1, JSTYPE_NUMBER, JSDOCSTR("[timeout=0] [,write=false]")
,JSDOCSTR("Poll socket for read or write ability (default is <i>read</i>), "
"default timeout value is 0.0 seconds (immediate timeout)")
,310
},
{"on", js_on, 2, JSTYPE_NUMBER, JSDOCSTR("('read' | 'write'), callback")
,JSDOCSTR("Execute callback whenever socket is readable/writable. Returns an id to be passed to <tt>js.clearOn()</tt>")
},
{"once", js_once, 2, JSTYPE_NUMBER, JSDOCSTR("('read' | 'write'), callback")
,JSDOCSTR("Execute callback next time socket is readable/writable Returns and id to be passed to <tt>js.clearOnce()</tt>")
},
{"clearOn", js_clearOn, 2, JSTYPE_NUMBER, JSDOCSTR("('read' | 'write'), id")
},
{"clearOnce", js_clearOnce, 2, JSTYPE_NUMBER, JSDOCSTR("('read' | 'write'), id")
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 */
};
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
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* js_CreateSocketObjectWithoutParent(JSContext* cx, SOCKET sock, CRYPT_CONTEXT session)
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
{
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)
2861
2862
2863
2864
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
{
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;
scfg = JS_GetRuntimePrivate(JS_GetRuntime(cx));
if (scfg == NULL) {
JS_ReportError(cx, __FUNCTION__ ": Unable to get private runtime");
return JS_FALSE;
}
if (argc < 2) {
JS_ReportError(cx, __FUNCTION__ ": 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, __FUNCTION__ ": 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, __FUNCTION__ ": 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, __FUNCTION__ ": 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, __FUNCTION__ ": Invalid proto property");
return JS_FALSE;
}
}