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 81.5 KB
Newer Older
rswindell's avatar
rswindell committed
1
/* Synchronet JavaScript "Socket" Object */
2
// vi: tabstop=4
rswindell's avatar
rswindell committed
3 4 5 6 7 8 9

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
10
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
rswindell's avatar
rswindell committed
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
 *																			*
 * 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										*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

deuce's avatar
deuce committed
37 38
#include <cryptlib.h>

rswindell's avatar
rswindell committed
39
#include "sbbs.h"
deuce's avatar
deuce committed
40
#include "js_socket.h"
41
#include "js_request.h"
deuce's avatar
deuce committed
42
#include "multisock.h"
43
#include "ssl.h"
rswindell's avatar
rswindell committed
44 45 46

#ifdef JAVASCRIPT

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
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(js_socket_private_t *p);
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);

85 86
static int do_cryptAttribute(const CRYPT_CONTEXT session, CRYPT_ATTRIBUTE_TYPE attr, int val)
{
deuce's avatar
deuce committed
87
	int ret;
88 89
	int level;
	char *estr;
deuce's avatar
deuce committed
90
	char action[32];
deuce's avatar
deuce committed
91 92 93 94 95 96 97 98 99 100 101 102 103 104

	/* 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);
105
	if(ret != CRYPT_OK) {
deuce's avatar
deuce committed
106 107
		sprintf(action, "setting attribute %d", attr);
		get_crypt_error_string(ret, session, &estr, action, &level);
108
		if (estr) {
109
			lprintf(level, "TLS %s", estr);
110
			free_crypt_attrstr(estr);
111 112
		}
	}
113 114 115
	return ret;
}

deuce's avatar
deuce committed
116 117
static int do_cryptAttributeString(const CRYPT_CONTEXT session, CRYPT_ATTRIBUTE_TYPE attr, void *val, int len)
{
118 119
	int level;
	char *estr;
deuce's avatar
deuce committed
120
	char action[48];
121

deuce's avatar
deuce committed
122
	int ret=cryptSetAttributeString(session, attr, val, len);
123
	if(ret != CRYPT_OK) {
deuce's avatar
deuce committed
124
		sprintf(action, "setting attribute string %d", attr);
125 126
		get_crypt_error_string(ret, session, &estr, "setting attribute string", &level);
		if (estr) {
127
			lprintf(level, "TLS %s", estr);
128
			free_crypt_attrstr(estr);
129 130
		}
	}
deuce's avatar
deuce committed
131 132 133
	return ret;
}

134 135
#define GCES(status, pdata, estr, action) do {                                               \
	int GCES_level;                                                                      \
136
	get_crypt_error_string(status, pdata->session, &estr, action, &GCES_level); \
137
	if (estr) {                                                                          \
138
		lprintf(GCES_level, "%04d TLS %s", p->sock, estr);                               \
139
		free_crypt_attrstr(estr);                                                                  \
140 141 142 143 144
	}                                                                                    \
} while(0)

#define GCESH(status, socket, handle, estr, action) do {                                     \
	int GCESH_level;                                                                     \
145
	get_crypt_error_string(status, handle, &estr, action, &GCESH_level);        \
146
	if (estr) {                                                                          \
147
		lprintf(GCESH_level, "%04d TLS %s", socket, estr);                               \
148
		free_crypt_attrstr(estr);                                                                  \
149 150 151
	}                                                                                    \
} while(0)

deuce's avatar
deuce committed
152
static bool do_CryptFlush(js_socket_private_t *p)
153
{
deuce's avatar
deuce committed
154
	int ret;
155
	char	*estr;
deuce's avatar
deuce committed
156

deuce's avatar
deuce committed
157 158
	if (p->unflushed) {
		ret = cryptFlushData(p->session);
159

deuce's avatar
deuce committed
160 161
		if(ret==CRYPT_OK) {
			p->unflushed = 0;
deuce's avatar
deuce committed
162
			return true;
deuce's avatar
deuce committed
163
		}
164 165
		if (ret != CRYPT_ERROR_COMPLETE)
			GCES(ret, p, estr, "flushing data");
deuce's avatar
deuce committed
166
		return false;
deuce's avatar
deuce committed
167
	}
deuce's avatar
deuce committed
168
	return true;
deuce's avatar
deuce committed
169 170 171 172 173 174 175 176
}

static void do_js_close(js_socket_private_t *p)
{
	if(p->session != -1) {
		cryptDestroySession(p->session);
		p->session=-1;
	}
deuce's avatar
deuce committed
177 178
	if(p->sock==INVALID_SOCKET) {
		p->is_connected = FALSE;
deuce's avatar
deuce committed
179
		return;
deuce's avatar
deuce committed
180
	}
deuce's avatar
deuce committed
181 182 183 184
	if(p->external==FALSE) {
		close_socket(p->sock);
		p->last_error = ERROR_VALUE;
	}
185 186
	else
		shutdown(p->sock, SHUT_RDWR);
deuce's avatar
deuce committed
187
	// This is a lie for external sockets... don't tell anyone.
188
	p->sock = INVALID_SOCKET;
deuce's avatar
deuce committed
189
	p->is_connected = FALSE;
190 191
}

192 193 194 195 196 197 198 199 200 201 202 203 204
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;
}

205 206 207
/* 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
208
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
209
{
210
	ptrdiff_t	total=0;
deuce's avatar
deuce committed
211
	int	copied,ret;
212 213
	fd_set		socket_set;
	struct		timeval tv = {0, 0};
214
	char *estr;
215 216
	time_t now = time(NULL);
	int status;
217

deuce's avatar
deuce committed
218 219
	if (len == 0)
		return total;
220
	if (p->session != -1) {
221 222 223 224 225 226 227 228 229 230 231 232 233 234
		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;
235 236 237
		if (do_cryptAttribute(p->session, CRYPT_OPTION_NET_READTIMEOUT, p->nonblocking?0:timeout) != CRYPT_OK)
			return -1;
	}
deuce's avatar
deuce committed
238
	do {
239 240 241 242
		if(p->session==-1) {
			FD_ZERO(&socket_set);
			FD_SET(p->sock,&socket_set);
			tv.tv_sec = timeout;
243
			if((ret = select(p->sock+1,&socket_set,NULL,NULL,&tv))==1)
244
				ret = recv(p->sock, buf, len, flags);
deuce's avatar
deuce committed
245
		}
deuce's avatar
deuce committed
246
		else {
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
			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;
		}
263
		if ((!(flags & MSG_WAITALL)) || p->nonblocking)
264
			return ret;
265
		total += ret;
266 267
		if(total>=(ptrdiff_t)len)
			return total;
268 269
		len-=ret;
		buf=((uint8_t *)buf) + ret;
270 271

		if(!socket_check(p->sock,NULL,NULL,0)) {
deuce's avatar
deuce committed
272 273 274
			if (total > 0)
				return total;
			return -1;
deuce's avatar
deuce committed
275
		}
276 277
		if (now + timeout > time(NULL))
			return total;
deuce's avatar
deuce committed
278
	} while(len);
279
	return total;
deuce's avatar
deuce committed
280 281
}

deuce's avatar
deuce committed
282
static ptrdiff_t js_socket_sendsocket(js_socket_private_t *p, const void *msg, size_t len, int flush)
deuce's avatar
deuce committed
283
{
284
	ptrdiff_t total=0;
deuce's avatar
deuce committed
285
	int copied=0,ret;
286
	char *estr;
287

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

deuce's avatar
deuce committed
320
static int js_socket_sendfilesocket(js_socket_private_t *p, int file, off_t *offset, off_t count)
deuce's avatar
deuce committed
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
{
	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
351
			do_CryptFlush(p);
deuce's avatar
deuce committed
352 353 354 355 356 357 358 359 360 361 362 363 364
			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
365
			do_CryptFlush(p);
deuce's avatar
deuce committed
366 367 368
			return(wr);
		}
		if(i!=rd) {
deuce's avatar
deuce committed
369
			do_CryptFlush(p);
deuce's avatar
deuce committed
370 371 372 373 374 375 376 377
			return(-1);
		}
		total+=rd;
	}

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

deuce's avatar
deuce committed
378
	do_CryptFlush(p);
deuce's avatar
deuce committed
379 380 381
	return(total);
}

deuce's avatar
deuce committed
382
static void dbprintf(BOOL error, js_socket_private_t* p, char* fmt, ...)
383 384 385 386
{
	va_list argptr;
	char sbuf[1024];

387
	if(p==NULL || (!p->debug /*&& !error */))
