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