diff options
author | Alon Zakai <alonzakai@gmail.com> | 2012-09-27 16:11:55 -0700 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2012-09-27 16:11:55 -0700 |
commit | 31f7b2a5a65d2d640343013dac4bb836c3bfba5f (patch) | |
tree | e677a00733440e57ab61fb00a696284c1d7b960b | |
parent | e546b42883bbcabb4657dfcdb523a93d0458f5b3 (diff) |
initial (not-yet-working) sockets implementation
77 files changed, 10617 insertions, 2 deletions
diff --git a/src/library.js b/src/library.js index 3531db97..06b03764 100644 --- a/src/library.js +++ b/src/library.js @@ -6315,6 +6315,75 @@ LibraryManager.library = { ntohl: 'htonl', ntohs: 'htons', + inet_pton__deps: ['__setErrNo', '$ERRNO_CODES'], + inet_pton: function(af, src, dst) { + // int af, const char *src, void *dst + if ((af ^ {{{ cDefine("AF_INET") }}}) !== 0) { ___setErrNo(ERRNO_CODES.EAFNOSUPPORT); return -1; } + var b = Pointer_stringify(src).split("."); + if (b.length !== 4) return 0; + var ret = Number(b[0]) | (Number(b[1]) << 8) | (Number(b[2]) << 16) | (Number(b[3]) << 24); + if (isNaN(ret)) return 0; + setValue(dst, ret, 'i32'); + return 1; + }, + + _inet_ntop_raw: function(addr) { + return (addr & 0xff) + '.' + ((addr >> 8) & 0xff) + '.' + ((addr >> 16) & 0xff) + '.' + ((addr >> 24) & 0xff) + }, + + inet_ntop__deps: ['_inet_ntop_raw'], + inet_ntop: function(af, src, dst, size) { + var addr = getValue(src, 'i32'); + var str = __inet_ntop_raw(addr); + writeStringToMemory(str.substr(0, size), dst, true); + return dst; + }, + + // ========================================================================== + // sockets + // ========================================================================== + + $Sockets__deps: ['__setErrNo', '$ERRNO_CODES'], + $Sockets: { + nextFd: 1, + fds: {}, + sockaddr_in_layout: Runtime.generateStructInfo([ + ['i16', 'sin_family'], + ['i16', 'sin_port'], + ['i32', 'sin_addr'], + ['i64', 'sin_zero'], + ]), + }, + + socket__deps: ['$Sockets'], + socket: function(family, type, protocol) { + var fd = Sockets.nextFd++; + Sockets.fds[fd] = { + connected: false + }; + return fd; + }, + + connect__deps: ['$Sockets', '_inet_ntop_raw', 'ntohs'], + connect: function(fd, addr, addrlen) { + var info = Sockets.fds[fd]; + info.connected = true; + info.addr = getValue(addr + Sockets.sockaddr_in_layout.sin_addr, 'i32'); + info.port = _ntohs(getValue(addr + Sockets.sockaddr_in_layout.sin_port, 'i16')); + info.host = __inet_ntop_raw(info.addr); + info.socket = new WebSocket('ws://' + info.host + ':' + info.port, ['binary']); + info.socket.onmessage = function (event) { + console.log(event.data + ',' + typeof event.data); + } + return 0; + }, + + recv__deps: ['$Sockets'], + recv: function(fd, buf, len, flags) { + //___setErrNo(ERRNO_CODES.EINTR); + return 0; // TODO + }, + // ========================================================================== // emscripten.h // ========================================================================== diff --git a/src/settings.js b/src/settings.js index f8a81711..0b9cf14b 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1124,5 +1124,8 @@ var C_DEFINES = {'SI_MESGQ': '5', 'AT_FDCWD': '-2', 'SIGTTOU': '22', '_CS_POSIX_V7_LP64_OFF64_LDFLAGS': '10', - '_SC_TTY_NAME_MAX': '41'}; + '_SC_TTY_NAME_MAX': '41', + 'AF_INET': '1', + 'AF_INET6': '6' +}; diff --git a/system/include/net/arpa/inet.h b/system/include/net/arpa/inet.h index 06d44183..9d078276 100644 --- a/system/include/net/arpa/inet.h +++ b/system/include/net/arpa/inet.h @@ -16,6 +16,9 @@ uint16_t ntohs(uint16_t netshort); int inet_aton(const char *cp, struct in_addr *addr); char *inet_ntoa(struct in_addr in); +int inet_pton(int af, const char *src, void *dst); +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); + typedef long in_addr_t; in_addr_t inet_addr(const char *cp); diff --git a/tests/runner.py b/tests/runner.py index 475acbe3..7896b7a0 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -7802,7 +7802,7 @@ elif 'browser' in str(sys.argv): print '(moving on..)' def with_report_result(self, code): - return ''' + return r''' #define REPORT_RESULT_INTERNAL(sync) \ char output[1000]; \ sprintf(output, \ @@ -8447,6 +8447,32 @@ elif 'browser' in str(sys.argv): ''') self.btest('pre_run_deps.cpp', expected='10', args=['--pre-js', 'pre.js']) + def zzztest_websockets(self): + try: + def server_func(): + os.system('while true; do (/bin/echo -en "test\x02") | nc -vvvl 8990; done;') + + server = multiprocessing.Process(target=server_func) + server.start() + print '[Socket server on process %d]' % server.pid + + def websockify_func(): + os.system('python ' + path_from_root('third_party', 'websockify', 'run') + ' -vvv 8991 127.0.0.1:8990') + + websockify = multiprocessing.Process(target=websockify_func) + websockify.start() + print '[Websockify on process %d]' % websockify.pid + + self.btest('websockets.c', expected='fleefl') + + finally: + try: + websockify.terminate() + print '[Cleaned up websockify]' + finally: + server.terminate() + print '[Cleaned up socket server]' + elif 'benchmark' in str(sys.argv): # Benchmarks. Run them with argument |benchmark|. To run a specific test, do # |benchmark.test_X|. diff --git a/tests/websockets.c b/tests/websockets.c new file mode 100644 index 00000000..5988b633 --- /dev/null +++ b/tests/websockets.c @@ -0,0 +1,119 @@ +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#if EMSCRIPTEN +#include <emscripten.h> +#endif + +#define EXPECTED_BYTES 5 + +int SocketFD; + +unsigned int get_all_buf(int sock, char* output, unsigned int maxsize) +{ + char buffer[1024]; + + int n; + unsigned int offset = 0; + while((errno = 0, (n = recv(sock, buffer, sizeof(buffer), 0))>0) || + errno == EINTR) { + if(n>0) + { + if (((unsigned int) n)+offset > maxsize) { fprintf(stderr, "too much data!"); exit(EXIT_FAILURE); } + memcpy(output+offset, buffer, n); + offset += n; + } + } + + if(n < 0) { + fprintf(stderr, "error in get_all_buf!"); + exit(EXIT_FAILURE); + } + return offset; +} + +int done = 0; + +void iter(void *arg) { + /* perform read write operations ... */ + static char out[1024*2]; + static int pos = 0; + int n = get_all_buf(SocketFD, out+pos, 1024-pos); + if (n) printf("read! %d\n", n); + pos += n; + if (pos >= EXPECTED_BYTES) { + int i, sum = 0; + for (i=0; i < pos; i++) { + printf("%x\n", out[i]); + sum += out[i]; + } + + shutdown(SocketFD, SHUT_RDWR); + + close(SocketFD); + + done = 1; + +#if EMSCRIPTEN + int result = sum; + REPORT_RESULT(); +#endif + } +} + +int main(void) +{ + struct sockaddr_in stSockAddr; + int Res; + SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (-1 == SocketFD) + { + perror("cannot create socket"); + exit(EXIT_FAILURE); + } + + memset(&stSockAddr, 0, sizeof(stSockAddr)); + + stSockAddr.sin_family = AF_INET; + stSockAddr.sin_port = htons( +#if EMSCRIPTEN + 8991 +#else + 8990 +#endif + ); + Res = inet_pton(AF_INET, "127.0.0.1", &stSockAddr.sin_addr); + + if (0 > Res) { + perror("error: first parameter is not a valid address family"); + close(SocketFD); + exit(EXIT_FAILURE); + } else if (0 == Res) { + perror("char string (second parameter does not contain valid ipaddress)"); + close(SocketFD); + exit(EXIT_FAILURE); + } + + if (-1 == connect(SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr))) { + perror("connect failed"); + close(SocketFD); + exit(EXIT_FAILURE); + + } + +#if EMSCRIPTEN + emscripten_set_main_loop(iter, 0); +#else + while (!done) iter(NULL); +#endif + + return EXIT_SUCCESS; +} + diff --git a/third_party/websockify/.gitignore b/third_party/websockify/.gitignore new file mode 100644 index 00000000..86b78922 --- /dev/null +++ b/third_party/websockify/.gitignore @@ -0,0 +1,10 @@ +*.pyc +*.o +*.so +other/.lein-deps-sum +other/classes +other/lib +.project +.pydevproject +target.cfg +target.cfg.d diff --git a/third_party/websockify/CHANGES.txt b/third_party/websockify/CHANGES.txt new file mode 100644 index 00000000..cbd3f73f --- /dev/null +++ b/third_party/websockify/CHANGES.txt @@ -0,0 +1,18 @@ +Changes +======= + +0.2.0 - Sep 17, 2012 +-------------------- + + * Binary data support in websock.js + * Target config file/dir and multiple targets with token selector + * IPv6 fixes + * SSL target support + * Proxy to/from unix socket + + +0.1.0 - May 11, 2012 +-------------------- + + * Initial versioned release. + diff --git a/third_party/websockify/LICENSE.txt b/third_party/websockify/LICENSE.txt new file mode 100644 index 00000000..916a77a0 --- /dev/null +++ b/third_party/websockify/LICENSE.txt @@ -0,0 +1,11 @@ +websockify is licensed under the LGPL version 3 (see docs/LICENSE.GPL-3 and +docs/LICENSE.LGPL-3) with the following exceptions: + + include/base64.js : Choice of MIT 1.1, GPL-2 or LGPL-2.1 + + include/web-socket-js/ : New BSD license. Source code at + https://github.com/gimite/web-socket-js + + other/kumina.c : Simplified BSD license (2 clause). + Original source at + https://github.com/kumina/wsproxy diff --git a/third_party/websockify/MANIFEST.in b/third_party/websockify/MANIFEST.in new file mode 100644 index 00000000..2dd46d25 --- /dev/null +++ b/third_party/websockify/MANIFEST.in @@ -0,0 +1 @@ +include CHANGES.txt *.py README.md LICENSE.txt diff --git a/third_party/websockify/Makefile b/third_party/websockify/Makefile new file mode 100644 index 00000000..7dc1bc4f --- /dev/null +++ b/third_party/websockify/Makefile @@ -0,0 +1,11 @@ +TARGETS=rebind.so +CFLAGS += -fPIC + +all: $(TARGETS) + +rebind.so: rebind.o + $(CC) $(LDFLAGS) $^ -shared -fPIC -ldl -o $@ + +clean: + rm -f rebind.o rebind.so + diff --git a/third_party/websockify/README.md b/third_party/websockify/README.md new file mode 100644 index 00000000..48c790af --- /dev/null +++ b/third_party/websockify/README.md @@ -0,0 +1,155 @@ +## websockify: WebSockets support for any application/server + +websockify was formerly named wsproxy and was part of the +[noVNC](https://github.com/kanaka/noVNC) project. + +At the most basic level, websockify just translates WebSockets traffic +to normal socket traffic. Websockify accepts the WebSockets handshake, +parses it, and then begins forwarding traffic between the client and +the target in both directions. + +### WebSockets binary data + +Websockify supports all versions of the WebSockets protocol (Hixie and +HyBI). The older Hixie versions of the protocol only support UTF-8 +text payloads. In order to transport binary data over UTF-8 an +encoding must used to encapsulate the data within UTF-8. Websockify +uses base64 to encode all traffic to and from the client. This does +not affect the data between websockify and the server. + +### Encrypted WebSocket connections (wss://) + +To encrypt the traffic using the WebSocket 'wss://' URI scheme you +need to generate a certificate for websockify to load. By default websockify +loads a certificate file name `self.pem` but the `--cert=CERT` option can +override the file name. You can generate a self-signed certificate using +openssl. When asked for the common name, use the hostname of the server where +the proxy will be running: + +``` +openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem +``` + + +### Websock Javascript library + + +The `include/websock.js` Javascript library library provides a Websock +object that is similar to the standard WebSocket object but Websock +enables communication with raw TCP sockets (i.e. the binary stream) +via websockify. This is accomplished by base64 encoding the data +stream between Websock and websockify. + +Websock has built-in receive queue buffering; the message event +does not contain actual data but is simply a notification that +there is new data available. Several rQ* methods are available to +read binary data off of the receive queue. + +The Websock API is documented on the [websock.js API wiki page](https://github.com/kanaka/websockify/wiki/websock.js) + +See the "Wrap a Program" section below for an example of using Websock +and websockify as a browser telnet client (`wstelnet.html`). + + +### Additional websockify features + +These are not necessary for the basic operation. + +* Daemonizing: When the `-D` option is specified, websockify runs + in the background as a daemon process. + +* SSL (the wss:// WebSockets URI): This is detected automatically by + websockify by sniffing the first byte sent from the client and then + wrapping the socket if the data starts with '\x16' or '\x80' + (indicating SSL). + +* Flash security policy: websockify detects flash security policy + requests (again by sniffing the first packet) and answers with an + appropriate flash security policy response (and then closes the + port). This means no separate flash security policy server is needed + for supporting the flash WebSockets fallback emulator. + +* Session recording: This feature that allows recording of the traffic + sent and received from the client to a file using the `--record` + option. + +* Mini-webserver: websockify can detect and respond to normal web + requests on the same port as the WebSockets proxy and Flash security + policy. This functionality is activate with the `--web DIR` option + where DIR is the root of the web directory to serve. + +* Wrap a program: see the "Wrap a Program" section below. |