js_socket.c 39.4 KB
Newer Older
rswindell's avatar
rswindell committed
1
2
3
4
5
6
7
8
9
10
/* js_socket.c */

/* Synchronet JavaScript "Socket" Object */

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
11
 * Copyright 2008 Rob Swindell - http://www.synchro.net/copyright.html		*
rswindell's avatar
rswindell committed
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
37
38
 *																			*
 * 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.	*
 ****************************************************************************/

#include "sbbs.h"
39
#include "js_request.h"
rswindell's avatar
rswindell committed
40
41
42

#ifdef JAVASCRIPT

43
44
45
46
47
typedef struct
{
	SOCKET	sock;
	BOOL	external;	/* externally created, don't close */
	BOOL	debug;
48
	BOOL	nonblocking;
49
	BOOL	is_connected;
50
	BOOL	network_byte_order;
51
	int		last_error;
52
	int		type;
53
	SOCKADDR_IN	remote_addr;
54
55

} private_t;
56

57
static const char* getprivate_failure = "line %d %s JS_GetPrivate failed";
58
59

static void dbprintf(BOOL error, private_t* p, char* fmt, ...)
60
61
62
63
{
	va_list argptr;
	char sbuf[1024];

64
	if(p==NULL || (!p->debug /*&& !error */))
65
66
67
		return;

    va_start(argptr,fmt);
68
69
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
70
71
    va_end(argptr);
	
72
	lprintf(LOG_DEBUG,"%04d Socket %s%s",p->sock,error ? "ERROR: ":"",sbuf);
73
74
}

rswindell's avatar
rswindell committed
75
76
77
78
/* Socket Destructor */

static void js_finalize_socket(JSContext *cx, JSObject *obj)
{
79
	private_t* p;
80

81
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL)
82
83
		return;

84
	if(p->external==FALSE && p->sock!=INVALID_SOCKET) {
85
		close_socket(p->sock);
rswindell's avatar
rswindell committed
86
87
		dbprintf(FALSE, p, "closed/deleted");
	}
88

89
	free(p);
rswindell's avatar
rswindell committed
90

91
	JS_SetPrivate(cx, obj, NULL);
rswindell's avatar
rswindell committed
92
93
94
95
96
}


/* Socket Object Methods */

97
98
99
static JSBool
js_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
100
	private_t*	p;
deuce's avatar
deuce committed
101
	jsrefcount	rc;
102

103
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
104
		JS_ReportError(cx,getprivate_failure,WHERE);
105
		return(JS_FALSE);
106
	}
107

108
	if(p->sock==INVALID_SOCKET)
109
110
		return(JS_TRUE);

111
	rc=JS_SUSPENDREQUEST(cx);
112
	close_socket(p->sock);
113

114
	p->last_error = ERROR_VALUE;
115

116
	dbprintf(FALSE, p, "closed");
117

118
119
	p->sock = INVALID_SOCKET; 
	p->is_connected = FALSE;
120
	JS_RESUMEREQUEST(cx, rc);
121
122
123
124

	return(JS_TRUE);
}

125
126
static ushort js_port(JSContext* cx, jsval val, int type)
{
127
	char*			cp;
128
	JSString*		str;
129
	int32			i=0;
130
	struct servent*	serv;
deuce's avatar
deuce committed
131
	jsrefcount		rc;
132

133
134
135
136
	if(JSVAL_IS_NUMBER(val)) {
		JS_ValueToInt32(cx,val,&i);
		return((ushort)i);
	}
137
138
	if(JSVAL_IS_STRING(val)) {
		str = JS_ValueToString(cx,val);
139
140
141
		cp = JS_GetStringBytes(str);
		if(isdigit(*cp))
			return((ushort)strtol(cp,NULL,0));
142
		rc=JS_SUSPENDREQUEST(cx);
143
		serv = getservbyname(cp,type==SOCK_STREAM ? "tcp":"udp");
144
		JS_RESUMEREQUEST(cx, rc);
145
146
147
148
149
150
		if(serv!=NULL)
			return(htons(serv->s_port));
	}
	return(0);
}

