Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

js_socket.c 82.9 KB
Newer Older
rswindell's avatar
rswindell committed
1 2 3 4 5 6
/* 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)		*
 *																			*
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
rswindell's avatar
rswindell committed
8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *																			*
 * 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.	*
 ****************************************************************************/

deuce's avatar
deuce committed
22 23
#include <cryptlib.h>

rswindell's avatar
rswindell committed
24
#include "sbbs.h"
deuce's avatar
deuce committed
25
#include "js_socket.h"
26
#include "js_request.h"
deuce's avatar
deuce committed
27
#include "multisock.h"
28
#include "ssl.h"
rswindell's avatar
rswindell committed
29 30 31

#ifdef JAVASCRIPT

32 33 34 35
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);
36
static void do_js_close(js_socket_private_t *p, bool finalize);
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
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 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(js_socket_private_t *p);
static JSBool js_socket_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
static ptrdiff_t js_socket_recv(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 int js_socket_sendfilesocket(js_socket_private_t *p, int file, off_t *offset, off_t count);
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);

70 71
static int do_cryptAttribute(const CRYPT_CONTEXT session, CRYPT_ATTRIBUTE_TYPE attr, int val)
{
deuce's avatar
deuce committed
72
	int ret;
73 74
	int level;
	char *estr;
deuce's avatar
deuce committed
75
	char action[32];
deuce's avatar
deuce committed
76 77 78 79 80 81 82 83 84 85 86 87 88 89

	/* Force "sane" values (requirements) */
	switch(attr) {
		case CRYPT_OPTION_NET_READTIMEOUT:
			if (val < 0)
				val = 0;
			if (val > 300)
				val = 300;
			break;
		default:
			break;
	}

	ret=cryptSetAttribute(session, attr, val);
90
	if(ret != CRYPT_OK) {
deuce's avatar
deuce committed
91 92
		sprintf(action, "setting attribute %d", attr);
		get_crypt_error_string(ret, session, &estr, action, &level);
93
		if (estr) {
94
			lprintf(level, "TLS %s", estr);
95
			free_crypt_attrstr(estr);
96 97
		}
	}
98 99 100
	return ret;
}

deuce's avatar
deuce committed
101 102
static int do_cryptAttributeString(const CRYPT_CONTEXT session, CRYPT_ATTRIBUTE_TYPE attr, void *val, int len)
{
103 104
	int level;
	char *estr;
deuce's avatar
deuce committed
105
	char action[48];
106

deuce's avatar
deuce committed
107
	int ret=cryptSetAttributeString(session, attr, val, len);
108
	if(ret != CRYPT_OK) {
deuce's avatar
deuce committed
109
		sprintf(action, "setting attribute string %d", attr);
110 111
		get_crypt_error_string(ret, session, &estr, "setting attribute string", &level);
		if (estr) {
112
			lprintf(level, "TLS %s", estr);
113
			free_crypt_attrstr(estr);
114 115
		}
	}
deuce's avatar
deuce committed
116 117 118
	return ret;
}

119 120
#define GCES(status, pdata, estr, action) do {                                               \
	int GCES_level;                                                                      \
121
	get_crypt_error_string(status, pdata->session, &estr, action, &GCES_level); \
122
	if (estr) {                                                                          \
123
		lprintf(GCES_level, "%04d TLS %s", p->sock, estr);                               \
124
		free_crypt_attrstr(estr);                                                                  \
125 126 127 128 129
	}                                                                                    \
} while(0)

#define GCESH(status, socket, handle, estr, action) do {                                     \
	int GCESH_level;                                                                     \
130
	get_crypt_error_string(status, handle, &estr, action, &GCESH_level);        \
131
	if (estr) {                                                                          \
132
		lprintf(GCESH_level, "%04d TLS %s", socket, estr);                               \
133
		free_crypt_attrstr(estr);                                                                  \
134 135 136
	}                                                                                    \
} while(0)