388 389 390
		return;

    va_start(argptr,fmt);
391 392
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
393
    va_end(argptr);
394
	lprintf(LOG_DEBUG,"%04d Socket %s%s",p->sock,error ? "ERROR: ":"",sbuf);
395 396
}

rswindell's avatar
rswindell committed
397 398 399 400
/* Socket Destructor */

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

deuce's avatar
deuce committed
403
	if((p=(js_socket_private_t*)JS_GetPrivate(cx,obj))==NULL)
404 405
		return;

406
	do_js_close(p);
407

deuce's avatar
deuce committed
408 409
	if(p->hostname)
		free(p->hostname);
410
	free(p);
rswindell's avatar
rswindell committed
411

412
	JS_SetPrivate(cx, obj, NULL);
rswindell's avatar
rswindell committed
413 414 415 416 417
}


/* Socket Object Methods */

418
extern JSClass js_socket_class;
419
static JSBool
420
js_close(JSContext *cx, uintN argc, jsval *arglist)
421
{
422
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
deuce's avatar
deuce committed
423
	js_socket_private_t*	p;
deuce's avatar
deuce committed
424
	jsrefcount	rc;
425

426 427
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

428
	if((p=(js_socket_private_t*)js_GetClassPrivate(cx, obj, &js_socket_class))==NULL) {
429
		return(JS_FALSE);
430
	}
431

432
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
433
	do_js_close(p);
434
	dbprintf(FALSE, p, "closed");
435
	JS_RESUMEREQUEST(cx, rc);
436 437 438 439

	return(JS_TRUE);
}