151
152
153
SOCKET DLLCALL js_socket(JSContext *cx, jsval val)
{
	void*		vp;
154
	JSClass*	cl;
155
156
	SOCKET		sock=INVALID_SOCKET;

157
158
	if(JSVAL_IS_OBJECT(val) && (cl=JS_GetClass(cx,JSVAL_TO_OBJECT(val)))!=NULL) {
		if(cl->flags&JSCLASS_HAS_PRIVATE)
159
160
			if((vp=JS_GetPrivate(cx,JSVAL_TO_OBJECT(val)))!=NULL)
				sock=*(SOCKET*)vp;
161
	} else if(val!=JSVAL_VOID)
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
		JS_ValueToInt32(cx,val,(int32*)&sock);

	return(sock);
}

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
180
181
182
static JSBool
js_bind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
183
	ulong		ip=0;
184
	private_t*	p;
185
	ushort		port=0;
186
	SOCKADDR_IN	addr;
deuce's avatar
deuce committed
187
	jsrefcount	rc;
rswindell's avatar
rswindell committed
188

189
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
190
		JS_ReportError(cx,getprivate_failure,WHERE);
191
		return(JS_FALSE);
192
	}
193
	
194
195
	memset(&addr,0,sizeof(addr));
	addr.sin_family = AF_INET;
rswindell's avatar
rswindell committed
196
197

	if(argc)
198
		port = js_port(cx,argv[0],p->type);
199
	addr.sin_port = htons(port);
200
201
	if(argc>1 
		&& (ip=inet_addr(JS_GetStringBytes(JS_ValueToString(cx,argv[1]))))!=INADDR_NONE)
202
		addr.sin_addr.s_addr = ip;
rswindell's avatar
rswindell committed
203

204
	rc=JS_SUSPENDREQUEST(cx);
205
	if(bind(p->sock, (struct sockaddr *) &addr, sizeof(addr))!=0) {
206
207
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "bind failed with error %d",ERROR_VALUE);
208
		*rval = JSVAL_FALSE;
209
		JS_RESUMEREQUEST(cx, rc);
210
		return(JS_TRUE);
rswindell's avatar
rswindell committed
211
212
	}

213
	dbprintf(FALSE, p, "bound to port %u",port);
214
	*rval = JSVAL_TRUE;
215
	JS_RESUMEREQUEST(cx, rc);
216
	return(JS_TRUE);
rswindell's avatar
rswindell committed
217
218
}

219
220
221
222
static JSBool
js_listen(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	private_t*	p;
rswindell's avatar
rswindell committed
223
	int32		backlog=1;
deuce's avatar
deuce committed
224
	jsrefcount	rc;
225

226
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
227
		JS_ReportError(cx,getprivate_failure,WHERE);
228
		return(JS_FALSE);
229
	}
230
	
231
	if(argc && argv[0]!=JSVAL_VOID)
232
233
		backlog = JS_ValueToInt32(cx,argv[0],&backlog);

234
	rc=JS_SUSPENDREQUEST(cx);
235
236
237
	if(listen(p->sock, backlog)!=0) {
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "listen failed with error %d",ERROR_VALUE);
238
		*rval = JSVAL_FALSE;
239
		JS_RESUMEREQUEST(cx, rc);
240
241
242
243
		return(JS_TRUE);
	}

	dbprintf(FALSE, p, "listening, backlog=%d",backlog);
244
	*rval = JSVAL_TRUE;
245
	JS_RESUMEREQUEST(cx, rc);
246
247
248
249
250
251
252
253
254
255
	return(JS_TRUE);
}

static JSBool
js_accept(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	private_t*	p;
	private_t*	new_p;
	JSObject*	sockobj;
	SOCKET		new_socket;
256
	socklen_t	addrlen;
deuce's avatar
deuce committed
257
	jsrefcount	rc;
258

259
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
260
		JS_ReportError(cx,getprivate_failure,WHERE);
261
		return(JS_FALSE);
262
	}