deuce's avatar
deuce committed
137
static bool do_CryptFlush(js_socket_private_t *p)
138
{
deuce's avatar
deuce committed
139
	int ret;
140
	char	*estr;
deuce's avatar
deuce committed
141

deuce's avatar
deuce committed
142 143
	if (p->unflushed) {
		ret = cryptFlushData(p->session);
144

deuce's avatar
deuce committed
145 146
		if(ret==CRYPT_OK) {
			p->unflushed = 0;
deuce's avatar
deuce committed
147
			return true;
deuce's avatar
deuce committed
148
		}
149 150
		if (ret != CRYPT_ERROR_COMPLETE)
			GCES(ret, p, estr, "flushing data");
deuce's avatar
deuce committed
151
		return false;
deuce's avatar
deuce committed
152
	}
deuce's avatar
deuce committed
153
	return true;
deuce's avatar
deuce committed
154 155
}

156
static void do_js_close(js_socket_private_t *p, bool finalize)
deuce's avatar
deuce committed
157 158 159 160 161
{
	if(p->session != -1) {
		cryptDestroySession(p->session);
		p->session=-1;
	}
deuce's avatar
deuce committed
162 163
	if(p->sock==INVALID_SOCKET) {
		p->is_connected = FALSE;
deuce's avatar
deuce committed
164
		return;
deuce's avatar
deuce committed
165
	}
deuce's avatar
deuce committed
166 167 168 169
	if(p->external==FALSE) {
		close_socket(p->sock);
		p->last_error = ERROR_VALUE;
	}
170 171 172 173
	else {
		if (!finalize)
			shutdown(p->sock, SHUT_RDWR);
	}
deuce's avatar
deuce committed
174
	// This is a lie for external sockets... don't tell anyone.
175
	p->sock = INVALID_SOCKET;
deuce's avatar
deuce committed
176
	p->is_connected = FALSE;
177 178
}

179 180 181 182 183 184 185 186 187 188 189 190 191
static BOOL js_socket_peek_byte(js_socket_private_t *p)
{
	if (do_cryptAttribute(p->session, CRYPT_OPTION_NET_READTIMEOUT, 0) != CRYPT_OK)
		return FALSE;
	if (p->peeked)
		return TRUE;
	if (js_socket_recv(p, &p->peeked_byte, 1, 0, 0) == 1) {
		p->peeked = TRUE;
		return TRUE;
	}
	return FALSE;
}

192 193 194
/* 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) */
deuce's avatar
deuce committed
195
static ptrdiff_t js_socket_recv(js_socket_private_t *p, void *buf, size_t len, int flags, int timeout)
deuce's avatar
deuce committed
196
{
197
	ptrdiff_t	total=0;
deuce's avatar
deuce committed
198
	int	copied,ret;
199 200
	fd_set		socket_set;
	struct		timeval tv = {0, 0};
201
	char *estr;
202 203
	time_t now = time(NULL);
	int status;
204

deuce's avatar
deuce committed
205 206
	if (len == 0)
		return total;
207
	if (p->session != -1) {
208 209 210 211 212 213 214 215 216 217 218 219 220 221
		if (flags & MSG_PEEK)
			js_socket_peek_byte(p);
		if (p->peeked) {
			*(uint8_t *)buf = p->peeked_byte;
			buf=((uint8_t *)buf) + 1;
			if (!(flags & MSG_PEEK))
				p->peeked = FALSE;
			total++;
			len--;
			if (len == 0)
				return total;
		}
		if (flags & MSG_PEEK)
			return total;
222 223 224
		if (do_cryptAttribute(p->session, CRYPT_OPTION_NET_READTIMEOUT, p->nonblocking?0:timeout) != CRYPT_OK)
			return -1;
	}
deuce's avatar
deuce committed
225
	do {
226
		if(p->session==-1) {
deuce's avatar
deuce committed
227 228 229 230 231 232 233 234 235
			if (p->sock == INVALID_SOCKET)
				ret = -1;
			else {
				FD_ZERO(&socket_set);
				FD_SET(p->sock,&socket_set);
				tv.tv_sec = timeout;
				if((ret = select(p->sock+1,&socket_set,NULL,NULL,&tv))==1)
					ret = recv(p->sock, buf, len, flags);
			}
deuce's avatar
deuce committed
236
		}
deuce's avatar
deuce committed
237
		else {
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
			status = cryptPopData(p->session, buf, len, &copied);
			if(cryptStatusOK(status))
				ret = copied;
			else {
				ret = -1;
				if (status == CRYPT_ERROR_TIMEOUT)
					ret = 0;
				else if (status != CRYPT_ERROR_COMPLETE)
					GCES(ret, p, estr, "popping data");
			}
		}
		if (ret == -1) {
			if (total > 0)
				return total;
			return ret;
		}
254
		if ((!(flags & MSG_WAITALL)) || p->nonblocking)
255
			return ret;
256
		total += ret;
257 258
		if(total>=(ptrdiff_t)len)
			return total;
259 260
		len-=ret;
		buf=((uint8_t *)buf) + ret;
261 262

		if(!socket_check(p->sock,NULL,NULL,0)) {
deuce's avatar
deuce committed
263 264 265
			if (total > 0)
				return total;
			return -1;
deuce's avatar
deuce committed
266
		}
267 268
		if (now + timeout > time(NULL))
			return total;
deuce's avatar
deuce committed
269
	} while(len);
270
	return total;
deuce's avatar
deuce committed
271 272
}

