Skip to content
Snippets Groups Projects
js_socket.c 47.5 KiB
Newer Older
rswindell's avatar
rswindell committed
/* 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)		*
 *																			*
 * Copyright 2012 Rob Swindell - http://www.synchro.net/copyright.html		*
rswindell's avatar
rswindell committed
 *																			*
 * 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
#include <cryptlib.h>

rswindell's avatar
rswindell committed
#include "sbbs.h"
rswindell's avatar
rswindell committed

#ifdef JAVASCRIPT

deuce's avatar
deuce committed
int cryptInitialized=0;

typedef struct
{
	SOCKET	sock;
	BOOL	external;	/* externally created, don't close */
	BOOL	debug;
	BOOL	nonblocking;
deuce's avatar
deuce committed
	CRYPT_SESSION	session;
	char	*hostname;
static const char* getprivate_failure = "line %d %s JS_GetPrivate failed";
deuce's avatar
deuce committed
static void do_cryptEnd(void)
{
	cryptEnd();
}

static ptrdiff_t js_socket_recv(private_t *p, void *buf, size_t len, int flags, int timeout)
deuce's avatar
deuce committed
{
deuce's avatar
deuce committed
	int	copied;
	
	if(p->session==-1)
		return(recv(p->sock, buf, len, flags));
	if(p->nonblocking) {
		cryptSetAttribute(p->session, CRYPT_OPTION_NET_READTIMEOUT, 0);
	}
	else {
		cryptSetAttribute(p->session, CRYPT_OPTION_NET_READTIMEOUT, timeout);
	}
	do {
		if(cryptPopData(p->session, buf, len, &copied)==CRYPT_OK) {
			if(p->nonblocking)
				return copied;
			total += copied;
			if(total>=len)
				return total;
			len-=copied;
			buf=((uint8_t *)buf) + copied;
deuce's avatar
deuce committed
		}
		else
			return total;
	} while(len);
	return total;	// Shouldn't happen...
}

deuce's avatar
deuce committed
static void doCryptFlush(const CRYPT_CONTEXT session)
{
	int ret=cryptFlushData(session);

	if(ret!=CRYPT_OK)
		lprintf(LOG_ERR, "cryptFlushData() returned %d", ret);
}

static ptrdiff_t js_socket_sendsocket(private_t *p, const void *msg, size_t len, int flush)
deuce's avatar
deuce committed
{
deuce's avatar
deuce committed
	int copied=0;
	
	if(p->session==-1)
		return sendsocket(p->sock, msg, len);
	do {
		if(cryptPushData(p->session, msg, len, &copied)==CRYPT_OK) {
			if(p->nonblocking) {
deuce's avatar
deuce committed
				if(flush) doCryptFlush(p->session);
deuce's avatar
deuce committed
				return copied;
			}
			total += copied;
			if(total >= len) {
deuce's avatar
deuce committed
				if(flush) doCryptFlush(p->session);
deuce's avatar
deuce committed
				return total;
			}
			len -= copied;
			msg=((uint8_t *)msg) + copied;
deuce's avatar
deuce committed
		}
		else {
deuce's avatar
deuce committed
			if(flush) doCryptFlush(p->session);
deuce's avatar
deuce committed
			return total;
		}
	} while(len);
deuce's avatar
deuce committed
	if(flush) doCryptFlush(p->session);
deuce's avatar
deuce committed
	return total; // shouldn't happen...
}

static int js_socket_sendfilesocket(private_t *p, int file, off_t *offset, off_t count)
{
	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
			doCryptFlush(p->session);
deuce's avatar
deuce committed
			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
			doCryptFlush(p->session);
deuce's avatar
deuce committed
			return(wr);
		}
		if(i!=rd) {
deuce's avatar
deuce committed
			doCryptFlush(p->session);
deuce's avatar
deuce committed
			return(-1);
		}
		total+=rd;
	}

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

deuce's avatar
deuce committed
	doCryptFlush(p->session);