263

264
265
	addrlen=sizeof(p->remote_addr);

266
	rc=JS_SUSPENDREQUEST(cx);
267
	if((new_socket=accept_socket(p->sock,(struct sockaddr *)&(p->remote_addr),&addrlen))==INVALID_SOCKET) {
268
269
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "accept failed with error %d",ERROR_VALUE);
270
		JS_RESUMEREQUEST(cx, rc);
271
272
273
274
275
		return(JS_TRUE);
	}

	if((sockobj=js_CreateSocketObject(cx, obj, "new_socket", new_socket))==NULL) {
		closesocket(new_socket);
276
		JS_RESUMEREQUEST(cx, rc);
277
		JS_ReportError(cx,"Error creating new socket object");
278
279
		return(JS_TRUE);
	}
280
	if((new_p=(private_t*)JS_GetPrivate(cx,sockobj))==NULL) {
281
		JS_RESUMEREQUEST(cx, rc);
282
		JS_ReportError(cx,getprivate_failure,WHERE);
283
		return(JS_FALSE);
284
	}
285
286
287
288
289
290
291
292
293

	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");
	*rval = OBJECT_TO_JSVAL(sockobj);
294
	JS_RESUMEREQUEST(cx, rc);
295
296
297
	return(JS_TRUE);
}

rswindell's avatar
rswindell committed
298
299
300
static JSBool
js_connect(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
301
	int			result;
302
	ulong		val;
rswindell's avatar
rswindell committed
303
304
305
	ulong		ip_addr;
	ushort		port;
	JSString*	str;
306
	private_t*	p;
307
308
	fd_set		socket_set;
	struct		timeval tv = {0, 0};
deuce's avatar
deuce committed
309
310
	jsrefcount	rc;
	char*		cstr;
311
	
312
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
313
		JS_ReportError(cx,getprivate_failure,WHERE);
314
		return(JS_FALSE);
315
	}
rswindell's avatar
rswindell committed
316
317

	str = JS_ValueToString(cx, argv[0]);
deuce's avatar
deuce committed
318
	cstr = JS_GetStringBytes(str);
319
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
320
	dbprintf(FALSE, p, "resolving hostname: %s", cstr);
321
	if((ip_addr=resolve_ip(JS_GetStringBytes(str)))==INADDR_NONE) {
322
323
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "resolve_ip failed with error %d",ERROR_VALUE);
324
		*rval = JSVAL_FALSE;
325
		JS_RESUMEREQUEST(cx, rc);
326
		return(JS_TRUE);
rswindell's avatar
rswindell committed
327
328
	}

329
	JS_RESUMEREQUEST(cx, rc);
330
	port = js_port(cx,argv[1],p->type);
rswindell's avatar
rswindell committed
331

332
333
334
	tv.tv_sec = 10;	/* default time-out */

	if(argc>2)	/* time-out value specified */
335
		js_timeval(cx,argv[2],&tv);
336

337
	rc=JS_SUSPENDREQUEST(cx);
338
	dbprintf(FALSE, p, "connecting to port %u at %s", port, JS_GetStringBytes(str));
339

340
341
342
343
	memset(&(p->remote_addr),0,sizeof(p->remote_addr));
	p->remote_addr.sin_addr.s_addr = ip_addr;
	p->remote_addr.sin_family = AF_INET;
	p->remote_addr.sin_port   = htons(port);
rswindell's avatar
rswindell committed
344

345
346
347
348
	/* always set to nonblocking here */
	val=1;
	ioctlsocket(p->sock,FIONBIO,&val);	

349
	result=connect(p->sock, (struct sockaddr *)&(p->remote_addr), sizeof(p->remote_addr));
deuce's avatar
deuce committed
350