deuce's avatar
deuce committed
273
static ptrdiff_t js_socket_sendsocket(js_socket_private_t *p, const void *msg, size_t len, int flush)
deuce's avatar
deuce committed
274
{
275
	ptrdiff_t total=0;
deuce's avatar
deuce committed
276
	int copied=0,ret;
277
	char *estr;
278

deuce's avatar
deuce committed
279 280 281
	if(p->session==-1)
		return sendsocket(p->sock, msg, len);
	do {
282 283
		// 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) {
deuce's avatar
deuce committed
284
			p->unflushed += copied;
deuce's avatar
deuce committed
285 286
			if(flush)
				do_CryptFlush(p);
deuce's avatar
deuce committed
287
			if(p->nonblocking)
deuce's avatar
deuce committed
288 289
				return copied;
			total += copied;
deuce's avatar
deuce committed
290
			if(total >= (ptrdiff_t)len)
deuce's avatar
deuce committed
291
				return total;
deuce's avatar
deuce committed
292
			do_CryptFlush(p);
deuce's avatar
deuce committed
293
			len -= copied;
294
			msg=((uint8_t *)msg) + copied;
deuce's avatar
deuce committed
295 296
		}
		else {
297 298
			if (ret != CRYPT_ERROR_COMPLETE)
				GCES(ret, p, estr, "pushing data");
deuce's avatar
deuce committed
299 300
			if(flush)
				do_CryptFlush(p);
deuce's avatar
deuce committed
301 302
			return total;
		}
303 304
		if(!socket_check(p->sock,NULL,NULL,0))
			break;
deuce's avatar
deuce committed
305
	} while(len);
deuce's avatar
deuce committed
306 307
	if(flush)
		do_CryptFlush(p);
308
	return total;
deuce's avatar
deuce committed
309 310
}

deuce's avatar
deuce committed
311
static int js_socket_sendfilesocket(js_socket_private_t *p, int file, off_t *offset, off_t count)
deuce's avatar
deuce committed
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
{
	char		buf[1024*16];
	off_t		len;
	int			rd;
	int			wr=0;
	int			total=0;
	int			i;

	if(p->session==-1)
		return sendfilesocket(p->sock, file, offset, count);

	len=filelength(file);

	if(offset!=NULL)
		if(lseek(file,*offset,SEEK_SET)<0)
			return(-1);

	if(count<1 || count>len) {
		count=len;
		count-=tell(file);		/* don't try to read beyond EOF */
	}

	if(count<0) {
		errno=EINVAL;
		return(-1);
	}

	while(total<count) {
		rd=read(file,buf,sizeof(buf));
		if(rd==-1) {
deuce's avatar
deuce committed
342
			do_CryptFlush(p);
deuce's avatar
deuce committed
343 344 345 346 347 348 349 350 351 352 353 354 355
			return(-1);
		}
		if(rd==0)
			break;
		for(i=wr=0;i<rd;i+=wr) {
			wr=js_socket_sendsocket(p,buf+i,rd-i,FALSE);
			if(wr>0)
				continue;
			if(wr==SOCKET_ERROR && ERROR_VALUE==EWOULDBLOCK) {
				wr=0;
				SLEEP(1);
				continue;
			}
deuce's avatar
deuce committed
356
			do_CryptFlush(p);
deuce's avatar
deuce committed
357 358 359
			return(wr);
		}
		if(i!=rd) {
deuce's avatar
deuce committed
360
			do_CryptFlush(p);
deuce's avatar
deuce committed
361 362 363 364 365 366 367 368
			return(-1);
		}
		total+=rd;
	}

	if(offset!=NULL)
		(*offset)+=total;

deuce's avatar
deuce committed
369
	do_CryptFlush(p);
deuce's avatar
deuce committed
370 371 372
	return(total);
}