deuce's avatar
deuce committed
	return(total);
}

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

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

    va_start(argptr,fmt);
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
	lprintf(LOG_DEBUG,"%04d Socket %s%s",p->sock,error ? "ERROR: ":"",sbuf);
rswindell's avatar
rswindell committed
/* Socket Destructor */

static void js_finalize_socket(JSContext *cx, JSObject *obj)
{
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL)
deuce's avatar
deuce committed
	if(p->session != -1) {
		cryptDestroySession(p->session);
		p->session=-1;
	}
	if(p->external==FALSE && p->sock!=INVALID_SOCKET) {
rswindell's avatar
rswindell committed
		dbprintf(FALSE, p, "closed/deleted");
	}
deuce's avatar
deuce committed
	if(p->hostname)
		free(p->hostname);
rswindell's avatar
rswindell committed

rswindell's avatar
rswindell committed
}


/* Socket Object Methods */

js_close(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
deuce's avatar
deuce committed
	jsrefcount	rc;
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
		JS_ReportError(cx,getprivate_failure,WHERE);
deuce's avatar
deuce committed
	if(p->session != -1) {
		cryptDestroySession(p->session);
		p->session=-1;
	}
	p->sock = INVALID_SOCKET; 
	p->is_connected = FALSE;
static ushort js_port(JSContext* cx, jsval val, int type)
{
deuce's avatar
deuce committed
	jsrefcount		rc;
	if(JSVAL_IS_NUMBER(val)) {
		JS_ValueToInt32(cx,val,&i);
		return((ushort)i);
	}
	if(JSVAL_IS_STRING(val)) {
		str = JS_ValueToString(cx,val);
		JSSTRING_TO_STRING(cx, str, cp, NULL);
		if(isdigit(*cp))
			return((ushort)strtol(cp,NULL,0));
		serv = getservbyname(cp,type==SOCK_STREAM ? "tcp":"udp");
		if(serv!=NULL)
			return(htons(serv->s_port));
	}
	return(0);
}

SOCKET DLLCALL js_socket(JSContext *cx, jsval val)
{
	void*		vp;
	if(JSVAL_IS_OBJECT(val) && (cl=JS_GetClass(cx,JSVAL_TO_OBJECT(val)))!=NULL) {
		if(cl->flags&JSCLASS_HAS_PRIVATE)
			if((vp=JS_GetPrivate(cx,JSVAL_TO_OBJECT(val)))!=NULL)
				sock=*(SOCKET*)vp;
		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
static JSBool
js_bind(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
	SOCKADDR_IN	addr;
deuce's avatar
deuce committed
	jsrefcount	rc;
deuce's avatar
deuce committed
	char		*cstr;
rswindell's avatar
rswindell committed

	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
		JS_ReportError(cx,getprivate_failure,WHERE);
	memset(&addr,0,sizeof(addr));
	addr.sin_family = AF_INET;
rswindell's avatar
rswindell committed

	if(argc)
		port = js_port(cx,argv[0],p->type);
	addr.sin_port = htons(port);
deuce's avatar
deuce committed
	if(argc > 1)
		JSVALUE_TO_STRING(cx, argv[1], cstr, NULL);
deuce's avatar
deuce committed
	if(argc>1 && cstr != NULL
		&& (ip=inet_addr(cstr))!=INADDR_NONE)
		addr.sin_addr.s_addr = ip;
rswindell's avatar
rswindell committed

	if(bind(p->sock, (struct sockaddr *) &addr, sizeof(addr))!=0) {
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "bind failed with error %d",ERROR_VALUE);
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
		return(JS_TRUE);
rswindell's avatar
rswindell committed
	}

	dbprintf(FALSE, p, "bound to port %u",port);
	JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
	return(JS_TRUE);
rswindell's avatar
rswindell committed
}

js_listen(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
rswindell's avatar
rswindell committed
	int32		backlog=1;
deuce's avatar
deuce committed
	jsrefcount	rc;
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
		JS_ReportError(cx,getprivate_failure,WHERE);
		backlog = JS_ValueToInt32(cx,argv[0],&backlog);

	if(listen(p->sock, backlog)!=0) {
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "listen failed with error %d",ERROR_VALUE);
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
		return(JS_TRUE);
	}

	dbprintf(FALSE, p, "listening, backlog=%d",backlog);
	JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
js_accept(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	private_t*	p;
	private_t*	new_p;
	JSObject*	sockobj;
	SOCKET		new_socket;
deuce's avatar
deuce committed
	jsrefcount	rc;
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
		JS_ReportError(cx,getprivate_failure,WHERE);
	if((new_socket=accept_socket(p->sock,(struct sockaddr *)&(p->remote_addr),&addrlen))==INVALID_SOCKET) {
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "accept failed with error %d",ERROR_VALUE);
		return(JS_TRUE);
	}

	if((sockobj=js_CreateSocketObject(cx, obj, "new_socket", new_socket))==NULL) {
		closesocket(new_socket);
		JS_ReportError(cx,"Error creating new socket object");
	if((new_p=(private_t*)JS_GetPrivate(cx,sockobj))==NULL) {
		JS_ReportError(cx,getprivate_failure,WHERE);

	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");
	JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(sockobj));
rswindell's avatar
rswindell committed
static JSBool
js_connect(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
rswindell's avatar
rswindell committed
	ulong		ip_addr;
	ushort		port;
	JSString*	str;
	fd_set		socket_set;
	struct		timeval tv = {0, 0};
deuce's avatar
deuce committed
	jsrefcount	rc;
	char*		cstr;
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
		JS_ReportError(cx,getprivate_failure,WHERE);
		return(JS_FALSE);
rswindell's avatar
rswindell committed

	str = JS_ValueToString(cx, argv[0]);
	JSSTRING_TO_STRING(cx, str, cstr, NULL);
deuce's avatar
deuce committed
	if(p->hostname)
		free(p->hostname);
	p->hostname=strdup(cstr);
deuce's avatar
deuce committed
	dbprintf(FALSE, p, "resolving hostname: %s", cstr);
deuce's avatar
deuce committed
	if((ip_addr=resolve_ip(cstr))==INADDR_NONE) {
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "resolve_ip failed with error %d",ERROR_VALUE);
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
		return(JS_TRUE);
rswindell's avatar
rswindell committed
	}

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

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

	if(argc>2)	/* time-out value specified */
deuce's avatar
deuce committed
	dbprintf(FALSE, p, "connecting to port %u at %s", port, cstr);
	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

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

	result=connect(p->sock, (struct sockaddr *)&(p->remote_addr), sizeof(p->remote_addr));
	if(result==SOCKET_ERROR
		&& (ERROR_VALUE==EWOULDBLOCK || ERROR_VALUE==EINPROGRESS)) {
		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) {
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "connect failed with error %d",ERROR_VALUE);
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
		return(JS_TRUE);
rswindell's avatar
rswindell committed
	}

	JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
deuce's avatar
deuce committed
	dbprintf(FALSE, p, "connected to port %u at %s", port, cstr);
rswindell's avatar
rswindell committed
	return(JS_TRUE);
}

static JSBool
js_send(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
rswindell's avatar
rswindell committed
	int			len;
	JSString*	str;
deuce's avatar
deuce committed
	jsrefcount	rc;
rswindell's avatar
rswindell committed

	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
		JS_ReportError(cx,getprivate_failure,WHERE);
rswindell's avatar
rswindell committed

	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
rswindell's avatar
rswindell committed

	str = JS_ValueToString(cx, argv[0]);
	JSSTRING_TO_STRING(cx, str, cp, NULL);
rswindell's avatar
rswindell committed

deuce's avatar
deuce committed
	if(js_socket_sendsocket(p,cp,len,TRUE)==len) {
		dbprintf(FALSE, p, "sent %u bytes",len);
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
	} else {
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "send of %u bytes failed",len);
	}
rswindell's avatar
rswindell committed
		
	return(JS_TRUE);
}