351
352
	if(result==SOCKET_ERROR
		&& (ERROR_VALUE==EWOULDBLOCK || ERROR_VALUE==EINPROGRESS)) {
353
354
355
356
357
358
359
360
361
362
		FD_ZERO(&socket_set);
		FD_SET(p->sock,&socket_set);
		if(select(p->sock+1,NULL,&socket_set,NULL,&tv)==1)
			result=0;	/* success */
	}

	/* Restore original setting here */
	ioctlsocket(p->sock,FIONBIO,(ulong*)&(p->nonblocking));

	if(result!=0) {
363
364
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "connect failed with error %d",ERROR_VALUE);
365
		*rval = JSVAL_FALSE;
366
		JS_RESUMEREQUEST(cx, rc);
367
		return(JS_TRUE);
rswindell's avatar
rswindell committed
368
369
	}

370
	p->is_connected = TRUE;
371
	*rval = JSVAL_TRUE;
372
	dbprintf(FALSE, p, "connected to port %u at %s", port, JS_GetStringBytes(str));
373
	JS_RESUMEREQUEST(cx, rc);
374

rswindell's avatar
rswindell committed
375
376
377
378
379
380
	return(JS_TRUE);
}

static JSBool
js_send(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
381
	char*		cp;
rswindell's avatar
rswindell committed
382
383
	int			len;
	JSString*	str;
384
	private_t*	p;
deuce's avatar
deuce committed
385
	jsrefcount	rc;
rswindell's avatar
rswindell committed
386

387
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
388
		JS_ReportError(cx,getprivate_failure,WHERE);
389
		return(JS_FALSE);
390
	}
rswindell's avatar
rswindell committed
391

392
	*rval = JSVAL_FALSE;
rswindell's avatar
rswindell committed
393
394

	str = JS_ValueToString(cx, argv[0]);
395
396
	cp	= JS_GetStringBytes(str);
	len	= JS_GetStringLength(str);
rswindell's avatar
rswindell committed
397

398
	rc=JS_SUSPENDREQUEST(cx);
399
	if(sendsocket(p->sock,cp,len)==len) {
400
		dbprintf(FALSE, p, "sent %u bytes",len);
401
		*rval = JSVAL_TRUE;
402
403
404
405
	} else {
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "send of %u bytes failed",len);
	}
406
	JS_RESUMEREQUEST(cx, rc);
rswindell's avatar
rswindell committed
407
408
409
410
		
	return(JS_TRUE);
}

411
412
413
414
415
416
417
418
419
420
421
static JSBool
js_sendto(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	char*		cp;
	int			len;
	ulong		ip_addr;
	ushort		port;
	JSString*	data_str;
	JSString*	ip_str;
	private_t*	p;
	SOCKADDR_IN	addr;
deuce's avatar
deuce committed
422
423
	jsrefcount	rc;
	char*		cstr;
424

425
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
426
		JS_ReportError(cx,getprivate_failure,WHERE);
427
		return(JS_FALSE);
428
	}
429

430
	*rval = JSVAL_FALSE;
431
432
433
434

	/* data */
	data_str = JS_ValueToString(cx, argv[0]);
	cp = JS_GetStringBytes(data_str);
435
	len = JS_GetStringLength(data_str);
436
437
438

	/* address */
	ip_str = JS_ValueToString(cx, argv[1]);
deuce's avatar
deuce committed
439
	cstr = JS_GetStringBytes(ip_str);
440
	rc=JS_SUSPENDREQUEST(cx);
441
	dbprintf(FALSE, p, "resolving hostname: %s", JS_GetStringBytes(ip_str));
deuce's avatar
deuce committed
442
	if((ip_addr=resolve_ip(cstr))==INADDR_NONE) {
443
444
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "resolve_ip failed with error %d",ERROR_VALUE);
445
		*rval = JSVAL_FALSE;
446
		JS_RESUMEREQUEST(cx, rc);
447
448
449
450
		return(JS_TRUE);
	}

	/* port */
451
	JS_RESUMEREQUEST(cx, rc);
452
	port = js_port(cx,argv[2],p->type);
453
	rc=JS_SUSPENDREQUEST(cx);