440 441
static ushort js_port(JSContext* cx, jsval val, int type)
{
442
	char*			cp;
443
	JSString*		str;
444
	int32			i=0;
445
	struct servent*	serv;
deuce's avatar
deuce committed
446
	jsrefcount		rc;
447

448 449 450 451
	if(JSVAL_IS_NUMBER(val)) {
		JS_ValueToInt32(cx,val,&i);
		return((ushort)i);
	}
452 453
	if(JSVAL_IS_STRING(val)) {
		str = JS_ValueToString(cx,val);
deuce's avatar
deuce committed
454
		JSSTRING_TO_ASTRING(cx, str, cp, 16, NULL);
455 456
		if(isdigit(*cp))
			return((ushort)strtol(cp,NULL,0));
457
		rc=JS_SUSPENDREQUEST(cx);
458
		serv = getservbyname(cp,type==SOCK_STREAM ? "tcp":"udp");
459
		JS_RESUMEREQUEST(cx, rc);
460 461 462 463 464 465
		if(serv!=NULL)
			return(htons(serv->s_port));
	}
	return(0);
}

466 467 468
SOCKET DLLCALL js_socket(JSContext *cx, jsval val)
{
	void*		vp;
469
	JSClass*	cl;
470 471
	SOCKET		sock=INVALID_SOCKET;

472 473
	if(JSVAL_IS_OBJECT(val) && (cl=JS_GetClass(cx,JSVAL_TO_OBJECT(val)))!=NULL) {
		if(cl->flags&JSCLASS_HAS_PRIVATE)
474
			if((vp=JS_GetInstancePrivate(cx,JSVAL_TO_OBJECT(val), &js_socket_class,NULL))!=NULL)
475
				sock=*(SOCKET*)vp;
476 477 478 479 480
	} else if(val!=JSVAL_VOID) {
		int32	i;
		if(JS_ValueToInt32(cx,val,&i))
			sock = i;
	}
481 482 483 484

	return(sock);
}

