2010-12-13 19:20:34 +00:00
|
|
|
/*
|
|
|
|
* 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:
|
2010-12-27 20:15:59 +00:00
|
|
|
* - 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)
|
2010-12-27 20:15:59 +00:00
|
|
|
* - programs using ppoll or epoll will not work correctly
|
2011-01-09 03:46:36 +00:00
|
|
|
* - 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.
|
|
|
|
* **********************************************************************
|
2010-12-27 20:15:59 +00:00
|
|
|
*/
|
2010-12-13 19:20:34 +00:00
|
|
|
|
2010-12-29 21:11:28 +00:00
|
|
|
#define DO_MSG 1
|
2011-01-09 03:46:36 +00:00
|
|
|
#define DO_DEBUG 1
|
|
|
|
#define DO_TRACE 1
|
2010-12-27 23:08:27 +00:00
|
|
|
|
2010-12-03 04:11:02 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#define __USE_GNU 1 // Pull in RTLD_NEXT
|
|
|
|
#include <dlfcn.h>
|
|
|
|
|
2010-12-27 20:15:59 +00:00
|
|
|
#include <poll.h>
|
2010-12-28 02:44:14 +00:00
|
|
|
#include <sys/poll.h>
|
2010-12-03 04:11:02 +00:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <resolv.h> /* base64 encode/decode */
|
2010-12-27 23:08:27 +00:00
|
|
|
#include <sys/time.h>
|
2010-12-03 04:11:02 +00:00
|
|
|
#include "md5.h"
|
2010-12-27 20:15:59 +00:00
|
|
|
#include "wswrapper.h"
|
2010-12-14 17:43:34 +00:00
|
|
|
|
2010-12-13 19:20:34 +00:00
|
|
|
/*
|
|
|
|
* If WSWRAP_PORT environment variable is set then listen to the bind fd that
|
2010-12-27 23:08:27 +00:00
|
|
|
* matches WSWRAP_PORT
|
2010-12-13 19:20:34 +00:00
|
|
|
*/
|
2010-12-16 20:04:16 +00:00
|
|
|
int _WS_listen_fd = -1;
|
2010-12-27 20:15:59 +00:00
|
|
|
int _WS_nfds = 0;
|
|
|
|
int _WS_fds[WS_MAX_FDS];
|
|
|
|
_WS_connection *_WS_connections[65536];
|
2010-12-13 19:20:34 +00:00
|
|
|
|
2010-12-03 04:11:02 +00:00
|
|
|
|
2010-12-27 23:08:27 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
2010-12-28 02:44:14 +00:00
|
|
|
* Return 1 if the difference is negative or 0, otherwise 0.
|
2010-12-27 23:08:27 +00:00
|
|
|
*/
|
|
|
|
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);
|
|
|
|
|
2010-12-28 02:44:14 +00:00
|
|
|
/* Return 1 if result is negative or 0. */
|
|
|
|
return x->tv_sec <= y->tv_sec;
|
2010-12-27 23:08:27 +00:00
|
|
|
}
|
|
|
|
|
2011-01-09 03:46:36 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-27 23:08:27 +00:00
|
|
|
|
2010-12-14 17:43:34 +00:00
|
|
|
/*
|
|
|
|
* WebSocket handshake routines
|
|
|
|
*/
|
2010-12-03 04:11:02 +00:00
|
|
|
|
2010-12-14 18:05:17 +00:00
|
|
|
/* For WebSockets v76, use key1, key2 and key3 to generate md5 hash */
|
2010-12-03 04:11:02 +00:00
|
|
|
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];
|
2010-12-14 18:05:17 +00:00
|
|
|
|
|
|
|
/* Parse number 1 from key 1 */
|
2010-12-03 04:11:02 +00:00
|
|
|
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;
|
|
|
|
|
2010-12-14 18:05:17 +00:00
|
|
|
/* Parse number 2 from key 2 */
|
2010-12-03 04:11:02 +00:00
|
|
|
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;
|
|
|
|
|
2010-12-14 18:05:17 +00:00
|
|
|
/* Pack it big-endian as the first 8 bytes */
|
2010-12-03 04:11:02 +00:00
|
|
|
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;
|
|
|
|
|
2010-12-14 18:05:17 +00:00
|
|
|
/* Add key 3 as the last 8 bytes */
|
2010-12-03 04:11:02 +00:00
|
|
|
strncpy(buf+8, key3, 8);
|
|
|
|
buf[16] = '\0';
|
|
|
|
|
2010-12-14 18:05:17 +00:00
|
|
|
/* md5 hash all 16 bytes to generate 16 byte target */
|
2010-12-03 04:11:02 +00:00
|
|
|
md5_buffer(buf, 16, target);
|
|
|
|
target[16] = '\0';
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-12-14 18:05:17 +00:00
|
|
|
/* Do v75 and v76 handshake on WebSocket connection */
|
2010-12-03 04:11:02 +00:00
|
|
|
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) {
|
2010-12-14 18:05:17 +00:00
|
|
|
MSG("Got non-WebSockets client connection\n");
|
2010-12-03 04:11:02 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
last = strstr(handshake, "\r\n\r\n");
|
|
|
|
if (! last) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (! strstr(handshake, "Upgrade: WebSocket\r\n")) {
|
|
|
|
MSG("Invalid WebSockets handshake\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-12-14 18:05:17 +00:00
|
|
|
/* Now parse out the data elements */
|
2010-12-03 04:11:02 +00:00
|
|
|
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);
|
2010-12-14 18:05:17 +00:00
|
|
|
MSG("New WebSockets client (v76)\n");
|
2010-12-03 04:11:02 +00:00
|
|
|
|
|
|
|
} else {
|
|
|
|
sprintf(prefix, "");
|
|
|
|
sprintf(key1, "");
|
|
|
|
sprintf(key2, "");
|
|
|
|
sprintf(key3, "");
|
|
|
|
sprintf(chksum, "");
|
|
|
|
|
|
|
|
//DEBUG("Got handshake (v75): %s\n", handshake);
|
2010-12-14 18:05:17 +00:00
|
|
|
MSG("New WebSockets client (v75)\n");
|
2010-12-03 04:11:02 +00:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-12-27 20:15:59 +00:00
|
|
|
/*
|
2010-12-27 23:08:27 +00:00
|
|
|
* 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).
|
2010-12-27 20:15:59 +00:00
|
|
|
*/
|
|
|
|
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);
|
2010-12-27 20:15:59 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-14 17:43:34 +00:00
|
|
|
/*
|
2010-12-14 18:05:17 +00:00
|
|
|
* WebSockets recv/read interposer routine
|
2010-12-14 17:43:34 +00:00
|
|
|
*/
|
2010-12-03 04:11:02 +00:00
|
|
|
ssize_t _WS_recv(int recvf, int sockfd, const void *buf,
|
|
|
|
size_t len, int flags)
|
|
|
|
{
|
2010-12-14 17:43:34 +00:00
|
|
|
_WS_connection *ws = _WS_connections[sockfd];
|
2010-12-27 20:15:59 +00:00
|
|
|
int rawcount, deccount, left, striplen, decodelen, ready;
|
|
|
|
ssize_t retlen, rawlen;
|
2010-12-03 04:11:02 +00:00
|
|
|
int sockflags;
|
|
|
|
int i;
|
2010-12-14 17:43:34 +00:00
|
|
|
char *fstart, *fend, *cstart;
|
2010-12-03 04:11:02 +00:00
|
|
|
|
|
|
|
static void * (*rfunc)(), * (*rfunc2)();
|
|
|
|
if (!rfunc) rfunc = (void *(*)()) dlsym(RTLD_NEXT, "recv");
|
|
|
|
if (!rfunc2) rfunc2 = (void *(*)()) dlsym(RTLD_NEXT, "read");
|
|
|
|
|
2010-12-14 17:43:34 +00:00
|
|
|
if (! ws) {
|
2010-12-03 04:11:02 +00:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
2010-12-27 20:15:59 +00:00
|
|
|
TRACE(">> _WS_recv(%d)\n", sockfd);
|
|
|
|
|
|
|
|
if (len == 0) {
|
|
|
|
TRACE("<< _WS_recv(%d) len == 0\n", sockfd);
|
|
|
|
return 0;
|
|
|
|
}
|
2010-12-03 04:11:02 +00:00
|
|
|
|
|
|
|
sockflags = fcntl(sockfd, F_GETFL, 0);
|
2010-12-27 20:15:59 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2010-12-03 04:11:02 +00:00
|
|
|
left = len;
|
|
|
|
retlen = 0;
|
|
|
|
|
2010-12-14 18:05:17 +00:00
|
|
|
/* first copy in carry-over bytes from previous recv/read */
|
2010-12-14 17:43:34 +00:00
|
|
|
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]);
|
2010-12-03 04:11:02 +00:00
|
|
|
} else {
|
|
|
|
RET_ERROR(EIO, "Too many carry-over bytes\n");
|
|
|
|
}
|
2010-12-14 17:43:34 +00:00
|
|
|
if (len <= ws->rcarry_cnt) {
|
2010-12-03 04:11:02 +00:00
|
|
|
DEBUG("final)\n");
|
2010-12-14 17:43:34 +00:00
|
|
|
memcpy((char *) buf, ws->rcarry, len);
|
|
|
|
ws->rcarry_cnt -= len;
|
2010-12-03 04:11:02 +00:00
|
|
|
return len;
|
|
|
|
} else {
|
|
|
|
DEBUG("prepending)\n");
|
2010-12-14 17:43:34 +00:00
|
|
|
memcpy((char *) buf, ws->rcarry, ws->rcarry_cnt);
|
|
|
|
retlen += ws->rcarry_cnt;
|
|
|
|
left -= ws->rcarry_cnt;
|
|
|
|
ws->rcarry_cnt = 0;
|
2010-12-03 04:11:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-14 18:05:17 +00:00
|
|
|
/* Determine the number of base64 encoded bytes needed */
|
2010-12-03 04:11:02 +00:00
|
|
|
rawcount = (left * 4) / 3 + 3;
|
|
|
|
rawcount -= rawcount%4;
|
|
|
|
|
2010-12-14 17:43:34 +00:00
|
|
|
if (rawcount > WS_BUFSIZE - 1) {
|
2010-12-03 04:11:02 +00:00
|
|
|
RET_ERROR(ENOMEM, "recv of %d bytes is larger than buffer\n", rawcount);
|
|
|
|
}
|
|
|
|
|
2010-12-27 20:15:59 +00:00
|
|
|
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;
|
2010-12-03 04:11:02 +00:00
|
|
|
}
|
2010-12-27 20:15:59 +00:00
|
|
|
TRACE("<< _WS_recv(%d, _, %d) retlen %d\n", sockfd, len, retlen);
|
|
|
|
return retlen;
|
|
|
|
}
|
2010-12-03 04:11:02 +00:00
|
|
|
|
2010-12-27 20:15:59 +00:00
|
|
|
/* We have enough data to return something */
|
2010-12-03 04:11:02 +00:00
|
|
|
|
2010-12-27 20:15:59 +00:00
|
|
|
/* 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");
|
2010-12-03 04:11:02 +00:00
|
|
|
}
|
2010-12-27 20:15:59 +00:00
|
|
|
fstart = ws->rbuf;
|
|
|
|
fstart[rawlen] = '\x00';
|
2010-12-03 04:11:02 +00:00
|
|
|
|
|
|
|
|
2010-12-14 17:43:34 +00:00
|
|
|
if (ws->newframe) {
|
2010-12-03 04:11:02 +00:00
|
|
|
if (fstart[0] != '\x00') {
|
|
|
|
RET_ERROR(EPROTO, "Missing frame start\n");
|
|
|
|
}
|
|
|
|
fstart++;
|
|
|
|
rawlen--;
|
2010-12-14 17:43:34 +00:00
|
|
|
ws->newframe = 0;
|
2010-12-03 04:11:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fend = memchr(fstart, '\xff', rawlen);
|
|
|
|
|
|
|
|
if (fend) {
|
2010-12-14 17:43:34 +00:00
|
|
|
ws->newframe = 1;
|
2010-12-03 04:11:02 +00:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-14 18:05:17 +00:00
|
|
|
/* Determine amount to consume */
|
2010-12-03 04:11:02 +00:00
|
|
|
if (rawcount < fend - fstart) {
|
2010-12-14 17:43:34 +00:00
|
|
|
ws->newframe = 0;
|
2010-12-03 04:11:02 +00:00
|
|
|
deccount = rawcount;
|
|
|
|
} else {
|
|
|
|
deccount = fend - fstart;
|
|
|
|
}
|
|
|
|
|
2010-12-27 23:08:27 +00:00
|
|
|
/* Now consume what was processed (if not MSG_PEEK) */
|
2010-12-03 04:11:02 +00:00
|
|
|
if (flags & MSG_PEEK) {
|
2010-12-27 20:15:59 +00:00
|
|
|
DEBUG("_WS_recv(%d, _, %d) MSG_PEEK, not consuming\n", sockfd, len);
|
2010-12-03 04:11:02 +00:00
|
|
|
} else {
|
2010-12-14 17:43:34 +00:00
|
|
|
rfunc(sockfd, ws->rbuf, fstart - ws->rbuf + deccount + ws->newframe, flags);
|
2010-12-03 04:11:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fstart[deccount] = '\x00'; // base64 terminator
|
|
|
|
|
2010-12-14 18:05:17 +00:00
|
|
|
/* Do base64 decode into the return buffer */
|
2010-12-03 04:11:02 +00:00
|
|
|
decodelen = b64_pton(fstart, (char *) buf + retlen, deccount);
|
|
|
|
if (decodelen <= 0) {
|
|
|
|
RET_ERROR(EPROTO, "Base64 decode error\n");
|
|
|
|
}
|
|
|
|
|
2010-12-14 18:05:17 +00:00
|
|
|
/* Calculate return length and carry-over */
|
2010-12-03 04:11:02 +00:00
|
|
|
if (decodelen <= left) {
|
|
|
|
retlen += decodelen;
|
|
|
|
} else {
|
|
|
|
retlen += left;
|
|
|
|
|
|
|
|
if (! (flags & MSG_PEEK)) {
|
2010-12-14 18:05:17 +00:00
|
|
|
/* Add anything left over to the carry-over */
|
2010-12-14 17:43:34 +00:00
|
|
|
ws->rcarry_cnt = decodelen - left;
|
|
|
|
if (ws->rcarry_cnt > 2) {
|
2010-12-03 04:11:02 +00:00
|
|
|
RET_ERROR(EPROTO, "Got too much base64 data\n");
|
|
|
|
}
|
2010-12-14 17:43:34 +00:00
|
|
|
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]);
|
2010-12-03 04:11:02 +00:00
|
|
|
} else {
|
2010-12-27 20:15:59 +00:00
|
|
|
RET_ERRO(EPROTO, "Too many carry bytes!\n");
|
2010-12-03 04:11:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
((char *) buf)[retlen] = '\x00';
|
|
|
|
|
2010-12-27 20:15:59 +00:00
|
|
|
TRACE("<< _WS_recv(%d) retlen %d\n", sockfd, retlen);
|
2010-12-03 04:11:02 +00:00
|
|
|
return retlen;
|
|
|
|
}
|
|
|
|
|
2010-12-14 17:43:34 +00:00
|
|
|
/*
|
|
|
|
* WebSockets send and write interposer routine
|
|
|
|
*/
|
2010-12-03 04:11:02 +00:00
|
|
|
ssize_t _WS_send(int sendf, int sockfd, const void *buf,
|
|
|
|
size_t len, int flags)
|
|
|
|
{
|
2010-12-14 17:43:34 +00:00
|
|
|
_WS_connection *ws = _WS_connections[sockfd];
|
2010-12-03 04:11:02 +00:00
|
|
|
int rawlen, enclen, rlen, over, left, clen, retlen, dbufsize;
|
|
|
|
int sockflags;
|
|
|
|
char * target;
|
|
|
|
int i;
|
|
|
|
static void * (*sfunc)(), * (*sfunc2)();
|
2010-12-27 20:15:59 +00:00
|
|
|
if (!sfunc) sfunc = (void *(*)()) dlsym(RTLD_NEXT, "send");
|
2010-12-03 04:11:02 +00:00
|
|
|
if (!sfunc2) sfunc2 = (void *(*)()) dlsym(RTLD_NEXT, "write");
|
|
|
|
|
2010-12-14 17:43:34 +00:00
|
|
|
if (! ws) {
|
2010-12-03 04:11:02 +00:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
2010-12-27 20:15:59 +00:00
|
|
|
TRACE(">> _WS_send(%d, _, %d)\n", sockfd, len);
|
2010-12-03 04:11:02 +00:00
|
|
|
|
|
|
|
sockflags = fcntl(sockfd, F_GETFL, 0);
|
|
|
|
|
2010-12-14 17:43:34 +00:00
|
|
|
dbufsize = (WS_BUFSIZE * 3)/4 - 2;
|
2010-12-03 04:11:02 +00:00
|
|
|
if (len > dbufsize) {
|
|
|
|
RET_ERROR(ENOMEM, "send of %d bytes is larger than send buffer\n", len);
|
|
|
|
}
|
|
|
|
|
2010-12-14 18:05:17 +00:00
|
|
|
/* base64 encode and add frame markers */
|
2010-12-03 04:11:02 +00:00
|
|
|
rawlen = 0;
|
2010-12-14 17:43:34 +00:00
|
|
|
ws->sbuf[rawlen++] = '\x00';
|
|
|
|
enclen = b64_ntop(buf, len, ws->sbuf+rawlen, WS_BUFSIZE-rawlen);
|
2010-12-03 04:11:02 +00:00
|
|
|
if (enclen < 0) {
|
|
|
|
RET_ERROR(EPROTO, "Base64 encoding error\n");
|
|
|
|
}
|
|
|
|
rawlen += enclen;
|
2010-12-14 17:43:34 +00:00
|
|
|
ws->sbuf[rawlen++] = '\xff';
|
2010-12-03 04:11:02 +00:00
|
|
|
|
2010-12-14 17:43:34 +00:00
|
|
|
rlen = (int) sfunc(sockfd, ws->sbuf, rawlen, flags);
|
2010-12-03 04:11:02 +00:00
|
|
|
|
|
|
|
if (rlen <= 0) {
|
2010-12-27 20:15:59 +00:00
|
|
|
TRACE("<< _WS_send(%d, _, %d) send failed, returning\n", sockfd, len);
|
2010-12-03 04:11:02 +00:00
|
|
|
return rlen;
|
|
|
|
} else if (rlen < rawlen) {
|
2010-12-14 18:05:17 +00:00
|
|
|
/* Spin until we can send a whole base64 chunck and frame end */
|
2010-12-03 04:11:02 +00:00
|
|
|
over = (rlen - 1) % 4;
|
|
|
|
left = (4 - over) % 4 + 1; // left to send
|
2010-12-14 18:05:17 +00:00
|
|
|
DEBUG("_WS_send: rlen: %d (over: %d, left: %d), rawlen: %d\n",
|
|
|
|
rlen, over, left, rawlen);
|
2010-12-03 04:11:02 +00:00
|
|
|
rlen += left;
|
2010-12-14 17:43:34 +00:00
|
|
|
ws->sbuf[rlen-1] = '\xff';
|
2010-12-03 04:11:02 +00:00
|
|
|
i = 0;
|
|
|
|
do {
|
|
|
|
i++;
|
2010-12-14 17:43:34 +00:00
|
|
|
clen = (int) sfunc(sockfd, ws->sbuf + rlen - left, left, flags);
|
2010-12-03 04:11:02 +00:00
|
|
|
if (clen > 0) {
|
|
|
|
left -= clen;
|
|
|
|
} else if (clen == 0) {
|
|
|
|
MSG("_WS_send: got clen %d\n", clen);
|
|
|
|
} else if (!(sockflags & O_NONBLOCK)) {
|
2010-12-27 20:15:59 +00:00
|
|
|
MSG("<< _WS_send: clen %d\n", clen);
|
2010-12-03 04:11:02 +00:00
|
|
|
return clen;
|
|
|
|
}
|
|
|
|
if (i > 1000000) {
|
2010-12-27 20:15:59 +00:00
|
|
|
RET_ERROR(EIO, "Could not send final part of frame\n");
|
2010-12-03 04:11:02 +00:00
|
|
|
}
|
|
|
|
} while (left > 0);
|
2010-12-27 20:15:59 +00:00
|
|
|
//DEBUG("_WS_send: spins until finished %d\n", i);
|
2010-12-03 04:11:02 +00:00
|
|
|
DEBUG("_WS_send: spins until finished %d\n", i);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Report back the number of original characters sent,
|
|
|
|
* not the raw number sent
|
|
|
|
*/
|
2010-12-14 18:05:17 +00:00
|
|
|
|
|
|
|
/* Adjust for framing */
|
2010-12-03 04:11:02 +00:00
|
|
|
retlen = rlen - 2;
|
2010-12-14 18:05:17 +00:00
|
|
|
|
|
|
|
/* Adjust for base64 padding */
|
2010-12-14 17:43:34 +00:00
|
|
|
if (ws->sbuf[rlen-1] == '=') { retlen --; }
|
|
|
|
if (ws->sbuf[rlen-2] == '=') { retlen --; }
|
2010-12-03 04:11:02 +00:00
|
|
|
|
2010-12-14 18:05:17 +00:00
|
|
|
/* Scale return value for base64 encoding size */
|
2010-12-03 04:11:02 +00:00
|
|
|
retlen = (retlen*3)/4;
|
|
|
|
|
2010-12-27 20:15:59 +00:00
|
|
|
TRACE(">> _WS_send(%d, _, %d) retlen %d\n", sockfd, len, retlen);
|
2010-12-03 04:11:02 +00:00
|
|
|
return (ssize_t) retlen;
|
|
|
|
}
|
|
|
|
|
2010-12-28 02:44:14 +00:00
|
|
|
/*
|
|
|
|
* 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) */
|
2010-12-27 20:15:59 +00:00
|
|
|
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;
|
2010-12-27 23:08:27 +00:00
|
|
|
/* Assumes timeptr is two longs whether timeval or timespec */
|
|
|
|
struct timeval savetv, starttv, nowtv, difftv;
|
2010-12-28 02:44:14 +00:00
|
|
|
int carrycnt = 0, less = 0;
|
|
|
|
int ret, i, ready, fd;
|
2010-12-27 23:08:27 +00:00
|
|
|
static void * (*func0)(), * (*func1)();
|
|
|
|
if (!func0) func0 = (void *(*)()) dlsym(RTLD_NEXT, "select");
|
|
|
|
if (!func1) func1 = (void *(*)()) dlsym(RTLD_NEXT, "pselect");
|
2010-12-27 20:15:59 +00:00
|
|
|
|
|
|
|
if ((_WS_listen_fd == -1) || (_WS_nfds == 0)) {
|
|
|
|
if (mode == 0) {
|
2010-12-27 23:08:27 +00:00
|
|
|
ret = (int) func0(nfds, readfds, writefds, exceptfds,
|
2010-12-28 02:44:14 +00:00
|
|
|
timeptr);
|
2010-12-27 20:15:59 +00:00
|
|
|
} else if (mode == 1) {
|
2010-12-27 23:08:27 +00:00
|
|
|
ret = (int) func1(nfds, readfds, writefds, exceptfds,
|
2010-12-28 02:44:14 +00:00
|
|
|
timeptr, sigmask);
|
2010-12-27 20:15:59 +00:00
|
|
|
}
|
|
|
|
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
|
2010-12-28 02:44:14 +00:00
|
|
|
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
|
2011-01-09 03:46:36 +00:00
|
|
|
if (timeptr) {
|
|
|
|
memcpy(&savetv, timeptr, sizeof(savetv));
|
|
|
|
gettimeofday(&starttv, NULL);
|
|
|
|
}
|
2010-12-27 20:15:59 +00:00
|
|
|
|
|
|
|
/* 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 {
|
2011-01-09 03:46:36 +00:00
|
|
|
if (timeptr) {
|
|
|
|
TRACE(" _WS_select tv/ts: %ld:%ld\n",
|
|
|
|
((struct timeval *) timeptr)->tv_sec,
|
|
|
|
((struct timeval *) timeptr)->tv_usec);
|
|
|
|
}
|
2010-12-27 20:15:59 +00:00
|
|
|
if (mode == 0) {
|
2010-12-27 23:08:27 +00:00
|
|
|
ret = (int) func0(nfds, readfds, writefds, exceptfds,
|
2010-12-28 02:44:14 +00:00
|
|
|
timeptr);
|
2010-12-27 20:15:59 +00:00
|
|
|
} else if (mode == 1) {
|
2010-12-27 23:08:27 +00:00
|
|
|
ret = (int) func1(nfds, readfds, writefds, exceptfds,
|
2010-12-28 02:44:14 +00:00
|
|
|
timeptr, sigmask);
|
2010-12-27 20:15:59 +00:00
|
|
|
}
|
|
|
|
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) {
|
2010-12-28 02:44:14 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2011-01-09 03:46:36 +00:00
|
|
|
if (! timeptr) {
|
|
|
|
/* No timeout, spin forever */
|
|
|
|
continue;
|
|
|
|
}
|
2010-12-27 20:15:59 +00:00
|
|
|
memcpy(readfds, &savefds, sizeof(savefds));
|
2010-12-27 23:08:27 +00:00
|
|
|
gettimeofday(&nowtv, NULL);
|
|
|
|
/* Amount of time that has passed */
|
2010-12-28 02:44:14 +00:00
|
|
|
_WS_subtract_time(&difftv, &nowtv, &starttv, 0);
|
2010-12-27 23:08:27 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
2010-12-27 20:15:59 +00:00
|
|
|
}
|
|
|
|
} while (ret == 0);
|
|
|
|
|
2010-12-27 23:08:27 +00:00
|
|
|
/* 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) {
|
2010-12-27 23:08:27 +00:00
|
|
|
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
|
2010-12-27 20:15:59 +00:00
|
|
|
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
|
2010-12-27 20:15:59 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2010-12-03 04:11:02 +00:00
|
|
|
|
2010-12-28 02:44:14 +00:00
|
|
|
/* 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)
|
2010-12-03 04:11:02 +00:00
|
|
|
{
|
2010-12-28 02:44:14 +00:00
|
|
|
_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));
|
|
|
|
}
|
2010-12-03 04:11:02 +00:00
|
|
|
|
2010-12-28 02:44:14 +00:00
|
|
|
TRACE("<< _WS_poll(%d, %ld, _, _, _, _) ret %d, errno %d\n",
|
|
|
|
mode, nfds, ret, errno);
|
|
|
|
return ret;
|
2010-12-03 04:11:02 +00:00
|
|
|
}
|
2010-12-28 02:44:14 +00:00
|
|
|
|
2010-12-29 21:11:28 +00:00
|
|
|
|
2010-12-28 02:44:14 +00:00
|
|
|
/*
|
|
|
|
* Overload (LD_PRELOAD) standard library network routines
|
|
|
|
*/
|
2010-12-03 04:11:02 +00:00
|
|
|
|
|
|
|
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
|
|
|
|
{
|
|
|
|
static void * (*func)();
|
2010-12-13 19:20:34 +00:00
|
|
|
struct sockaddr_in * addr_in = (struct sockaddr_in *)addr;
|
|
|
|
char * WSWRAP_PORT, * end;
|
2010-12-16 20:04:16 +00:00
|
|
|
int ret, envport, bindport = htons(addr_in->sin_port);
|
2010-12-03 04:11:02 +00:00
|
|
|
if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "bind");
|
2010-12-28 02:44:14 +00:00
|
|
|
TRACE(">> bind(%d, _, %d)\n", sockfd, addrlen);
|
2010-12-03 04:11:02 +00:00
|
|
|
|
2010-12-16 20:04:16 +00:00
|
|
|
ret = (int) func(sockfd, addr, addrlen);
|
2010-12-13 19:20:34 +00:00
|
|
|
|
|
|
|
if (addr_in->sin_family != AF_INET) {
|
|
|
|
// TODO: handle IPv6
|
2010-12-27 20:15:59 +00:00
|
|
|
TRACE("<< bind, ignoring non-IPv4 socket\n");
|
2010-12-16 20:04:16 +00:00
|
|
|
return ret;
|
2010-12-13 19:20:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
WSWRAP_PORT = getenv("WSWRAP_PORT");
|
|
|
|
if ((! WSWRAP_PORT) || (*WSWRAP_PORT == '\0')) {
|
2010-12-27 20:15:59 +00:00
|
|
|
// TODO: interpose on all sockets when WSWRAP_PORT not set
|
|
|
|
TRACE("<< bind, not interposing: WSWRAP_PORT is not set\n");
|
2010-12-16 20:04:16 +00:00
|
|
|
return ret;
|
2010-12-13 19:20:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
envport = strtol(WSWRAP_PORT, &end, 10);
|
|
|
|
if ((envport == 0) || (*end != '\0')) {
|
2010-12-27 20:15:59 +00:00
|
|
|
TRACE("<< bind, not interposing: WSWRAP_PORT is not a number\n");
|
2010-12-16 20:04:16 +00:00
|
|
|
return ret;
|
2010-12-13 19:20:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (envport != bindport) {
|
2010-12-27 20:15:59 +00:00
|
|
|
TRACE("<< bind, not interposing on port: %d (fd %d)\n", bindport, sockfd);
|
2010-12-16 20:04:16 +00:00
|
|
|
return ret;
|
2010-12-13 19:20:34 +00:00
|
|
|
}
|
|
|
|
|
2010-12-16 20:04:16 +00:00
|
|
|
_WS_listen_fd = sockfd;
|
2010-12-13 19:20:34 +00:00
|
|
|
|
2011-01-09 03:46:36 +00:00
|
|
|
TRACE("<< bind, listening for WebSockets connections on port: %d (fd %d)\n", envport, sockfd);
|
2010-12-16 20:04:16 +00:00
|
|
|
return ret;
|
2010-12-03 04:11:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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");
|
2010-12-28 02:44:14 +00:00
|
|
|
TRACE("<< accept(%d, _, _)\n", sockfd);
|
2010-12-03 04:11:02 +00:00
|
|
|
|
|
|
|
fd = (int) func(sockfd, addr, addrlen);
|
|
|
|
|
2010-12-16 20:04:16 +00:00
|
|
|
if (_WS_listen_fd == -1) {
|
2010-12-27 20:15:59 +00:00
|
|
|
TRACE("<< accept: not interposing\n");
|
2010-12-13 19:20:34 +00:00
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2010-12-16 20:04:16 +00:00
|
|
|
if (_WS_listen_fd != sockfd) {
|
2010-12-27 20:15:59 +00:00
|
|
|
TRACE("<< accept: not interposing on fd %d\n", sockfd);
|
2010-12-16 20:04:16 +00:00
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-14 17:43:34 +00:00
|
|
|
if (_WS_connections[fd]) {
|
2010-12-27 20:15:59 +00:00
|
|
|
RET_ERROR(EINVAL, "already interposing on fd %d\n", fd);
|
2010-12-14 17:43:34 +00:00
|
|
|
} else {
|
2010-12-14 18:05:17 +00:00
|
|
|
/* It's a port we're interposing on so allocate memory for it */
|
2010-12-27 20:15:59 +00:00
|
|
|
if (_WS_nfds >= WS_MAX_FDS) {
|
|
|
|
RET_ERROR(ENOMEM, "Too many interposer fds\n");
|
|
|
|
}
|
2011-01-09 03:46:36 +00:00
|
|
|
if (_WS_alloc(fd) < 0) {
|
|
|
|
return -1;
|
2010-12-03 04:11:02 +00:00
|
|
|
}
|
2010-12-27 20:15:59 +00:00
|
|
|
|
2010-12-14 17:43:34 +00:00
|
|
|
ret = _WS_handshake(fd);
|
2010-12-03 04:11:02 +00:00
|
|
|
if (ret < 0) {
|
2011-01-09 03:46:36 +00:00
|
|
|
_WS_free(fd);
|
2010-12-03 04:11:02 +00:00
|
|
|
errno = EPROTO;
|
2010-12-27 20:15:59 +00:00
|
|
|
TRACE("<< accept(%d, _, _): ret %d\n", sockfd, ret);
|
2010-12-03 04:11:02 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2010-12-14 17:43:34 +00:00
|
|
|
MSG("interposing on fd %d (allocated memory)\n", fd);
|
2010-12-03 04:11:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
int close(int fd)
|
|
|
|
{
|
|
|
|
static void * (*func)();
|
|
|
|
if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "close");
|
|
|
|
|
2011-01-09 03:46:36 +00:00
|
|
|
TRACE("close(%d) called\n", fd);
|
2010-12-27 20:15:59 +00:00
|
|
|
|
2011-01-09 03:46:36 +00:00
|
|
|
_WS_free(fd);
|
2010-12-27 20:15:59 +00:00
|
|
|
|
2010-12-03 04:11:02 +00:00
|
|
|
return (int) func(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ssize_t read(int fd, void *buf, size_t count)
|
|
|
|
{
|
2011-01-09 03:46:36 +00:00
|
|
|
TRACE("read(%d, _, %d) called\n", fd, count);
|
2010-12-03 04:11:02 +00:00
|
|
|
return (ssize_t) _WS_recv(0, fd, buf, count, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t write(int fd, const void *buf, size_t count)
|
|
|
|
{
|
2011-01-09 03:46:36 +00:00
|
|
|
TRACE("write(%d, _, %d) called\n", fd, count);
|
2010-12-03 04:11:02 +00:00
|
|
|
return (ssize_t) _WS_send(0, fd, buf, count, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t recv(int sockfd, void *buf, size_t len, int flags)
|
|
|
|
{
|
2011-01-09 03:46:36 +00:00
|
|
|
TRACE("recv(%d, _, %d, %d) called\n", sockfd, len, flags);
|
2010-12-03 04:11:02 +00:00
|
|
|
return (ssize_t) _WS_recv(1, sockfd, buf, len, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t send(int sockfd, const void *buf, size_t len, int flags)
|
|
|
|
{
|
2011-01-09 03:46:36 +00:00
|
|
|
TRACE("send(%d, _, %d, %d) called\n", sockfd, len, flags);
|
2010-12-03 04:11:02 +00:00
|
|
|
return (ssize_t) _WS_send(1, sockfd, buf, len, flags);
|
|
|
|
}
|
|
|
|
|
2010-12-27 20:15:59 +00:00
|
|
|
int select(int nfds, fd_set *readfds, fd_set *writefds,
|
|
|
|
fd_set *exceptfds, struct timeval *timeout)
|
|
|
|
{
|
2011-01-09 03:46:36 +00:00
|
|
|
TRACE("select(%d, _, _, _, _) called\n", nfds);
|
2010-12-27 20:15:59 +00:00
|
|
|
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)
|
|
|
|
{
|
2010-12-28 02:44:14 +00:00
|
|
|
TRACE("poll(_, %ld, %d) called\n", nfds, timeout);
|
|
|
|
return _WS_poll(0, fds, nfds, timeout, NULL, NULL);
|
|
|
|
}
|
2010-12-27 20:15:59 +00:00
|
|
|
|
2010-12-28 02:44:14 +00:00
|
|
|
int ppoll(struct pollfd *fds, nfds_t nfds,
|
|
|
|
const struct timespec *timeout, const sigset_t *sigmask)
|
|
|
|
{
|
|
|
|
TRACE("ppoll(_, %ld, _, _) called\n", nfds);
|
2011-01-09 03:46:36 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2011-01-09 03:46:36 +00:00
|
|
|
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);
|
2011-01-09 03:46:36 +00:00
|
|
|
|
|
|
|
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])) {
|
2011-01-09 03:46:36 +00:00
|
|
|
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);
|
2011-01-09 03:46:36 +00:00
|
|
|
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);
|
2011-01-09 03:46:36 +00:00
|
|
|
/* 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);
|
2011-01-09 03:46:36 +00:00
|
|
|
return ret;
|
|
|
|
|
2010-12-27 20:15:59 +00:00
|
|
|
}
|
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;
|
|
|
|
}
|
|
|
|
|