deuce's avatar
deuce committed
373
static void dbprintf(BOOL error, js_socket_private_t* p, char* fmt, ...)
374 375 376 377
{
	va_list argptr;
	char sbuf[1024];

378
	if(p==NULL || (!p->debug /*&& !error */))
379 380 381
		return;

    va_start(argptr,fmt);
382 383
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
384
    va_end(argptr);
385
	lprintf(LOG_DEBUG,"%04d Socket %s%s",p->sock,error ? "ERROR: ":"",sbuf);
386 387
}

rswindell's avatar
rswindell committed
388 389 390 391
/* Socket Destructor */

static void js_finalize_socket(JSContext *cx, JSObject *obj)
{
deuce's avatar
deuce committed
392
	js_socket_private_t* p;
393

deuce's avatar
deuce committed
394
	if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL)
395 396
		return;

397
	do_js_close(p, true);
398

deuce's avatar
deuce committed
399 400
	if(p->hostname)
		free(p->hostname);
401
	free(p);
rswindell's avatar
rswindell committed
402

403
	JS_SetPrivate(cx, obj, NULL);
rswindell's avatar
rswindell committed
404 405 406 407 408
}


/* Socket Object Methods */

409
extern JSClass js_socket_class;
410
static JSBool
411
js_close(JSContext *cx, uintN argc, jsval *arglist)
412
{
413
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
deuce's avatar
deuce committed
414
	js_socket_private_t*	p;
deuce's avatar
deuce committed
415
	jsrefcount	rc;
416

417 418
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

419
	if((p=(js_socket_private_t*)js_GetClassPrivate(cx, obj, &js_socket_class))==NULL) {
420
		return(JS_FALSE);
421
	}
422

423
	rc=JS_SUSPENDREQUEST(cx);
424
	do_js_close(p, false);
425
	dbprintf(FALSE, p, "closed");
426
	JS_RESUMEREQUEST(cx, rc);
427 428 429 430

	return(JS_TRUE);
}

431 432
static ushort js_port(JSContext* cx, jsval val, int type)
{
433
	char*			cp;
434
	JSString*		str;
435
	int32			i=0;
436
	struct servent*	serv;
deuce's avatar
deuce committed
437
	jsrefcount		rc;
438

439 440 441 442
	if(JSVAL_IS_NUMBER(val)) {
		JS_ValueToInt32(cx,val,&i);
		return((ushort)i);
	}
443 444
	if(JSVAL_IS_STRING(val)) {
		str = JS_ValueToString(cx,val);
deuce's avatar
deuce committed
445
		JSSTRING_TO_ASTRING(cx, str, cp, 16, NULL);
446
		if(IS_DIGIT(*cp))
447
			return((ushort)strtol(cp,NULL,0));
448
		rc=JS_SUSPENDREQUEST(cx);
449
		serv = getservbyname(cp,type==SOCK_STREAM ? "tcp":"udp");
450
		JS_RESUMEREQUEST(cx, rc);
451 452 453 454 455 456
		if(serv!=NULL)
			return(htons(serv->s_port));
	}
	return(0);
}

457 458 459
SOCKET DLLCALL js_socket(JSContext *cx, jsval val)
{
	void*		vp;
460
	JSClass*	cl;
461 462
	SOCKET		sock=INVALID_SOCKET;

463 464
	if(JSVAL_IS_OBJECT(val) && (cl=JS_GetClass(cx,JSVAL_TO_OBJECT(val)))!=NULL) {
		if(cl->flags&JSCLASS_HAS_PRIVATE)
465
			if((vp=JS_GetInstancePrivate(cx,JSVAL_TO_OBJECT(val), &js_socket_class,NULL))!=NULL)
466
				sock=*(SOCKET*)vp;
467 468 469 470 471
	} else if(val!=JSVAL_VOID) {
		int32	i;
		if(JS_ValueToInt32(cx,val,&i))
			sock = i;
	}
472 473 474 475

	return(sock);
}