deuce's avatar
deuce committed
485 486 487 488 489
SOCKET DLLCALL js_socket_add(JSContext *cx, jsval val, fd_set *fds)
{
	js_socket_private_t	*p;
	JSClass*	cl;
	SOCKET		sock=INVALID_SOCKET;
490 491
	size_t		i;
	int32_t		intval;
deuce's avatar
deuce committed
492 493 494

	if(JSVAL_IS_OBJECT(val) && (cl=JS_GetClass(cx,JSVAL_TO_OBJECT(val)))!=NULL) {
		if(cl->flags&JSCLASS_HAS_PRIVATE) {
495
			if((p=(js_socket_private_t *)JS_GetInstancePrivate(cx,JSVAL_TO_OBJECT(val),&js_socket_class,NULL))!=NULL) {
deuce's avatar
deuce committed
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
				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) {
513 514
		if(JS_ValueToInt32(cx,val,&intval)) {
			sock = intval;
deuce's avatar
deuce committed
515 516 517 518 519 520 521 522 523 524
			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;
525 526
	size_t		i;
	int			intval;
deuce's avatar
deuce committed
527 528 529

	if(JSVAL_IS_OBJECT(val) && (cl=JS_GetClass(cx,JSVAL_TO_OBJECT(val)))!=NULL) {
		if(cl->flags&JSCLASS_HAS_PRIVATE) {
530
			if((p=(js_socket_private_t *)JS_GetInstancePrivate(cx,JSVAL_TO_OBJECT(val),&js_socket_class,NULL))!=NULL) {
deuce's avatar
deuce committed
531 532 533 534 535 536 537 538 539
				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
540
					if (p->sock == INVALID_SOCKET)
deuce's avatar
deuce committed
541 542
						return TRUE;
					else {
543 544 545
						if(FD_ISSET(p->sock, fds))
							return TRUE;
					}
deuce's avatar
deuce committed
546 547 548 549
				}
			}
		}
	} else if(val!=JSVAL_VOID) {
550 551
		if(JS_ValueToInt32(cx,val,&intval)) {
			if(FD_ISSET(intval, fds))
deuce's avatar
deuce committed
552 553 554 555 556 557
				return TRUE;
		}
	}
	return FALSE;
}

558 559 560 561 562 563 564 565 566 567 568 569 570
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
571
static JSBool
572
js_bind(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
573
{
574 575
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
deuce's avatar
deuce committed
576
	js_socket_private_t*	p;
577
	ushort		port=0;
deuce's avatar
deuce committed
578
	union xp_sockaddr	addr;
deuce's avatar
deuce committed
579
	jsrefcount	rc;
deuce's avatar
deuce committed
580 581 582 583
	char		*cstr=NULL;
	char		portstr[6];
	struct addrinfo	hints, *res, *tres;
	int			ret;
rswindell's avatar
rswindell committed
584

deuce's avatar
deuce committed
585
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
586

587
	if((p=(js_socket_private_t*)js_GetClassPrivate(cx, obj, &js_socket_class))==NULL) {
588
		return(JS_FALSE);
589
	}
590

591
	memset(&addr,0,sizeof(addr));
deuce's avatar
deuce committed
592
	memset(&hints, 0, sizeof(hints));
rswindell's avatar
rswindell committed
593 594

	if(argc)
595
		port = js_port(cx,argv[0],p->type);
596
	if(argc > 1 && argv[1] != JSVAL_VOID) {
deuce's avatar
deuce committed
597 598 599 600 601 602 603 604
		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
605

606
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
607 608
	if((ret=getaddrinfo(cstr, portstr, &hints, &res)) != 0) {
		JS_RESUMEREQUEST(cx,rc);
609
		dbprintf(TRUE, p, "getaddrinfo(%s, %s) failed with error %d", cstr, portstr, ret);
610
		p->last_error=ERROR_VALUE;
611
		return(JS_TRUE);
rswindell's avatar
rswindell committed
612
	}
613
	for(tres=res; tres; tres=tres->ai_next) {
614
		if(bind(p->sock, tres->ai_addr, tres->ai_addrlen)!=0) {
deuce's avatar
deuce committed
615 616 617 618 619 620 621 622
			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);
			}
		}
623 624
		else
			break;
deuce's avatar
deuce committed
625 626
	}
	freeaddrinfo(res);
rswindell's avatar
rswindell committed
627

628
	dbprintf(FALSE, p, "bound to port %u",port);
629
	JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
630
	JS_RESUMEREQUEST(cx, rc);
631
	return(JS_TRUE);
rswindell's avatar
rswindell committed
632 633
}

634
static JSBool
635
js_listen(JSContext *cx, uintN argc, jsval *arglist)
636
{
637 638
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
deuce's avatar
deuce committed
639
	js_socket_private_t*	p;
rswindell's avatar
rswindell committed
640
	int32		backlog=1;
deuce's avatar
deuce committed
641
	jsrefcount	rc;
642

643 644
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

645
	if((p=(js_socket_private_t*)js_GetClassPrivate(cx, obj, &js_socket_class))==NULL) {
646
		return(JS_FALSE);
647
	}
648

649
	if(argc && argv[0]!=JSVAL_VOID)
650 651
		backlog = JS_ValueToInt32(cx,argv[0],&backlog);

652
	rc=JS_SUSPENDREQUEST(cx);
653 654 655
	if(listen(p->sock, backlog)!=0) {
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "listen failed with error %d",ERROR_VALUE);
656
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
657
		JS_RESUMEREQUEST(cx, rc);
658 659 660 661
		return(JS_TRUE);
	}

	dbprintf(FALSE, p, "listening, backlog=%d",backlog);
662
	JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
663
	JS_RESUMEREQUEST(cx, rc);
664 665 666 667
	return(JS_TRUE);
}

static JSBool
668
js_accept(JSContext *cx, uintN argc, jsval *arglist)
669
{
670
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
deuce's avatar
deuce committed
671 672
	js_socket_private_t*	p;
	js_socket_private_t*	new_p;
673 674
	JSObject*	sockobj;
	SOCKET		new_socket;
675
	socklen_t	addrlen;
deuce's avatar
deuce committed
676
	jsrefcount	rc;
677

678 679
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

680
	if((p=(js_socket_private_t*)js_GetClassPrivate(cx, obj, &js_socket_class))==NULL) {
681
		return(JS_FALSE);
682
	}
683

684 685
	addrlen=sizeof(p->remote_addr);

686
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
687 688 689 690 691 692 693
	if(p->set) {
		if((new_socket=xpms_accept(p->set,&(p->remote_addr),&addrlen,XPMS_FOREVER,NULL))==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
		call_socket_open_callback(TRUE);
deuce's avatar
deuce committed
695 696 697 698 699 700 701 702
	}
	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);
		}
703 704
	}

705
	if((sockobj=js_CreateSocketObject(cx, obj, "new_socket", new_socket, -1))==NULL) {
706
		closesocket(new_socket);
707
		JS_RESUMEREQUEST(cx, rc);
708
		JS_ReportError(cx,"Error creating new socket object");
709 710
		return(JS_TRUE);
	}
deuce's avatar
deuce committed
711
	if((new_p=(js_socket_private_t*)JS_GetPrivate(cx,sockobj))==NULL) {
712
		JS_RESUMEREQUEST(cx, rc);
713
		return(JS_FALSE);
714
	}
715 716 717 718 719 720 721 722

	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");
723
	JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(sockobj));