js_sendto(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
	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
	jsrefcount	rc;
	char*		cstr;
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
		JS_ReportError(cx,getprivate_failure,WHERE);
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);

	/* data */
	data_str = JS_ValueToString(cx, argv[0]);
	JSSTRING_TO_STRING(cx, data_str, cp, NULL);
	len = JS_GetStringLength(data_str);

	/* address */
	ip_str = JS_ValueToString(cx, argv[1]);
	JSSTRING_TO_STRING(cx, ip_str, cstr, NULL);
deuce's avatar
deuce committed
	if(p->hostname)
		free(p->hostname);
	p->hostname=strdup(cstr);
deuce's avatar
deuce committed
	dbprintf(FALSE, p, "resolving hostname: %s", cstr);
deuce's avatar
deuce committed
	if((ip_addr=resolve_ip(cstr))==INADDR_NONE) {
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "resolve_ip failed with error %d",ERROR_VALUE);
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);

	dbprintf(FALSE, p, "sending %d bytes to port %u at %s"
deuce's avatar
deuce committed
		,len, port, cstr);

	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);
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
	} else {
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "send of %u bytes failed",len);
	}
static JSBool
js_sendfile(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
	char*		fname;
	long		len;
	int			file;
	private_t*	p;
deuce's avatar
deuce committed
	jsrefcount	rc;
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
		JS_ReportError(cx,getprivate_failure,WHERE);
		return(JS_FALSE);
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	JSVALUE_TO_STRING(cx, argv[0], fname, NULL);
deuce's avatar
deuce committed
	if(fname==NULL) {
		JS_ReportError(cx,"Failure reading filename");
		return(JS_FALSE);
deuce's avatar
deuce committed
	if((file=nopen(fname,O_RDONLY|O_BINARY))==-1) {
		return(JS_TRUE);
deuce's avatar
deuce committed
	len = js_socket_sendfilesocket(p, file, NULL, 0);
	if(len > 0) {
		dbprintf(FALSE, p, "sent %u bytes",len);
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
	} else {
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "send of %s failed",fname);
	}
	return(JS_TRUE);
}

js_sendbin(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
	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
	jsrefcount	rc;
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);

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

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

	switch(size) {
		case sizeof(BYTE):
			b = (BYTE)val;
deuce's avatar
deuce committed
			wr=js_socket_sendsocket(p,&b,size,TRUE);
			break;
		case sizeof(WORD):
			w = (WORD)val;
			if(p->network_byte_order)
				w=htons(w);
deuce's avatar
deuce committed
			wr=js_socket_sendsocket(p,(BYTE*)&w,size,TRUE);
			break;
		case sizeof(DWORD):
			l = val;
			if(p->network_byte_order)
				l=htonl(l);
deuce's avatar
deuce committed
			wr=js_socket_sendsocket(p,(BYTE*)&l,size,TRUE);
			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);
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
	} else {
		p->last_error=ERROR_VALUE;
		dbprintf(TRUE, p, "send of %u bytes (binary) failed",size);
	}
		