deuce's avatar
deuce committed
476 477 478 479 480
SOCKET DLLCALL js_socket_add(JSContext *cx, jsval val, fd_set *fds)
{
	js_socket_private_t	*p;
	JSClass*	cl;
	SOCKET		sock=INVALID_SOCKET;
481 482
	size_t		i;
	int32_t		intval;
deuce's avatar
deuce committed
483 484 485

	if(JSVAL_IS_OBJECT(val) && (cl=JS_GetClass(cx,JSVAL_TO_OBJECT(val)))!=NULL) {
		if(cl->flags&JSCLASS_HAS_PRIVATE) {
486
			if((p=(js_socket_private_t *)JS_GetInstancePrivate(cx,JSVAL_TO_OBJECT(val),&js_socket_class,NULL))!=NULL) {
deuce's avatar
deuce committed
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
				if(p->set) {
					for(i=0; i<p->set->sock_count; i++) {
						if(p->set->socks[i].sock == INVALID_SOCKET)
							continue;
						FD_SET(p->set->socks[i].sock, fds);
						if(p->set->socks[i].sock > sock)
							sock = p->set->socks[i].sock;
					}
				}
				else {
					sock = p->sock;
					if(sock != INVALID_SOCKET)
						FD_SET(p->sock, fds);
				}
			}
		}
	} else if(val!=JSVAL_VOID) {
504 505
		if(JS_ValueToInt32(cx,val,&intval)) {
			sock = intval;
deuce's avatar
deuce committed
506 507 508 509 510 511 512 513 514 515
			FD_SET(sock, fds);
		}
	}
	return sock;
}

BOOL DLLCALL  js_socket_isset(JSContext *cx, jsval val, fd_set *fds)
{
	js_socket_private_t	*p;
	JSClass*	cl;
516 517
	size_t		i;
	int			intval;
deuce's avatar
deuce committed
518 519 520

	if(JSVAL_IS_OBJECT(val) && (cl=JS_GetClass(cx,JSVAL_TO_OBJECT(val)))!=NULL) {
		if(cl->flags&JSCLASS_HAS_PRIVATE) {
521
			if((p=(js_socket_private_t *)JS_GetInstancePrivate(cx,JSVAL_TO_OBJECT(val),&js_socket_class,NULL))!=NULL) {
deuce's avatar
deuce committed
522 523 524 525 526 527 528 529 530
				if(p->set) {
					for(i=0; i<p->set->sock_count; i++) {
						if(p->set->socks[i].sock == INVALID_SOCKET)
							continue;
						if(FD_ISSET(p->set->socks[i].sock, fds))
							return TRUE;
					}
				}
				else {
deuce's avatar
deuce committed
531
					if (p->sock == INVALID_SOCKET)
deuce's avatar
deuce committed
532 533
						return TRUE;
					else {
534 535 536
						if(FD_ISSET(p->sock, fds))
							return TRUE;
					}
deuce's avatar
deuce committed
537 538 539 540
				}
			}
		}
	} else if(val!=JSVAL_VOID) {
541 542
		if(JS_ValueToInt32(cx,val,&intval)) {
			if(FD_ISSET(intval, fds))
deuce's avatar
deuce committed
543 544 545 546 547 548
				return TRUE;
		}
	}
	return FALSE;
}

549 550 551 552 553 554 555 556 557 558 559 560 561
void DLLCALL js_timeval(JSContext* cx, jsval val, struct timeval* tv)
{
	jsdouble jsd;

	if(JSVAL_IS_INT(val))
		tv->tv_sec = JSVAL_TO_INT(val);
	else if(JSVAL_IS_DOUBLE(val)) {
		JS_ValueToNumber(cx,val,&jsd);
		tv->tv_sec = (int)jsd;
		tv->tv_usec = (int)(jsd*1000000.0)%1000000;
	}
}