454
455
456
457
458
459
460
461
462
463
464

	dbprintf(FALSE, p, "sending %d bytes to port %u at %s"
		,len, port, JS_GetStringBytes(ip_str));

	memset(&addr,0,sizeof(addr));
	addr.sin_addr.s_addr = ip_addr;
	addr.sin_family = AF_INET;
	addr.sin_port   = htons(port);

	if(sendto(p->sock,cp,len,0 /* flags */,(SOCKADDR*)&addr,sizeof(addr))==len) {
		dbprintf(FALSE, p, "sent %u bytes",len);
465
		*rval = JSVAL_TRUE;
466
467
468
469
	} else {
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "send of %u bytes failed",len);
	}
470
	JS_RESUMEREQUEST(cx, rc);
471
472
473
474
475

	return(JS_TRUE);
}


rswindell's avatar
rswindell committed
476
477
478
479
480
481
482
483
static JSBool
js_sendfile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	char*		fname;
	long		len;
	int			file;
	JSString*	str;
	private_t*	p;
deuce's avatar
deuce committed
484
	jsrefcount	rc;
rswindell's avatar
rswindell committed
485

486
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
487
		JS_ReportError(cx,getprivate_failure,WHERE);
rswindell's avatar
rswindell committed
488
		return(JS_FALSE);
489
	}
rswindell's avatar
rswindell committed
490

491
	*rval = JSVAL_FALSE;
rswindell's avatar
rswindell committed
492

493
494
495
	if((str = JS_ValueToString(cx, argv[0]))==NULL
		|| (fname=JS_GetStringBytes(str))==NULL) {
		JS_ReportError(cx,"Failure reading filename");
rswindell's avatar
rswindell committed
496
		return(JS_FALSE);
497
	}
rswindell's avatar
rswindell committed
498

499
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
500
	if((file=nopen(fname,O_RDONLY|O_BINARY))==-1) {
501
		JS_RESUMEREQUEST(cx, rc);
rswindell's avatar
rswindell committed
502
		return(JS_TRUE);
deuce's avatar
deuce committed
503
	}
rswindell's avatar
rswindell committed
504

deuce's avatar
deuce committed
505
#if 0
rswindell's avatar
rswindell committed
506
	len=filelength(file);
507
	/* TODO: Probobly too big for alloca()... also, this is insane. */
rswindell's avatar
rswindell committed
508
509
510
511
512
	if((buf=malloc(len))==NULL) {
		close(file);
		return(JS_TRUE);
	}
	if(read(file,buf,len)!=len) {
513
		free(buf);
rswindell's avatar
rswindell committed
514
515
516
517
518
519
520
		close(file);
		return(JS_TRUE);
	}
	close(file);

	if(sendsocket(p->sock,buf,len)==len) {
		dbprintf(FALSE, p, "sent %u bytes",len);
521
		*rval = JSVAL_TRUE;
rswindell's avatar
rswindell committed
522
523
524
525
526
	} else {
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "send of %u bytes failed",len);
	}
	free(buf);
deuce's avatar
deuce committed
527
528
529
530
531
532
533
534
535
536
#else
	len = sendfilesocket(p->sock, file, NULL, 0);
	if(len > 0) {
		dbprintf(FALSE, p, "sent %u bytes",len);
		*rval = JSVAL_TRUE;
	} else {
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "send of %s failed",fname);
	}
#endif
rswindell's avatar
rswindell committed
537

538
	JS_RESUMEREQUEST(cx, rc);
rswindell's avatar
rswindell committed
539
540
541
	return(JS_TRUE);
}

542
543
544
545
546
547
548
549
550
551
static JSBool
js_sendbin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	BYTE		b;
	WORD		w;
	DWORD		l;
	int32		val=0;
	size_t		wr=0;
	size_t		size=sizeof(DWORD);
	private_t*	p;
deuce's avatar
deuce committed
552
	jsrefcount	rc;
553
554
555
556
557
558
559
560

	*rval = JSVAL_FALSE;

	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
		JS_ReportError(cx,getprivate_failure,WHERE);
		return(JS_FALSE);
	}