rswindell's avatar
rswindell committed
static JSBool
js_recv(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
rswindell's avatar
rswindell committed
	JSString*	str;
deuce's avatar
deuce committed
	jsrefcount	rc;
rswindell's avatar
rswindell committed

	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
		JS_ReportError(cx,getprivate_failure,WHERE);
rswindell's avatar
rswindell committed

		JS_ValueToInt32(cx,argv[0],&len);
rswindell's avatar
rswindell committed

	if((buf=(char*)malloc(len+1))==NULL) {
		JS_ReportError(cx,"Error allocating %u bytes",len+1);
rswindell's avatar
rswindell committed

deuce's avatar
deuce committed
	len = js_socket_recv(p,buf,len,0,120);
		JS_SET_RVAL(cx, arglist, JSVAL_NULL);
rswindell's avatar
rswindell committed
	buf[len]=0;

	str = JS_NewStringCopyN(cx, buf, len);
		return(JS_FALSE);
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
	dbprintf(FALSE, p, "received %u bytes",len);
rswindell's avatar
rswindell committed
	return(JS_TRUE);
}

js_recvfrom(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
	char*		buf;
	char		ip_addr[64];
	char		port[32];
	uintN		n;
	BOOL		binary=FALSE;
	BYTE		b;
	WORD		w;
	DWORD		l;
	jsval		data_val=JSVAL_NULL;
	JSString*	str;
	JSObject*	retobj;
	SOCKADDR_IN	addr;
	socklen_t	addrlen;
deuce's avatar
deuce committed
	jsrefcount	rc;
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
		JS_ReportError(cx,getprivate_failure,WHERE);
	JS_SET_RVAL(cx, arglist, JSVAL_NULL);
	for(n=0;n<argc;n++) {
		if(JSVAL_IS_BOOLEAN(argv[n])) {
			binary=JSVAL_TO_BOOLEAN(argv[n]);
			if(binary)
				len=sizeof(DWORD);
			JS_ValueToInt32(cx,argv[n],&len);

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

		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;
rswindell's avatar
rswindell committed
			default:
			case sizeof(DWORD):
				if((rd=recvfrom(p->sock,(BYTE*)&l,len,0,(SOCKADDR*)&addr,&addrlen))==len) {
					if(p->network_byte_order)
						l=ntohl(l);
					data_val=UINT_TO_JSVAL(l);
		if(rd!=len) {
			p->last_error=ERROR_VALUE;
			return(JS_TRUE);
		}

	} else {		/* String Data */

		if((buf=(char*)malloc(len+1))==NULL) {
			JS_ReportError(cx,"Error allocating %u bytes",len+1);
			return(JS_FALSE);
		}

		len = recvfrom(p->sock,buf,len,0,(SOCKADDR*)&addr,&addrlen);
		if(len<0) {
			p->last_error=ERROR_VALUE;
		str = JS_NewStringCopyN(cx, buf, len);
		if(str==NULL)
			return(JS_FALSE);

		data_val = STRING_TO_JSVAL(str);
	}
	if((retobj=JS_NewObject(cx,NULL,NULL,obj))==NULL) {
		JS_ReportError(cx,"JS_NewObject failed");
		return(JS_FALSE);
	JS_DefineProperty(cx, retobj, "data"
		,NULL,NULL,JSPROP_ENUMERATE);

	sprintf(port,"%u",ntohs(addr.sin_port));
	if((str=JS_NewStringCopyZ(cx,port))==NULL)
		return(JS_FALSE);
	JS_DefineProperty(cx, retobj, "port"
		,STRING_TO_JSVAL(str)
		,NULL,NULL,JSPROP_ENUMERATE);

	SAFECOPY(ip_addr,inet_ntoa(addr.sin_addr));
	if((str=JS_NewStringCopyZ(cx,ip_addr))==NULL)
		return(JS_FALSE);
	JS_DefineProperty(cx, retobj, "ip_address"
		,STRING_TO_JSVAL(str)
		,NULL,NULL,JSPROP_ENUMERATE);

	JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(retobj));
	dbprintf(FALSE, p, "received %u bytes from %s:%s",len,ip_addr,port);
js_peek(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
deuce's avatar
deuce committed
	jsrefcount	rc;
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
		JS_ReportError(cx,getprivate_failure,WHERE);
		JS_ValueToInt32(cx,argv[0],&len);
	if((buf=(char*)malloc(len+1))==NULL) {
		JS_ReportError(cx,"Error allocating %u bytes",len+1);
deuce's avatar
deuce committed
	if(p->session==-1)
		len = js_socket_recv(p,buf,len,MSG_PEEK,120);
	else
		len=0;
		JS_SET_RVAL(cx, arglist, JSVAL_NULL);
	str = JS_NewStringCopyN(cx, buf, len);
	if(str==NULL)
		return(JS_FALSE);
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
rswindell's avatar
rswindell committed
	dbprintf(FALSE, p, "received %u bytes, lasterror=%d"
		,len,ERROR_VALUE);