724
	JS_RESUMEREQUEST(cx, rc);
725 726 727
	return(JS_TRUE);
}

rswindell's avatar
rswindell committed
728
static JSBool
729
js_connect(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
730
{
731 732
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
733
	int			result;
734
	ulong		val;
rswindell's avatar
rswindell committed
735 736
	ushort		port;
	JSString*	str;
deuce's avatar
deuce committed
737
	js_socket_private_t*	p;
738 739
	fd_set		socket_set;
	struct		timeval tv = {0, 0};
deuce's avatar
deuce committed
740
	jsrefcount	rc;
deuce's avatar
deuce committed
741 742
	char		ip_str[256];
	struct addrinfo	hints,*res,*cur;
743

744
	if((p=(js_socket_private_t*)js_GetClassPrivate(cx, obj, &js_socket_class))==NULL) {
745
		return(JS_FALSE);
746
	}
rswindell's avatar
rswindell committed
747 748

	str = JS_ValueToString(cx, argv[0]);
deuce's avatar
deuce committed
749 750
	if(p->hostname)
		free(p->hostname);
deuce's avatar
deuce committed
751
	JSSTRING_TO_MSTRING(cx, str, p->hostname, NULL);
deuce's avatar
deuce committed
752
	port = js_port(cx,argv[1],p->type);
deuce's avatar
deuce committed
753 754
	rc=JS_SUSPENDREQUEST(cx);
	dbprintf(FALSE, p, "resolving hostname: %s", p->hostname);
deuce's avatar
deuce committed
755 756 757 758 759 760

	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) {
761
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
762 763
		p->last_error = ERROR_VALUE;
		dbprintf(TRUE, p, "getaddrinfo(%s) failed with error %d", p->hostname, result);
764
		JS_RESUMEREQUEST(cx, rc);
765
		return(JS_TRUE);
rswindell's avatar
rswindell committed
766
	}
767 768
	/* always set to nonblocking here */
	val=1;
769
	ioctlsocket(p->sock,FIONBIO,&val);
deuce's avatar
deuce committed
770 771
	for(cur=res,result=1; result && cur; cur=cur->ai_next) {
		tv.tv_sec = 10;	/* default time-out */
772

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

deuce's avatar
deuce committed
776 777 778
		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);
779

deuce's avatar
deuce committed
780 781 782 783 784 785
		result=connect(p->sock, cur->ai_addr, cur->ai_addrlen);

		if(result==SOCKET_ERROR
				&& (ERROR_VALUE==EWOULDBLOCK || ERROR_VALUE==EINPROGRESS)) {
			FD_ZERO(&socket_set);
			FD_SET(p->sock,&socket_set);
786 787 788 789 790 791
			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 */
			}
deuce's avatar
deuce committed
792 793 794 795
		}
		if(result==0)
			break;
	}
796 797 798 799
	/* Restore original setting here */
	ioctlsocket(p->sock,FIONBIO,(ulong*)&(p->nonblocking));

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

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

rswindell's avatar
rswindell committed
815 816 817 818
	return(JS_TRUE);
}

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

830 831
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

842
	rc=JS_SUSPENDREQUEST(cx);
843 844 845 846
	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));
847 848 849 850
	} else {
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "send of %u bytes failed",len);
	}
deuce's avatar
deuce committed
851
	free(cp);
852
	JS_RESUMEREQUEST(cx, rc);
853

rswindell's avatar
rswindell committed
854