diff --git a/src/sbbs3/js_socket.c b/src/sbbs3/js_socket.c index 33641a8a3f763e79917ed70f32b7d8bbba87c2ec..2dfb7aac05f00f7e35a0f2b48eb0c165967a5d24 100644 --- a/src/sbbs3/js_socket.c +++ b/src/sbbs3/js_socket.c @@ -46,6 +46,7 @@ typedef struct BOOL debug; BOOL nonblocking; BOOL is_connected; + BOOL network_byte_order; int last_error; int type; @@ -160,12 +161,12 @@ js_bind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if(bind(p->sock, (struct sockaddr *) &addr, sizeof(addr))!=0) { p->last_error=ERROR_VALUE; dbprintf(TRUE, p, "bind failed with error %d",ERROR_VALUE); - *rval = BOOLEAN_TO_JSVAL(JS_FALSE); + *rval = JSVAL_FALSE; return(JS_TRUE); } dbprintf(FALSE, p, "bound to port %u",port); - *rval = BOOLEAN_TO_JSVAL(JS_TRUE); + *rval = JSVAL_TRUE; return(JS_TRUE); } @@ -186,12 +187,12 @@ js_listen(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if(listen(p->sock, backlog)!=0) { p->last_error=ERROR_VALUE; dbprintf(TRUE, p, "listen failed with error %d",ERROR_VALUE); - *rval = BOOLEAN_TO_JSVAL(JS_FALSE); + *rval = JSVAL_FALSE; return(JS_TRUE); } dbprintf(FALSE, p, "listening, backlog=%d",backlog); - *rval = BOOLEAN_TO_JSVAL(JS_TRUE); + *rval = JSVAL_TRUE; return(JS_TRUE); } @@ -256,7 +257,7 @@ js_connect(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if((ip_addr=resolve_ip(JS_GetStringBytes(str)))==INADDR_NONE) { p->last_error=ERROR_VALUE; dbprintf(TRUE, p, "resolve_ip failed with error %d",ERROR_VALUE); - *rval = BOOLEAN_TO_JSVAL(JS_FALSE); + *rval = JSVAL_FALSE; return(JS_TRUE); } @@ -272,12 +273,12 @@ js_connect(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if(connect(p->sock, (struct sockaddr *)&addr, sizeof(addr))!=0) { p->last_error=ERROR_VALUE; dbprintf(TRUE, p, "connect failed with error %d",ERROR_VALUE); - *rval = BOOLEAN_TO_JSVAL(JS_FALSE); + *rval = JSVAL_FALSE; return(JS_TRUE); } p->is_connected = TRUE; - *rval = BOOLEAN_TO_JSVAL(JS_TRUE); + *rval = JSVAL_TRUE; dbprintf(FALSE, p, "connected to port %u at %s", port, JS_GetStringBytes(str)); return(JS_TRUE); @@ -296,7 +297,7 @@ js_send(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return(JS_FALSE); } - *rval = BOOLEAN_TO_JSVAL(JS_FALSE); + *rval = JSVAL_FALSE; str = JS_ValueToString(cx, argv[0]); cp=JS_GetStringBytes(str); @@ -304,7 +305,7 @@ js_send(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if(sendsocket(p->sock,cp,len)==len) { dbprintf(FALSE, p, "sent %u bytes",len); - *rval = BOOLEAN_TO_JSVAL(JS_TRUE); + *rval = JSVAL_TRUE; } else { p->last_error=ERROR_VALUE; dbprintf(TRUE, p, "send of %u bytes failed",len); @@ -330,7 +331,7 @@ js_sendto(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return(JS_FALSE); } - *rval = BOOLEAN_TO_JSVAL(JS_FALSE); + *rval = JSVAL_FALSE; /* data */ data_str = JS_ValueToString(cx, argv[0]); @@ -343,7 +344,7 @@ js_sendto(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if((ip_addr=resolve_ip(JS_GetStringBytes(ip_str)))==INADDR_NONE) { p->last_error=ERROR_VALUE; dbprintf(TRUE, p, "resolve_ip failed with error %d",ERROR_VALUE); - *rval = BOOLEAN_TO_JSVAL(JS_FALSE); + *rval = JSVAL_FALSE; return(JS_TRUE); } @@ -360,7 +361,7 @@ js_sendto(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if(sendto(p->sock,cp,len,0 /* flags */,(SOCKADDR*)&addr,sizeof(addr))==len) { dbprintf(FALSE, p, "sent %u bytes",len); - *rval = BOOLEAN_TO_JSVAL(JS_TRUE); + *rval = JSVAL_TRUE; } else { p->last_error=ERROR_VALUE; dbprintf(TRUE, p, "send of %u bytes failed",len); @@ -385,7 +386,7 @@ js_sendfile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return(JS_FALSE); } - *rval = BOOLEAN_TO_JSVAL(JS_FALSE); + *rval = JSVAL_FALSE; if((str = JS_ValueToString(cx, argv[0]))==NULL || (fname=JS_GetStringBytes(str))==NULL) { @@ -410,7 +411,7 @@ js_sendfile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if(sendsocket(p->sock,buf,len)==len) { dbprintf(FALSE, p, "sent %u bytes",len); - *rval = BOOLEAN_TO_JSVAL(JS_TRUE); + *rval = JSVAL_TRUE; } else { p->last_error=ERROR_VALUE; dbprintf(TRUE, p, "send of %u bytes failed",len); @@ -420,6 +421,64 @@ js_sendfile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return(JS_TRUE); } +static JSBool +js_sendbin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + BYTE b; + WORD w; + DWORD l; + int32 val=0; + size_t wr=0; + size_t size=sizeof(DWORD); + private_t* p; + + *rval = JSVAL_FALSE; + + if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + JS_ReportError(cx,getprivate_failure,WHERE); + return(JS_FALSE); + } + + if(argc>1) + JS_ValueToInt32(cx,argv[1],(int32*)&size); + + switch(size) { + case sizeof(BYTE): + JS_ValueToInt32(cx,argv[0],&val); + b = (BYTE)val; + wr=sendsocket(p->sock,&b,size); + break; + case sizeof(WORD): + JS_ValueToInt32(cx,argv[0],&val); + w = (WORD)val; + if(p->network_byte_order) + w=htons(w); + wr=sendsocket(p->sock,(BYTE*)&w,size); + break; + case sizeof(DWORD): + JS_ValueToInt32(cx,argv[0],&val); + l = val; + if(p->network_byte_order) + l=htonl(l); + wr=sendsocket(p->sock,(BYTE*)&l,size); + break; + default: + /* unknown size */ + dbprintf(TRUE, p, "unsupported binary write size: %d",size); + break; + } + if(wr==size) { + dbprintf(FALSE, p, "sent %u bytes (binary)",size); + *rval = JSVAL_TRUE; + } else { + p->last_error=ERROR_VALUE; + dbprintf(TRUE, p, "send of %u bytes (binary) failed",size); + } + + return(JS_TRUE); +} + + static JSBool js_recv(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { @@ -482,7 +541,14 @@ js_recvfrom(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) char* buf; char ip_addr[64]; char port[32]; + int rd=0; int32 len=512; + uintN n; + BOOL binary=FALSE; + BYTE b; + WORD w; + DWORD l; + jsval data_val=JSVAL_NULL; JSString* str; JSObject* retobj; SOCKADDR_IN addr; @@ -495,29 +561,71 @@ js_recvfrom(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return(JS_FALSE); } - if(argc) - JS_ValueToInt32(cx,argv[0],&len); + *rval = JSVAL_NULL; - if((buf=(char*)malloc(len+1))==NULL) { - JS_ReportError(cx,"Error allocating %u bytes",len+1); - return(JS_FALSE); + for(n=0;n<argc;n++) { + if(JSVAL_IS_BOOLEAN(argv[n])) { + binary=JSVAL_TO_BOOLEAN(argv[n]); + if(binary) + len=sizeof(DWORD); + } else + JS_ValueToInt32(cx,argv[n],&len); } addrlen=sizeof(addr); - len = recvfrom(p->sock,buf,len,0,(SOCKADDR*)&addr,&addrlen); - if(len<0) { + + if(binary) { /* Binary/Integer Data */ + + switch(len) { + case sizeof(BYTE): + if((rd=recvfrom(p->sock,&b,len,0,(SOCKADDR*)&addr,&addrlen))==len) + data_val = INT_TO_JSVAL(b); + break; + case sizeof(WORD): + if((rd=recvfrom(p->sock,(BYTE*)&w,len,0,(SOCKADDR*)&addr,&addrlen))==len) { + if(p->network_byte_order) + w=ntohs(w); + data_val = INT_TO_JSVAL(w); + } + break; + case sizeof(DWORD): + if((rd=recvfrom(p->sock,(BYTE*)&l,len,0,(SOCKADDR*)&addr,&addrlen))==len) { + if(p->network_byte_order) + l=ntohl(l); + data_val = INT_TO_JSVAL(l); + } + break; + } + + if(rd!=len) { + p->last_error=ERROR_VALUE; + return(JS_TRUE); + } + + } else { /* String Data */ + + if((buf=(char*)malloc(len+1))==NULL) { + JS_ReportError(cx,"Error allocating %u bytes",len+1); + return(JS_FALSE); + } + + len = recvfrom(p->sock,buf,len,0,(SOCKADDR*)&addr,&addrlen); + if(len<0) { + free(buf); + p->last_error=ERROR_VALUE; + return(JS_TRUE); + } + buf[len]=0; + + str = JS_NewStringCopyZ(cx, buf); free(buf); - p->last_error=ERROR_VALUE; - *rval = JSVAL_NULL; - return(JS_TRUE); - } - buf[len]=0; - str = JS_NewStringCopyZ(cx, buf); - free(buf); + if(str==NULL) + return(JS_FALSE); + + data_val = STRING_TO_JSVAL(str); + } - if(str==NULL) - return(JS_FALSE); if((retobj=JS_NewObject(cx,&js_recvfrom_class,NULL,obj))==NULL) { JS_ReportError(cx,"JS_NewObject failed"); @@ -525,7 +633,7 @@ js_recvfrom(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } JS_DefineProperty(cx, retobj, "data" - ,STRING_TO_JSVAL(str) + ,data_val ,NULL,NULL,JSPROP_ENUMERATE); sprintf(port,"%u",ntohs(addr.sin_port)); @@ -667,6 +775,54 @@ js_recvline(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return(JS_TRUE); } +static JSBool +js_recvbin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + BYTE b; + WORD w; + DWORD l; + int size=sizeof(DWORD); + int rd=0; + private_t* p; + + *rval = INT_TO_JSVAL(-1); + + if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) { + JS_ReportError(cx,getprivate_failure,WHERE); + return(JS_FALSE); + } + + if(argc) + JS_ValueToInt32(cx,argv[0],(int32*)&size); + + switch(size) { + case sizeof(BYTE): + if((rd=recv(p->sock,&b,size,0))==size) + *rval = INT_TO_JSVAL(b); + break; + case sizeof(WORD): + if((rd=recv(p->sock,(BYTE*)&w,size,0))==size) { + if(p->network_byte_order) + w=ntohs(w); + *rval = INT_TO_JSVAL(w); + } + break; + case sizeof(DWORD): + if((rd=recv(p->sock,(BYTE*)&l,size,0))==size) { + if(p->network_byte_order) + l=ntohl(l); + *rval = INT_TO_JSVAL(l); + } + break; + } + + if(rd!=size) + p->last_error=ERROR_VALUE; + + return(JS_TRUE); +} + + static JSBool js_getsockopt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { @@ -814,6 +970,7 @@ enum { ,SOCK_PROP_REMOTE_IP ,SOCK_PROP_REMOTE_PORT ,SOCK_PROP_TYPE + ,SOCK_PROP_NETWORK_ORDER }; @@ -831,6 +988,7 @@ static char* socket_prop_desc[] = { ,"remote IP address (in dotted-decimal format)" ,"remote TCP or UDP port number" ,"socket type, <tt>SOCK_STREAM</tt> (TCP) or <tt>SOCK_DGRAM</tt> (UDP)" + ,"<i>true</i> if binary data is to be sent in Network Byte Order (big end first)" ,NULL }; #endif @@ -863,6 +1021,9 @@ static JSBool js_socket_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp) JS_ValueToBoolean(cx,*vp,&(p->nonblocking)); ioctlsocket(p->sock,FIONBIO,(ulong*)&(p->nonblocking)); break; + case SOCK_PROP_NETWORK_ORDER: + JS_ValueToBoolean(cx,*vp,&(p->network_byte_order)); + break; } return(JS_TRUE); @@ -960,6 +1121,10 @@ static JSBool js_socket_get(JSContext *cx, JSObject *obj, jsval id, jsval *vp) case SOCK_PROP_TYPE: *vp = INT_TO_JSVAL(p->type); break; + case SOCK_PROP_NETWORK_ORDER: + *vp = INT_TO_JSVAL(p->network_byte_order); + break; + } return(TRUE); @@ -982,6 +1147,7 @@ static struct JSPropertySpec js_socket_properties[] = { { "remote_ip_address" ,SOCK_PROP_REMOTE_IP ,SOCK_PROP_FLAGS, NULL,NULL}, { "remote_port" ,SOCK_PROP_REMOTE_PORT ,SOCK_PROP_FLAGS, NULL,NULL}, { "type" ,SOCK_PROP_TYPE ,SOCK_PROP_FLAGS, NULL,NULL}, + { "network_byte_order",SOCK_PROP_NETWORK_ORDER,JSPROP_ENUMERATE, NULL,NULL}, {0} }; @@ -1024,6 +1190,10 @@ static jsMethodSpec js_socket_functions[] = { {"sendfile", js_sendfile, 1, JSTYPE_BOOLEAN, JSDOCSTR("filename") ,JSDOCSTR("send an entire file over the socket") }, + {"writeBin", js_sendbin, 1, JSTYPE_ALIAS }, + {"sendBin", js_sendbin, 1, JSTYPE_BOOLEAN, JSDOCSTR("number value [,number bytes]") + ,JSDOCSTR("send a binary integer over the socket, default number of bytes is 4 (32-bits)") + }, {"read", js_recv, 1, JSTYPE_ALIAS }, {"recv", js_recv, 1, JSTYPE_STRING, JSDOCSTR("[maxlen]") ,JSDOCSTR("receive a string, default maxlen is 512 characters (AKA read)") @@ -1036,8 +1206,14 @@ static jsMethodSpec js_socket_functions[] = { {"recvline", js_recvline, 0, JSTYPE_STRING, JSDOCSTR("[maxlen] [,timeout]") ,JSDOCSTR("receive a line-feed terminated string, default maxlen is 512 characters, default timeout is 30 seconds (AKA readline and readln)") }, - {"recvfrom", js_recvfrom, 0, JSTYPE_OBJECT, JSDOCSTR("[maxlen]") - ,JSDOCSTR("receive a string from (typically UDP) socket, returns object with <i>ip_address</i> and <i>port</i> of sender along with <i>data</i>") + {"recvfrom", js_recvfrom, 0, JSTYPE_OBJECT, JSDOCSTR("[bool binary] [,maxlen or int_size]") + ,JSDOCSTR("receive data (string or integer) from a socket (typically UDP)" + "<p>returns object with <i>ip_address</i> and <i>port</i> of sender along with <i>data</i>" + "<p><i>binary</i> defaults to <i>false</i>, <i>int_size</i> defaults to 4 bytes (32-bits)") + }, + {"readBin", js_recvbin, 0, JSTYPE_ALIAS }, + {"recvBin", js_recvbin, 0, JSTYPE_NUMBER, JSDOCSTR("[number bytes]") + ,JSDOCSTR("receive a binary integer from the socket, default number of bytes is 4 (32-bits)") }, {"getoption", js_getsockopt, 1, JSTYPE_NUMBER, JSDOCSTR("option") ,JSDOCSTR("get socket option value, option may be socket option name (see sockopts in sockdefs.js) or number") @@ -1078,6 +1254,7 @@ js_socket_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsv return(JS_FALSE); } p->type = type; + p->network_byte_order = TRUE; if(!JS_SetPrivate(cx, obj, p)) { JS_ReportError(cx,"JS_SetPrivate failed"); @@ -1135,6 +1312,7 @@ JSObject* DLLCALL js_CreateSocketObject(JSContext* cx, JSObject* parent, char *n p->sock = sock; p->external = TRUE; + p->network_byte_order = TRUE; if(!JS_SetPrivate(cx, obj, p)) { dbprintf(TRUE, p, "JS_SetPrivate failed");