diff --git a/utils/Makefile b/utils/Makefile index d816e772..646adcb4 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -6,6 +6,7 @@ all: $(TARGETS) wsproxy: wsproxy.o websocket.o md5.o $(CC) $(LDFLAGS) $^ -lssl -lcrypto -lresolv -o $@ +wswrapper.o: wswrapper.h wswrapper.so: wswrapper.o md5.o $(CC) $(LDFLAGS) $^ -shared -fPIC -ldl -lresolv -o $@ diff --git a/utils/wswrapper.c b/utils/wswrapper.c index ca4bd987..b3fd716a 100644 --- a/utils/wswrapper.c +++ b/utils/wswrapper.c @@ -3,10 +3,15 @@ * Copyright 2010 Joel Martin * Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3) * - * Use wswrap to run a program using the wrapper. + * wswrapper.so is meant to be LD preloaded. Use wswrap to run a program using + * wswrapper.so. */ -/* WARNING: multi-threaded programs may not work */ +/* + * Limitations: + * - multi-threaded programs may not work + * - programs using ppoll or epoll will not work correctly + */ #include #include @@ -14,55 +19,13 @@ #define __USE_GNU 1 // Pull in RTLD_NEXT #include +#include #include #include #include #include /* base64 encode/decode */ #include "md5.h" - -//#define DO_DEBUG 1 - -#ifdef DO_DEBUG -#define DEBUG(...) \ - if (DO_DEBUG) { \ - fprintf(stderr, "wswrapper: "); \ - fprintf(stderr, __VA_ARGS__); \ - } -#else -#define DEBUG(...) -#endif - -#define MSG(...) \ - fprintf(stderr, "wswrapper: "); \ - fprintf(stderr, __VA_ARGS__); - -#define RET_ERROR(eno, ...) \ - fprintf(stderr, "wswrapper error: "); \ - fprintf(stderr, __VA_ARGS__); \ - errno = eno; \ - return -1; - - -const char _WS_response[] = "\ -HTTP/1.1 101 Web Socket Protocol Handshake\r\n\ -Upgrade: WebSocket\r\n\ -Connection: Upgrade\r\n\ -%sWebSocket-Origin: %s\r\n\ -%sWebSocket-Location: %s://%s%s\r\n\ -%sWebSocket-Protocol: sample\r\n\ -\r\n%s"; - -#define WS_BUFSIZE 65536 - -/* Buffers and state for each wrapped WebSocket connection */ -typedef struct { - char rbuf[WS_BUFSIZE]; - char sbuf[WS_BUFSIZE]; - int rcarry_cnt; - char rcarry[3]; - int newframe; -} _WS_connection; - +#include "wswrapper.h" /* * If WSWRAP_PORT environment variable is set then listen to the bind fd that @@ -70,7 +33,9 @@ typedef struct { * called on. */ int _WS_listen_fd = -1; -_WS_connection *_WS_connections[65546]; +int _WS_nfds = 0; +int _WS_fds[WS_MAX_FDS]; +_WS_connection *_WS_connections[65536]; /* @@ -243,6 +208,71 @@ int _WS_handshake(int sockfd) return ret; } +/* + * Check WebSockets socket and return a positive value if there is enough data + * to base64 decode (a 4 byte chunk). If nonblock is not set then it will + * block until there is enough data (or until an error occurs). + */ +ssize_t _WS_ready(int sockfd, int nonblock) +{ + _WS_connection *ws = _WS_connections[sockfd]; + char buf[6]; + int count, len, flags, i; + static void * (*rfunc)(); + if (!rfunc) rfunc = (void *(*)()) dlsym(RTLD_NEXT, "recv"); + + TRACE(">> _WS_ready(%d, %d)\n", sockfd, nonblock); + + count = 4 + ws->newframe; + flags = MSG_PEEK; + if (nonblock) { + flags |= MSG_DONTWAIT; + } + while (1) { + len = (int) rfunc(sockfd, buf, count, flags); + if (len < 1) { + TRACE("<< _WS_ready(%d, %d) len < 1, errno: %d\n", + sockfd, nonblock, errno); + return len; + } + if (len >= 2 && buf[0] == '\x00' && buf[1] == '\xff') { + /* Strip emtpy frame */ + DEBUG("_WS_ready(%d, %d), strip empty\n", sockfd, nonblock); + len = (int) rfunc(sockfd, buf, 2, 0); + if (len < 2) { + MSG("Failed to strip empty frame headers\n"); + TRACE("<< _WS_ready: failed to strip empty frame headers\n"); + return len; + } else if (len == 2 && nonblock) { + errno = EAGAIN; + TRACE("<< _WS_ready(%d, %d), len == 2, EAGAIN\n", + sockfd, nonblock); + return -1; + } + continue; + } + if (len < count) { + if (nonblock) { + errno = EAGAIN; + TRACE("<< _WS_ready(%d, %d), len < count, EAGAIN\n", + sockfd, nonblock); + return -1; + } else { + fprintf(stderr, "_WS_ready(%d, %d), loop: len %d, buf:", + sockfd, nonblock, len, (unsigned char) buf[0]); + for (i = 0; i < len; i++) { + fprintf(stderr, "%d", (unsigned char) buf[i]); + } + fprintf(stderr, "\n"); + + continue; + } + } + TRACE("<< _WS_ready(%d, %d) len: %d\n", sockfd, nonblock, len); + return len; + } +} + /* * WebSockets recv/read interposer routine */ @@ -250,7 +280,8 @@ ssize_t _WS_recv(int recvf, int sockfd, const void *buf, size_t len, int flags) { _WS_connection *ws = _WS_connections[sockfd]; - int rawcount, deccount, left, rawlen, retlen, decodelen; + int rawcount, deccount, left, striplen, decodelen, ready; + ssize_t retlen, rawlen; int sockflags; int i; char *fstart, *fend, *cstart; @@ -259,10 +290,6 @@ ssize_t _WS_recv(int recvf, int sockfd, const void *buf, if (!rfunc) rfunc = (void *(*)()) dlsym(RTLD_NEXT, "recv"); if (!rfunc2) rfunc2 = (void *(*)()) dlsym(RTLD_NEXT, "read"); - if (len == 0) { - return 0; - } - if (! ws) { // Not our file descriptor, just pass through if (recvf) { @@ -271,9 +298,20 @@ ssize_t _WS_recv(int recvf, int sockfd, const void *buf, return (ssize_t) rfunc2(sockfd, buf, len); } } - DEBUG("_WS_recv(%d, _, %d) called\n", sockfd, len); + TRACE(">> _WS_recv(%d)\n", sockfd); + + if (len == 0) { + TRACE("<< _WS_recv(%d) len == 0\n", sockfd); + return 0; + } sockflags = fcntl(sockfd, F_GETFL, 0); + if (sockflags & O_NONBLOCK) { + TRACE("_WS_recv(%d, _, %d) with O_NONBLOCK\n", sockfd, len); + } else { + TRACE("_WS_recv(%d, _, %d) without O_NONBLOCK\n", sockfd, len); + } + left = len; retlen = 0; @@ -309,54 +347,29 @@ ssize_t _WS_recv(int recvf, int sockfd, const void *buf, RET_ERROR(ENOMEM, "recv of %d bytes is larger than buffer\n", rawcount); } - i = 0; - while (1) { - /* Peek at everything available */ - rawlen = (int) rfunc(sockfd, ws->rbuf, WS_BUFSIZE-1, - flags | MSG_PEEK); - if (rawlen <= 0) { - DEBUG("_WS_recv: returning because rawlen %d\n", rawlen); - return (ssize_t) rawlen; - } - fstart = ws->rbuf; - - /* Strip empty frames */ - if (rawlen >= 2 && fstart[0] == '\x00' && fstart[1] == '\xff') { - rawlen = (int) rfunc(sockfd, ws->rbuf, 2, flags); - if (rawlen != 2) { - RET_ERROR(EIO, "Could not strip empty frame headers\n"); - } - continue; - } - - fstart[rawlen] = '\x00'; - - if (rawlen - ws->newframe >= 4) { - /* We have enough to base64 decode at least 1 byte */ - break; - } - /* Not enough to base64 decode */ - if (sockflags & O_NONBLOCK) { - /* Just tell the caller to call again */ - DEBUG("_WS_recv: returning because O_NONBLOCK, rawlen %d\n", rawlen); - errno = EAGAIN; - return -1; - } - /* Repeat until at least 1 byte (4 raw bytes) to decode */ - i++; - if (i > 1000000) { - MSG("Could not send final part of frame\n"); + ready = _WS_ready(sockfd, 0); + if (ready < 1) { + if (retlen) { + /* We had some carry over, don't error until next call */ + errno = 0; + } else { + retlen = ready; } + TRACE("<< _WS_recv(%d, _, %d) retlen %d\n", sockfd, len, retlen); + return retlen; } - /* - DEBUG("_WS_recv, left: %d, len: %d, rawlen: %d, newframe: %d, raw: ", - left, len, rawlen, _WS_newframe); - for (i = 0; i < rawlen; i++) { - DEBUG("%u,", (unsigned char) ((char *) fstart)[i]); + /* We have enough data to return something */ + + /* Peek at everything available */ + rawlen = (ssize_t) rfunc(sockfd, ws->rbuf, WS_BUFSIZE-1, + flags | MSG_PEEK); + if (rawlen <= 0) { + RET_ERROR(EPROTO, "Socket was ready but then had failure"); } - DEBUG("\n"); - */ + fstart = ws->rbuf; + fstart[rawlen] = '\x00'; + if (ws->newframe) { if (fstart[0] != '\x00') { @@ -391,7 +404,7 @@ ssize_t _WS_recv(int recvf, int sockfd, const void *buf, /* Now consume what was processed */ if (flags & MSG_PEEK) { - MSG("*** Got MSG_PEEK ***\n"); + DEBUG("_WS_recv(%d, _, %d) MSG_PEEK, not consuming\n", sockfd, len); } else { rfunc(sockfd, ws->rbuf, fstart - ws->rbuf + deccount + ws->newframe, flags); } @@ -423,19 +436,13 @@ ssize_t _WS_recv(int recvf, int sockfd, const void *buf, DEBUG("Saving carry bytes: %u,%u\n", ws->rcarry[0], ws->rcarry[1]); } else { - MSG("Waah2!\n"); + RET_ERRO(EPROTO, "Too many carry bytes!\n"); } } } ((char *) buf)[retlen] = '\x00'; - /* - DEBUG("*** recv %s as ", fstart); - for (i = 0; i < retlen; i++) { - DEBUG("%u,", (unsigned char) ((char *) buf)[i]); - } - DEBUG(" (%d -> %d): %d\n", deccount, decodelen, retlen); - */ + TRACE("<< _WS_recv(%d) retlen %d\n", sockfd, retlen); return retlen; } @@ -451,7 +458,7 @@ ssize_t _WS_send(int sendf, int sockfd, const void *buf, char * target; int i; static void * (*sfunc)(), * (*sfunc2)(); - if (!sfunc) sfunc = (void *(*)()) dlsym(RTLD_NEXT, "send"); + if (!sfunc) sfunc = (void *(*)()) dlsym(RTLD_NEXT, "send"); if (!sfunc2) sfunc2 = (void *(*)()) dlsym(RTLD_NEXT, "write"); if (! ws) { @@ -462,7 +469,7 @@ ssize_t _WS_send(int sendf, int sockfd, const void *buf, return (ssize_t) sfunc2(sockfd, buf, len); } } - DEBUG("_WS_send(%d, _, %d) called\n", sockfd, len); + TRACE(">> _WS_send(%d, _, %d)\n", sockfd, len); sockflags = fcntl(sockfd, F_GETFL, 0); @@ -484,7 +491,7 @@ ssize_t _WS_send(int sendf, int sockfd, const void *buf, rlen = (int) sfunc(sockfd, ws->sbuf, rawlen, flags); if (rlen <= 0) { - /* Couldn't send, just return */ + TRACE("<< _WS_send(%d, _, %d) send failed, returning\n", sockfd, len); return rlen; } else if (rlen < rawlen) { /* Spin until we can send a whole base64 chunck and frame end */ @@ -503,13 +510,14 @@ ssize_t _WS_send(int sendf, int sockfd, const void *buf, } else if (clen == 0) { MSG("_WS_send: got clen %d\n", clen); } else if (!(sockflags & O_NONBLOCK)) { - MSG("_WS_send: clen %d\n", clen); + MSG("<< _WS_send: clen %d\n", clen); return clen; } if (i > 1000000) { - MSG("Could not send final part of frame\n"); + RET_ERROR(EIO, "Could not send final part of frame\n"); } } while (left > 0); + //DEBUG("_WS_send: spins until finished %d\n", i); DEBUG("_WS_send: spins until finished %d\n", i); } @@ -529,16 +537,114 @@ ssize_t _WS_send(int sendf, int sockfd, const void *buf, /* Scale return value for base64 encoding size */ retlen = (retlen*3)/4; - /* - DEBUG("*** send "); - for (i = 0; i < retlen; i++) { - DEBUG("%u,", (unsigned char) ((char *)buf)[i]); - } - DEBUG(" as '%s' (%d)\n", ws->sbuf+1, rlen); - */ + TRACE(">> _WS_send(%d, _, %d) retlen %d\n", sockfd, len, retlen); return (ssize_t) retlen; } +int _WS_select(int mode, int nfds, fd_set *readfds, + fd_set *writefds, fd_set *exceptfds, + void *timeptr, const sigset_t *sigmask) +{ + _WS_connection *ws; + fd_set carryfds, savefds; + struct timeval savetv; + struct timespec savets; + int carrycnt = 0; + int ret, i, ready, fd; + static void * (*func)(), * (*func2)(); + if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "select"); + if (!func2) func2 = (void *(*)()) dlsym(RTLD_NEXT, "pselect"); + + if ((_WS_listen_fd == -1) || (_WS_nfds == 0)) { + if (mode == 0) { + ret = (int) func(nfds, readfds, writefds, exceptfds, + (struct timeval *)timeptr); + } else if (mode == 1) { + ret = (int) func2(nfds, readfds, writefds, exceptfds, + (struct timespec *)timeptr, sigmask); + } + return ret; + } + + TRACE(">> _WS_select(%d, %d, _, _, _, _) called\n", mode, nfds); + memcpy(&savetv, timeptr, sizeof(savetv)); + memcpy(&savets, timeptr, sizeof(savets)); + + /* If we have carry-over return it right away */ + FD_ZERO(&carryfds); + if (readfds) { + memcpy(&savefds, readfds, sizeof(savefds)); + for (i = 0; i < _WS_nfds; i++) { + fd = _WS_fds[i]; + ws = _WS_connections[fd]; + if ((ws->rcarry_cnt) && (FD_ISSET(fd, readfds))) { + FD_SET(fd, &carryfds); + carrycnt++; + } + } + } + if (carrycnt) { + if (writefds) { + FD_ZERO(writefds); + } + if (exceptfds) { + FD_ZERO(writefds); + } + memcpy(readfds, &carryfds, sizeof(carryfds)); + TRACE("<< _WS_select(%d, %d, _, _, _, _) carrycnt %d\n", + mode, nfds, carrycnt); + return carrycnt; + } + + do { + if (mode == 0) { + ret = (int) func(nfds, readfds, writefds, exceptfds, + (struct timeval *)timeptr); + } else if (mode == 1) { + ret = (int) func2(nfds, readfds, writefds, exceptfds, + (struct timespec *)timeptr, sigmask); + } + if (! readfds) { + break; + } + if (ret <= 0) { + break; + } + + for (i = 0; i < _WS_nfds; i++) { + fd = _WS_fds[i]; + ws = _WS_connections[fd]; + if (FD_ISSET(fd, readfds)) { + ready = _WS_ready(fd, 1); + if (ready == 0) { + /* 0 means EOF which is also a ready condition */ + DEBUG("_WS_select: detected %d is closed\n", fd); + } else if (ready < 0) { + DEBUG("_WS_select: FD_CLR(%d,readfds) - not enough to decode\n", fd); + FD_CLR(fd, readfds); + ret--; + } + } + } + errno = 0; /* errno could be set by _WS_ready */ + + /* + * If all the ready readfds were WebSockets, but none of + * them were really ready (empty frames) then repeat. + */ + if (ret == 0) { + /* Restore original values*/ + /* TODO: fix timeptr for repeat */ + memcpy(timeptr, &savetv, sizeof(savetv)); + memcpy(timeptr, &savets, sizeof(savets)); + memcpy(readfds, &savefds, sizeof(savefds)); + } + } while (ret == 0); + + TRACE("<< _WS_select(%d, %d, _, _, _, _) ret %d, errno %d\n", + mode, nfds, ret, errno); + return ret; +} /* * Overload (LD_PRELOAD) standard library network routines @@ -562,37 +668,37 @@ int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) char * WSWRAP_PORT, * end; int ret, envport, bindport = htons(addr_in->sin_port); if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "bind"); - DEBUG("bind(%d, _, %d) called\n", sockfd, addrlen); + TRACE(">> bind(%d, _, %d) called\n", sockfd, addrlen); ret = (int) func(sockfd, addr, addrlen); if (addr_in->sin_family != AF_INET) { // TODO: handle IPv6 - DEBUG("bind, ignoring non-IPv4 socket\n"); + TRACE("<< bind, ignoring non-IPv4 socket\n"); return ret; } WSWRAP_PORT = getenv("WSWRAP_PORT"); if ((! WSWRAP_PORT) || (*WSWRAP_PORT == '\0')) { - // TODO: interpose on all sockets - DEBUG("bind, not interposing: WSWRAP_PORT is not set\n"); + // TODO: interpose on all sockets when WSWRAP_PORT not set + TRACE("<< bind, not interposing: WSWRAP_PORT is not set\n"); return ret; } envport = strtol(WSWRAP_PORT, &end, 10); if ((envport == 0) || (*end != '\0')) { - MSG("bind, not interposing: WSWRAP_PORT is not a number\n"); + TRACE("<< bind, not interposing: WSWRAP_PORT is not a number\n"); return ret; } if (envport != bindport) { - DEBUG("bind, not interposing on port: %d (fd %d)\n", bindport, sockfd); + TRACE("<< bind, not interposing on port: %d (fd %d)\n", bindport, sockfd); return ret; } - MSG("bind, interposing on port: %d (fd %d)\n", envport, sockfd); _WS_listen_fd = sockfd; + TRACE("<< bind, interposing on port: %d (fd %d)\n", envport, sockfd); return ret; } @@ -601,25 +707,28 @@ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) int fd, ret, envfd; static void * (*func)(); if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "accept"); - DEBUG("accept(%d, _, _) called\n", sockfd); + TRACE("<< accept(%d, _, _) called\n", sockfd); fd = (int) func(sockfd, addr, addrlen); if (_WS_listen_fd == -1) { - DEBUG("not interposing\n"); + TRACE("<< accept: not interposing\n"); return fd; } if (_WS_listen_fd != sockfd) { - DEBUG("not interposing on fd %d\n", sockfd); + TRACE("<< accept: not interposing on fd %d\n", sockfd); return fd; } if (_WS_connections[fd]) { - MSG("error, already interposing on fd %d\n", fd); + RET_ERROR(EINVAL, "already interposing on fd %d\n", fd); } else { /* It's a port we're interposing on so allocate memory for it */ + if (_WS_nfds >= WS_MAX_FDS) { + RET_ERROR(ENOMEM, "Too many interposer fds\n"); + } if (! (_WS_connections[fd] = malloc(sizeof(_WS_connection)))) { RET_ERROR(ENOMEM, "Could not allocate interposer memory\n"); } @@ -627,11 +736,16 @@ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) _WS_connections[fd]->rcarry[0] = '\0'; _WS_connections[fd]->newframe = 1; + /* Add to search list for select/pselect */ + _WS_fds[_WS_nfds] = fd; + _WS_nfds++; + ret = _WS_handshake(fd); if (ret < 0) { free(_WS_connections[fd]); _WS_connections[fd] = NULL; errno = EPROTO; + TRACE("<< accept(%d, _, _): ret %d\n", sockfd, ret); return ret; } MSG("interposing on fd %d (allocated memory)\n", fd); @@ -642,13 +756,28 @@ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) int close(int fd) { + int i; static void * (*func)(); if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "close"); if (_WS_connections[fd]) { + TRACE(">> close(%d)\n", fd); free(_WS_connections[fd]); _WS_connections[fd] = NULL; + + /* Remove from the search list for select/pselect */ + for (i = 0; i < _WS_nfds; i++) { + if (_WS_fds[i] == fd) { + break; + } + } + if (_WS_nfds - i - 1 > 0) { + memmove(_WS_fds + i, _WS_fds + i + 1, _WS_nfds - i - 1); + } + _WS_nfds--; + MSG("finished interposing on fd %d (freed memory)\n", fd); + TRACE("<< close(%d)\n", fd); } return (int) func(fd); } @@ -656,25 +785,50 @@ int close(int fd) ssize_t read(int fd, void *buf, size_t count) { - //DEBUG("read(%d, _, %d) called\n", fd, count); + //TRACE("read(%d, _, %d) called\n", fd, count); return (ssize_t) _WS_recv(0, fd, buf, count, 0); } ssize_t write(int fd, const void *buf, size_t count) { - //DEBUG("write(%d, _, %d) called\n", fd, count); + //TRACE("write(%d, _, %d) called\n", fd, count); return (ssize_t) _WS_send(0, fd, buf, count, 0); } ssize_t recv(int sockfd, void *buf, size_t len, int flags) { - //DEBUG("recv(%d, _, %d, %d) called\n", sockfd, len, flags); + //TRACE("recv(%d, _, %d, %d) called\n", sockfd, len, flags); return (ssize_t) _WS_recv(1, sockfd, buf, len, flags); } ssize_t send(int sockfd, const void *buf, size_t len, int flags) { - //DEBUG("send(%d, _, %d, %d) called\n", sockfd, len, flags); + //TRACE("send(%d, _, %d, %d) called\n", sockfd, len, flags); return (ssize_t) _WS_send(1, sockfd, buf, len, flags); } +int select(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout) +{ + //TRACE("select(%d, _, _, _, _) called\n", nfds); + return _WS_select(0, nfds, readfds, writefds, exceptfds, + (void *) timeout, NULL); +} + +int pselect(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, const struct timespec *timeout, + const sigset_t *sigmask) +{ + TRACE("pselect(%d, _, _, _, _, _) called\n", nfds); + return _WS_select(1, nfds, readfds, writefds, exceptfds, + (void *) timeout, sigmask); +} + +int poll(struct pollfd *fds, nfds_t nfds, int timeout) +{ + //TRACE("poll(_, %d, _) called\n", nfds); + static void * (*func)(); + if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "poll"); + + return (int) func(fds, nfds, timeout); +} diff --git a/utils/wswrapper.h b/utils/wswrapper.h new file mode 100644 index 00000000..48d4aa2d --- /dev/null +++ b/utils/wswrapper.h @@ -0,0 +1,65 @@ +/* + * wswrap/wswrapper: Add WebSockets support to any service. + * Copyright 2010 Joel Martin + * Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3) + * + * wswrapper.so is meant to be LD preloaded. Use wswrap to run a program using + * wswrapper.so. + */ + +//#define DO_DEBUG 1 +//#define DO_TRACE 1 + +#ifdef DO_DEBUG +#define DEBUG(...) \ + if (DO_DEBUG) { \ + fprintf(stderr, "wswrapper: "); \ + fprintf(stderr, __VA_ARGS__); \ + } +#else +#define DEBUG(...) +#endif + +#ifdef DO_TRACE +#define TRACE(...) \ + if (DO_TRACE) { \ + fprintf(stderr, "wswrapper: "); \ + fprintf(stderr, __VA_ARGS__); \ + } +#else +#define TRACE(...) +#endif + +#define MSG(...) \ + fprintf(stderr, "wswrapper: "); \ + fprintf(stderr, __VA_ARGS__); + +#define RET_ERROR(eno, ...) \ + fprintf(stderr, "wswrapper error: "); \ + fprintf(stderr, __VA_ARGS__); \ + errno = eno; \ + return -1; + + +const char _WS_response[] = "\ +HTTP/1.1 101 Web Socket Protocol Handshake\r\n\ +Upgrade: WebSocket\r\n\ +Connection: Upgrade\r\n\ +%sWebSocket-Origin: %s\r\n\ +%sWebSocket-Location: %s://%s%s\r\n\ +%sWebSocket-Protocol: sample\r\n\ +\r\n%s"; + +#define WS_BUFSIZE 65536 +#define WS_MAX_FDS 1024 + +/* Buffers and state for each wrapped WebSocket connection */ +typedef struct { + char rbuf[WS_BUFSIZE]; + char sbuf[WS_BUFSIZE]; + int rcarry_cnt; + char rcarry[3]; + int newframe; +} _WS_connection; + +