|
|
1.1 root 1: /* js_socket.c */
2:
3: /* Synchronet JavaScript "Socket" Object */
4:
1.1.1.2 ! root 5: /* $Id: js_socket.c,v 1.137 2011/09/09 19:41:54 deuce Exp $ */
1.1 root 6:
7: /****************************************************************************
8: * @format.tab-size 4 (Plain Text/Source Code File Header) *
9: * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
10: * *
1.1.1.2 ! root 11: * Copyright 2011 Rob Swindell - http://www.synchro.net/copyright.html *
1.1 root 12: * *
13: * This program is free software; you can redistribute it and/or *
14: * modify it under the terms of the GNU General Public License *
15: * as published by the Free Software Foundation; either version 2 *
16: * of the License, or (at your option) any later version. *
17: * See the GNU General Public License for more details: gpl.txt or *
18: * http://www.fsf.org/copyleft/gpl.html *
19: * *
20: * Anonymous FTP access to the most recent released source is available at *
21: * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
22: * *
23: * Anonymous CVS access to the development source and modification history *
24: * is available at cvs.synchro.net:/cvsroot/sbbs, example: *
25: * cvs -d :pserver:[email protected]:/cvsroot/sbbs login *
26: * (just hit return, no password is necessary) *
27: * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src *
28: * *
29: * For Synchronet coding style and modification guidelines, see *
30: * http://www.synchro.net/source.html *
31: * *
32: * You are encouraged to submit any modifications (preferably in Unix diff *
33: * format) via e-mail to [email protected] *
34: * *
35: * Note: If this box doesn't appear square, then you need to fix your tabs. *
36: ****************************************************************************/
37:
38: #include "sbbs.h"
1.1.1.2 ! root 39: #include "js_request.h"
1.1 root 40:
41: #ifdef JAVASCRIPT
42:
43: typedef struct
44: {
45: SOCKET sock;
46: BOOL external; /* externally created, don't close */
47: BOOL debug;
48: BOOL nonblocking;
49: BOOL is_connected;
50: BOOL network_byte_order;
51: int last_error;
52: int type;
1.1.1.2 ! root 53: SOCKADDR_IN remote_addr;
1.1 root 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: va_list argptr;
62: char sbuf[1024];
63:
64: if(p==NULL || (!p->debug /*&& !error */))
65: return;
66:
67: va_start(argptr,fmt);
68: vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
69: sbuf[sizeof(sbuf)-1]=0;
70: va_end(argptr);
71:
72: lprintf(LOG_DEBUG,"%04d Socket %s%s",p->sock,error ? "ERROR: ":"",sbuf);
73: }
74:
75: /* Socket Destructor */
76:
77: static void js_finalize_socket(JSContext *cx, JSObject *obj)
78: {
79: private_t* p;
80:
81: if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL)
82: return;
83:
84: if(p->external==FALSE && p->sock!=INVALID_SOCKET) {
85: close_socket(p->sock);
86: dbprintf(FALSE, p, "closed/deleted");
87: }
88:
89: free(p);
90:
91: JS_SetPrivate(cx, obj, NULL);
92: }
93:
94:
95: /* Socket Object Methods */
96:
97: static JSBool
98: js_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
99: {
100: private_t* p;
1.1.1.2 ! root 101: jsrefcount rc;
1.1 root 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: return(JS_TRUE);
110:
1.1.1.2 ! root 111: rc=JS_SUSPENDREQUEST(cx);
1.1 root 112: close_socket(p->sock);
113:
114: p->last_error = ERROR_VALUE;
115:
116: dbprintf(FALSE, p, "closed");
117:
118: p->sock = INVALID_SOCKET;
119: p->is_connected = FALSE;
1.1.1.2 ! root 120: JS_RESUMEREQUEST(cx, rc);
1.1 root 121:
122: return(JS_TRUE);
123: }
124:
125: static ushort js_port(JSContext* cx, jsval val, int type)
126: {
127: char* cp;
128: JSString* str;
129: int32 i=0;
130: struct servent* serv;
1.1.1.2 ! root 131: jsrefcount rc;
1.1 root 132:
133: if(JSVAL_IS_NUMBER(val)) {
134: JS_ValueToInt32(cx,val,&i);
135: return((ushort)i);
136: }
137: if(JSVAL_IS_STRING(val)) {
138: str = JS_ValueToString(cx,val);
139: cp = JS_GetStringBytes(str);
140: if(isdigit(*cp))
141: return((ushort)strtol(cp,NULL,0));
1.1.1.2 ! root 142: rc=JS_SUSPENDREQUEST(cx);
1.1 root 143: serv = getservbyname(cp,type==SOCK_STREAM ? "tcp":"udp");
1.1.1.2 ! root 144: JS_RESUMEREQUEST(cx, rc);
1.1 root 145: if(serv!=NULL)
146: return(htons(serv->s_port));
147: }
148: return(0);
149: }
150:
151: SOCKET DLLCALL js_socket(JSContext *cx, jsval val)
152: {
153: void* vp;
154: JSClass* cl;
155: SOCKET sock=INVALID_SOCKET;
156:
157: if(JSVAL_IS_OBJECT(val) && (cl=JS_GetClass(cx,JSVAL_TO_OBJECT(val)))!=NULL) {
158: if(cl->flags&JSCLASS_HAS_PRIVATE)
159: if((vp=JS_GetPrivate(cx,JSVAL_TO_OBJECT(val)))!=NULL)
160: sock=*(SOCKET*)vp;
161: } else if(val!=JSVAL_VOID)
162: JS_ValueToInt32(cx,val,(int32*)&sock);
163:
164: return(sock);
165: }
166:
167: void DLLCALL js_timeval(JSContext* cx, jsval val, struct timeval* tv)
168: {
169: jsdouble jsd;
170:
171: if(JSVAL_IS_INT(val))
172: tv->tv_sec = JSVAL_TO_INT(val);
173: else if(JSVAL_IS_DOUBLE(val)) {
174: JS_ValueToNumber(cx,val,&jsd);
175: tv->tv_sec = (int)jsd;
176: tv->tv_usec = (int)(jsd*1000000.0)%1000000;
177: }
178: }
179:
180: static JSBool
181: js_bind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
182: {
183: ulong ip=0;
184: private_t* p;
185: ushort port=0;
1.1.1.2 ! root 186: SOCKADDR_IN addr;
! 187: jsrefcount rc;
1.1 root 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: memset(&addr,0,sizeof(addr));
195: addr.sin_family = AF_INET;
196:
197: if(argc)
198: port = js_port(cx,argv[0],p->type);
199: addr.sin_port = htons(port);
200: if(argc>1
201: && (ip=inet_addr(JS_GetStringBytes(JS_ValueToString(cx,argv[1]))))!=INADDR_NONE)
202: addr.sin_addr.s_addr = ip;
203:
1.1.1.2 ! root 204: rc=JS_SUSPENDREQUEST(cx);
1.1 root 205: if(bind(p->sock, (struct sockaddr *) &addr, sizeof(addr))!=0) {
206: p->last_error=ERROR_VALUE;
207: dbprintf(TRUE, p, "bind failed with error %d",ERROR_VALUE);
208: *rval = JSVAL_FALSE;
1.1.1.2 ! root 209: JS_RESUMEREQUEST(cx, rc);
1.1 root 210: return(JS_TRUE);
211: }
212:
213: dbprintf(FALSE, p, "bound to port %u",port);
214: *rval = JSVAL_TRUE;
1.1.1.2 ! root 215: JS_RESUMEREQUEST(cx, rc);
1.1 root 216: return(JS_TRUE);
217: }
218:
219: static JSBool
220: js_listen(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
221: {
222: private_t* p;
223: int32 backlog=1;
1.1.1.2 ! root 224: jsrefcount rc;
1.1 root 225:
226: if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
227: JS_ReportError(cx,getprivate_failure,WHERE);
228: return(JS_FALSE);
229: }
230:
1.1.1.2 ! root 231: if(argc && argv[0]!=JSVAL_VOID)
1.1 root 232: backlog = JS_ValueToInt32(cx,argv[0],&backlog);
233:
1.1.1.2 ! root 234: rc=JS_SUSPENDREQUEST(cx);
1.1 root 235: if(listen(p->sock, backlog)!=0) {
236: p->last_error=ERROR_VALUE;
237: dbprintf(TRUE, p, "listen failed with error %d",ERROR_VALUE);
238: *rval = JSVAL_FALSE;
1.1.1.2 ! root 239: JS_RESUMEREQUEST(cx, rc);
1.1 root 240: return(JS_TRUE);
241: }
242:
243: dbprintf(FALSE, p, "listening, backlog=%d",backlog);
244: *rval = JSVAL_TRUE;
1.1.1.2 ! root 245: JS_RESUMEREQUEST(cx, rc);
1.1 root 246: return(JS_TRUE);
247: }
248:
249: static JSBool
250: js_accept(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
251: {
252: private_t* p;
253: private_t* new_p;
254: JSObject* sockobj;
255: SOCKET new_socket;
1.1.1.2 ! root 256: socklen_t addrlen;
! 257: jsrefcount rc;
1.1 root 258:
259: if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
260: JS_ReportError(cx,getprivate_failure,WHERE);
261: return(JS_FALSE);
262: }
263:
1.1.1.2 ! root 264: addrlen=sizeof(p->remote_addr);
! 265:
! 266: rc=JS_SUSPENDREQUEST(cx);
! 267: if((new_socket=accept_socket(p->sock,(struct sockaddr *)&(p->remote_addr),&addrlen))==INVALID_SOCKET) {
1.1 root 268: p->last_error=ERROR_VALUE;
269: dbprintf(TRUE, p, "accept failed with error %d",ERROR_VALUE);
1.1.1.2 ! root 270: JS_RESUMEREQUEST(cx, rc);
1.1 root 271: return(JS_TRUE);
272: }
273:
274: if((sockobj=js_CreateSocketObject(cx, obj, "new_socket", new_socket))==NULL) {
275: closesocket(new_socket);
1.1.1.2 ! root 276: JS_RESUMEREQUEST(cx, rc);
1.1 root 277: JS_ReportError(cx,"Error creating new socket object");
278: return(JS_TRUE);
279: }
280: if((new_p=(private_t*)JS_GetPrivate(cx,sockobj))==NULL) {
1.1.1.2 ! root 281: JS_RESUMEREQUEST(cx, rc);
1.1 root 282: JS_ReportError(cx,getprivate_failure,WHERE);
283: return(JS_FALSE);
284: }
285:
286: new_p->type=p->type;
287: new_p->debug=p->debug;
288: new_p->nonblocking=p->nonblocking;
289: new_p->external=FALSE; /* let destructor close socket */
290: new_p->is_connected=TRUE;
291:
292: dbprintf(FALSE, p, "accepted connection");
293: *rval = OBJECT_TO_JSVAL(sockobj);
1.1.1.2 ! root 294: JS_RESUMEREQUEST(cx, rc);
1.1 root 295: return(JS_TRUE);
296: }
297:
298: static JSBool
299: js_connect(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
300: {
301: int result;
302: ulong val;
303: ulong ip_addr;
304: ushort port;
305: JSString* str;
306: private_t* p;
307: fd_set socket_set;
308: struct timeval tv = {0, 0};
1.1.1.2 ! root 309: jsrefcount rc;
! 310: char* cstr;
1.1 root 311:
312: if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
313: JS_ReportError(cx,getprivate_failure,WHERE);
314: return(JS_FALSE);
315: }
316:
317: str = JS_ValueToString(cx, argv[0]);
1.1.1.2 ! root 318: cstr = JS_GetStringBytes(str);
! 319: rc=JS_SUSPENDREQUEST(cx);
! 320: dbprintf(FALSE, p, "resolving hostname: %s", cstr);
1.1 root 321: if((ip_addr=resolve_ip(JS_GetStringBytes(str)))==INADDR_NONE) {
322: p->last_error=ERROR_VALUE;
323: dbprintf(TRUE, p, "resolve_ip failed with error %d",ERROR_VALUE);
324: *rval = JSVAL_FALSE;
1.1.1.2 ! root 325: JS_RESUMEREQUEST(cx, rc);
1.1 root 326: return(JS_TRUE);
327: }
328:
1.1.1.2 ! root 329: JS_RESUMEREQUEST(cx, rc);
1.1 root 330: port = js_port(cx,argv[1],p->type);
331:
332: tv.tv_sec = 10; /* default time-out */
333:
334: if(argc>2) /* time-out value specified */
335: js_timeval(cx,argv[2],&tv);
336:
1.1.1.2 ! root 337: rc=JS_SUSPENDREQUEST(cx);
1.1 root 338: dbprintf(FALSE, p, "connecting to port %u at %s", port, JS_GetStringBytes(str));
339:
1.1.1.2 ! root 340: memset(&(p->remote_addr),0,sizeof(p->remote_addr));
! 341: p->remote_addr.sin_addr.s_addr = ip_addr;
! 342: p->remote_addr.sin_family = AF_INET;
! 343: p->remote_addr.sin_port = htons(port);
1.1 root 344:
345: /* always set to nonblocking here */
346: val=1;
347: ioctlsocket(p->sock,FIONBIO,&val);
348:
1.1.1.2 ! root 349: result=connect(p->sock, (struct sockaddr *)&(p->remote_addr), sizeof(p->remote_addr));
! 350:
1.1 root 351: if(result==SOCKET_ERROR
352: && (ERROR_VALUE==EWOULDBLOCK || ERROR_VALUE==EINPROGRESS)) {
353: FD_ZERO(&socket_set);
354: FD_SET(p->sock,&socket_set);
355: if(select(p->sock+1,NULL,&socket_set,NULL,&tv)==1)
356: result=0; /* success */
357: }
358:
359: /* Restore original setting here */
360: ioctlsocket(p->sock,FIONBIO,(ulong*)&(p->nonblocking));
361:
362: if(result!=0) {
363: p->last_error=ERROR_VALUE;
364: dbprintf(TRUE, p, "connect failed with error %d",ERROR_VALUE);
365: *rval = JSVAL_FALSE;
1.1.1.2 ! root 366: JS_RESUMEREQUEST(cx, rc);
1.1 root 367: return(JS_TRUE);
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));
1.1.1.2 ! root 373: JS_RESUMEREQUEST(cx, rc);
1.1 root 374:
375: return(JS_TRUE);
376: }
377:
378: static JSBool
379: js_send(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
380: {
381: char* cp;
382: int len;
383: JSString* str;
384: private_t* p;
1.1.1.2 ! root 385: jsrefcount rc;
1.1 root 386:
387: if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
388: JS_ReportError(cx,getprivate_failure,WHERE);
389: return(JS_FALSE);
390: }
391:
392: *rval = JSVAL_FALSE;
393:
394: str = JS_ValueToString(cx, argv[0]);
395: cp = JS_GetStringBytes(str);
396: len = JS_GetStringLength(str);
397:
1.1.1.2 ! root 398: rc=JS_SUSPENDREQUEST(cx);
1.1 root 399: if(sendsocket(p->sock,cp,len)==len) {
400: dbprintf(FALSE, p, "sent %u bytes",len);
401: *rval = JSVAL_TRUE;
402: } else {
403: p->last_error=ERROR_VALUE;
404: dbprintf(TRUE, p, "send of %u bytes failed",len);
405: }
1.1.1.2 ! root 406: JS_RESUMEREQUEST(cx, rc);
1.1 root 407:
408: return(JS_TRUE);
409: }
410:
411: static JSBool
412: js_sendto(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
413: {
414: char* cp;
415: int len;
416: ulong ip_addr;
417: ushort port;
418: JSString* data_str;
419: JSString* ip_str;
420: private_t* p;
421: SOCKADDR_IN addr;
1.1.1.2 ! root 422: jsrefcount rc;
! 423: char* cstr;
1.1 root 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: /* data */
433: data_str = JS_ValueToString(cx, argv[0]);
434: cp = JS_GetStringBytes(data_str);
435: len = JS_GetStringLength(data_str);
436:
437: /* address */
438: ip_str = JS_ValueToString(cx, argv[1]);
1.1.1.2 ! root 439: cstr = JS_GetStringBytes(ip_str);
! 440: rc=JS_SUSPENDREQUEST(cx);
1.1 root 441: dbprintf(FALSE, p, "resolving hostname: %s", JS_GetStringBytes(ip_str));
1.1.1.2 ! root 442: if((ip_addr=resolve_ip(cstr))==INADDR_NONE) {
1.1 root 443: p->last_error=ERROR_VALUE;
444: dbprintf(TRUE, p, "resolve_ip failed with error %d",ERROR_VALUE);
445: *rval = JSVAL_FALSE;
1.1.1.2 ! root 446: JS_RESUMEREQUEST(cx, rc);
1.1 root 447: return(JS_TRUE);
448: }
449:
450: /* port */
1.1.1.2 ! root 451: JS_RESUMEREQUEST(cx, rc);
1.1 root 452: port = js_port(cx,argv[2],p->type);
1.1.1.2 ! root 453: rc=JS_SUSPENDREQUEST(cx);
1.1 root 454:
455: dbprintf(FALSE, p, "sending %d bytes to port %u at %s"
456: ,len, port, JS_GetStringBytes(ip_str));
457:
458: memset(&addr,0,sizeof(addr));
459: addr.sin_addr.s_addr = ip_addr;
460: addr.sin_family = AF_INET;
461: addr.sin_port = htons(port);
462:
463: if(sendto(p->sock,cp,len,0 /* flags */,(SOCKADDR*)&addr,sizeof(addr))==len) {
464: dbprintf(FALSE, p, "sent %u bytes",len);
465: *rval = JSVAL_TRUE;
466: } else {
467: p->last_error=ERROR_VALUE;
468: dbprintf(TRUE, p, "send of %u bytes failed",len);
469: }
1.1.1.2 ! root 470: JS_RESUMEREQUEST(cx, rc);
1.1 root 471:
472: return(JS_TRUE);
473: }
474:
475:
476: static JSBool
477: js_sendfile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
478: {
479: char* fname;
480: long len;
481: int file;
482: JSString* str;
483: private_t* p;
1.1.1.2 ! root 484: jsrefcount rc;
1.1 root 485:
486: if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
487: JS_ReportError(cx,getprivate_failure,WHERE);
488: return(JS_FALSE);
489: }
490:
491: *rval = JSVAL_FALSE;
492:
493: if((str = JS_ValueToString(cx, argv[0]))==NULL
494: || (fname=JS_GetStringBytes(str))==NULL) {
495: JS_ReportError(cx,"Failure reading filename");
496: return(JS_FALSE);
497: }
498:
1.1.1.2 ! root 499: rc=JS_SUSPENDREQUEST(cx);
! 500: if((file=nopen(fname,O_RDONLY|O_BINARY))==-1) {
! 501: JS_RESUMEREQUEST(cx, rc);
1.1 root 502: return(JS_TRUE);
1.1.1.2 ! root 503: }
1.1 root 504:
505: #if 0
506: len=filelength(file);
507: /* TODO: Probobly too big for alloca()... also, this is insane. */
508: if((buf=malloc(len))==NULL) {
509: close(file);
510: return(JS_TRUE);
511: }
512: if(read(file,buf,len)!=len) {
513: free(buf);
514: close(file);
515: return(JS_TRUE);
516: }
517: close(file);
518:
519: if(sendsocket(p->sock,buf,len)==len) {
520: dbprintf(FALSE, p, "sent %u bytes",len);
521: *rval = JSVAL_TRUE;
522: } else {
523: p->last_error=ERROR_VALUE;
524: dbprintf(TRUE, p, "send of %u bytes failed",len);
525: }
526: free(buf);
527: #else
528: len = sendfilesocket(p->sock, file, NULL, 0);
1.1.1.2 ! root 529: close(file);
1.1 root 530: if(len > 0) {
531: dbprintf(FALSE, p, "sent %u bytes",len);
532: *rval = JSVAL_TRUE;
533: } else {
534: p->last_error=ERROR_VALUE;
535: dbprintf(TRUE, p, "send of %s failed",fname);
536: }
537: #endif
538:
1.1.1.2 ! root 539: JS_RESUMEREQUEST(cx, rc);
1.1 root 540: return(JS_TRUE);
541: }
542:
543: static JSBool
544: js_sendbin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
545: {
546: BYTE b;
547: WORD w;
548: DWORD l;
549: int32 val=0;
550: size_t wr=0;
551: size_t size=sizeof(DWORD);
552: private_t* p;
1.1.1.2 ! root 553: jsrefcount rc;
1.1 root 554:
555: *rval = JSVAL_FALSE;
556:
557: if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
558: JS_ReportError(cx,getprivate_failure,WHERE);
559: return(JS_FALSE);
560: }
561:
1.1.1.2 ! root 562: if(argv[0]!=JSVAL_VOID)
! 563: JS_ValueToInt32(cx,argv[0],&val);
! 564: if(argc>1 && argv[1]!=JSVAL_VOID)
1.1 root 565: JS_ValueToInt32(cx,argv[1],(int32*)&size);
566:
1.1.1.2 ! root 567: rc=JS_SUSPENDREQUEST(cx);
1.1 root 568: switch(size) {
569: case sizeof(BYTE):
570: b = (BYTE)val;
571: wr=sendsocket(p->sock,&b,size);
572: break;
573: case sizeof(WORD):
574: w = (WORD)val;
575: if(p->network_byte_order)
576: w=htons(w);
577: wr=sendsocket(p->sock,(BYTE*)&w,size);
578: break;
579: case sizeof(DWORD):
580: l = val;
581: if(p->network_byte_order)
582: l=htonl(l);
583: wr=sendsocket(p->sock,(BYTE*)&l,size);
584: break;
585: default:
586: /* unknown size */
587: dbprintf(TRUE, p, "unsupported binary write size: %d",size);
588: break;
589: }
590: if(wr==size) {
591: dbprintf(FALSE, p, "sent %u bytes (binary)",size);
592: *rval = JSVAL_TRUE;
593: } else {
594: p->last_error=ERROR_VALUE;
595: dbprintf(TRUE, p, "send of %u bytes (binary) failed",size);
596: }
597:
1.1.1.2 ! root 598: JS_RESUMEREQUEST(cx, rc);
1.1 root 599: return(JS_TRUE);
600: }
601:
602:
603: static JSBool
604: js_recv(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
605: {
606: char* buf;
607: int32 len=512;
608: JSString* str;
1.1.1.2 ! root 609: jsrefcount rc;
1.1 root 610:
611: private_t* p;
612:
613: if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
614: JS_ReportError(cx,getprivate_failure,WHERE);
615: return(JS_FALSE);
616: }
617:
1.1.1.2 ! root 618: if(argc && argv[0]!=JSVAL_VOID)
1.1 root 619: JS_ValueToInt32(cx,argv[0],&len);
620:
621: if((buf=(char*)alloca(len+1))==NULL) {
622: JS_ReportError(cx,"Error allocating %u bytes",len+1);
623: return(JS_FALSE);
624: }
625:
1.1.1.2 ! root 626: rc=JS_SUSPENDREQUEST(cx);
1.1 root 627: len = recv(p->sock,buf,len,0);
1.1.1.2 ! root 628: JS_RESUMEREQUEST(cx, rc);
1.1 root 629: if(len<0) {
630: p->last_error=ERROR_VALUE;
631: *rval = JSVAL_NULL;
632: return(JS_TRUE);
633: }
634: buf[len]=0;
635:
636: str = JS_NewStringCopyN(cx, buf, len);
637: if(str==NULL)
638: return(JS_FALSE);
639:
640: *rval = STRING_TO_JSVAL(str);
1.1.1.2 ! root 641: rc=JS_SUSPENDREQUEST(cx);
1.1 root 642: dbprintf(FALSE, p, "received %u bytes",len);
1.1.1.2 ! root 643: JS_RESUMEREQUEST(cx, rc);
1.1 root 644:
645: return(JS_TRUE);
646: }
647:
648: static JSBool
649: js_recvfrom(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
650: {
651: char* buf;
652: char ip_addr[64];
653: char port[32];
654: int rd=0;
655: int32 len=512;
656: uintN n;
657: BOOL binary=FALSE;
658: BYTE b;
659: WORD w;
660: DWORD l;
661: jsval data_val=JSVAL_NULL;
662: JSString* str;
663: JSObject* retobj;
664: SOCKADDR_IN addr;
665: socklen_t addrlen;
1.1.1.2 ! root 666: jsrefcount rc;
1.1 root 667:
668: private_t* p;
669:
670: if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
671: JS_ReportError(cx,getprivate_failure,WHERE);
672: return(JS_FALSE);
673: }
674:
675: *rval = JSVAL_NULL;
676:
677: for(n=0;n<argc;n++) {
678: if(JSVAL_IS_BOOLEAN(argv[n])) {
679: binary=JSVAL_TO_BOOLEAN(argv[n]);
680: if(binary)
681: len=sizeof(DWORD);
1.1.1.2 ! root 682: } else if(argv[n]!=JSVAL_VOID)
1.1 root 683: JS_ValueToInt32(cx,argv[n],&len);
684: }
685:
686: addrlen=sizeof(addr);
687:
688: if(binary) { /* Binary/Integer Data */
689:
1.1.1.2 ! root 690: rc=JS_SUSPENDREQUEST(cx);
1.1 root 691: switch(len) {
692: case sizeof(BYTE):
693: if((rd=recvfrom(p->sock,&b,len,0,(SOCKADDR*)&addr,&addrlen))==len)
694: data_val = INT_TO_JSVAL(b);
695: break;
696: case sizeof(WORD):
697: if((rd=recvfrom(p->sock,(BYTE*)&w,len,0,(SOCKADDR*)&addr,&addrlen))==len) {
698: if(p->network_byte_order)
699: w=ntohs(w);
700: data_val = INT_TO_JSVAL(w);
701: }
702: break;
1.1.1.2 ! root 703: default:
1.1 root 704: case sizeof(DWORD):
705: if((rd=recvfrom(p->sock,(BYTE*)&l,len,0,(SOCKADDR*)&addr,&addrlen))==len) {
706: if(p->network_byte_order)
707: l=ntohl(l);
1.1.1.2 ! root 708: JS_RESUMEREQUEST(cx, rc);
1.1 root 709: JS_NewNumberValue(cx,l,&data_val);
1.1.1.2 ! root 710: rc=JS_SUSPENDREQUEST(cx);
1.1 root 711: }
712: break;
713: }
714:
1.1.1.2 ! root 715: JS_RESUMEREQUEST(cx, rc);
! 716:
1.1 root 717: if(rd!=len) {
718: p->last_error=ERROR_VALUE;
719: return(JS_TRUE);
720: }
721:
722: } else { /* String Data */
723:
724: if((buf=(char*)alloca(len+1))==NULL) {
725: JS_ReportError(cx,"Error allocating %u bytes",len+1);
726: return(JS_FALSE);
727: }
728:
1.1.1.2 ! root 729: rc=JS_SUSPENDREQUEST(cx);
1.1 root 730: len = recvfrom(p->sock,buf,len,0,(SOCKADDR*)&addr,&addrlen);
1.1.1.2 ! root 731: JS_RESUMEREQUEST(cx, rc);
1.1 root 732: if(len<0) {
733: p->last_error=ERROR_VALUE;
734: return(JS_TRUE);
735: }
736: buf[len]=0;
737:
738: str = JS_NewStringCopyN(cx, buf, len);
739:
740: if(str==NULL)
741: return(JS_FALSE);
742:
743: data_val = STRING_TO_JSVAL(str);
744: }
745:
746:
747: if((retobj=JS_NewObject(cx,NULL,NULL,obj))==NULL) {
748: JS_ReportError(cx,"JS_NewObject failed");
749: return(JS_FALSE);
750: }
751:
752: JS_DefineProperty(cx, retobj, "data"
753: ,data_val
754: ,NULL,NULL,JSPROP_ENUMERATE);
755:
756: sprintf(port,"%u",ntohs(addr.sin_port));
757: if((str=JS_NewStringCopyZ(cx,port))==NULL)
758: return(JS_FALSE);
759: JS_DefineProperty(cx, retobj, "port"
760: ,STRING_TO_JSVAL(str)
761: ,NULL,NULL,JSPROP_ENUMERATE);
762:
763: SAFECOPY(ip_addr,inet_ntoa(addr.sin_addr));
764: if((str=JS_NewStringCopyZ(cx,ip_addr))==NULL)
765: return(JS_FALSE);
766: JS_DefineProperty(cx, retobj, "ip_address"
767: ,STRING_TO_JSVAL(str)
768: ,NULL,NULL,JSPROP_ENUMERATE);
769:
770: *rval = OBJECT_TO_JSVAL(retobj);
771:
1.1.1.2 ! root 772: rc=JS_SUSPENDREQUEST(cx);
1.1 root 773: dbprintf(FALSE, p, "received %u bytes from %s:%s",len,ip_addr,port);
1.1.1.2 ! root 774: JS_RESUMEREQUEST(cx, rc);
1.1 root 775:
776: return(JS_TRUE);
777: }
778:
779:
780: static JSBool
781: js_peek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
782: {
783: char* buf;
784: int32 len=512;
785: JSString* str;
1.1.1.2 ! root 786: jsrefcount rc;
1.1 root 787:
788: private_t* p;
789:
790: if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
791: JS_ReportError(cx,getprivate_failure,WHERE);
792: return(JS_FALSE);
793: }
794:
1.1.1.2 ! root 795: if(argc && argv[0]!=JSVAL_VOID)
1.1 root 796: JS_ValueToInt32(cx,argv[0],&len);
797:
798: if((buf=(char*)alloca(len+1))==NULL) {
799: JS_ReportError(cx,"Error allocating %u bytes",len+1);
800: return(JS_FALSE);
801: }
1.1.1.2 ! root 802: rc=JS_SUSPENDREQUEST(cx);
1.1 root 803: len = recv(p->sock,buf,len,MSG_PEEK);
1.1.1.2 ! root 804: JS_RESUMEREQUEST(cx, rc);
1.1 root 805: if(len<0) {
806: p->last_error=ERROR_VALUE;
807: *rval = JSVAL_NULL;
808: return(JS_TRUE);
809: }
810: buf[len]=0;
811:
812: str = JS_NewStringCopyN(cx, buf, len);
813: if(str==NULL)
814: return(JS_FALSE);
815:
816: *rval = STRING_TO_JSVAL(str);
1.1.1.2 ! root 817: rc=JS_SUSPENDREQUEST(cx);
1.1 root 818: dbprintf(FALSE, p, "received %u bytes, lasterror=%d"
819: ,len,ERROR_VALUE);
1.1.1.2 ! root 820: JS_RESUMEREQUEST(cx, rc);
1.1 root 821:
822: return(JS_TRUE);
823: }
824:
825: static JSBool
826: js_recvline(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
827: {
828: char ch;
829: char* buf;
830: int i;
831: int32 len=512;
832: BOOL rd;
833: time_t start;
834: int32 timeout=30; /* seconds */
835: JSString* str;
836: private_t* p;
1.1.1.2 ! root 837: jsrefcount rc;
1.1 root 838:
839: if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
840: JS_ReportError(cx,getprivate_failure,WHERE);
841: return(JS_FALSE);
842: }
843:
1.1.1.2 ! root 844: if(argc && argv[0]!=JSVAL_VOID)
1.1 root 845: JS_ValueToInt32(cx,argv[0],&len);
846:
847: if((buf=(char*)alloca(len+1))==NULL) {
848: JS_ReportError(cx,"Error allocating %u bytes",len+1);
849: return(JS_FALSE);
850: }
851:
1.1.1.2 ! root 852: if(argc>1 && argv[1]!=JSVAL_VOID)
! 853: JS_ValueToInt32(cx,argv[1],&timeout);
1.1 root 854:
855: start=time(NULL);
1.1.1.2 ! root 856: rc=JS_SUSPENDREQUEST(cx);
1.1 root 857: for(i=0;i<len;) {
858:
859: if(!socket_check(p->sock,&rd,NULL,1000)) {
860: p->last_error=ERROR_VALUE;
1.1.1.2 ! root 861: if(i==0) {
! 862: *rval = JSVAL_NULL;
! 863: JS_RESUMEREQUEST(cx, rc);
! 864: return(JS_TRUE); /* socket closed */
! 865: }
1.1 root 866: break; /* disconnected */
867: }
868:
869: if(!rd) {
870: if(time(NULL)-start>timeout) {
871: dbprintf(FALSE, p, "recvline timeout (received: %d)",i);
872: *rval = JSVAL_NULL;
1.1.1.2 ! root 873: JS_RESUMEREQUEST(cx, rc);
1.1 root 874: return(JS_TRUE); /* time-out */
875: }
876: continue; /* no data */
877: }
878:
879: if(recv(p->sock, &ch, 1, 0)!=1) {
880: p->last_error=ERROR_VALUE;
881: break;
882: }
883:
884: if(ch=='\n' /* && i>=1 */) /* Mar-9-2003: terminate on sole LF */
885: break;
886:
887: buf[i++]=ch;
888: }
889: if(i>0 && buf[i-1]=='\r')
890: buf[i-1]=0;
891: else
892: buf[i]=0;
893:
1.1.1.2 ! root 894: JS_RESUMEREQUEST(cx, rc);
1.1 root 895: str = JS_NewStringCopyZ(cx, buf);
896: if(str==NULL)
897: return(JS_FALSE);
898:
899: *rval = STRING_TO_JSVAL(str);
1.1.1.2 ! root 900: rc=JS_SUSPENDREQUEST(cx);
1.1 root 901: dbprintf(FALSE, p, "received %u bytes (recvline) lasterror=%d"
902: ,i,ERROR_VALUE);
1.1.1.2 ! root 903: JS_RESUMEREQUEST(cx, rc);
1.1 root 904:
905: return(JS_TRUE);
906: }
907:
908: static JSBool
909: js_recvbin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
910: {
911: BYTE b;
912: WORD w;
913: DWORD l;
914: int size=sizeof(DWORD);
915: int rd=0;
916: private_t* p;
1.1.1.2 ! root 917: jsrefcount rc;
1.1 root 918:
919: *rval = INT_TO_JSVAL(-1);
920:
921: if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
922: JS_ReportError(cx,getprivate_failure,WHERE);
923: return(JS_FALSE);
924: }
925:
1.1.1.2 ! root 926: if(argc && argv[0]!=JSVAL_VOID)
1.1 root 927: JS_ValueToInt32(cx,argv[0],(int32*)&size);
928:
1.1.1.2 ! root 929: rc=JS_SUSPENDREQUEST(cx);
1.1 root 930: switch(size) {
931: case sizeof(BYTE):
932: if((rd=recv(p->sock,&b,size,0))==size)
933: *rval = INT_TO_JSVAL(b);
934: break;
935: case sizeof(WORD):
936: if((rd=recv(p->sock,(BYTE*)&w,size,0))==size) {
937: if(p->network_byte_order)
938: w=ntohs(w);
939: *rval = INT_TO_JSVAL(w);
940: }
941: break;
942: case sizeof(DWORD):
943: if((rd=recv(p->sock,(BYTE*)&l,size,0))==size) {
944: if(p->network_byte_order)
945: l=ntohl(l);
1.1.1.2 ! root 946: JS_RESUMEREQUEST(cx, rc);
1.1 root 947: JS_NewNumberValue(cx,l,rval);
1.1.1.2 ! root 948: rc=JS_SUSPENDREQUEST(cx);
1.1 root 949: }
950: break;
951: }
952:
953: if(rd!=size)
954: p->last_error=ERROR_VALUE;
955:
1.1.1.2 ! root 956: JS_RESUMEREQUEST(cx, rc);
1.1 root 957: return(JS_TRUE);
958: }
959:
960:
961: static JSBool
962: js_getsockopt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
963: {
964: int opt;
965: int level;
966: int val;
967: private_t* p;
968: LINGER linger;
969: void* vp=&val;
970: socklen_t len=sizeof(val);
1.1.1.2 ! root 971: jsrefcount rc;
1.1 root 972:
973: *rval = INT_TO_JSVAL(-1);
974:
975: if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
976: JS_ReportError(cx,getprivate_failure,WHERE);
977: return(JS_FALSE);
978: }
979:
1.1.1.2 ! root 980: rc=JS_SUSPENDREQUEST(cx);
! 981: if((opt = getSocketOptionByName(JS_GetStringBytes(JS_ValueToString(cx,argv[0])),&level)) == -1) {
! 982: JS_RESUMEREQUEST(cx, rc);
1.1 root 983: return(JS_TRUE);
1.1.1.2 ! root 984: }
1.1 root 985:
986: if(opt == SO_LINGER) {
987: vp=&linger;
988: len=sizeof(linger);
989: }
990: if(getsockopt(p->sock, level, opt, vp, &len)==0) {
991: if(opt == SO_LINGER) {
992: if(linger.l_onoff==TRUE)
993: val = linger.l_linger;
994: else
995: val = 0;
996: }
1.1.1.2 ! root 997: JS_RESUMEREQUEST(cx, rc);
1.1 root 998: JS_NewNumberValue(cx,val,rval);
1.1.1.2 ! root 999: rc=JS_SUSPENDREQUEST(cx);
1.1 root 1000: } else {
1001: p->last_error=ERROR_VALUE;
1002: dbprintf(TRUE, p, "error %d getting option %d"
1003: ,ERROR_VALUE,opt);
1004: }
1005:
1.1.1.2 ! root 1006: JS_RESUMEREQUEST(cx, rc);
1.1 root 1007: return(JS_TRUE);
1008: }
1009:
1010:
1011: static JSBool
1012: js_setsockopt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1013: {
1014: int opt;
1015: int level;
1016: int32 val=1;
1017: private_t* p;
1018: LINGER linger;
1019: void* vp=&val;
1020: socklen_t len=sizeof(val);
1.1.1.2 ! root 1021: jsrefcount rc;
1.1 root 1022:
1023: if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
1024: JS_ReportError(cx,getprivate_failure,WHERE);
1025: return(JS_FALSE);
1026: }
1027:
1.1.1.2 ! root 1028: rc=JS_SUSPENDREQUEST(cx);
1.1 root 1029: opt = getSocketOptionByName(JS_GetStringBytes(JS_ValueToString(cx,argv[0])),&level);
1.1.1.2 ! root 1030: if(argv[1]!=JSVAL_VOID) {
! 1031: JS_RESUMEREQUEST(cx, rc);
! 1032: JS_ValueToInt32(cx,argv[1],&val);
! 1033: rc=JS_SUSPENDREQUEST(cx);
! 1034: }
1.1 root 1035:
1036: if(opt == SO_LINGER) {
1037: if(val) {
1038: linger.l_onoff = TRUE;
1039: linger.l_linger = (ushort)val;
1040: } else {
1041: ZERO_VAR(linger);
1042: }
1043: vp=&linger;
1044: len=sizeof(linger);
1045: }
1046:
1047: *rval = BOOLEAN_TO_JSVAL(
1048: setsockopt(p->sock, level, opt, vp, len)==0);
1049: p->last_error=ERROR_VALUE;
1050:
1.1.1.2 ! root 1051: JS_RESUMEREQUEST(cx, rc);
1.1 root 1052: return(JS_TRUE);
1053: }
1054:
1055: static JSBool
1056: js_ioctlsocket(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1057: {
1.1.1.2 ! root 1058: int32 cmd=0;
1.1 root 1059: int32 arg=0;
1060: private_t* p;
1.1.1.2 ! root 1061: jsrefcount rc;
1.1 root 1062:
1063: if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
1064: JS_ReportError(cx,getprivate_failure,WHERE);
1065: return(JS_FALSE);
1066: }
1067:
1.1.1.2 ! root 1068: if(argv[0]!=JSVAL_VOID)
! 1069: JS_ValueToInt32(cx,argv[0],&cmd);
! 1070: if(argc>1 && argv[1]!=JSVAL_VOID)
1.1 root 1071: JS_ValueToInt32(cx,argv[1],&arg);
1072:
1.1.1.2 ! root 1073: rc=JS_SUSPENDREQUEST(cx);
! 1074: if(ioctlsocket(p->sock,cmd,(ulong*)&arg)==0) {
! 1075: JS_RESUMEREQUEST(cx, rc);
1.1 root 1076: JS_NewNumberValue(cx,arg,rval);
1.1.1.2 ! root 1077: }
! 1078: else {
1.1 root 1079: *rval = INT_TO_JSVAL(-1);
1.1.1.2 ! root 1080: JS_RESUMEREQUEST(cx, rc);
! 1081: }
1.1 root 1082:
1083: p->last_error=ERROR_VALUE;
1084:
1085: return(JS_TRUE);
1086: }
1087:
1088: static JSBool
1089: js_poll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1090: {
1091: private_t* p;
1092: BOOL poll_for_write=FALSE;
1093: fd_set socket_set;
1094: fd_set* rd_set=NULL;
1095: fd_set* wr_set=NULL;
1096: uintN argn;
1097: int result;
1098: struct timeval tv = {0, 0};
1.1.1.2 ! root 1099: jsrefcount rc;
1.1 root 1100:
1101: if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
1102: JS_ReportError(cx,getprivate_failure,WHERE);
1103: return(JS_FALSE);
1104: }
1105:
1106: if(p->sock==INVALID_SOCKET) {
1107: dbprintf(TRUE, p, "INVALID SOCKET in call to poll");
1108: *rval = INT_TO_JSVAL(-1);
1109: return(JS_TRUE);
1110: }
1111:
1112: for(argn=0;argn<argc;argn++) {
1113: if(JSVAL_IS_BOOLEAN(argv[argn]))
1114: poll_for_write=JSVAL_TO_BOOLEAN(argv[argn]);
1115: else if(JSVAL_IS_NUMBER(argv[argn]))
1116: js_timeval(cx,argv[argn],&tv);
1117: }
1118:
1.1.1.2 ! root 1119: rc=JS_SUSPENDREQUEST(cx);
1.1 root 1120: FD_ZERO(&socket_set);
1121: FD_SET(p->sock,&socket_set);
1122: if(poll_for_write)
1123: wr_set=&socket_set;
1124: else
1125: rd_set=&socket_set;
1126:
1127: result = select(p->sock+1,rd_set,wr_set,NULL,&tv);
1128:
1129: p->last_error=ERROR_VALUE;
1130:
1131: dbprintf(FALSE, p, "poll: select returned %d (errno %d)"
1132: ,result,p->last_error);
1133:
1134: *rval = INT_TO_JSVAL(result);
1.1.1.2 ! root 1135: JS_RESUMEREQUEST(cx, rc);
1.1 root 1136:
1137: return(JS_TRUE);
1138: }
1139:
1140:
1141: /* Socket Object Properites */
1142: enum {
1143: SOCK_PROP_LAST_ERROR
1144: ,SOCK_PROP_IS_CONNECTED
1145: ,SOCK_PROP_IS_WRITEABLE
1146: ,SOCK_PROP_DATA_WAITING
1147: ,SOCK_PROP_NREAD
1148: ,SOCK_PROP_DEBUG
1149: ,SOCK_PROP_DESCRIPTOR
1150: ,SOCK_PROP_NONBLOCKING
1151: ,SOCK_PROP_LOCAL_IP
1152: ,SOCK_PROP_LOCAL_PORT
1153: ,SOCK_PROP_REMOTE_IP
1154: ,SOCK_PROP_REMOTE_PORT
1155: ,SOCK_PROP_TYPE
1156: ,SOCK_PROP_NETWORK_ORDER
1157:
1158: };
1159:
1160: #ifdef BUILD_JSDOCS
1161: static char* socket_prop_desc[] = {
1162: "error status for the last socket operation that failed - <small>READ ONLY</small>"
1163: ,"<i>true</i> if socket is in a connected state - <small>READ ONLY</small>"
1.1.1.2 ! root 1164: ,"<i>true</i> if socket can accept written data - Setting to false will shutdown the write end of the socket."
1.1 root 1165: ,"<i>true</i> if data is waiting to be read from socket - <small>READ ONLY</small>"
1166: ,"number of bytes waiting to be read - <small>READ ONLY</small>"
1167: ,"enable debug logging"
1168: ,"socket descriptor (advanced uses only)"
1169: ,"use non-blocking operation (default is <i>false</i>)"
1170: ,"local IP address (string in dotted-decimal format)"
1171: ,"local TCP or UDP port number"
1172: ,"remote IP address (string in dotted-decimal format)"
1173: ,"remote TCP or UDP port number"
1174: ,"socket type, <tt>SOCK_STREAM</tt> (TCP) or <tt>SOCK_DGRAM</tt> (UDP)"
1175: ,"<i>true</i> if binary data is to be sent in Network Byte Order (big end first), default is <i>true</i>"
1176: /* statically-defined properties: */
1177: ,"array of socket option names supported by the current platform"
1178: ,NULL
1179: };
1180: #endif
1181:
1182: static JSBool js_socket_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1183: {
1184: jsint tiny;
1185: private_t* p;
1.1.1.2 ! root 1186: jsrefcount rc;
! 1187: BOOL b;
1.1 root 1188:
1189: if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
1.1.1.2 ! root 1190: // Prototype access
! 1191: return(JS_TRUE);
1.1 root 1192: }
1193:
1194: tiny = JSVAL_TO_INT(id);
1195:
1.1.1.2 ! root 1196: rc=JS_SUSPENDREQUEST(cx);
1.1 root 1197: dbprintf(FALSE, p, "setting property %d",tiny);
1.1.1.2 ! root 1198: JS_RESUMEREQUEST(cx, rc);
1.1 root 1199:
1200: switch(tiny) {
1201: case SOCK_PROP_DEBUG:
1202: JS_ValueToBoolean(cx,*vp,&(p->debug));
1203: break;
1204: case SOCK_PROP_DESCRIPTOR:
1205: JS_ValueToInt32(cx,*vp,(int32*)&(p->sock));
1.1.1.2 ! root 1206: p->is_connected=TRUE;
1.1 root 1207: break;
1208: case SOCK_PROP_LAST_ERROR:
1209: JS_ValueToInt32(cx,*vp,(int32*)&(p->last_error));
1210: break;
1211: case SOCK_PROP_NONBLOCKING:
1212: JS_ValueToBoolean(cx,*vp,&(p->nonblocking));
1.1.1.2 ! root 1213: rc=JS_SUSPENDREQUEST(cx);
1.1 root 1214: ioctlsocket(p->sock,FIONBIO,(ulong*)&(p->nonblocking));
1.1.1.2 ! root 1215: JS_RESUMEREQUEST(cx, rc);
1.1 root 1216: break;
1217: case SOCK_PROP_NETWORK_ORDER:
1218: JS_ValueToBoolean(cx,*vp,&(p->network_byte_order));
1219: break;
1.1.1.2 ! root 1220: case SOCK_PROP_IS_WRITEABLE:
! 1221: JS_ValueToBoolean(cx,*vp,&b);
! 1222: if(!b)
! 1223: shutdown(p->sock,SHUT_WR);
! 1224: break;
1.1 root 1225: }
1226:
1227: return(JS_TRUE);
1228: }
1229:
1230: static JSBool js_socket_get(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1231: {
1232: jsint tiny;
1233: ulong cnt;
1234: BOOL rd;
1235: BOOL wr;
1236: private_t* p;
1237: JSString* js_str;
1.1.1.2 ! root 1238: SOCKADDR_IN addr;
! 1239: socklen_t len=sizeof(addr);
! 1240: jsrefcount rc;
1.1 root 1241:
1242: if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
1.1.1.2 ! root 1243: // Protoype access
! 1244: return(JS_TRUE);
1.1 root 1245: }
1246:
1247: tiny = JSVAL_TO_INT(id);
1248:
1.1.1.2 ! root 1249: rc=JS_SUSPENDREQUEST(cx);
1.1 root 1250: #if 0 /* just too much */
1251: dbprintf(FALSE, p, "getting property %d",tiny);
1252: #endif
1253:
1254: switch(tiny) {
1255: case SOCK_PROP_LAST_ERROR:
1256: *vp = INT_TO_JSVAL(p->last_error);
1257: break;
1258: case SOCK_PROP_IS_CONNECTED:
1.1.1.2 ! root 1259: if(!p->is_connected)
1.1 root 1260: *vp = JSVAL_FALSE;
1261: else
1262: *vp = BOOLEAN_TO_JSVAL(socket_check(p->sock,NULL,NULL,0));
1263: break;
1264: case SOCK_PROP_IS_WRITEABLE:
1265: socket_check(p->sock,NULL,&wr,0);
1266: *vp = BOOLEAN_TO_JSVAL(wr);
1267: break;
1268: case SOCK_PROP_DATA_WAITING:
1269: socket_check(p->sock,&rd,NULL,0);
1270: *vp = BOOLEAN_TO_JSVAL(rd);
1271: break;
1272: case SOCK_PROP_NREAD:
1273: cnt=0;
1.1.1.2 ! root 1274: if(ioctlsocket(p->sock, FIONREAD, &cnt)==0) {
! 1275: JS_RESUMEREQUEST(cx, rc);
1.1 root 1276: JS_NewNumberValue(cx,cnt,vp);
1.1.1.2 ! root 1277: rc=JS_SUSPENDREQUEST(cx);
! 1278: }
1.1 root 1279: else
1280: *vp = JSVAL_ZERO;
1281: break;
1282: case SOCK_PROP_DEBUG:
1283: *vp = INT_TO_JSVAL(p->debug);
1284: break;
1285: case SOCK_PROP_DESCRIPTOR:
1286: *vp = INT_TO_JSVAL(p->sock);
1287: break;
1288: case SOCK_PROP_NONBLOCKING:
1289: *vp = BOOLEAN_TO_JSVAL(p->nonblocking);
1290: break;
1291: case SOCK_PROP_LOCAL_IP:
1.1.1.2 ! root 1292: if(p->sock != INVALID_SOCKET) {
! 1293: if(getsockname(p->sock, (struct sockaddr *)&addr,&len)!=0)
! 1294: return(JS_FALSE);
! 1295: JS_RESUMEREQUEST(cx, rc);
1.1 root 1296: if((js_str=JS_NewStringCopyZ(cx,inet_ntoa(addr.sin_addr)))==NULL)
1297: return(JS_FALSE);
1298: *vp = STRING_TO_JSVAL(js_str);
1.1.1.2 ! root 1299: rc=JS_SUSPENDREQUEST(cx);
1.1 root 1300: }
1.1.1.2 ! root 1301: else
! 1302: *vp=JSVAL_VOID;
1.1 root 1303: break;
1304: case SOCK_PROP_LOCAL_PORT:
1.1.1.2 ! root 1305: if(p->sock != INVALID_SOCKET) {
! 1306: if(getsockname(p->sock, (struct sockaddr *)&addr,&len)!=0)
! 1307: return(JS_FALSE);
! 1308: JS_RESUMEREQUEST(cx, rc);
! 1309: if((js_str=JS_NewStringCopyZ(cx,inet_ntoa(addr.sin_addr)))==NULL)
! 1310: return(JS_FALSE);
! 1311:
1.1 root 1312: *vp = INT_TO_JSVAL(ntohs(addr.sin_port));
1.1.1.2 ! root 1313: rc=JS_SUSPENDREQUEST(cx);
! 1314: }
! 1315: else
! 1316: *vp=JSVAL_VOID;
1.1 root 1317: break;
1318: case SOCK_PROP_REMOTE_IP:
1.1.1.2 ! root 1319: if(p->is_connected) {
! 1320: JS_RESUMEREQUEST(cx, rc);
! 1321: if((js_str=JS_NewStringCopyZ(cx,inet_ntoa(p->remote_addr.sin_addr)))==NULL)
1.1 root 1322: return(JS_FALSE);
1323: *vp = STRING_TO_JSVAL(js_str);
1.1.1.2 ! root 1324: rc=JS_SUSPENDREQUEST(cx);
1.1 root 1325: }
1.1.1.2 ! root 1326: else
! 1327: *vp=JSVAL_VOID;
1.1 root 1328: break;
1329: case SOCK_PROP_REMOTE_PORT:
1.1.1.2 ! root 1330: if(p->is_connected)
! 1331: *vp = INT_TO_JSVAL(ntohs(p->remote_addr.sin_port));
! 1332: else
! 1333: *vp=JSVAL_VOID;
1.1 root 1334: break;
1335: case SOCK_PROP_TYPE:
1336: *vp = INT_TO_JSVAL(p->type);
1337: break;
1338: case SOCK_PROP_NETWORK_ORDER:
1339: *vp = BOOLEAN_TO_JSVAL(p->network_byte_order);
1340: break;
1341:
1342: }
1343:
1.1.1.2 ! root 1344: JS_RESUMEREQUEST(cx, rc);
1.1 root 1345: return(TRUE);
1346: }
1347:
1348: #define SOCK_PROP_FLAGS JSPROP_ENUMERATE|JSPROP_READONLY
1349:
1350: static jsSyncPropertySpec js_socket_properties[] = {
1351: /* name ,tinyid ,flags, ver */
1352:
1353: { "error" ,SOCK_PROP_LAST_ERROR ,SOCK_PROP_FLAGS, 311 },
1354: { "last_error" ,SOCK_PROP_LAST_ERROR ,JSPROP_READONLY, 310 }, /* alias */
1355: { "is_connected" ,SOCK_PROP_IS_CONNECTED ,SOCK_PROP_FLAGS, 310 },
1.1.1.2 ! root 1356: { "is_writeable" ,SOCK_PROP_IS_WRITEABLE ,JSPROP_ENUMERATE, 311 },
! 1357: { "is_writable" ,SOCK_PROP_IS_WRITEABLE ,JSPROP_ENUMERATE, 312 }, /* alias */
1.1 root 1358: { "data_waiting" ,SOCK_PROP_DATA_WAITING ,SOCK_PROP_FLAGS, 310 },
1359: { "nread" ,SOCK_PROP_NREAD ,SOCK_PROP_FLAGS, 310 },
1360: { "debug" ,SOCK_PROP_DEBUG ,JSPROP_ENUMERATE, 310 },
1361: { "descriptor" ,SOCK_PROP_DESCRIPTOR ,JSPROP_ENUMERATE, 310 },
1362: { "nonblocking" ,SOCK_PROP_NONBLOCKING ,JSPROP_ENUMERATE, 310 },
1363: { "local_ip_address" ,SOCK_PROP_LOCAL_IP ,SOCK_PROP_FLAGS, 310 },
1364: { "local_port" ,SOCK_PROP_LOCAL_PORT ,SOCK_PROP_FLAGS, 310 },
1365: { "remote_ip_address" ,SOCK_PROP_REMOTE_IP ,SOCK_PROP_FLAGS, 310 },
1366: { "remote_port" ,SOCK_PROP_REMOTE_PORT ,SOCK_PROP_FLAGS, 310 },
1367: { "type" ,SOCK_PROP_TYPE ,SOCK_PROP_FLAGS, 310 },
1368: { "network_byte_order",SOCK_PROP_NETWORK_ORDER,JSPROP_ENUMERATE, 311 },
1369: {0}
1370: };
1371:
1372: static jsSyncMethodSpec js_socket_functions[] = {
1373: {"close", js_close, 0, JSTYPE_VOID, ""
1374: ,JSDOCSTR("close (shutdown) the socket immediately")
1375: ,310
1376: },
1377: {"bind", js_bind, 0, JSTYPE_BOOLEAN, JSDOCSTR("[port] [,ip_address]")
1378: ,JSDOCSTR("bind socket to a TCP or UDP <i>port</i> (number or service name), "
1379: "optionally specifying a network interface (via <i>ip_address</i>)")
1380: ,311
1381: },
1382: {"connect", js_connect, 2, JSTYPE_BOOLEAN, JSDOCSTR("host, port [,timeout=<tt>10.0</tt>]")
1383: ,JSDOCSTR("connect to a remote port (number or service name) on the specified host (IP address or host name)"
1384: ", default <i>timeout</i> value is <i>10.0</i> (seconds)")
1385: ,311
1386: },
1387: {"listen", js_listen, 0, JSTYPE_BOOLEAN, JSDOCSTR("")
1388: ,JSDOCSTR("place socket in a state to listen for incoming connections (use before an accept)")
1389: ,310
1390: },
1391: {"accept", js_accept, 0, JSTYPE_OBJECT, JSDOCSTR("")
1392: ,JSDOCSTR("accept an incoming connection, returns a new <i>Socket</i> object representing the new connection")
1393: ,310
1394: },
1395: {"write", js_send, 1, JSTYPE_ALIAS },
1396: {"send", js_send, 1, JSTYPE_BOOLEAN, JSDOCSTR("data")
1397: ,JSDOCSTR("send a string (AKA write)")
1398: ,310
1399: },
1400: {"sendto", js_sendto, 3, JSTYPE_BOOLEAN, JSDOCSTR("data, address, port")
1401: ,JSDOCSTR("send data to a specific host (IP address or host name) and port (number or service name), for UDP sockets")
1402: ,310
1403: },
1404: {"sendfile", js_sendfile, 1, JSTYPE_BOOLEAN, JSDOCSTR("path/filename")
1405: ,JSDOCSTR("send an entire file over the socket")
1406: ,310
1407: },
1408: {"writeBin", js_sendbin, 1, JSTYPE_ALIAS },
1409: {"sendBin", js_sendbin, 1, JSTYPE_BOOLEAN, JSDOCSTR("value [,bytes=<tt>4</tt>]")
1410: ,JSDOCSTR("send a binary integer over the socket, default number of bytes is 4 (32-bits)")
1411: ,311
1412: },
1413: {"read", js_recv, 1, JSTYPE_ALIAS },
1414: {"recv", js_recv, 1, JSTYPE_STRING, JSDOCSTR("[maxlen=<tt>512</tt>]")
1415: ,JSDOCSTR("receive a string, default maxlen is 512 characters (AKA read)")
1416: ,310
1417: },
1418: {"peek", js_peek, 0, JSTYPE_STRING, JSDOCSTR("[maxlen=<tt>512</tt>]")
1419: ,JSDOCSTR("receive a string, default maxlen is 512 characters, leaves string in receive buffer")
1420: ,310
1421: },
1422: {"readline", js_recvline, 0, JSTYPE_ALIAS },
1423: {"readln", js_recvline, 0, JSTYPE_ALIAS },
1424: {"recvline", js_recvline, 0, JSTYPE_STRING, JSDOCSTR("[maxlen=<tt>512</tt>] [,timeout=<tt>30.0</tt>]")
1425: ,JSDOCSTR("receive a line-feed terminated string, default maxlen is 512 characters, default timeout is 30 seconds (AKA readline and readln)")
1426: ,310
1427: },
1428: {"recvfrom", js_recvfrom, 0, JSTYPE_OBJECT, JSDOCSTR("[binary=<tt>false</tt>] [,maxlen=<tt>512</tt> or int_size=<tt>4</tt>]")
1429: ,JSDOCSTR("receive data (string or integer) from a socket (typically UDP)"
1430: "<p>returns object with <i>ip_address</i> and <i>port</i> of sender along with <i>data</i> properties"
1431: "<p><i>binary</i> defaults to <i>false</i>, <i>maxlen</i> defaults to 512 chars, <i>int_size</i> defaults to 4 bytes (32-bits)")
1432: ,311
1433: },
1434: {"readBin", js_recvbin, 0, JSTYPE_ALIAS },
1435: {"recvBin", js_recvbin, 0, JSTYPE_NUMBER, JSDOCSTR("[bytes=<tt>4</tt>]")
1436: ,JSDOCSTR("receive a binary integer from the socket, default number of bytes is 4 (32-bits)")
1437: ,311
1438: },
1439: {"getoption", js_getsockopt, 1, JSTYPE_NUMBER, JSDOCSTR("option")
1440: ,JSDOCSTR("get socket option value, option may be socket option name "
1441: "(see <tt>sockopts</tt> in <tt>sockdefs.js</tt>) or number")
1442: ,310
1443: },
1444: {"setoption", js_setsockopt, 2, JSTYPE_BOOLEAN, JSDOCSTR("option, value")
1445: ,JSDOCSTR("set socket option value, option may be socket option name "
1446: "(see <tt>sockopts</tt> in <tt>sockdefs.js</tt>) or number")
1447: ,310
1448: },
1449: {"ioctl", js_ioctlsocket, 1, JSTYPE_NUMBER, JSDOCSTR("command [,argument=<tt>0</tt>]")
1450: ,JSDOCSTR("send socket IOCTL (advanced)")
1451: ,310
1452: },
1453: {"poll", js_poll, 1, JSTYPE_NUMBER, JSDOCSTR("[timeout=<tt>0</tt>] [,write=<tt>false</tt>]")
1454: ,JSDOCSTR("poll socket for read or write ability (default is <i>read</i>), "
1455: "default timeout value is 0.0 seconds (immediate timeout)")
1456: ,310
1457: },
1458: {0}
1459: };
1460:
1.1.1.2 ! root 1461: static JSBool js_socket_resolve(JSContext *cx, JSObject *obj, jsval id)
! 1462: {
! 1463: char* name=NULL;
! 1464:
! 1465: if(id != JSVAL_NULL)
! 1466: name=JS_GetStringBytes(JSVAL_TO_STRING(id));
! 1467:
! 1468: return(js_SyncResolve(cx, obj, name, js_socket_properties, js_socket_functions, NULL, 0));
! 1469: }
! 1470:
! 1471: static JSBool js_socket_enumerate(JSContext *cx, JSObject *obj)
! 1472: {
! 1473: return(js_socket_resolve(cx, obj, JSVAL_NULL));
! 1474: }
! 1475:
! 1476: static JSClass js_socket_class = {
! 1477: "Socket" /* name */
! 1478: ,JSCLASS_HAS_PRIVATE /* flags */
! 1479: ,JS_PropertyStub /* addProperty */
! 1480: ,JS_PropertyStub /* delProperty */
! 1481: ,js_socket_get /* getProperty */
! 1482: ,js_socket_set /* setProperty */
! 1483: ,js_socket_enumerate /* enumerate */
! 1484: ,js_socket_resolve /* resolve */
! 1485: ,JS_ConvertStub /* convert */
! 1486: ,js_finalize_socket /* finalize */
! 1487: };
! 1488:
1.1 root 1489: static BOOL js_DefineSocketOptionsArray(JSContext *cx, JSObject *obj, int type)
1490: {
1491: size_t i;
1492: size_t count=0;
1493: jsval val;
1494: JSObject* array;
1495: socket_option_t* options;
1496:
1497: if((options=getSocketOptionList())==NULL)
1498: return(FALSE);
1499:
1500: if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)
1501: return(FALSE);
1502:
1503: if(!JS_DefineProperty(cx, obj, "option_list", OBJECT_TO_JSVAL(array)
1504: , NULL, NULL, JSPROP_ENUMERATE))
1505: return(FALSE);
1506:
1507: for(i=0; options[i].name!=NULL; i++) {
1508: if(options[i].type && options[i].type!=type)
1509: continue;
1510: val=STRING_TO_JSVAL(JS_NewStringCopyZ(cx,options[i].name));
1511: JS_SetElement(cx, array, count++, &val);
1512: }
1513: return(TRUE);
1514: }
1515:
1516: /* Socket Constructor (creates socket descriptor) */
1517:
1518: static JSBool
1519: js_socket_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1520: {
1521: int32 type=SOCK_STREAM; /* default = TCP */
1522: uintN i;
1523: private_t* p;
1524: char* protocol=NULL;
1525:
1526: for(i=0;i<argc;i++) {
1527: if(JSVAL_IS_NUMBER(argv[i]))
1528: JS_ValueToInt32(cx,argv[i],&type);
1529: else if(protocol==NULL)
1530: protocol=JS_GetStringBytes(JS_ValueToString(cx,argv[i]));
1531: }
1532:
1533: if((p=(private_t*)malloc(sizeof(private_t)))==NULL) {
1534: JS_ReportError(cx,"malloc failed");
1535: return(JS_FALSE);
1536: }
1537: memset(p,0,sizeof(private_t));
1538:
1539: if((p->sock=open_socket(type,protocol))==INVALID_SOCKET) {
1540: JS_ReportError(cx,"open_socket failed with error %d",ERROR_VALUE);
1541: return(JS_FALSE);
1542: }
1543: p->type = type;
1544: p->network_byte_order = TRUE;
1545:
1546: if(!JS_SetPrivate(cx, obj, p)) {
1547: JS_ReportError(cx,"JS_SetPrivate failed");
1548: return(JS_FALSE);
1549: }
1550:
1551: if(!js_DefineSocketOptionsArray(cx, obj, type))
1552: return(JS_FALSE);
1553:
1554: #ifdef BUILD_JSDOCS
1555: js_DescribeSyncObject(cx,obj,"Class used for TCP/IP socket communications",310);
1556: js_DescribeSyncConstructor(cx,obj,"To create a new Socket object: "
1557: "<tt>load('sockdefs.js'); var s = new Socket(<i>type</i>, <i>protocol</i>)</tt><br>"
1558: "where <i>type</i> = <tt>SOCK_STREAM</tt> for TCP (default) or <tt>SOCK_DGRAM</tt> for UDP<br>"
1559: "and <i>protocol</i> (optional) = the name of the protocol or service the socket is to be used for"
1560: );
1561: js_CreateArrayOfStrings(cx, obj, "_property_desc_list", socket_prop_desc, JSPROP_READONLY);
1562: #endif
1563:
1564: dbprintf(FALSE, p, "object constructed");
1565: return(JS_TRUE);
1566: }
1567:
1568: JSObject* DLLCALL js_CreateSocketClass(JSContext* cx, JSObject* parent)
1569: {
1570: JSObject* sockobj;
1571:
1572: sockobj = JS_InitClass(cx, parent, NULL
1573: ,&js_socket_class
1574: ,js_socket_constructor
1575: ,0 /* number of constructor args */
1576: ,NULL /* props, specified in constructor */
1577: ,NULL /* funcs, specified in constructor */
1578: ,NULL,NULL);
1579:
1580: return(sockobj);
1581: }
1582:
1583: JSObject* DLLCALL js_CreateSocketObject(JSContext* cx, JSObject* parent, char *name, SOCKET sock)
1584: {
1585: JSObject* obj;
1586: private_t* p;
1587: int type=SOCK_STREAM;
1588: socklen_t len;
1589:
1590: obj = JS_DefineObject(cx, parent, name, &js_socket_class, NULL
1591: ,JSPROP_ENUMERATE|JSPROP_READONLY);
1592:
1593: if(obj==NULL)
1594: return(NULL);
1595:
1596: len = sizeof(type);
1597: getsockopt(sock,SOL_SOCKET,SO_TYPE,(void*)&type,&len);
1598:
1599: if(!js_DefineSocketOptionsArray(cx, obj, type))
1600: return(NULL);
1601:
1602: if((p=(private_t*)malloc(sizeof(private_t)))==NULL)
1603: return(NULL);
1604: memset(p,0,sizeof(private_t));
1605:
1606: p->sock = sock;
1607: p->external = TRUE;
1608: p->network_byte_order = TRUE;
1609:
1.1.1.2 ! root 1610: len=sizeof(p->remote_addr);
! 1611: if(getpeername(p->sock, (struct sockaddr *)&p->remote_addr,&len)==0)
! 1612: p->is_connected=TRUE;
! 1613:
1.1 root 1614: if(!JS_SetPrivate(cx, obj, p)) {
1615: dbprintf(TRUE, p, "JS_SetPrivate failed");
1616: return(NULL);
1617: }
1618:
1619: dbprintf(FALSE, p, "object created");
1620:
1621: return(obj);
1622: }
1623:
1624: #endif /* JAVSCRIPT */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.