rswindell's avatar
rswindell committed
562
static JSBool
563
js_bind(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
564
{
565 566
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
deuce's avatar
deuce committed
567
	js_socket_private_t*	p;
568
	ushort		port=0;
deuce's avatar
deuce committed
569
	union xp_sockaddr	addr;
deuce's avatar
deuce committed
570
	jsrefcount	rc;
deuce's avatar
deuce committed
571 572 573 574
	char		*cstr=NULL;
	char		portstr[6];
	struct addrinfo	hints, *res, *tres;
	int			ret;
rswindell's avatar
rswindell committed
575

deuce's avatar
deuce committed
576
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
577

578
	if((p=(js_socket_private_t*)js_GetClassPrivate(cx, obj, &js_socket_class))==NULL) {
579
		return(JS_FALSE);
580
	}
581

582
	memset(&addr,0,sizeof(addr));
deuce's avatar
deuce committed
583
	memset(&hints, 0, sizeof(hints));
rswindell's avatar
rswindell committed
584 585

	if(argc)
586
		port = js_port(cx,argv[0],p->type);
587
	if(argc > 1 && argv[1] != JSVAL_VOID) {
deuce's avatar
deuce committed
588 589 590 591 592 593 594 595
		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);
rswindell's avatar
rswindell committed
596

597
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
598 599
	if((ret=getaddrinfo(cstr, portstr, &hints, &res)) != 0) {
		JS_RESUMEREQUEST(cx,rc);
600
		dbprintf(TRUE, p, "getaddrinfo(%s, %s) failed with error %d", cstr, portstr, ret);
601
		p->last_error=ERROR_VALUE;
602
		return(JS_TRUE);
rswindell's avatar
rswindell committed
603
	}
604
	for(tres=res; tres; tres=tres->ai_next) {
605
		if(bind(p->sock, tres->ai_addr, tres->ai_addrlen)!=0) {
deuce's avatar
deuce committed
606 607 608 609 610 611 612 613
			if (tres->ai_next == NULL) {
				p->last_error=ERROR_VALUE;
				dbprintf(TRUE, p, "bind failed with error %d",ERROR_VALUE);
				freeaddrinfo(res);
				JS_RESUMEREQUEST(cx, rc);
				return(JS_TRUE);
			}
		}
614 615
		else
			break;
deuce's avatar
deuce committed
616 617
	}
	freeaddrinfo(res);
rswindell's avatar
rswindell committed
618

619
	dbprintf(FALSE, p, "bound to port %u",port);
620
	JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
621
	JS_RESUMEREQUEST(cx, rc);
622
	return(JS_TRUE);
rswindell's avatar
rswindell committed
623 624
}

625
static JSBool
626
js_listen(JSContext *cx, uintN argc, jsval *arglist)
627
{
628 629
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
deuce's avatar
deuce committed
630
	js_socket_private_t*	p;
rswindell's avatar
rswindell committed
631
	int32		backlog=1;
deuce's avatar
deuce committed
632
	jsrefcount	rc;
633

634 635
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

636
	if((p=(js_socket_private_t*)js_GetClassPrivate(cx, obj, &js_socket_class))==NULL) {
637
		return(JS_FALSE);
638
	}
639

640
	if(argc && argv[0]!=JSVAL_VOID)
641 642
		backlog = JS_ValueToInt32(cx,argv[0],&backlog);

643
	rc=JS_SUSPENDREQUEST(cx);
644 645 646
	if(listen(p->sock, backlog)!=0) {
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "listen failed with error %d",ERROR_VALUE);
647
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
648
		JS_RESUMEREQUEST(cx, rc);
649 650 651 652
		return(JS_TRUE);
	}

	dbprintf(FALSE, p, "listening, backlog=%d",backlog);
653
	JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
654
	JS_RESUMEREQUEST(cx, rc);
655 656 657 658
	return(JS_TRUE);
}

