websockify/other/wswrapper.c

1157 lines
35 KiB
C
Raw Normal View History

/*
* wswrap/wswrapper: Add WebSockets support to any service.
* Copyright 2010 Joel Martin
* Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
*
wsproxy, wstelnet: wrap command, WS telnet client. wswrapper: Getting the wswrapper.c LD_PRELOAD model working has turned out to involve too many dark corners of the glibc/POSIX file descriptor space. I realized that 95% of what I want can be accomplished by adding a "wrap command" mode to wsproxy. The code is still there for now, but consider it experimental at best. Minor fix to dup2 and add dup and dup3 logging. wsproxy Wrap Command: In wsproxy wrap command mode, a command line is specified instead of a target address and port. wsproxy then uses a much simpler LD_PRELOAD library, rebind.so, to move intercept any bind() system calls made by the program. If the bind() call is for the wsproxy listen port number then the real bind() system call is issued for an alternate (free high) port on loopback/localhost. wsproxy then forwards from the listen address/port to the moved port. The --wrap-mode argument takes three options that determine the behavior of wsproxy when the wrapped command returns an exit code (exit or daemonizing): ignore, exit, respawn. For example, this runs vncserver on turns port 5901 into a WebSockets port (rebind.so must be built first): ./utils/wsproxy.py --wrap-mode=ignore 5901 -- vncserver :1 The vncserver command backgrounds itself so the wrap mode is set to "ignore" so that wsproxy keeps running even after it receives an exit code from vncserver. wstelnet: To demonstrate the wrap command mode, I added WebSockets telnet client. For example, this runs telnetd (krb5-telnetd) on turns port 2023 into a WebSockets port (using "respawn" mode since telnetd exits after each connection closes): sudo ./utils/wsproxy.py --wrap-mode=respawn 2023 -- telnetd -debug 2023 Then the utils/wstelnet.html page can be used to connect to the telnetd server on port 2023. The telnet client includes VT100.js (from http://code.google.com/p/sshconsole) which handles the terminal emulation and rendering. rebind: The rebind LD_PRELOAD library is used by wsproxy in wrap command mode to intercept bind() system calls and move the port to a different port on loopback/localhost. The rebind.so library can be built by running make in the utils directory. The rebind library can be used separately from wsproxy by setting the REBIND_OLD_PORT and REBIND_NEW_PORT environment variables prior to executing a command. For example: export export REBIND_PORT_OLD="23" export export REBIND_PORT_NEW="65023" LD_PRELOAD=./rebind.so telnetd -debug 23 Alternately, the rebind script does the same thing: rebind 23 65023 telnetd -debug 23 Other changes/notes: - wsproxy no longer daemonizes by default. Remove -f/--foreground option and add -D/--deamon option. - When wsproxy is used to wrap a command in "respawn" mode, the command will not be respawn more often than 3 times within 10 seconds. - Move getKeysym routine out of Canvas object so that it can be called directly.
2011-01-12 19:15:11 +00:00
* wswrapper is an LD_PRELOAD library that converts a TCP listen socket of an
* existing program to a be a WebSockets socket. The `wswrap` script can be
* used to easily launch a program using wswrapper. Here is an example of
* using wswrapper with vncserver. wswrapper will convert the socket listening
* on port 5901 to be a WebSockets port:
*
* cd noVNC/utils
* ./wswrap 5901 vncserver -geometry 640x480 :1
*
* This is tricky a subtle process so there are some serious limitations:
* - multi-threaded programs may not work
wsproxy, wstelnet: wrap command, WS telnet client. wswrapper: Getting the wswrapper.c LD_PRELOAD model working has turned out to involve too many dark corners of the glibc/POSIX file descriptor space. I realized that 95% of what I want can be accomplished by adding a "wrap command" mode to wsproxy. The code is still there for now, but consider it experimental at best. Minor fix to dup2 and add dup and dup3 logging. wsproxy Wrap Command: In wsproxy wrap command mode, a command line is specified instead of a target address and port. wsproxy then uses a much simpler LD_PRELOAD library, rebind.so, to move intercept any bind() system calls made by the program. If the bind() call is for the wsproxy listen port number then the real bind() system call is issued for an alternate (free high) port on loopback/localhost. wsproxy then forwards from the listen address/port to the moved port. The --wrap-mode argument takes three options that determine the behavior of wsproxy when the wrapped command returns an exit code (exit or daemonizing): ignore, exit, respawn. For example, this runs vncserver on turns port 5901 into a WebSockets port (rebind.so must be built first): ./utils/wsproxy.py --wrap-mode=ignore 5901 -- vncserver :1 The vncserver command backgrounds itself so the wrap mode is set to "ignore" so that wsproxy keeps running even after it receives an exit code from vncserver. wstelnet: To demonstrate the wrap command mode, I added WebSockets telnet client. For example, this runs telnetd (krb5-telnetd) on turns port 2023 into a WebSockets port (using "respawn" mode since telnetd exits after each connection closes): sudo ./utils/wsproxy.py --wrap-mode=respawn 2023 -- telnetd -debug 2023 Then the utils/wstelnet.html page can be used to connect to the telnetd server on port 2023. The telnet client includes VT100.js (from http://code.google.com/p/sshconsole) which handles the terminal emulation and rendering. rebind: The rebind LD_PRELOAD library is used by wsproxy in wrap command mode to intercept bind() system calls and move the port to a different port on loopback/localhost. The rebind.so library can be built by running make in the utils directory. The rebind library can be used separately from wsproxy by setting the REBIND_OLD_PORT and REBIND_NEW_PORT environment variables prior to executing a command. For example: export export REBIND_PORT_OLD="23" export export REBIND_PORT_NEW="65023" LD_PRELOAD=./rebind.so telnetd -debug 23 Alternately, the rebind script does the same thing: rebind 23 65023 telnetd -debug 23 Other changes/notes: - wsproxy no longer daemonizes by default. Remove -f/--foreground option and add -D/--deamon option. - When wsproxy is used to wrap a command in "respawn" mode, the command will not be respawn more often than 3 times within 10 seconds. - Move getKeysym routine out of Canvas object so that it can be called directly.
2011-01-12 19:15:11 +00:00
* - programs that fork may behave in strange and mysterious ways (such as
* fork bombing your system)
* - programs using ppoll or epoll will not work correctly
* - doesn't support fopencookie, streams, putc, etc.
wsproxy, wstelnet: wrap command, WS telnet client. wswrapper: Getting the wswrapper.c LD_PRELOAD model working has turned out to involve too many dark corners of the glibc/POSIX file descriptor space. I realized that 95% of what I want can be accomplished by adding a "wrap command" mode to wsproxy. The code is still there for now, but consider it experimental at best. Minor fix to dup2 and add dup and dup3 logging. wsproxy Wrap Command: In wsproxy wrap command mode, a command line is specified instead of a target address and port. wsproxy then uses a much simpler LD_PRELOAD library, rebind.so, to move intercept any bind() system calls made by the program. If the bind() call is for the wsproxy listen port number then the real bind() system call is issued for an alternate (free high) port on loopback/localhost. wsproxy then forwards from the listen address/port to the moved port. The --wrap-mode argument takes three options that determine the behavior of wsproxy when the wrapped command returns an exit code (exit or daemonizing): ignore, exit, respawn. For example, this runs vncserver on turns port 5901 into a WebSockets port (rebind.so must be built first): ./utils/wsproxy.py --wrap-mode=ignore 5901 -- vncserver :1 The vncserver command backgrounds itself so the wrap mode is set to "ignore" so that wsproxy keeps running even after it receives an exit code from vncserver. wstelnet: To demonstrate the wrap command mode, I added WebSockets telnet client. For example, this runs telnetd (krb5-telnetd) on turns port 2023 into a WebSockets port (using "respawn" mode since telnetd exits after each connection closes): sudo ./utils/wsproxy.py --wrap-mode=respawn 2023 -- telnetd -debug 2023 Then the utils/wstelnet.html page can be used to connect to the telnetd server on port 2023. The telnet client includes VT100.js (from http://code.google.com/p/sshconsole) which handles the terminal emulation and rendering. rebind: The rebind LD_PRELOAD library is used by wsproxy in wrap command mode to intercept bind() system calls and move the port to a different port on loopback/localhost. The rebind.so library can be built by running make in the utils directory. The rebind library can be used separately from wsproxy by setting the REBIND_OLD_PORT and REBIND_NEW_PORT environment variables prior to executing a command. For example: export export REBIND_PORT_OLD="23" export export REBIND_PORT_NEW="65023" LD_PRELOAD=./rebind.so telnetd -debug 23 Alternately, the rebind script does the same thing: rebind 23 65023 telnetd -debug 23 Other changes/notes: - wsproxy no longer daemonizes by default. Remove -f/--foreground option and add -D/--deamon option. - When wsproxy is used to wrap a command in "respawn" mode, the command will not be respawn more often than 3 times within 10 seconds. - Move getKeysym routine out of Canvas object so that it can be called directly.
2011-01-12 19:15:11 +00:00
*
* **********************************************************************
* WARNING:
* Due to the above limitations, this code should be considered an experiment
* only. Consider using the program wrap mode of wsproxy.py instead.
* **********************************************************************
*/
#define DO_MSG 1
#define DO_DEBUG 1
#define DO_TRACE 1
#include <stdio.h>
#include <stdlib.h>
#define __USE_GNU 1 // Pull in RTLD_NEXT
#include <dlfcn.h>
#include <poll.h>
#include <sys/poll.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <resolv.h> /* base64 encode/decode */
#include <sys/time.h>
#include "md5.h"
#include "wswrapper.h"
/*
* If WSWRAP_PORT environment variable is set then listen to the bind fd that
* matches WSWRAP_PORT
*/
int _WS_listen_fd = -1;
int _WS_nfds = 0;
int _WS_fds[WS_MAX_FDS];
_WS_connection *_WS_connections[65536];
/*
* Utillity routines
*/
/*
* Subtract the `struct timeval' values X and Y, storing the
* result in RESULT. If TS is set then RESULT and X are really
* type-cast `struct timespec` so scale them appropriately.
* Return 1 if the difference is negative or 0, otherwise 0.
*/
int _WS_subtract_time (result, x, y, ts)
struct timeval *result, *x, *y;
{
int scale = ts ? 1000 : 1;
/* Perform the carry for the later subtraction by updating y. */
if ((x->tv_usec / scale) < y->tv_usec) {
int sec = (y->tv_usec - (x->tv_usec / scale)) / 1000000 + 1;
y->tv_usec -= 1000000 * sec;
y->tv_sec += sec;
}
if ((x->tv_usec / scale) - y->tv_usec > 1000000) {
int sec = ((x->tv_usec / scale) - y->tv_usec) / 1000000;
y->tv_usec += 1000000 * sec;
y->tv_sec -= sec;
}
/* Compute the time remaining to wait.
* tv_usec is certainly positive. */
result->tv_sec = x->tv_sec - y->tv_sec;
result->tv_usec = x->tv_usec - (y->tv_usec * scale);
/* Return 1 if result is negative or 0. */
return x->tv_sec <= y->tv_sec;
}
int _WS_alloc(int fd) {
if (_WS_connections[fd]) {
RET_ERROR(ENOMEM, "Memory already allocated for fd %d\n", fd);
}
if (! (_WS_connections[fd] = malloc(sizeof(_WS_connection)))) {
RET_ERROR(ENOMEM, "Could not allocate interposer memory\n");
}
_WS_connections[fd]->rcarry_cnt = 0;
_WS_connections[fd]->rcarry[0] = '\0';
_WS_connections[fd]->newframe = 1;
_WS_connections[fd]->refcnt = 1;
/* Add to search list for select/pselect */
_WS_fds[_WS_nfds] = fd;
_WS_nfds++;
return 0;
}
int _WS_free(int fd) {
int i;
_WS_connection * wsptr;
wsptr = _WS_connections[fd];
if (wsptr) {
TRACE(">> _WS_free(%d)\n", fd);
wsptr->refcnt--;
if (wsptr->refcnt <= 0) {
free(wsptr);
DEBUG("freed memory for fd %d\n", 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\n", fd);
TRACE("<< _WS_free(%d)\n", fd);
}
}
/*
* WebSocket handshake routines
*/
/* For WebSockets v76, use key1, key2 and key3 to generate md5 hash */
int _WS_gen_md5(char *key1, char *key2, char *key3, char *target) {
unsigned int i, spaces1 = 0, spaces2 = 0;
unsigned long num1 = 0, num2 = 0;
unsigned char buf[17];
/* Parse number 1 from key 1 */
for (i=0; i < strlen(key1); i++) {
if (key1[i] == ' ') {
spaces1 += 1;
}
if ((key1[i] >= 48) && (key1[i] <= 57)) {
num1 = num1 * 10 + (key1[i] - 48);
}
}
num1 = num1 / spaces1;
/* Parse number 2 from key 2 */
for (i=0; i < strlen(key2); i++) {
if (key2[i] == ' ') {
spaces2 += 1;
}
if ((key2[i] >= 48) && (key2[i] <= 57)) {
num2 = num2 * 10 + (key2[i] - 48);
}
}
num2 = num2 / spaces2;
/* Pack it big-endian as the first 8 bytes */
buf[0] = (num1 & 0xff000000) >> 24;
buf[1] = (num1 & 0xff0000) >> 16;
buf[2] = (num1 & 0xff00) >> 8;
buf[3] = num1 & 0xff;
buf[4] = (num2 & 0xff000000) >> 24;
buf[5] = (num2 & 0xff0000) >> 16;
buf[6] = (num2 & 0xff00) >> 8;
buf[7] = num2 & 0xff;
/* Add key 3 as the last 8 bytes */
strncpy(buf+8, key3, 8);
buf[16] = '\0';
/* md5 hash all 16 bytes to generate 16 byte target */
md5_buffer(buf, 16, target);
target[16] = '\0';
return 1;
}
/* Do v75 and v76 handshake on WebSocket connection */
int _WS_handshake(int sockfd)
{
int sz = 0, len, idx;
int ret = -1, save_errno = EPROTO;
char *last, *start, *end;
long flags;
char handshake[4096], response[4096],
path[1024], prefix[5] = "", scheme[10] = "ws", host[1024],
origin[1024], key1[100], key2[100], key3[9], chksum[17];
static void * (*rfunc)(), * (*wfunc)();
if (!rfunc) rfunc = (void *(*)()) dlsym(RTLD_NEXT, "recv");
if (!wfunc) wfunc = (void *(*)()) dlsym(RTLD_NEXT, "send");
DEBUG("_WS_handshake starting\n");
/* Disable NONBLOCK if set */
flags = fcntl(sockfd, F_GETFL, 0);
if (flags & O_NONBLOCK) {
fcntl(sockfd, F_SETFL, flags^O_NONBLOCK);
}
while (1) {
len = (int) rfunc(sockfd, handshake+sz, 4095, 0);
if (len < 1) {
ret = len;
save_errno = errno;
break;
}
sz += len;
handshake[sz] = '\x00';
if (sz < 4) {
// Not enough yet
continue;
}
if (strstr(handshake, "GET ") != handshake) {
MSG("Got non-WebSockets client connection\n");
break;
}
last = strstr(handshake, "\r\n\r\n");
if (! last) {
continue;
}
if (! strstr(handshake, "Upgrade: WebSocket\r\n")) {
MSG("Invalid WebSockets handshake\n");
break;
}
/* Now parse out the data elements */
start = handshake+4;
end = strstr(start, " HTTP/1.1");
if (!end) { break; }
snprintf(path, end-start+1, "%s", start);
start = strstr(handshake, "\r\nHost: ");
if (!start) { break; }
start += 8;
end = strstr(start, "\r\n");
snprintf(host, end-start+1, "%s", start);
start = strstr(handshake, "\r\nOrigin: ");
if (!start) { break; }
start += 10;
end = strstr(start, "\r\n");
snprintf(origin, end-start+1, "%s", start);
start = strstr(handshake, "\r\n\r\n") + 4;
if (strlen(start) == 8) {
sprintf(prefix, "Sec-");
snprintf(key3, 8+1, "%s", start);
start = strstr(handshake, "\r\nSec-WebSocket-Key1: ");
if (!start) { break; }
start += 22;
end = strstr(start, "\r\n");
snprintf(key1, end-start+1, "%s", start);
start = strstr(handshake, "\r\nSec-WebSocket-Key2: ");
if (!start) { break; }
start += 22;
end = strstr(start, "\r\n");
snprintf(key2, end-start+1, "%s", start);
_WS_gen_md5(key1, key2, key3, chksum);
//DEBUG("Got handshake (v76): %s\n", handshake);
MSG("New WebSockets client (v76)\n");
} else {
sprintf(prefix, "");
sprintf(key1, "");
sprintf(key2, "");
sprintf(key3, "");
sprintf(chksum, "");
//DEBUG("Got handshake (v75): %s\n", handshake);
MSG("New WebSockets client (v75)\n");
}
sprintf(response, _WS_response, prefix, origin, prefix, scheme,
host, path, prefix, chksum);
//DEBUG("Handshake response: %s\n", response);
wfunc(sockfd, response, strlen(response), 0);
save_errno = 0;
ret = 0;
break;
}
/* Re-enable NONBLOCK if it was set */
if (flags & O_NONBLOCK) {
fcntl(sockfd, F_SETFL, flags);
}
errno = save_errno;
return ret;
}
/*
* Strip empty WebSockets frames 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) {
wsproxy, wstelnet: wrap command, WS telnet client. wswrapper: Getting the wswrapper.c LD_PRELOAD model working has turned out to involve too many dark corners of the glibc/POSIX file descriptor space. I realized that 95% of what I want can be accomplished by adding a "wrap command" mode to wsproxy. The code is still there for now, but consider it experimental at best. Minor fix to dup2 and add dup and dup3 logging. wsproxy Wrap Command: In wsproxy wrap command mode, a command line is specified instead of a target address and port. wsproxy then uses a much simpler LD_PRELOAD library, rebind.so, to move intercept any bind() system calls made by the program. If the bind() call is for the wsproxy listen port number then the real bind() system call is issued for an alternate (free high) port on loopback/localhost. wsproxy then forwards from the listen address/port to the moved port. The --wrap-mode argument takes three options that determine the behavior of wsproxy when the wrapped command returns an exit code (exit or daemonizing): ignore, exit, respawn. For example, this runs vncserver on turns port 5901 into a WebSockets port (rebind.so must be built first): ./utils/wsproxy.py --wrap-mode=ignore 5901 -- vncserver :1 The vncserver command backgrounds itself so the wrap mode is set to "ignore" so that wsproxy keeps running even after it receives an exit code from vncserver. wstelnet: To demonstrate the wrap command mode, I added WebSockets telnet client. For example, this runs telnetd (krb5-telnetd) on turns port 2023 into a WebSockets port (using "respawn" mode since telnetd exits after each connection closes): sudo ./utils/wsproxy.py --wrap-mode=respawn 2023 -- telnetd -debug 2023 Then the utils/wstelnet.html page can be used to connect to the telnetd server on port 2023. The telnet client includes VT100.js (from http://code.google.com/p/sshconsole) which handles the terminal emulation and rendering. rebind: The rebind LD_PRELOAD library is used by wsproxy in wrap command mode to intercept bind() system calls and move the port to a different port on loopback/localhost. The rebind.so library can be built by running make in the utils directory. The rebind library can be used separately from wsproxy by setting the REBIND_OLD_PORT and REBIND_NEW_PORT environment variables prior to executing a command. For example: export export REBIND_PORT_OLD="23" export export REBIND_PORT_NEW="65023" LD_PRELOAD=./rebind.so telnetd -debug 23 Alternately, the rebind script does the same thing: rebind 23 65023 telnetd -debug 23 Other changes/notes: - wsproxy no longer daemonizes by default. Remove -f/--foreground option and add -D/--deamon option. - When wsproxy is used to wrap a command in "respawn" mode, the command will not be respawn more often than 3 times within 10 seconds. - Move getKeysym routine out of Canvas object so that it can be called directly.
2011-01-12 19:15:11 +00:00
TRACE("<< _WS_ready(%d, %d) len %d, errno: %d\n",
sockfd, nonblock, len, 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
*/
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, striplen, decodelen, ready;
ssize_t retlen, rawlen;
int sockflags;
int i;
char *fstart, *fend, *cstart;
static void * (*rfunc)(), * (*rfunc2)();
if (!rfunc) rfunc = (void *(*)()) dlsym(RTLD_NEXT, "recv");
if (!rfunc2) rfunc2 = (void *(*)()) dlsym(RTLD_NEXT, "read");
if (! ws) {
// Not our file descriptor, just pass through
if (recvf) {
return (ssize_t) rfunc(sockfd, buf, len, flags);
} else {
return (ssize_t) rfunc2(sockfd, buf, 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;
/* first copy in carry-over bytes from previous recv/read */
if (ws->rcarry_cnt) {
if (ws->rcarry_cnt == 1) {
DEBUG("Using carry byte: %u (", ws->rcarry[0]);
} else if (ws->rcarry_cnt == 2) {
DEBUG("Using carry bytes: %u,%u (", ws->rcarry[0],
ws->rcarry[1]);
} else {
RET_ERROR(EIO, "Too many carry-over bytes\n");
}
if (len <= ws->rcarry_cnt) {
DEBUG("final)\n");
memcpy((char *) buf, ws->rcarry, len);
ws->rcarry_cnt -= len;
return len;
} else {
DEBUG("prepending)\n");
memcpy((char *) buf, ws->rcarry, ws->rcarry_cnt);
retlen += ws->rcarry_cnt;
left -= ws->rcarry_cnt;
ws->rcarry_cnt = 0;
}
}
/* Determine the number of base64 encoded bytes needed */
rawcount = (left * 4) / 3 + 3;
rawcount -= rawcount%4;
if (rawcount > WS_BUFSIZE - 1) {
RET_ERROR(ENOMEM, "recv of %d bytes is larger than buffer\n", rawcount);
}
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;
}
/* 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");
}
fstart = ws->rbuf;
fstart[rawlen] = '\x00';
if (ws->newframe) {
if (fstart[0] != '\x00') {
RET_ERROR(EPROTO, "Missing frame start\n");
}
fstart++;
rawlen--;
ws->newframe = 0;
}
fend = memchr(fstart, '\xff', rawlen);
if (fend) {
ws->newframe = 1;
if ((fend - fstart) % 4) {
RET_ERROR(EPROTO, "Frame length is not multiple of 4\n");
}
} else {
fend = fstart + rawlen - (rawlen % 4);
if (fend - fstart < 4) {
RET_ERROR(EPROTO, "Frame too short\n");
}
}
/* Determine amount to consume */
if (rawcount < fend - fstart) {
ws->newframe = 0;
deccount = rawcount;
} else {
deccount = fend - fstart;
}
/* Now consume what was processed (if not MSG_PEEK) */
if (flags & MSG_PEEK) {
DEBUG("_WS_recv(%d, _, %d) MSG_PEEK, not consuming\n", sockfd, len);
} else {
rfunc(sockfd, ws->rbuf, fstart - ws->rbuf + deccount + ws->newframe, flags);
}
fstart[deccount] = '\x00'; // base64 terminator
/* Do base64 decode into the return buffer */
decodelen = b64_pton(fstart, (char *) buf + retlen, deccount);
if (decodelen <= 0) {
RET_ERROR(EPROTO, "Base64 decode error\n");
}
/* Calculate return length and carry-over */
if (decodelen <= left) {
retlen += decodelen;
} else {
retlen += left;
if (! (flags & MSG_PEEK)) {
/* Add anything left over to the carry-over */
ws->rcarry_cnt = decodelen - left;
if (ws->rcarry_cnt > 2) {
RET_ERROR(EPROTO, "Got too much base64 data\n");
}
memcpy(ws->rcarry, buf + retlen, ws->rcarry_cnt);
if (ws->rcarry_cnt == 1) {
DEBUG("Saving carry byte: %u\n", ws->rcarry[0]);
} else if (ws->rcarry_cnt == 2) {
DEBUG("Saving carry bytes: %u,%u\n", ws->rcarry[0],
ws->rcarry[1]);
} else {
RET_ERRO(EPROTO, "Too many carry bytes!\n");
}
}
}
((char *) buf)[retlen] = '\x00';
TRACE("<< _WS_recv(%d) retlen %d\n", sockfd, retlen);
return retlen;
}
/*
* WebSockets send and write interposer routine
*/
ssize_t _WS_send(int sendf, int sockfd, const void *buf,
size_t len, int flags)
{
_WS_connection *ws = _WS_connections[sockfd];
int rawlen, enclen, rlen, over, left, clen, retlen, dbufsize;
int sockflags;
char * target;
int i;
static void * (*sfunc)(), * (*sfunc2)();
if (!sfunc) sfunc = (void *(*)()) dlsym(RTLD_NEXT, "send");
if (!sfunc2) sfunc2 = (void *(*)()) dlsym(RTLD_NEXT, "write");
if (! ws) {
// Not our file descriptor, just pass through
if (sendf) {
return (ssize_t) sfunc(sockfd, buf, len, flags);
} else {
return (ssize_t) sfunc2(sockfd, buf, len);
}
}
TRACE(">> _WS_send(%d, _, %d)\n", sockfd, len);
sockflags = fcntl(sockfd, F_GETFL, 0);
dbufsize = (WS_BUFSIZE * 3)/4 - 2;
if (len > dbufsize) {
RET_ERROR(ENOMEM, "send of %d bytes is larger than send buffer\n", len);
}
/* base64 encode and add frame markers */
rawlen = 0;
ws->sbuf[rawlen++] = '\x00';
enclen = b64_ntop(buf, len, ws->sbuf+rawlen, WS_BUFSIZE-rawlen);
if (enclen < 0) {
RET_ERROR(EPROTO, "Base64 encoding error\n");
}
rawlen += enclen;
ws->sbuf[rawlen++] = '\xff';
rlen = (int) sfunc(sockfd, ws->sbuf, rawlen, flags);
if (rlen <= 0) {
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 */
over = (rlen - 1) % 4;
left = (4 - over) % 4 + 1; // left to send
DEBUG("_WS_send: rlen: %d (over: %d, left: %d), rawlen: %d\n",
rlen, over, left, rawlen);
rlen += left;
ws->sbuf[rlen-1] = '\xff';
i = 0;
do {
i++;
clen = (int) sfunc(sockfd, ws->sbuf + rlen - left, left, flags);
if (clen > 0) {
left -= clen;
} else if (clen == 0) {
MSG("_WS_send: got clen %d\n", clen);
} else if (!(sockflags & O_NONBLOCK)) {
MSG("<< _WS_send: clen %d\n", clen);
return clen;
}
if (i > 1000000) {
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);
}
/*
* Report back the number of original characters sent,
* not the raw number sent
*/
/* Adjust for framing */
retlen = rlen - 2;
/* Adjust for base64 padding */
if (ws->sbuf[rlen-1] == '=') { retlen --; }
if (ws->sbuf[rlen-2] == '=') { retlen --; }
/* Scale return value for base64 encoding size */
retlen = (retlen*3)/4;
TRACE(">> _WS_send(%d, _, %d) retlen %d\n", sockfd, len, retlen);
return (ssize_t) retlen;
}
/*
* Interpose select/pselect/poll.
*
* WebSocket descriptors are not ready until we have received a frame start
* ('\x00') and at least 4 bytes of base64 encoded data. In addition we may
* have carry-over data from the last 4 bytes of base64 data in which case the
* WebSockets socket is ready even though there might not be data in the raw
* socket itself.
*/
/* Interpose on select (mode==0) and pselect (mode==1) */
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;
/* Assumes timeptr is two longs whether timeval or timespec */
struct timeval savetv, starttv, nowtv, difftv;
int carrycnt = 0, less = 0;
int ret, i, ready, fd;
static void * (*func0)(), * (*func1)();
if (!func0) func0 = (void *(*)()) dlsym(RTLD_NEXT, "select");
if (!func1) func1 = (void *(*)()) dlsym(RTLD_NEXT, "pselect");
if ((_WS_listen_fd == -1) || (_WS_nfds == 0)) {
if (mode == 0) {
ret = (int) func0(nfds, readfds, writefds, exceptfds,
timeptr);
} else if (mode == 1) {
ret = (int) func1(nfds, readfds, writefds, exceptfds,
timeptr, sigmask);
}
return ret;
}
wsproxy, wstelnet: wrap command, WS telnet client. wswrapper: Getting the wswrapper.c LD_PRELOAD model working has turned out to involve too many dark corners of the glibc/POSIX file descriptor space. I realized that 95% of what I want can be accomplished by adding a "wrap command" mode to wsproxy. The code is still there for now, but consider it experimental at best. Minor fix to dup2 and add dup and dup3 logging. wsproxy Wrap Command: In wsproxy wrap command mode, a command line is specified instead of a target address and port. wsproxy then uses a much simpler LD_PRELOAD library, rebind.so, to move intercept any bind() system calls made by the program. If the bind() call is for the wsproxy listen port number then the real bind() system call is issued for an alternate (free high) port on loopback/localhost. wsproxy then forwards from the listen address/port to the moved port. The --wrap-mode argument takes three options that determine the behavior of wsproxy when the wrapped command returns an exit code (exit or daemonizing): ignore, exit, respawn. For example, this runs vncserver on turns port 5901 into a WebSockets port (rebind.so must be built first): ./utils/wsproxy.py --wrap-mode=ignore 5901 -- vncserver :1 The vncserver command backgrounds itself so the wrap mode is set to "ignore" so that wsproxy keeps running even after it receives an exit code from vncserver. wstelnet: To demonstrate the wrap command mode, I added WebSockets telnet client. For example, this runs telnetd (krb5-telnetd) on turns port 2023 into a WebSockets port (using "respawn" mode since telnetd exits after each connection closes): sudo ./utils/wsproxy.py --wrap-mode=respawn 2023 -- telnetd -debug 2023 Then the utils/wstelnet.html page can be used to connect to the telnetd server on port 2023. The telnet client includes VT100.js (from http://code.google.com/p/sshconsole) which handles the terminal emulation and rendering. rebind: The rebind LD_PRELOAD library is used by wsproxy in wrap command mode to intercept bind() system calls and move the port to a different port on loopback/localhost. The rebind.so library can be built by running make in the utils directory. The rebind library can be used separately from wsproxy by setting the REBIND_OLD_PORT and REBIND_NEW_PORT environment variables prior to executing a command. For example: export export REBIND_PORT_OLD="23" export export REBIND_PORT_NEW="65023" LD_PRELOAD=./rebind.so telnetd -debug 23 Alternately, the rebind script does the same thing: rebind 23 65023 telnetd -debug 23 Other changes/notes: - wsproxy no longer daemonizes by default. Remove -f/--foreground option and add -D/--deamon option. - When wsproxy is used to wrap a command in "respawn" mode, the command will not be respawn more often than 3 times within 10 seconds. - Move getKeysym routine out of Canvas object so that it can be called directly.
2011-01-12 19:15:11 +00:00
#ifdef DO_TRACE
TRACE(">> _WS_select(%d, %d, _, _, _, _)\n", mode, nfds);
wsproxy, wstelnet: wrap command, WS telnet client. wswrapper: Getting the wswrapper.c LD_PRELOAD model working has turned out to involve too many dark corners of the glibc/POSIX file descriptor space. I realized that 95% of what I want can be accomplished by adding a "wrap command" mode to wsproxy. The code is still there for now, but consider it experimental at best. Minor fix to dup2 and add dup and dup3 logging. wsproxy Wrap Command: In wsproxy wrap command mode, a command line is specified instead of a target address and port. wsproxy then uses a much simpler LD_PRELOAD library, rebind.so, to move intercept any bind() system calls made by the program. If the bind() call is for the wsproxy listen port number then the real bind() system call is issued for an alternate (free high) port on loopback/localhost. wsproxy then forwards from the listen address/port to the moved port. The --wrap-mode argument takes three options that determine the behavior of wsproxy when the wrapped command returns an exit code (exit or daemonizing): ignore, exit, respawn. For example, this runs vncserver on turns port 5901 into a WebSockets port (rebind.so must be built first): ./utils/wsproxy.py --wrap-mode=ignore 5901 -- vncserver :1 The vncserver command backgrounds itself so the wrap mode is set to "ignore" so that wsproxy keeps running even after it receives an exit code from vncserver. wstelnet: To demonstrate the wrap command mode, I added WebSockets telnet client. For example, this runs telnetd (krb5-telnetd) on turns port 2023 into a WebSockets port (using "respawn" mode since telnetd exits after each connection closes): sudo ./utils/wsproxy.py --wrap-mode=respawn 2023 -- telnetd -debug 2023 Then the utils/wstelnet.html page can be used to connect to the telnetd server on port 2023. The telnet client includes VT100.js (from http://code.google.com/p/sshconsole) which handles the terminal emulation and rendering. rebind: The rebind LD_PRELOAD library is used by wsproxy in wrap command mode to intercept bind() system calls and move the port to a different port on loopback/localhost. The rebind.so library can be built by running make in the utils directory. The rebind library can be used separately from wsproxy by setting the REBIND_OLD_PORT and REBIND_NEW_PORT environment variables prior to executing a command. For example: export export REBIND_PORT_OLD="23" export export REBIND_PORT_NEW="65023" LD_PRELOAD=./rebind.so telnetd -debug 23 Alternately, the rebind script does the same thing: rebind 23 65023 telnetd -debug 23 Other changes/notes: - wsproxy no longer daemonizes by default. Remove -f/--foreground option and add -D/--deamon option. - When wsproxy is used to wrap a command in "respawn" mode, the command will not be respawn more often than 3 times within 10 seconds. - Move getKeysym routine out of Canvas object so that it can be called directly.
2011-01-12 19:15:11 +00:00
for (i = 0; i < _WS_nfds; i++) {
fd = _WS_fds[i];
if (readfds && (FD_ISSET(fd, readfds))) {
TRACE(" WS %d is in readfds\n", fd, nfds);
}
if (writefds && (FD_ISSET(fd, writefds))) {
TRACE(" WS %d is in writefds\n", fd, nfds);
}
if (exceptfds && (FD_ISSET(fd, exceptfds))) {
TRACE(" WS %d is in exceptfds\n", fd, nfds);
}
}
#endif
if (timeptr) {
memcpy(&savetv, timeptr, sizeof(savetv));
gettimeofday(&starttv, NULL);
}
/* 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 (timeptr) {
TRACE(" _WS_select tv/ts: %ld:%ld\n",
((struct timeval *) timeptr)->tv_sec,
((struct timeval *) timeptr)->tv_usec);
}
if (mode == 0) {
ret = (int) func0(nfds, readfds, writefds, exceptfds,
timeptr);
} else if (mode == 1) {
ret = (int) func1(nfds, readfds, writefds, exceptfds,
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 (ret == 0) {
/*
* If all the ready readfds were WebSockets, but none of
* them were really ready (empty frames) then we select again. But
* first restore original values less passage of time.
*/
if (! timeptr) {
/* No timeout, spin forever */
continue;
}
memcpy(readfds, &savefds, sizeof(savefds));
gettimeofday(&nowtv, NULL);
/* Amount of time that has passed */
_WS_subtract_time(&difftv, &nowtv, &starttv, 0);
/* Subtract from original timout */
less = _WS_subtract_time((struct timeval *) timeptr,
&savetv, &difftv, mode);
if (less) {
/* Timer has expired */
TRACE(" _WS_select expired timer\n", mode, nfds);
break;
}
}
} while (ret == 0);
/* Restore original time value for pselect glibc does */
wsproxy, wstelnet: wrap command, WS telnet client. wswrapper: Getting the wswrapper.c LD_PRELOAD model working has turned out to involve too many dark corners of the glibc/POSIX file descriptor space. I realized that 95% of what I want can be accomplished by adding a "wrap command" mode to wsproxy. The code is still there for now, but consider it experimental at best. Minor fix to dup2 and add dup and dup3 logging. wsproxy Wrap Command: In wsproxy wrap command mode, a command line is specified instead of a target address and port. wsproxy then uses a much simpler LD_PRELOAD library, rebind.so, to move intercept any bind() system calls made by the program. If the bind() call is for the wsproxy listen port number then the real bind() system call is issued for an alternate (free high) port on loopback/localhost. wsproxy then forwards from the listen address/port to the moved port. The --wrap-mode argument takes three options that determine the behavior of wsproxy when the wrapped command returns an exit code (exit or daemonizing): ignore, exit, respawn. For example, this runs vncserver on turns port 5901 into a WebSockets port (rebind.so must be built first): ./utils/wsproxy.py --wrap-mode=ignore 5901 -- vncserver :1 The vncserver command backgrounds itself so the wrap mode is set to "ignore" so that wsproxy keeps running even after it receives an exit code from vncserver. wstelnet: To demonstrate the wrap command mode, I added WebSockets telnet client. For example, this runs telnetd (krb5-telnetd) on turns port 2023 into a WebSockets port (using "respawn" mode since telnetd exits after each connection closes): sudo ./utils/wsproxy.py --wrap-mode=respawn 2023 -- telnetd -debug 2023 Then the utils/wstelnet.html page can be used to connect to the telnetd server on port 2023. The telnet client includes VT100.js (from http://code.google.com/p/sshconsole) which handles the terminal emulation and rendering. rebind: The rebind LD_PRELOAD library is used by wsproxy in wrap command mode to intercept bind() system calls and move the port to a different port on loopback/localhost. The rebind.so library can be built by running make in the utils directory. The rebind library can be used separately from wsproxy by setting the REBIND_OLD_PORT and REBIND_NEW_PORT environment variables prior to executing a command. For example: export export REBIND_PORT_OLD="23" export export REBIND_PORT_NEW="65023" LD_PRELOAD=./rebind.so telnetd -debug 23 Alternately, the rebind script does the same thing: rebind 23 65023 telnetd -debug 23 Other changes/notes: - wsproxy no longer daemonizes by default. Remove -f/--foreground option and add -D/--deamon option. - When wsproxy is used to wrap a command in "respawn" mode, the command will not be respawn more often than 3 times within 10 seconds. - Move getKeysym routine out of Canvas object so that it can be called directly.
2011-01-12 19:15:11 +00:00
if (timeptr && mode == 1) {
memcpy(timeptr, &savetv, sizeof(savetv));
}
wsproxy, wstelnet: wrap command, WS telnet client. wswrapper: Getting the wswrapper.c LD_PRELOAD model working has turned out to involve too many dark corners of the glibc/POSIX file descriptor space. I realized that 95% of what I want can be accomplished by adding a "wrap command" mode to wsproxy. The code is still there for now, but consider it experimental at best. Minor fix to dup2 and add dup and dup3 logging. wsproxy Wrap Command: In wsproxy wrap command mode, a command line is specified instead of a target address and port. wsproxy then uses a much simpler LD_PRELOAD library, rebind.so, to move intercept any bind() system calls made by the program. If the bind() call is for the wsproxy listen port number then the real bind() system call is issued for an alternate (free high) port on loopback/localhost. wsproxy then forwards from the listen address/port to the moved port. The --wrap-mode argument takes three options that determine the behavior of wsproxy when the wrapped command returns an exit code (exit or daemonizing): ignore, exit, respawn. For example, this runs vncserver on turns port 5901 into a WebSockets port (rebind.so must be built first): ./utils/wsproxy.py --wrap-mode=ignore 5901 -- vncserver :1 The vncserver command backgrounds itself so the wrap mode is set to "ignore" so that wsproxy keeps running even after it receives an exit code from vncserver. wstelnet: To demonstrate the wrap command mode, I added WebSockets telnet client. For example, this runs telnetd (krb5-telnetd) on turns port 2023 into a WebSockets port (using "respawn" mode since telnetd exits after each connection closes): sudo ./utils/wsproxy.py --wrap-mode=respawn 2023 -- telnetd -debug 2023 Then the utils/wstelnet.html page can be used to connect to the telnetd server on port 2023. The telnet client includes VT100.js (from http://code.google.com/p/sshconsole) which handles the terminal emulation and rendering. rebind: The rebind LD_PRELOAD library is used by wsproxy in wrap command mode to intercept bind() system calls and move the port to a different port on loopback/localhost. The rebind.so library can be built by running make in the utils directory. The rebind library can be used separately from wsproxy by setting the REBIND_OLD_PORT and REBIND_NEW_PORT environment variables prior to executing a command. For example: export export REBIND_PORT_OLD="23" export export REBIND_PORT_NEW="65023" LD_PRELOAD=./rebind.so telnetd -debug 23 Alternately, the rebind script does the same thing: rebind 23 65023 telnetd -debug 23 Other changes/notes: - wsproxy no longer daemonizes by default. Remove -f/--foreground option and add -D/--deamon option. - When wsproxy is used to wrap a command in "respawn" mode, the command will not be respawn more often than 3 times within 10 seconds. - Move getKeysym routine out of Canvas object so that it can be called directly.
2011-01-12 19:15:11 +00:00
#ifdef DO_TRACE
TRACE("<< _WS_select(%d, %d, _, _, _, _) ret %d, errno %d\n",
mode, nfds, ret, errno);
wsproxy, wstelnet: wrap command, WS telnet client. wswrapper: Getting the wswrapper.c LD_PRELOAD model working has turned out to involve too many dark corners of the glibc/POSIX file descriptor space. I realized that 95% of what I want can be accomplished by adding a "wrap command" mode to wsproxy. The code is still there for now, but consider it experimental at best. Minor fix to dup2 and add dup and dup3 logging. wsproxy Wrap Command: In wsproxy wrap command mode, a command line is specified instead of a target address and port. wsproxy then uses a much simpler LD_PRELOAD library, rebind.so, to move intercept any bind() system calls made by the program. If the bind() call is for the wsproxy listen port number then the real bind() system call is issued for an alternate (free high) port on loopback/localhost. wsproxy then forwards from the listen address/port to the moved port. The --wrap-mode argument takes three options that determine the behavior of wsproxy when the wrapped command returns an exit code (exit or daemonizing): ignore, exit, respawn. For example, this runs vncserver on turns port 5901 into a WebSockets port (rebind.so must be built first): ./utils/wsproxy.py --wrap-mode=ignore 5901 -- vncserver :1 The vncserver command backgrounds itself so the wrap mode is set to "ignore" so that wsproxy keeps running even after it receives an exit code from vncserver. wstelnet: To demonstrate the wrap command mode, I added WebSockets telnet client. For example, this runs telnetd (krb5-telnetd) on turns port 2023 into a WebSockets port (using "respawn" mode since telnetd exits after each connection closes): sudo ./utils/wsproxy.py --wrap-mode=respawn 2023 -- telnetd -debug 2023 Then the utils/wstelnet.html page can be used to connect to the telnetd server on port 2023. The telnet client includes VT100.js (from http://code.google.com/p/sshconsole) which handles the terminal emulation and rendering. rebind: The rebind LD_PRELOAD library is used by wsproxy in wrap command mode to intercept bind() system calls and move the port to a different port on loopback/localhost. The rebind.so library can be built by running make in the utils directory. The rebind library can be used separately from wsproxy by setting the REBIND_OLD_PORT and REBIND_NEW_PORT environment variables prior to executing a command. For example: export export REBIND_PORT_OLD="23" export export REBIND_PORT_NEW="65023" LD_PRELOAD=./rebind.so telnetd -debug 23 Alternately, the rebind script does the same thing: rebind 23 65023 telnetd -debug 23 Other changes/notes: - wsproxy no longer daemonizes by default. Remove -f/--foreground option and add -D/--deamon option. - When wsproxy is used to wrap a command in "respawn" mode, the command will not be respawn more often than 3 times within 10 seconds. - Move getKeysym routine out of Canvas object so that it can be called directly.
2011-01-12 19:15:11 +00:00
for (i = 0; i < _WS_nfds; i++) {
fd = _WS_fds[i];
if (readfds && (FD_ISSET(fd, readfds))) {
TRACE(" WS %d is set in readfds\n", fd, nfds);
}
if (writefds && (FD_ISSET(fd, writefds))) {
TRACE(" WS %d is set in writefds\n", fd, nfds);
}
if (exceptfds && (FD_ISSET(fd, exceptfds))) {
TRACE(" WS %d is set in exceptfds\n", fd, nfds);
}
}
#endif
return ret;
}
/* Interpose on poll (mode==0) and ppoll (mode==1) */
int _WS_poll(int mode, struct pollfd *fds, nfds_t nfds, int timeout,
struct timespec *ptimeout, sigset_t *sigmask)
{
_WS_connection *ws;
int savetimeout;
struct timespec savets;
struct timeval starttv, nowtv, difftv;
struct pollfd *pfd;
int carrycnt = 0, less = 0;
int ret, i, ready, fd;
static void * (*func0)(), * (*func1)();
if (!func0) func0 = (void *(*)()) dlsym(RTLD_NEXT, "poll");
if (!func1) func1 = (void *(*)()) dlsym(RTLD_NEXT, "ppoll");
if ((_WS_listen_fd == -1) || (_WS_nfds == 0)) {
if (mode == 0) {
ret = (int) func0(fds, nfds, timeout);
} else if (mode == 1) {
ret = (int) func1(fds, nfds, ptimeout, sigmask);
}
return ret;
}
TRACE(">> _WS_poll(%d, %ld, _, _, _, _)\n", mode, nfds);
if (mode == 0) {
savetimeout = timeout;
} else if (mode == 1) {
memcpy(&savets, ptimeout, sizeof(savets));
}
gettimeofday(&starttv, NULL);
do {
TRACE(" _WS_poll(%d, %ld, _, _, _, _) tv/ts: %ld:%ld\n", mode, nfds,
ptimeout->tv_sec, ptimeout->tv_nsec);
if (mode == 0) {
ret = (int) func0(fds, nfds, timeout);
} else if (mode == 1) {
ret = (int) func1(fds, nfds, ptimeout, sigmask);
}
if (ret <= 0) {
break;
}
for (i = 0; i < nfds; i++) {
pfd = &fds[i];
if (! (pfd->events & POLLIN)) {
continue;
}
ws = _WS_connections[pfd->fd];
if (! ws) {
continue;
}
if (ws->rcarry_cnt) {
if (! (pfd->revents & POLLIN)) {
pfd->revents |= POLLIN;
ret++;
}
} else if (pfd->revents & POLLIN) {
ready = _WS_ready(pfd->fd, 1);
if (ready == 0) {
/* 0 means EOF which is also a ready condition */
DEBUG("_WS_poll: detected %d is closed\n", fd);
} else if (ready < 0) {
DEBUG("_WS_poll: not enough to decode\n", fd);
pfd->revents -= POLLIN;
ret--;
}
}
}
errno = 0; /* errno could be set by _WS_ready */
if (ret == 0) {
/*
* If all the ready readfds were WebSockets, but none of
* them were really ready (empty frames) then we select again. But
* first restore original values less passage of time.
*/
gettimeofday(&nowtv, NULL);
/* Amount of time that has passed */
_WS_subtract_time(&difftv, &nowtv, &starttv, 0);
if (mode == 0) {
if (timeout < 0) {
/* Negative timeout means infinite */
continue;
}
timeout -= difftv.tv_sec * 1000 + difftv.tv_usec / 1000;
if (timeout <= 0) {
less = 1;
}
} else if (mode == 1) {
/* Subtract from original timout */
less = _WS_subtract_time((struct timeval *) ptimeout,
(struct timeval *) &savets,
&difftv, 1);
}
if (less) {
/* Timer has expired */
TRACE(" _WS_poll expired timer\n", mode, nfds);
break;
}
}
} while (ret == 0);
/* Restore original time value for pselect glibc does */
if (mode == 1) {
memcpy(ptimeout, &savets, sizeof(savets));
}
TRACE("<< _WS_poll(%d, %ld, _, _, _, _) ret %d, errno %d\n",
mode, nfds, ret, errno);
return ret;
}
/*
* Overload (LD_PRELOAD) standard library network routines
*/
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
static void * (*func)();
struct sockaddr_in * addr_in = (struct sockaddr_in *)addr;
char * WSWRAP_PORT, * end;
int ret, envport, bindport = htons(addr_in->sin_port);
if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "bind");
TRACE(">> bind(%d, _, %d)\n", sockfd, addrlen);
ret = (int) func(sockfd, addr, addrlen);
if (addr_in->sin_family != AF_INET) {
// TODO: handle IPv6
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 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')) {
TRACE("<< bind, not interposing: WSWRAP_PORT is not a number\n");
return ret;
}
if (envport != bindport) {
TRACE("<< bind, not interposing on port: %d (fd %d)\n", bindport, sockfd);
return ret;
}
_WS_listen_fd = sockfd;
TRACE("<< bind, listening for WebSockets connections on port: %d (fd %d)\n", envport, sockfd);
return ret;
}
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");
TRACE("<< accept(%d, _, _)\n", sockfd);
fd = (int) func(sockfd, addr, addrlen);
if (_WS_listen_fd == -1) {
TRACE("<< accept: not interposing\n");
return fd;
}
if (_WS_listen_fd != sockfd) {
TRACE("<< accept: not interposing on fd %d\n", sockfd);
return fd;
}
if (_WS_connections[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_alloc(fd) < 0) {
return -1;
}
ret = _WS_handshake(fd);
if (ret < 0) {
_WS_free(fd);
errno = EPROTO;
TRACE("<< accept(%d, _, _): ret %d\n", sockfd, ret);
return ret;
}
MSG("interposing on fd %d (allocated memory)\n", fd);
}
return fd;
}
int close(int fd)
{
static void * (*func)();
if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "close");
TRACE("close(%d) called\n", fd);
_WS_free(fd);
return (int) func(fd);
}
ssize_t read(int fd, void *buf, size_t 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)
{
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)
{
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)
{
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(_, %ld, %d) called\n", nfds, timeout);
return _WS_poll(0, fds, nfds, timeout, NULL, NULL);
}
int ppoll(struct pollfd *fds, nfds_t nfds,
const struct timespec *timeout, const sigset_t *sigmask)
{
TRACE("ppoll(_, %ld, _, _) called\n", nfds);
return _WS_poll(0, fds, nfds, 0, (struct timespec *)timeout,
(sigset_t *)sigmask);
}
wsproxy, wstelnet: wrap command, WS telnet client. wswrapper: Getting the wswrapper.c LD_PRELOAD model working has turned out to involve too many dark corners of the glibc/POSIX file descriptor space. I realized that 95% of what I want can be accomplished by adding a "wrap command" mode to wsproxy. The code is still there for now, but consider it experimental at best. Minor fix to dup2 and add dup and dup3 logging. wsproxy Wrap Command: In wsproxy wrap command mode, a command line is specified instead of a target address and port. wsproxy then uses a much simpler LD_PRELOAD library, rebind.so, to move intercept any bind() system calls made by the program. If the bind() call is for the wsproxy listen port number then the real bind() system call is issued for an alternate (free high) port on loopback/localhost. wsproxy then forwards from the listen address/port to the moved port. The --wrap-mode argument takes three options that determine the behavior of wsproxy when the wrapped command returns an exit code (exit or daemonizing): ignore, exit, respawn. For example, this runs vncserver on turns port 5901 into a WebSockets port (rebind.so must be built first): ./utils/wsproxy.py --wrap-mode=ignore 5901 -- vncserver :1 The vncserver command backgrounds itself so the wrap mode is set to "ignore" so that wsproxy keeps running even after it receives an exit code from vncserver. wstelnet: To demonstrate the wrap command mode, I added WebSockets telnet client. For example, this runs telnetd (krb5-telnetd) on turns port 2023 into a WebSockets port (using "respawn" mode since telnetd exits after each connection closes): sudo ./utils/wsproxy.py --wrap-mode=respawn 2023 -- telnetd -debug 2023 Then the utils/wstelnet.html page can be used to connect to the telnetd server on port 2023. The telnet client includes VT100.js (from http://code.google.com/p/sshconsole) which handles the terminal emulation and rendering. rebind: The rebind LD_PRELOAD library is used by wsproxy in wrap command mode to intercept bind() system calls and move the port to a different port on loopback/localhost. The rebind.so library can be built by running make in the utils directory. The rebind library can be used separately from wsproxy by setting the REBIND_OLD_PORT and REBIND_NEW_PORT environment variables prior to executing a command. For example: export export REBIND_PORT_OLD="23" export export REBIND_PORT_NEW="65023" LD_PRELOAD=./rebind.so telnetd -debug 23 Alternately, the rebind script does the same thing: rebind 23 65023 telnetd -debug 23 Other changes/notes: - wsproxy no longer daemonizes by default. Remove -f/--foreground option and add -D/--deamon option. - When wsproxy is used to wrap a command in "respawn" mode, the command will not be respawn more often than 3 times within 10 seconds. - Move getKeysym routine out of Canvas object so that it can be called directly.
2011-01-12 19:15:11 +00:00
int dup(int oldfd) {
int ret;
static void * (*func)();
if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "dup");
TRACE(">> dup(%d) called\n", oldfd);
ret = (int) func(oldfd);
TRACE("<< dup(%d) ret %d\n", oldfd, ret);
return ret;
}
int dup2(int oldfd, int newfd) {
int ret;
static void * (*func)();
if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "dup2");
wsproxy, wstelnet: wrap command, WS telnet client. wswrapper: Getting the wswrapper.c LD_PRELOAD model working has turned out to involve too many dark corners of the glibc/POSIX file descriptor space. I realized that 95% of what I want can be accomplished by adding a "wrap command" mode to wsproxy. The code is still there for now, but consider it experimental at best. Minor fix to dup2 and add dup and dup3 logging. wsproxy Wrap Command: In wsproxy wrap command mode, a command line is specified instead of a target address and port. wsproxy then uses a much simpler LD_PRELOAD library, rebind.so, to move intercept any bind() system calls made by the program. If the bind() call is for the wsproxy listen port number then the real bind() system call is issued for an alternate (free high) port on loopback/localhost. wsproxy then forwards from the listen address/port to the moved port. The --wrap-mode argument takes three options that determine the behavior of wsproxy when the wrapped command returns an exit code (exit or daemonizing): ignore, exit, respawn. For example, this runs vncserver on turns port 5901 into a WebSockets port (rebind.so must be built first): ./utils/wsproxy.py --wrap-mode=ignore 5901 -- vncserver :1 The vncserver command backgrounds itself so the wrap mode is set to "ignore" so that wsproxy keeps running even after it receives an exit code from vncserver. wstelnet: To demonstrate the wrap command mode, I added WebSockets telnet client. For example, this runs telnetd (krb5-telnetd) on turns port 2023 into a WebSockets port (using "respawn" mode since telnetd exits after each connection closes): sudo ./utils/wsproxy.py --wrap-mode=respawn 2023 -- telnetd -debug 2023 Then the utils/wstelnet.html page can be used to connect to the telnetd server on port 2023. The telnet client includes VT100.js (from http://code.google.com/p/sshconsole) which handles the terminal emulation and rendering. rebind: The rebind LD_PRELOAD library is used by wsproxy in wrap command mode to intercept bind() system calls and move the port to a different port on loopback/localhost. The rebind.so library can be built by running make in the utils directory. The rebind library can be used separately from wsproxy by setting the REBIND_OLD_PORT and REBIND_NEW_PORT environment variables prior to executing a command. For example: export export REBIND_PORT_OLD="23" export export REBIND_PORT_NEW="65023" LD_PRELOAD=./rebind.so telnetd -debug 23 Alternately, the rebind script does the same thing: rebind 23 65023 telnetd -debug 23 Other changes/notes: - wsproxy no longer daemonizes by default. Remove -f/--foreground option and add -D/--deamon option. - When wsproxy is used to wrap a command in "respawn" mode, the command will not be respawn more often than 3 times within 10 seconds. - Move getKeysym routine out of Canvas object so that it can be called directly.
2011-01-12 19:15:11 +00:00
TRACE(">> dup2(%d, %d) called\n", oldfd, newfd);
ret = (int) func(oldfd, newfd);
wsproxy, wstelnet: wrap command, WS telnet client. wswrapper: Getting the wswrapper.c LD_PRELOAD model working has turned out to involve too many dark corners of the glibc/POSIX file descriptor space. I realized that 95% of what I want can be accomplished by adding a "wrap command" mode to wsproxy. The code is still there for now, but consider it experimental at best. Minor fix to dup2 and add dup and dup3 logging. wsproxy Wrap Command: In wsproxy wrap command mode, a command line is specified instead of a target address and port. wsproxy then uses a much simpler LD_PRELOAD library, rebind.so, to move intercept any bind() system calls made by the program. If the bind() call is for the wsproxy listen port number then the real bind() system call is issued for an alternate (free high) port on loopback/localhost. wsproxy then forwards from the listen address/port to the moved port. The --wrap-mode argument takes three options that determine the behavior of wsproxy when the wrapped command returns an exit code (exit or daemonizing): ignore, exit, respawn. For example, this runs vncserver on turns port 5901 into a WebSockets port (rebind.so must be built first): ./utils/wsproxy.py --wrap-mode=ignore 5901 -- vncserver :1 The vncserver command backgrounds itself so the wrap mode is set to "ignore" so that wsproxy keeps running even after it receives an exit code from vncserver. wstelnet: To demonstrate the wrap command mode, I added WebSockets telnet client. For example, this runs telnetd (krb5-telnetd) on turns port 2023 into a WebSockets port (using "respawn" mode since telnetd exits after each connection closes): sudo ./utils/wsproxy.py --wrap-mode=respawn 2023 -- telnetd -debug 2023 Then the utils/wstelnet.html page can be used to connect to the telnetd server on port 2023. The telnet client includes VT100.js (from http://code.google.com/p/sshconsole) which handles the terminal emulation and rendering. rebind: The rebind LD_PRELOAD library is used by wsproxy in wrap command mode to intercept bind() system calls and move the port to a different port on loopback/localhost. The rebind.so library can be built by running make in the utils directory. The rebind library can be used separately from wsproxy by setting the REBIND_OLD_PORT and REBIND_NEW_PORT environment variables prior to executing a command. For example: export export REBIND_PORT_OLD="23" export export REBIND_PORT_NEW="65023" LD_PRELOAD=./rebind.so telnetd -debug 23 Alternately, the rebind script does the same thing: rebind 23 65023 telnetd -debug 23 Other changes/notes: - wsproxy no longer daemonizes by default. Remove -f/--foreground option and add -D/--deamon option. - When wsproxy is used to wrap a command in "respawn" mode, the command will not be respawn more often than 3 times within 10 seconds. - Move getKeysym routine out of Canvas object so that it can be called directly.
2011-01-12 19:15:11 +00:00
if ((! _WS_connections[oldfd]) && (! _WS_connections[newfd])) {
return ret;
}
wsproxy, wstelnet: wrap command, WS telnet client. wswrapper: Getting the wswrapper.c LD_PRELOAD model working has turned out to involve too many dark corners of the glibc/POSIX file descriptor space. I realized that 95% of what I want can be accomplished by adding a "wrap command" mode to wsproxy. The code is still there for now, but consider it experimental at best. Minor fix to dup2 and add dup and dup3 logging. wsproxy Wrap Command: In wsproxy wrap command mode, a command line is specified instead of a target address and port. wsproxy then uses a much simpler LD_PRELOAD library, rebind.so, to move intercept any bind() system calls made by the program. If the bind() call is for the wsproxy listen port number then the real bind() system call is issued for an alternate (free high) port on loopback/localhost. wsproxy then forwards from the listen address/port to the moved port. The --wrap-mode argument takes three options that determine the behavior of wsproxy when the wrapped command returns an exit code (exit or daemonizing): ignore, exit, respawn. For example, this runs vncserver on turns port 5901 into a WebSockets port (rebind.so must be built first): ./utils/wsproxy.py --wrap-mode=ignore 5901 -- vncserver :1 The vncserver command backgrounds itself so the wrap mode is set to "ignore" so that wsproxy keeps running even after it receives an exit code from vncserver. wstelnet: To demonstrate the wrap command mode, I added WebSockets telnet client. For example, this runs telnetd (krb5-telnetd) on turns port 2023 into a WebSockets port (using "respawn" mode since telnetd exits after each connection closes): sudo ./utils/wsproxy.py --wrap-mode=respawn 2023 -- telnetd -debug 2023 Then the utils/wstelnet.html page can be used to connect to the telnetd server on port 2023. The telnet client includes VT100.js (from http://code.google.com/p/sshconsole) which handles the terminal emulation and rendering. rebind: The rebind LD_PRELOAD library is used by wsproxy in wrap command mode to intercept bind() system calls and move the port to a different port on loopback/localhost. The rebind.so library can be built by running make in the utils directory. The rebind library can be used separately from wsproxy by setting the REBIND_OLD_PORT and REBIND_NEW_PORT environment variables prior to executing a command. For example: export export REBIND_PORT_OLD="23" export export REBIND_PORT_NEW="65023" LD_PRELOAD=./rebind.so telnetd -debug 23 Alternately, the rebind script does the same thing: rebind 23 65023 telnetd -debug 23 Other changes/notes: - wsproxy no longer daemonizes by default. Remove -f/--foreground option and add -D/--deamon option. - When wsproxy is used to wrap a command in "respawn" mode, the command will not be respawn more often than 3 times within 10 seconds. - Move getKeysym routine out of Canvas object so that it can be called directly.
2011-01-12 19:15:11 +00:00
if ((ret < 0) || (oldfd == newfd) ||
(_WS_connections[oldfd] == _WS_connections[newfd])) {
TRACE("<< dup2(%d, %d) ret %d\n", oldfd, newfd, ret);
return ret;
}
/* dup2 behavior is to close newfd if it's open */
if (_WS_connections[newfd]) {
_WS_free(newfd);
}
wsproxy, wstelnet: wrap command, WS telnet client. wswrapper: Getting the wswrapper.c LD_PRELOAD model working has turned out to involve too many dark corners of the glibc/POSIX file descriptor space. I realized that 95% of what I want can be accomplished by adding a "wrap command" mode to wsproxy. The code is still there for now, but consider it experimental at best. Minor fix to dup2 and add dup and dup3 logging. wsproxy Wrap Command: In wsproxy wrap command mode, a command line is specified instead of a target address and port. wsproxy then uses a much simpler LD_PRELOAD library, rebind.so, to move intercept any bind() system calls made by the program. If the bind() call is for the wsproxy listen port number then the real bind() system call is issued for an alternate (free high) port on loopback/localhost. wsproxy then forwards from the listen address/port to the moved port. The --wrap-mode argument takes three options that determine the behavior of wsproxy when the wrapped command returns an exit code (exit or daemonizing): ignore, exit, respawn. For example, this runs vncserver on turns port 5901 into a WebSockets port (rebind.so must be built first): ./utils/wsproxy.py --wrap-mode=ignore 5901 -- vncserver :1 The vncserver command backgrounds itself so the wrap mode is set to "ignore" so that wsproxy keeps running even after it receives an exit code from vncserver. wstelnet: To demonstrate the wrap command mode, I added WebSockets telnet client. For example, this runs telnetd (krb5-telnetd) on turns port 2023 into a WebSockets port (using "respawn" mode since telnetd exits after each connection closes): sudo ./utils/wsproxy.py --wrap-mode=respawn 2023 -- telnetd -debug 2023 Then the utils/wstelnet.html page can be used to connect to the telnetd server on port 2023. The telnet client includes VT100.js (from http://code.google.com/p/sshconsole) which handles the terminal emulation and rendering. rebind: The rebind LD_PRELOAD library is used by wsproxy in wrap command mode to intercept bind() system calls and move the port to a different port on loopback/localhost. The rebind.so library can be built by running make in the utils directory. The rebind library can be used separately from wsproxy by setting the REBIND_OLD_PORT and REBIND_NEW_PORT environment variables prior to executing a command. For example: export export REBIND_PORT_OLD="23" export export REBIND_PORT_NEW="65023" LD_PRELOAD=./rebind.so telnetd -debug 23 Alternately, the rebind script does the same thing: rebind 23 65023 telnetd -debug 23 Other changes/notes: - wsproxy no longer daemonizes by default. Remove -f/--foreground option and add -D/--deamon option. - When wsproxy is used to wrap a command in "respawn" mode, the command will not be respawn more often than 3 times within 10 seconds. - Move getKeysym routine out of Canvas object so that it can be called directly.
2011-01-12 19:15:11 +00:00
if (! _WS_connections[oldfd]) {
TRACE("<< dup2(%d, %d) ret %d\n", oldfd, newfd, ret);
return ret;
}
MSG("interposing on duplicated fd %d\n", newfd);
/* oldfd and newfd are now descriptors for the same socket,
* re-use the same context memory area */
_WS_connections[newfd] = _WS_connections[oldfd];
_WS_connections[newfd]->refcnt++;
/* Add to search list for select/pselect */
_WS_fds[_WS_nfds] = newfd;
_WS_nfds++;
wsproxy, wstelnet: wrap command, WS telnet client. wswrapper: Getting the wswrapper.c LD_PRELOAD model working has turned out to involve too many dark corners of the glibc/POSIX file descriptor space. I realized that 95% of what I want can be accomplished by adding a "wrap command" mode to wsproxy. The code is still there for now, but consider it experimental at best. Minor fix to dup2 and add dup and dup3 logging. wsproxy Wrap Command: In wsproxy wrap command mode, a command line is specified instead of a target address and port. wsproxy then uses a much simpler LD_PRELOAD library, rebind.so, to move intercept any bind() system calls made by the program. If the bind() call is for the wsproxy listen port number then the real bind() system call is issued for an alternate (free high) port on loopback/localhost. wsproxy then forwards from the listen address/port to the moved port. The --wrap-mode argument takes three options that determine the behavior of wsproxy when the wrapped command returns an exit code (exit or daemonizing): ignore, exit, respawn. For example, this runs vncserver on turns port 5901 into a WebSockets port (rebind.so must be built first): ./utils/wsproxy.py --wrap-mode=ignore 5901 -- vncserver :1 The vncserver command backgrounds itself so the wrap mode is set to "ignore" so that wsproxy keeps running even after it receives an exit code from vncserver. wstelnet: To demonstrate the wrap command mode, I added WebSockets telnet client. For example, this runs telnetd (krb5-telnetd) on turns port 2023 into a WebSockets port (using "respawn" mode since telnetd exits after each connection closes): sudo ./utils/wsproxy.py --wrap-mode=respawn 2023 -- telnetd -debug 2023 Then the utils/wstelnet.html page can be used to connect to the telnetd server on port 2023. The telnet client includes VT100.js (from http://code.google.com/p/sshconsole) which handles the terminal emulation and rendering. rebind: The rebind LD_PRELOAD library is used by wsproxy in wrap command mode to intercept bind() system calls and move the port to a different port on loopback/localhost. The rebind.so library can be built by running make in the utils directory. The rebind library can be used separately from wsproxy by setting the REBIND_OLD_PORT and REBIND_NEW_PORT environment variables prior to executing a command. For example: export export REBIND_PORT_OLD="23" export export REBIND_PORT_NEW="65023" LD_PRELOAD=./rebind.so telnetd -debug 23 Alternately, the rebind script does the same thing: rebind 23 65023 telnetd -debug 23 Other changes/notes: - wsproxy no longer daemonizes by default. Remove -f/--foreground option and add -D/--deamon option. - When wsproxy is used to wrap a command in "respawn" mode, the command will not be respawn more often than 3 times within 10 seconds. - Move getKeysym routine out of Canvas object so that it can be called directly.
2011-01-12 19:15:11 +00:00
TRACE("<< dup2(%d, %d) ret %d\n", oldfd, newfd, ret);
return ret;
}
wsproxy, wstelnet: wrap command, WS telnet client. wswrapper: Getting the wswrapper.c LD_PRELOAD model working has turned out to involve too many dark corners of the glibc/POSIX file descriptor space. I realized that 95% of what I want can be accomplished by adding a "wrap command" mode to wsproxy. The code is still there for now, but consider it experimental at best. Minor fix to dup2 and add dup and dup3 logging. wsproxy Wrap Command: In wsproxy wrap command mode, a command line is specified instead of a target address and port. wsproxy then uses a much simpler LD_PRELOAD library, rebind.so, to move intercept any bind() system calls made by the program. If the bind() call is for the wsproxy listen port number then the real bind() system call is issued for an alternate (free high) port on loopback/localhost. wsproxy then forwards from the listen address/port to the moved port. The --wrap-mode argument takes three options that determine the behavior of wsproxy when the wrapped command returns an exit code (exit or daemonizing): ignore, exit, respawn. For example, this runs vncserver on turns port 5901 into a WebSockets port (rebind.so must be built first): ./utils/wsproxy.py --wrap-mode=ignore 5901 -- vncserver :1 The vncserver command backgrounds itself so the wrap mode is set to "ignore" so that wsproxy keeps running even after it receives an exit code from vncserver. wstelnet: To demonstrate the wrap command mode, I added WebSockets telnet client. For example, this runs telnetd (krb5-telnetd) on turns port 2023 into a WebSockets port (using "respawn" mode since telnetd exits after each connection closes): sudo ./utils/wsproxy.py --wrap-mode=respawn 2023 -- telnetd -debug 2023 Then the utils/wstelnet.html page can be used to connect to the telnetd server on port 2023. The telnet client includes VT100.js (from http://code.google.com/p/sshconsole) which handles the terminal emulation and rendering. rebind: The rebind LD_PRELOAD library is used by wsproxy in wrap command mode to intercept bind() system calls and move the port to a different port on loopback/localhost. The rebind.so library can be built by running make in the utils directory. The rebind library can be used separately from wsproxy by setting the REBIND_OLD_PORT and REBIND_NEW_PORT environment variables prior to executing a command. For example: export export REBIND_PORT_OLD="23" export export REBIND_PORT_NEW="65023" LD_PRELOAD=./rebind.so telnetd -debug 23 Alternately, the rebind script does the same thing: rebind 23 65023 telnetd -debug 23 Other changes/notes: - wsproxy no longer daemonizes by default. Remove -f/--foreground option and add -D/--deamon option. - When wsproxy is used to wrap a command in "respawn" mode, the command will not be respawn more often than 3 times within 10 seconds. - Move getKeysym routine out of Canvas object so that it can be called directly.
2011-01-12 19:15:11 +00:00
int dup3(int oldfd, int newfd, int flags) {
int ret;
static void * (*func)();
if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "dup3");
TRACE(">> dup3(%d, %d, %d) called\n", oldfd, newfd, flags);
ret = (int) func(oldfd, newfd, flags);
TRACE("<< dup3(%d, %d, %d) ret %d\n", oldfd, newfd, flags, ret);
return ret;
}