561
562
563
	if(argv[0]!=JSVAL_VOID)
		JS_ValueToInt32(cx,argv[0],&val);
	if(argc>1 && argv[1]!=JSVAL_VOID) 
564
565
		JS_ValueToInt32(cx,argv[1],(int32*)&size);

566
	rc=JS_SUSPENDREQUEST(cx);
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
	switch(size) {
		case sizeof(BYTE):
			b = (BYTE)val;
			wr=sendsocket(p->sock,&b,size);
			break;
		case sizeof(WORD):
			w = (WORD)val;
			if(p->network_byte_order)
				w=htons(w);
			wr=sendsocket(p->sock,(BYTE*)&w,size);
			break;
		case sizeof(DWORD):
			l = val;
			if(p->network_byte_order)
				l=htonl(l);
			wr=sendsocket(p->sock,(BYTE*)&l,size);
			break;
		default:	
			/* unknown size */
			dbprintf(TRUE, p, "unsupported binary write size: %d",size);
			break;
	}
	if(wr==size) {
		dbprintf(FALSE, p, "sent %u bytes (binary)",size);
		*rval = JSVAL_TRUE;
	} else {
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "send of %u bytes (binary) failed",size);
	}
		
597
	JS_RESUMEREQUEST(cx, rc);
598
599
600
601
	return(JS_TRUE);
}


rswindell's avatar
rswindell committed
602
603
604
static JSBool
js_recv(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
605
	char*		buf;
606
	int32		len=512;
rswindell's avatar
rswindell committed
607
	JSString*	str;
deuce's avatar
deuce committed
608
	jsrefcount	rc;
rswindell's avatar
rswindell committed
609

610
611
	private_t*	p;

612
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
613
		JS_ReportError(cx,getprivate_failure,WHERE);
614
		return(JS_FALSE);
615
	}
rswindell's avatar
rswindell committed
616

617
	if(argc && argv[0]!=JSVAL_VOID)
618
		JS_ValueToInt32(cx,argv[0],&len);
rswindell's avatar
rswindell committed
619

620
	if((buf=(char*)alloca(len+1))==NULL) {
621
		JS_ReportError(cx,"Error allocating %u bytes",len+1);
622
623
		return(JS_FALSE);
	}
rswindell's avatar
rswindell committed
624

625
	rc=JS_SUSPENDREQUEST(cx);
626
	len = recv(p->sock,buf,len,0);
627
	JS_RESUMEREQUEST(cx, rc);
628
629
	if(len<0) {
		p->last_error=ERROR_VALUE;
630
631
		*rval = JSVAL_NULL;
		return(JS_TRUE);
632
	}
rswindell's avatar
rswindell committed
633
634
	buf[len]=0;

635
	str = JS_NewStringCopyN(cx, buf, len);
636
637
	if(str==NULL)
		return(JS_FALSE);
638

639
	*rval = STRING_TO_JSVAL(str);
640
	rc=JS_SUSPENDREQUEST(cx);
641
	dbprintf(FALSE, p, "received %u bytes",len);
642
	JS_RESUMEREQUEST(cx, rc);
643
	
rswindell's avatar
rswindell committed
644
645
646
	return(JS_TRUE);
}

647
648
649
650
651
652
static JSBool
js_recvfrom(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	char*		buf;
	char		ip_addr[64];
	char		port[32];
653
	int			rd=0;
654
	int32		len=512;
655
656
657
658
659
660
	uintN		n;
	BOOL		binary=FALSE;
	BYTE		b;
	WORD		w;
	DWORD		l;
	jsval		data_val=JSVAL_NULL;
661
662
663
664
	JSString*	str;
	JSObject*	retobj;
	SOCKADDR_IN	addr;
	socklen_t	addrlen;
deuce's avatar
deuce committed
665
	jsrefcount	rc;
666
667
668

	private_t*	p;

669
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
670
		JS_ReportError(cx,getprivate_failure,WHERE);
671
		return(JS_FALSE);
672
	}