static JSBool
659
js_accept(JSContext *cx, uintN argc, jsval *arglist)
660
{
661
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
deuce's avatar
deuce committed
662 663
	js_socket_private_t*	p;
	js_socket_private_t*	new_p;
664 665
	JSObject*	sockobj;
	SOCKET		new_socket;
666
	socklen_t	addrlen;
deuce's avatar
deuce committed
667
	jsrefcount	rc;
668

669 670
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

671
	if((p=(js_socket_private_t*)js_GetClassPrivate(cx, obj, &js_socket_class))==NULL) {
672
		return(JS_FALSE);
673
	}
674

675 676
	addrlen=sizeof(p->remote_addr);

677
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
678
	if(p->set) {
679
		if((new_socket=xpms_accept(p->set,&(p->remote_addr),&addrlen,XPMS_FOREVER,XPMS_FLAGS_NONE,NULL))==INVALID_SOCKET) {
deuce's avatar
deuce committed
680 681 682 683 684
			p->last_error=ERROR_VALUE;
			dbprintf(TRUE, p, "accept failed with error %d",ERROR_VALUE);
			JS_RESUMEREQUEST(cx, rc);
			return(JS_TRUE);
		}
685
		call_socket_open_callback(TRUE);
deuce's avatar
deuce committed
686 687 688 689 690 691 692 693
	}
	else {
		if((new_socket=accept_socket(p->sock,&(p->remote_addr),&addrlen))==INVALID_SOCKET) {
			p->last_error=ERROR_VALUE;
			dbprintf(TRUE, p, "accept failed with error %d",ERROR_VALUE);
			JS_RESUMEREQUEST(cx, rc);
			return(JS_TRUE);
		}
694 695
	}

696
	if((sockobj=js_CreateSocketObject(cx, obj, "new_socket", new_socket, -1))==NULL) {
697
		closesocket(new_socket);
698
		JS_RESUMEREQUEST(cx, rc);
699
		JS_ReportError(cx,"Error creating new socket object");
700 701
		return(JS_TRUE);
	}
deuce's avatar
deuce committed
702
	if((new_p=(js_socket_private_t*)JS_GetPrivate(cx,sockobj))==NULL) {
703
		JS_RESUMEREQUEST(cx, rc);
704
		return(JS_FALSE);
705
	}
706 707 708 709 710 711 712 713

	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");
714
	JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(sockobj));
715
	JS_RESUMEREQUEST(cx, rc);
716 717 718
	return(JS_TRUE);
}