673

674
	*rval = JSVAL_NULL;
675

676
677
678
679
680
	for(n=0;n<argc;n++) {
		if(JSVAL_IS_BOOLEAN(argv[n])) {
			binary=JSVAL_TO_BOOLEAN(argv[n]);
			if(binary)
				len=sizeof(DWORD);
681
		} else if(argv[n]!=JSVAL_VOID)
682
			JS_ValueToInt32(cx,argv[n],&len);
683
684
685
	}

	addrlen=sizeof(addr);
686
687
688

	if(binary) {	/* Binary/Integer Data */

689
		rc=JS_SUSPENDREQUEST(cx);
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
		switch(len) {
			case sizeof(BYTE):
				if((rd=recvfrom(p->sock,&b,len,0,(SOCKADDR*)&addr,&addrlen))==len)
					data_val = INT_TO_JSVAL(b);
				break;
			case sizeof(WORD):
				if((rd=recvfrom(p->sock,(BYTE*)&w,len,0,(SOCKADDR*)&addr,&addrlen))==len) {
					if(p->network_byte_order)
						w=ntohs(w);
					data_val = INT_TO_JSVAL(w);
				}
				break;
			case sizeof(DWORD):
				if((rd=recvfrom(p->sock,(BYTE*)&l,len,0,(SOCKADDR*)&addr,&addrlen))==len) {
					if(p->network_byte_order)
						l=ntohl(l);
706
					JS_RESUMEREQUEST(cx, rc);
707
					JS_NewNumberValue(cx,l,&data_val);
708
					rc=JS_SUSPENDREQUEST(cx);
709
710
711
712
				}
				break;
		}

713
714
		JS_RESUMEREQUEST(cx, rc);

715
716
717
718
719
720
721
		if(rd!=len) {
			p->last_error=ERROR_VALUE;
			return(JS_TRUE);
		}

	} else {		/* String Data */

722
		if((buf=(char*)alloca(len+1))==NULL) {
723
724
725
726
			JS_ReportError(cx,"Error allocating %u bytes",len+1);
			return(JS_FALSE);
		}

727
		rc=JS_SUSPENDREQUEST(cx);
728
		len = recvfrom(p->sock,buf,len,0,(SOCKADDR*)&addr,&addrlen);
729
		JS_RESUMEREQUEST(cx, rc);
730
731
732
733
734
735
		if(len<0) {
			p->last_error=ERROR_VALUE;
			return(JS_TRUE);
		}
		buf[len]=0;

736
		str = JS_NewStringCopyN(cx, buf, len);
737

738
739
740
741
742
		if(str==NULL)
			return(JS_FALSE);

		data_val = STRING_TO_JSVAL(str);
	}
743

744

745
	if((retobj=JS_NewObject(cx,NULL,NULL,obj))==NULL) {
746
		JS_ReportError(cx,"JS_NewObject failed");
rswindell's avatar
rswindell committed
747
		return(JS_FALSE);
748
	}
749

750
	JS_DefineProperty(cx, retobj, "data"
751
		,data_val
752
753
		,NULL,NULL,JSPROP_ENUMERATE);

754
	sprintf(port,"%u",ntohs(addr.sin_port));
755
756
	if((str=JS_NewStringCopyZ(cx,port))==NULL)
		return(JS_FALSE);
757
	JS_DefineProperty(cx, retobj, "port"
758
		,STRING_TO_JSVAL(str)
759
760
761
		,NULL,NULL,JSPROP_ENUMERATE);

	SAFECOPY(ip_addr,inet_ntoa(addr.sin_addr));
762
763
	if((str=JS_NewStringCopyZ(cx,ip_addr))==NULL)
		return(JS_FALSE);
764
765
766
767
768
769
	JS_DefineProperty(cx, retobj, "ip_address"
		,STRING_TO_JSVAL(str)
		,NULL,NULL,JSPROP_ENUMERATE);

	*rval = OBJECT_TO_JSVAL(retobj);

770
	rc=JS_SUSPENDREQUEST(cx);
771
	dbprintf(FALSE, p, "received %u bytes from %s:%s",len,ip_addr,port);
772
	JS_RESUMEREQUEST(cx, rc);
773
774
775
776
777
		
	return(JS_TRUE);
}


778
779
780
static JSBool
js_peek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
781
	char*		buf;
782
	int32		len=512;
783
	JSString*	str;
deuce's avatar
deuce committed
784
	jsrefcount	rc;
785

786
787
	private_t*	p;

788
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
789
		JS_ReportError(cx,getprivate_failure,WHERE);
790
		return(JS_FALSE);
791
	}
792

793
	if(argc && argv[0]!=JSVAL_VOID)
794
		JS_ValueToInt32(cx,argv[0],&len);
795

796
	if((buf=(char*)alloca(len+1))==NULL) {
797
		JS_ReportError(cx,"Error allocating %u bytes",len+1);
798
799
		return(JS_FALSE);
	}
800
	rc=JS_SUSPENDREQUEST(cx);
801
	len = recv(p->sock,buf,len,MSG_PEEK);
802
	JS_RESUMEREQUEST(cx, rc);
803
804
	if(len<0) {
		p->last_error=ERROR_VALUE;	
805
806
		*rval = JSVAL_NULL;
		return(JS_TRUE);
807
	}
808
809
	buf[len]=0;

810
	str = JS_NewStringCopyN(cx, buf, len);
811
812
	if(str==NULL)
		return(JS_FALSE);
813

814
	*rval = STRING_TO_JSVAL(str);
815
	rc=JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
816
817
	dbprintf(FALSE, p, "received %u bytes, lasterror=%d"
		,len,ERROR_VALUE);
818
	JS_RESUMEREQUEST(cx, rc);
819
820
821
822
823
824
825
826
		
	return(JS_TRUE);
}

static JSBool
js_recvline(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	char		ch;
827
	char*		buf;
828
	int			i;
829
	int32		len=512;
830
831
	BOOL		rd;
	time_t		start;
832
	int32		timeout=30;	/* seconds */
833
	JSString*	str;
834
	private_t*	p;
deuce's avatar
deuce committed
835
	jsrefcount	rc;
836

837
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
838
		JS_ReportError(cx,getprivate_failure,WHERE);
839
		return(JS_FALSE);
840
	}
841

842
	if(argc && argv[0]!=JSVAL_VOID)
843
		JS_ValueToInt32(cx,argv[0],&len);
844

845
	if((buf=(char*)alloca(len+1))==NULL) {
846
		JS_ReportError(cx,"Error allocating %u bytes",len+1);
847
848
		return(JS_FALSE);
	}
849

850
	if(argc>1 && argv[1]!=JSVAL_VOID)
rswindell's avatar
rswindell committed
851
		JS_ValueToInt32(cx,argv[1],(int32*)&timeout);
852

853
	start=time(NULL);
854
	rc=JS_SUSPENDREQUEST(cx);
855
	for(i=0;i<len;) {
856

857
		if(!socket_check(p->sock,&rd,NULL,1000)) {
858
			p->last_error=ERROR_VALUE;
859
			break;		/* disconnected */
860
		}
861

862
		if(!rd) {
863
			if(time(NULL)-start>timeout) {
rswindell's avatar
rswindell committed
864
				dbprintf(FALSE, p, "recvline timeout (received: %d)",i);
865
				*rval = JSVAL_NULL;
866
				JS_RESUMEREQUEST(cx, rc);
867
868
				return(JS_TRUE);	/* time-out */
			}
869
			continue;	/* no data */
870
		}
871

872
873
		if(recv(p->sock, &ch, 1, 0)!=1) {
			p->last_error=ERROR_VALUE;
874
			break;
875
		}
876

877
		if(ch=='\n' /* && i>=1 */) /* Mar-9-2003: terminate on sole LF */
878
879
			break;