rswindell's avatar
rswindell committed
719
static JSBool
720
js_connect(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
721
{
722 723
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
724
	int			result;
725
	ulong		val;
rswindell's avatar
rswindell committed
726 727
	ushort		port;
	JSString*	str;
deuce's avatar
deuce committed
728
	js_socket_private_t*	p;
729 730
	fd_set		socket_set;
	struct		timeval tv = {0, 0};
deuce's avatar
deuce committed
731
	jsrefcount	rc;
deuce's avatar
deuce committed
732 733
	char		ip_str[256];
	struct addrinfo	hints,*res,*cur;
734

735
	if((p=(js_socket_private_t*)js_GetClassPrivate(cx, obj, &js_socket_class))==NULL) {
736
		return(JS_FALSE);
737
	}
rswindell's avatar
rswindell committed
738 739

	str = JS_ValueToString(cx, argv[0]);
deuce's avatar
deuce committed
740 741
	if(p->hostname)
		free(p->hostname);
deuce's avatar
deuce committed
742
	JSSTRING_TO_MSTRING(cx, str, p->hostname, NULL);
deuce's avatar
deuce committed
743
	port = js_port(cx,argv[1],p->type);
deuce's avatar
deuce committed
744 745
	rc=JS_SUSPENDREQUEST(cx);
	dbprintf(FALSE, p, "resolving hostname: %s", p->hostname);
deuce's avatar
deuce committed
746 747 748 749 750 751

	memset(&hints, 0, sizeof(hints));
	hints.ai_socktype = p->type;
	hints.ai_flags = AI_ADDRCONFIG;
	result = getaddrinfo(p->hostname, NULL, &hints, &res);
	if(result != 0) {
752
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
753 754
		p->last_error = ERROR_VALUE;
		dbprintf(TRUE, p, "getaddrinfo(%s) failed with error %d", p->hostname, result);
755
		JS_RESUMEREQUEST(cx, rc);
756
		return(JS_TRUE);
rswindell's avatar
rswindell committed
757
	}
758 759
	/* always set to nonblocking here */
	val=1;
760
	ioctlsocket(p->sock,FIONBIO,&val);
761 762
	result = SOCKET_ERROR;
	for(cur=res; cur != NULL; cur=cur->ai_next) {
deuce's avatar
deuce committed
763
		tv.tv_sec = 10;	/* default time-out */
764

deuce's avatar
deuce committed
765 766
		if(argc>2)	/* time-out value specified */
			js_timeval(cx,argv[2],&tv);
deuce's avatar
deuce committed
767

deuce's avatar
deuce committed
768 769 770
		inet_addrtop((void *)cur->ai_addr, ip_str, sizeof(ip_str));
		dbprintf(FALSE, p, "connecting to %s on port %u at %s", ip_str, port, p->hostname);
		inet_setaddrport((void *)cur->ai_addr, port);
771

772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787
		result = connect(p->sock, cur->ai_addr, cur->ai_addrlen);

		if(result == SOCKET_ERROR) {
			result = ERROR_VALUE;
			if(result == EWOULDBLOCK || result == EINPROGRESS) {
				FD_ZERO(&socket_set);
				FD_SET(p->sock,&socket_set);
				result = ETIMEDOUT;
				if(select(p->sock+1,NULL,&socket_set,NULL,&tv)==1) {
					int so_error = -1;
					socklen_t optlen = sizeof(so_error);
					if(getsockopt(p->sock, SOL_SOCKET, SO_ERROR, (void*)&so_error, &optlen) == 0 && so_error == 0)
						result = 0;	/* success */
					else
						result = so_error;
				}
788
			}
deuce's avatar
deuce committed
789
		}
790
		if(result == 0)
deuce's avatar
deuce committed
791 792
			break;
	}
793 794 795 796
	/* Restore original setting here */
	ioctlsocket(p->sock,FIONBIO,(ulong*)&(p->nonblocking));

	if(result!=0) {
deuce's avatar
deuce committed
797
		freeaddrinfo(res);
798 799
		p->last_error = result;
		dbprintf(TRUE, p, "connect failed with error %d", result);
800
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
801
		JS_RESUMEREQUEST(cx, rc);
802
		return(JS_TRUE);
rswindell's avatar
rswindell committed
803
	}
deuce's avatar
deuce committed
804 805
	memcpy(&p->remote_addr, cur->ai_addr, cur->ai_addrlen);
	freeaddrinfo(res);
rswindell's avatar
rswindell committed
806

807
	p->is_connected = TRUE;
808
	JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
deuce's avatar
deuce committed
809
	dbprintf(FALSE, p, "connected to %s on port %u at %s", ip_str, port, p->hostname);
810
	JS_RESUMEREQUEST(cx, rc);
811

rswindell's avatar
rswindell committed
812 813 814 815
	return(JS_TRUE);
}

static JSBool
816
js_send(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
817
{
818 819
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
820
	char*		cp = NULL;
deuce's avatar
deuce committed
821
	size_t		len;
rswindell's avatar
rswindell committed
822
	JSString*	str;
deuce's avatar
deuce committed
823
	js_socket_private_t*	p;
deuce's avatar
deuce committed
824
	jsrefcount	rc;
825
	int		ret;
rswindell's avatar
rswindell committed
826

827 828
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

829
	if((p=(js_socket_private_t*)js_GetClassPrivate(cx, obj, &js_socket_class))==NULL) {
830
		return(JS_FALSE);
831
	}
rswindell's avatar
rswindell committed
832 833

	str = JS_ValueToString(cx, argv[0]);
deuce's avatar
deuce committed
834
	JSSTRING_TO_MSTRING(cx, str, cp, &len);
835
	HANDLE_PENDING(cx, cp);
deuce's avatar
deuce committed
836 837
	if(cp==NULL)
		return JS_TRUE;
rswindell's avatar
rswindell committed
838

839
	rc=JS_SUSPENDREQUEST(cx);
840 841 842 843
	ret = js_socket_sendsocket(p,cp,len,TRUE);
	if(ret >= 0) {
		dbprintf(FALSE, p, "sent %d of %u bytes",ret,len);
		JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(ret));