Commit 61265b3a authored by Joel Martin's avatar Joel Martin

Remove files that are now in websockify.

https://github.com/kanaka/websockify is now the canonical location of
websockify (formerly wsproxy). A copy of the python version is kept
here for backwards compatibility and ease-of-use. The other versions
and related test scripts are in websockify.
parent 66937e39
../utils/wsecho.html
\ No newline at end of file
<html>
<head><title>WebSockets Test</title></head>
<body>
Host: <input id='host' style='width:100'>&nbsp;
Port: <input id='port' style='width:50'>&nbsp;
Encrypt: <input id='encrypt' type='checkbox'>&nbsp;
<input id='connectButton' type='button' value='Start' style='width:100px'
onclick="connect();">&nbsp;
<br>
Messages:<br>
<textarea id="messages" style="font-size: 9;" cols=80 rows=25></textarea>
</body>
<!-- Uncomment to activate firebug lite -->
<!--
<script type='text/javascript'
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
-->
<script src="include/base64.js"></script>
<script src="include/util.js"></script>
<script src="include/webutil.js"></script>
<script>
var host = null, port = null;
var ws = null;
var VNC_native_ws = true;
function message(str) {
console.log(str);
cell = $D('messages');
cell.innerHTML += str + "\n";
cell.scrollTop = cell.scrollHeight;
}
function print_response(str) {
message("str.length: " + str.length);
for (i=0; i < str.length; i++) {
message(i + ": " + (str.charCodeAt(i) % 256));
}
}
function send() {
var str = "";
str = str + String.fromCharCode(0x81);
str = str + String.fromCharCode(0xff);
for (var i=0; i<256; i+=4) {
str = str + String.fromCharCode(i);
}
str = str + String.fromCharCode(0);
str = str + String.fromCharCode(0x40);
str = str + String.fromCharCode(0x41);
str = str + String.fromCharCode(0xff);
str = str + String.fromCharCode(0x81);
ws.send(str);
}
function init_ws() {
console.log(">> init_ws");
var scheme = "ws://";
if ($D('encrypt').checked) {
scheme = "wss://";
}
var uri = scheme + host + ":" + port;
console.log("connecting to " + uri);
ws = new WebSocket(uri);
ws.onmessage = function(e) {
console.log(">> WebSockets.onmessage");
print_response(e.data);
console.log("<< WebSockets.onmessage");
};
ws.onopen = function(e) {
console.log(">> WebSockets.onopen");
send();
console.log("<< WebSockets.onopen");
};
ws.onclose = function(e) {
console.log(">> WebSockets.onclose");
console.log("<< WebSockets.onclose");
};
ws.onerror = function(e) {
console.log(">> WebSockets.onerror");
console.log(" " + e);
console.log("<< WebSockets.onerror");
};
console.log("<< init_ws");
}
function connect() {
console.log(">> connect");
host = $D('host').value;
port = $D('port').value;
if ((!host) || (!port)) {
console.log("must set host and port");
return;
}
if (ws) {
ws.close();
}
init_ws();
$D('connectButton').value = "Stop";
$D('connectButton').onclick = disconnect;
console.log("<< connect");
}
function disconnect() {
console.log(">> disconnect");
if (ws) {
ws.close();
}
$D('connectButton').value = "Start";
$D('connectButton').onclick = connect;
console.log("<< disconnect");
}
/* If no builtin websockets then load web_socket.js */
if (! window.WebSocket) {
console.log("Loading web-socket-js flash bridge");
var extra = "<script src='include/web-socket-js/swfobject.js'><\/script>";
extra += "<script src='include/web-socket-js/FABridge.js'><\/script>";
extra += "<script src='include/web-socket-js/web_socket.js'><\/script>";
document.write(extra);
VNC_native_ws = false;
}
window.onload = function() {
console.log("onload");
if (! VNC_native_ws) {
WebSocket.__swfLocation = "include/web-socket-js/WebSocketMain.swf";
WebSocket.__initialize();
}
var url = document.location.href;
$D('host').value = (url.match(/host=([^&#]*)/) || ['',''])[1];
$D('port').value = (url.match(/port=([^&#]*)/) || ['',''])[1];
}
</script>
</html>
#!/usr/bin/python
'''
WebSocket server-side load test program. Sends and receives traffic
that has a random payload (length and content) that is checksummed and
given a sequence number. Any errors are reported and counted.
'''
import sys, os, socket, ssl, time, traceback
import random, time
from base64 import b64encode, b64decode
from codecs import utf_8_encode, utf_8_decode
from select import select
sys.path.insert(0,os.path.dirname(__file__) + "/../utils/")
from websocket import *
buffer_size = 65536
recv_cnt = send_cnt = 0
def check(buf):
if buf[0] != '\x00' or buf[-1] != '\xff':
raise Exception("Invalid WS packet")
for decoded in decode(buf):
nums = [ord(c) for c in decoded]
print "Received nums: ", nums
return
def responder(client):
cpartial = ""
socks = [client]
sent = False
received = False
while True:
ins, outs, excepts = select(socks, socks, socks, 1)
if excepts: raise Exception("Socket exception")
if client in ins:
buf = client.recv(buffer_size)
if len(buf) == 0: raise Exception("Client closed")
received = True
#print "Client recv: %s (%d)" % (repr(buf[1:-1]), len(buf))
if buf[-1] == '\xff':
if cpartial:
err = check(cpartial + buf)
cpartial = ""
else:
err = check(buf)
if err:
print err
else:
print "received partitial"
cpartial = cpartial + buf
if received and not sent and client in outs:
sent = True
#nums = "".join([unichr(c) for c in range(0,256)])
#nums = "".join([chr(c) for c in range(1,128)])
#nums = nums + chr(194) + chr(128) + chr(194) + chr(129)
#nums = "".join([chr(c) for c in range(0,256)])
nums = "\x81\xff"
nums = nums + "".join([chr(c) for c in range(0,256,4)])
nums = nums + "\x00\x40\x41\xff\x81"
# print nums
client.send(encode(nums))
# client.send("\x00" + nums + "\xff")
# print "Sent characters 0-255"
# #print "Client send: %s (%d)" % (repr(nums), len(nums))
if __name__ == '__main__':
try:
if len(sys.argv) < 2: raise
listen_port = int(sys.argv[1])
except:
print "Usage: <listen_port>"
sys.exit(1)
settings['listen_port'] = listen_port
settings['daemon'] = False
settings['handler'] = responder
start_server()
../utils/wstest.html
\ No newline at end of file
../utils/wstest.py
\ No newline at end of file
TARGETS=wsproxy wswrapper.so rebind.so
TARGETS=rebind.so
CFLAGS += -fPIC
all: $(TARGETS)
wsproxy: wsproxy.o websocket.o md5.o
$(CC) $(LDFLAGS) $^ -lssl -lcrypto -lresolv -o $@
wswrapper.o: wswrapper.h
wswrapper.so: wswrapper.o md5.o
$(CC) $(LDFLAGS) $^ -shared -fPIC -ldl -lresolv -o $@
rebind.so: rebind.o
$(CC) $(LDFLAGS) $^ -shared -fPIC -ldl -o $@
websocket.o: websocket.c websocket.h md5.h
wsproxy.o: wsproxy.c websocket.h
wswrapper.o: wswrapper.c
$(CC) -c $(CFLAGS) -o $@ $*.c
md5.o: md5.c md5.h
$(CC) -c $(CFLAGS) -o $@ $*.c -DHAVE_MEMCPY -DSTDC_HEADERS
clean:
rm -f wsproxy wswrapper.so *.o
rm -f rebind.o rebind.so
## WebSockets Proxy
### wsproxy
At the most basic level, wsproxy just translates WebSockets traffic
to normal socket traffic. wsproxy accepts the WebSockets handshake,
parses it, and then begins forwarding traffic between the client and
the target in both directions. WebSockets payload data is UTF-8
encoded so in order to transport binary data it must use an encoding
that can be encapsulated within UTF-8. wsproxy uses base64 to encode
all traffic to and from the client. Also, WebSockets traffic starts
with '\0' (0) and ends with '\xff' (255). Some buffering is done in
case the data from the client is not a full WebSockets frame (i.e.
does not end in 255).
#### Additional wsproxy features
These are not necessary for the basic operation.
* Daemonizing: When the `-D` option is specified, wsproxy runs
in the background as a daemon process.
* SSL (the wss:// WebSockets URI): This is detected automatically by
wsproxy by sniffing the first byte sent from the client and then
wrapping the socket if the data starts with '\x16' or '\x80'
(indicating SSL).
* Flash security policy: wsproxy detects flash security policy
requests (again by sniffing the first packet) and answers with an
appropriate flash security policy response (and then closes the
port). This means no separate flash security policy server is needed
for supporting the flash WebSockets fallback emulator.
* Session recording: This feature that allows recording of the traffic
sent and received from the client to a file using the `--record`
option.
* Mini-webserver: wsproxy can detect and respond to normal web
requests on the same port as the WebSockets proxy and Flash security
policy. This functionality is activate with the `--web DIR` option
where DIR is the root of the web directory to serve.
* Wrap a program: see the "Wrap a Program" section below.
#### Implementations of wsproxy
There are three implementations of wsproxy: python, C, and Node
(node.js). wswrapper is only implemented in C.
Here is the feature support matrix for the the wsproxy
implementations:
<table>
<tr>
<th>Program</th>
<th>Language</th>
<th>Multiprocess</th>
<th>Daemonize</th>
<th>SSL/wss</th>
<th>Flash Policy Server</th>
<th>Session Record</th>
<th>Web Server</th>
<th>Program Wrap</th>
</tr> <tr>
<td>wsproxy.py</td>
<td>python</td>
<td>yes</td>
<td>yes</td>
<td>yes 1</td>
<td>yes</td>
<td>yes</td>
<td>yes</td>
<td>yes</td>
</tr> <tr>
<td>wsproxy</td>
<td>C</td>
<td>yes</td>
<td>yes</td>
<td>yes</td>
<td>yes</td>
<td>no</td>
<td>no</td>
<td>no</td>
</tr>
</tr> <tr>
<td>wsproxy.js</td>
<td>Node (node.js)</td>
<td>yes</td>
<td>no</td>
<td>no</td>
<td>no</td>
<td>no</td>
<td>no</td>
<td>no</td>
</tr>
</table>
* Note 1: to use SSL/wss with python 2.5 or older, see the following
section on *Building the Python ssl module*.
### Wrap a Program
In addition to proxying from a source address to a target address
(which may be on a different system), wsproxy has the ability to
launch a program on the local system and proxy WebSockets traffic to
a normal TCP port owned/bound by the program.
The is accomplished with a small LD_PRELOAD library (`rebind.so`)
which intercepts bind() system calls by the program. The specified
port is moved to a new localhost/loopback free high port. wsproxy
then proxies WebSockets traffic directed to the original port to the
new (moved) port of the program.
The program wrap mode is invoked by replacing the target with `--`
followed by the program command line to wrap.
`./utils/wsproxy.py 2023 -- PROGRAM ARGS`
The `--wrap-mode` option can be used to indicate what action to take
when the wrapped program exits or daemonizes.
Here is an example of using wsproxy to wrap the vncserver command
(which backgrounds itself):
`./utils/wsproxy.py 5901 --wrap-mode=ignore -- vncserver -geometry 1024x768 :1`
Here is an example of wrapping telnetd (from krb5-telnetd).telnetd
exits after the connection closes so the wrap mode is set to respawn
the command:
`sudo ./utils/wsproxy.py 2023 --wrap-mode=respawn -- telnetd -debug 2023`
The `utils/wstelnet.html` page demonstrates a simple WebSockets based
telnet client.
### Building the Python ssl module (for python 2.5 and older)
* Install the build dependencies. On Ubuntu use this command:
`sudo aptitude install python-dev bluetooth-dev`
* Download, build the ssl module and symlink to it:
`cd noVNC/utils`
`wget http://pypi.python.org/packages/source/s/ssl/ssl-1.15.tar.gz`
`tar xvzf ssl-1.15.tar.gz`
`cd ssl-1.15`
`make`
`cd ../`
`ln -sf ssl-1.15/build/lib.linux-*/ssl ssl`
wsproxy has become [websockify](https://github.com/kanaka/websockify).
A copy of the python version of websockify (named wsproxy.py) is kept
here for ease of use. The other versions of websockify (C, Node.js)
and the associated test programs have been moved to
[websockify](https://github.com/kanaka/websockify).
For more detailed description and usage information please refer to
the [websockify README](https://github.com/kanaka/websockify/blob/master/README.md).
// VT100.js -- a text terminal emulator in JavaScript with a ncurses-like
// interface and a POSIX-like interface. (The POSIX-like calls are
// implemented on top of the ncurses-like calls, not the other way round.)
//
// Released under the GNU LGPL v2.1, by Frank Bi <bi@zompower.tk>
//
// 2007-08-12 - refresh():
// - factor out colour code to html_colours_()
// - fix handling of A_REVERSE | A_DIM
// - simplify initial <br /> output code
// - fix underlining colour
// - fix attron() not to turn off attributes
// - decouple A_STANDOUT and A_BOLD
// 2007-08-11 - getch() now calls refresh()
// 2007-08-06 - Safari compat fix -- turn '\r' into '\n' for onkeypress
// 2007-08-05 - Opera compat fixes for onkeypress
// 2007-07-30 - IE compat fixes:
// - change key handling code
// - add <br />...<br />&nbsp; so that 1st and last lines align
// 2007-07-28 - change wrapping behaviour -- writing at the right edge no
// longer causes the cursor to immediately wrap around
// - add <b>...</b> to output to make A_STANDOUT stand out more
// - add handling of backspace, tab, return keys
// - fix doc. of VT100() constructor
// - change from GPL to LGPL
// 2007-07-09 - initial release
//
// class VT100
// A_NORMAL, A_UNDERLINE, A_REVERSE, A_BLINK, A_DIM, A_BOLD, A_STANDOUT
// =class constants=
// Attribute constants.
// VT100(wd, ht, scr_id) =constructor=
// Creates a virtual terminal with width `wd', and
// height `ht'. The terminal will be displayed between
// <pre>...</pre> tags which have element ID `scr_id'.
// addch(ch [, attr])
// Writes out the character `ch'. If `attr' is given,
// it specifies the attributes for the character,
// otherwise the current attributes are used.
// addstr(stuff) Writes out the string `stuff' using the current
// attributes.
// attroff(mode) Turns off any current options given in mode.
// attron(mode) Turns on any options given in mode.
// attrset(mode) Sets the current options to mode.
// bkgdset(attr) Sets the background attributes to attr.
// clear() Clears the terminal using the background attributes,
// and homes the cursor.
// clrtobol() Clears the portion of the terminal from the cursor
// to the bottom.
// clrtoeol() Clears the portion of the current line after the
// cursor.
// curs_set(vis [, grab])
// If `vis' is 0, makes the cursor invisible; otherwise
// make it visible. If `grab' is given and true, starts
// capturing keyboard events (for `getch()'); if given
// and false, stops capturing events.
// echo() Causes key strokes to be automatically echoed on the
// terminal.
// erase() Same as `clear()'.
// getch(isr) Arranges to call `isr' when a key stroke is
// received. The received character and the terminal
// object are passed as arguments to `isr'.
// getmaxyx() Returns an associative array with the maximum row
// (`y') and column (`x') numbers for the terminal.
// getyx() Returns an associative array with the current row
// (`y') and column (`x') of the cursor.
// move(r, c) Moves the cursor to row `r', column `c'.
// noecho() Stops automatically echoing key strokes.
// refresh() Updates the display.
// scroll() Scrolls the terminal up one line.
// standend() Same as `attrset(VT100.A_NORMAL)'.
// standout() Same as `attron(VT100.A_STANDOUT)'.
// write(stuff) Writes `stuff' to the terminal and immediately
// updates the display; (some) escape sequences are
// interpreted and acted on.
// constructor
function VT100(wd, ht, scr_id)
{
var r;
var c;
var scr = document.getElementById(scr_id);
this.wd_ = wd;
this.ht_ = ht;
this.scrolled_ = 0;
this.bkgd_ = {
mode: VT100.A_NORMAL,
fg: VT100.COLOR_WHITE,
bg: VT100.COLOR_BLACK
};
this.c_attr_ = {
mode: VT100.A_NORMAL,
fg: VT100.COLOR_WHITE,
bg: VT100.COLOR_BLACK
};
this.text_ = new Array(ht);
this.attr_ = new Array(ht);
for (r = 0; r < ht; ++r) {
this.text_[r] = new Array(wd);
this.attr_[r] = new Array(wd);
}
this.scr_ = scr;
this.cursor_vis_ = true;
this.grab_events_ = false;
this.getch_isr_ = undefined;
this.key_buf_ = [];
this.echo_ = true;
this.esc_state_ = 0;
// Internal debug setting.
this.debug_ = 0;
this.clear();
this.refresh();
}
// public constants -- colours and colour pairs
VT100.COLOR_BLACK = 0;
VT100.COLOR_BLUE = 1;
VT100.COLOR_GREEN = 2;
VT100.COLOR_CYAN = 3;
VT100.COLOR_RED = 4;
VT100.COLOR_MAGENTA = 5;
VT100.COLOR_YELLOW = 6;
VT100.COLOR_WHITE = 7;
VT100.COLOR_PAIRS = 256;
VT100.COLORS = 8;
// public constants -- attributes
VT100.A_NORMAL = 0;
VT100.A_UNDERLINE = 1;
VT100.A_REVERSE = 2;
VT100.A_BLINK = 4;
VT100.A_DIM = 8;
VT100.A_BOLD = 16;
VT100.A_STANDOUT = 32;
VT100.A_PROTECT = VT100.A_INVIS = 0; // ?
// other public constants
VT100.TABSIZE = 8;
// private constants
VT100.ATTR_FLAGS_ = VT100.A_UNDERLINE | VT100.A_REVERSE | VT100.A_BLINK |
VT100.A_DIM | VT100.A_BOLD | VT100.A_STANDOUT |
VT100.A_PROTECT | VT100.A_INVIS;
VT100.COLOR_SHIFT_ = 6;
VT100.browser_ie_ = (navigator.appName.indexOf("Microsoft") != -1);
VT100.browser_opera_ = (navigator.appName.indexOf("Opera") != -1);
// class variables
VT100.the_vt_ = undefined;
// class methods
// this is actually an event handler
VT100.handle_onkeypress_ = function VT100_handle_onkeypress(event)
{
var vt = VT100.the_vt_, ch;
if (vt === undefined)
return true;
if (VT100.browser_ie_ || VT100.browser_opera_) {
ch = event.keyCode;
if (ch == 13)
ch = 10;
else if (ch > 255 || (ch < 32 && ch != 8))
return true;
ch = String.fromCharCode(ch);
} else {
ch = event.charCode;
//dump("ch: " + ch + "\n");
//dump("ctrl?: " + event.ctrlKey + "\n");
vt.debug("onkeypress:: keyCode: " + event.keyCode + ", ch: " + event.charCode);
if (ch) {
if (ch > 255)
return true;
if (event.ctrlKey && event.shiftKey) {
// Don't send the copy/paste commands.
var charStr = String.fromCharCode(ch);
if (charStr == 'C' || charStr == 'V') {
return false;
}
}
if (event.ctrlKey) {
ch = String.fromCharCode(ch - 96);
} else {
ch = String.fromCharCode(ch);
if (ch == '\r')
ch = '\n';
}
} else {
switch (event.keyCode) {
case event.DOM_VK_BACK_SPACE:
ch = '\b';
break;
case event.DOM_VK_TAB:
ch = '\t';
// Stop tab from moving to another element.
event.preventDefault();
break;
case event.DOM_VK_RETURN:
case event.DOM_VK_ENTER:
ch = '\n';
break;
case event.DOM_VK_UP:
ch = '\x1b[A';
break;
case event.DOM_VK_DOWN:
ch = '\x1b[B';
break;
case event.DOM_VK_RIGHT:
ch = '\x1b[C';
break;
case event.DOM_VK_LEFT:
ch = '\x1b[D';
break;
case event.DOM_VK_DELETE:
ch = '\x1b[3~';
break;
case event.DOM_VK_HOME:
ch = '\x1b[H';
break;
case event.DOM_VK_ESCAPE:
ch = '\x1bc';
break;
default:
return true;
}
}
}
vt.key_buf_.push(ch);
setTimeout(VT100.go_getch_, 0);
return false;
}
// this is actually an event handler
VT100.handle_onkeydown_ = function VT100_handle_onkeydown()
{
var vt = VT100.the_vt_, ch;
switch (event.keyCode) {
case 8:
ch = '\b'; break;
default:
return true;
}
vt.key_buf_.push(ch);
setTimeout(VT100.go_getch_, 0);
return false;
}
VT100.go_getch_ = function VT100_go_getch()
{
var vt = VT100.the_vt_;
if (vt === undefined)
return;
var isr = vt.getch_isr_;
vt.getch_isr_ = undefined;
if (isr === undefined)
return;
var ch = vt.key_buf_.shift();
if (ch === undefined) {
vt.getch_isr_ = isr;
return;
}
if (vt.echo_)
vt.addch(ch);
isr(ch, vt);
}
// object methods
VT100.prototype.may_scroll_ = function()
{
var ht = this.ht_, cr = this.row_;
while (cr >= ht) {
this.scroll();
--cr;
}
this.row_ = cr;
}
VT100.prototype.html_colours_ = function(attr)
{
var fg, bg, co0, co1;
fg = attr.fg;
bg = attr.bg;
switch (attr.mode & (VT100.A_REVERSE | VT100.A_DIM | VT100.A_BOLD)) {
case 0:
case VT100.A_DIM | VT100.A_BOLD:
co0 = '00'; co1 = 'c0';
break;
case VT100.A_BOLD:
co0 = '00'; co1 = 'ff';
break;
case VT100.A_DIM:
if (fg == VT100.COLOR_BLACK)
co0 = '40';
else
co0 = '00';
co1 = '40';
break;
case VT100.A_REVERSE:
case VT100.A_REVERSE | VT100.A_DIM | VT100.A_BOLD:
co0 = 'c0'; co1 = '40';
break;
case VT100.A_REVERSE | VT100.A_BOLD:
co0 = 'c0'; co1 = '00';
break;
default:
if (fg == VT100.COLOR_BLACK)
co0 = '80';
else
co0 = 'c0';
co1 = 'c0';
}
return {
f: '#' + (fg & 4 ? co1 : co0) +
(fg & 2 ? co1 : co0) +
(fg & 1 ? co1 : co0),
b: '#' + (bg & 4 ? co1 : co0) +
(bg & 2 ? co1 : co0) +
(bg & 1 ? co1 : co0)
};
}
VT100.prototype.addch = function(ch, attr)
{
var cc = this.col_;
this.debug("addch:: ch: " + ch + ", attr: " + attr);
switch (ch) {
case '\b':
if (cc != 0)
--cc;
break;
case '\n':
++this.row_;
cc = 0;
this.clrtoeol();
this.may_scroll_();
break;
case '\r':
this.may_scroll_();
cc = 0;
break;
case '\t':
this.may_scroll_();
cc += VT100.TABSIZE - cc % VT100.TABSIZE;
if (cc >= this.wd_) {
++this.row_;
cc -= this.wd_;
}
break;
default:
if (attr === undefined)
attr = this._cloneAttr(this.c_attr_);
if (cc >= this.wd_) {
++this.row_;
cc = 0;
}
this.may_scroll_();
this.text_[this.row_][cc] = ch;
this.attr_[this.row_][cc] = attr;
++cc;
}
this.col_ = cc;
}
VT100.prototype.addstr = function(stuff)
{
for (var i = 0; i < stuff.length; ++i)
this.addch(stuff.charAt(i));
}
VT100.prototype._cloneAttr = function VT100_cloneAttr(a)
{
return {
mode: a.mode,
fg: a.fg,
bg: a.bg
};
}
VT100.prototype.attroff = function(a)
{
//dump("attroff: " + a + "\n");
a &= VT100.ATTR_FLAGS_;
this.c_attr_.mode &= ~a;
}
VT100.prototype.attron = function(a)
{
//dump("attron: " + a + "\n");
a &= VT100.ATTR_FLAGS_;
this.c_attr_.mode |= a;
}
VT100.prototype.attrset = function(a)
{
//dump("attrset: " + a + "\n");
this.c_attr_.mode = a;
}
VT100.prototype.fgset = function(fg)
{
//dump("fgset: " + fg + "\n");
this.c_attr_.fg = fg;
}
VT100.prototype.bgset = function(bg)
{
//dump("bgset: " + bg + "\n");
if (bg !== 0) {
this.warn("bgset: " + bg + "\n");
}
this.c_attr_.bg = bg;
}
VT100.prototype.bkgdset = function(a)
{
this.bkgd_ = a;
}
VT100.prototype.clear = function()
{
this.debug("clear");
this.row_ = this.col_ = 0;
this.scrolled_ = 0;
for (r = 0; r < this.ht_; ++r) {
for (c = 0; c < this.wd_; ++c) {
this.text_[r][c] = ' ';
this.attr_[r][c] = this._cloneAttr(this.bkgd_);
}
}
}
VT100.prototype.clrtobot = function()
{
this.debug("clrtobot, row: " + this.row_);
var ht = this.ht_;
var wd = this.wd_;
this.clrtoeol();
for (var r = this.row_ + 1; r < ht; ++r) {
for (var c = 0; c < wd; ++c) {
this.text_[r][c] = ' ';
this.attr_[r][c] = this.bkgd_;
}
}
}
VT100.prototype.clrtoeol = function()
{
this.debug("clrtoeol, col: " + this.col_);
var r = this.row_;
if (r >= this.ht_)
return;
for (var c = this.col_; c < this.wd_; ++c) {
this.text_[r][c] = ' ';
this.attr_[r][c] = this.bkgd_;
}
}
VT100.prototype.clearpos = function(row, col)
{
this.debug("clearpos (" + row + ", " + col + ")");
if (row < 0 || row >= this.ht_)
return;
if (col < 0 || col >= this.wd_)
return;
this.text_[row][col] = ' ';
this.attr_[row][col] = this.bkgd_;
}
VT100.prototype.curs_set = function(vis, grab, eventist)
{
this.debug("curs_set:: vis: " + vis + ", grab: " + grab);
if (vis !== undefined)
this.cursor_vis_ = (vis > 0);
if (eventist === undefined)
eventist = window;
if (grab === true || grab === false) {
if (grab === this.grab_events_)
return;
if (grab) {
this.grab_events_ = true;
VT100.the_vt_ = this;
eventist.addEventListener("keypress", VT100.handle_onkeypress_, false);
if (VT100.browser_ie_)
document.onkeydown = VT100.handle_onkeydown_;
} else {
eventist.removeEventListener("keypress", VT100.handle_onkeypress_, false);
if (VT100.browser_ie_)
document.onkeydown = VT100.handle_onkeydown_;
this.grab_events_ = false;
VT100.the_vt_ = undefined;
}
}
}
VT100.prototype.echo = function()
{
this.debug("echo on");
this.echo_ = true;
}
VT100.prototype.erase = VT100.prototype.clear;
VT100.prototype.getch = function(isr)
{
this.debug("getch");
this.refresh();
this.getch_isr_ = isr;
setTimeout(VT100.go_getch_, 0);
}
VT100.prototype.getmaxyx = function()
{
return { y: this.ht_ - 1, x: this.wd_ - 1 };
}
VT100.prototype.getyx = function()
{
return { y: this.row_, x: this.col_ };
}
VT100.prototype.move = function(r, c)
{
this.debug("move: (" + r + ", " + c + ")");
if (r < 0)
r = 0;
else if (r >= this.ht_)
r = this.ht_ - 1;
if (c < 0)
c = 0;
else if (c >= this.wd_)
c = this.wd_ - 1;
this.row_ = r;
this.col_ = c;
}
VT100.prototype.noecho = function()
{
this.debug("echo off");
this.echo_ = false;
}
VT100.prototype.refresh = function()
{
this.debug("refresh");
var r, c, stuff = "", start_tag = "", end_tag = "", at = -1, n_at, ch,
pair, cr, cc, ht, wd, cv, added_end_tag;
ht = this.ht_;
wd = this.wd_;
cr = this.row_;
cc = this.col_;
cv = this.cursor_vis_;
var innerHTML = this.scr_.innerHTML;
if (cc >= wd)
cc = wd - 1;
for (r = 0; r < ht; ++r) {
if (r > 0) {
stuff += '\n';
}
for (c = 0; c < wd; ++c) {
added_end_tag = false;
n_at = this.attr_[r][c];
if (cv && r == cr && c == cc) {
// Draw the cursor here.
n_at = this._cloneAttr(n_at);
n_at.mode ^= VT100.A_REVERSE;
}
// If the attributes changed, make a new span.
if (n_at.mode != at.mode || n_at.fg != at.fg || n_at.bg != at.bg) {
if (c > 0) {
stuff += end_tag;
}
start_tag = "";
end_tag = "";
if (n_at.mode & VT100.A_BLINK) {
start_tag = "<blink>";
end_tag = "</blink>" + end_tag;
}
if (n_at.mode & VT100.A_STANDOUT)
n_at.mode |= VT100.A_BOLD;
pair = this.html_colours_(n_at);
start_tag += '<span style="color:' + pair.f +
';background-color:' + pair.b;
if (n_at.mode & VT100.A_UNDERLINE)
start_tag += ';text-decoration:underline';
start_tag += ';">';
stuff += start_tag;
end_tag = "</span>" + end_tag;
at = n_at;
added_end_tag = true;
} else if (c == 0) {
stuff += start_tag;
}
ch = this.text_[r][c];
switch (ch) {
case '&':
stuff += '&amp;'; break;
case '<':
stuff += '&lt;'; break;
case '>':
stuff += '&gt;'; break;
case ' ':
//stuff += '&nbsp;'; break;
stuff += ' '; break;
default:
stuff += ch;
}
}
if (!added_end_tag)
stuff += end_tag;
}
this.scr_.innerHTML = "<b>" + stuff + "</b>\n";
}
VT100.prototype.scroll = function()
{
this.scrolled_ += 1;
this.debug("scrolled: " + this.scrolled_);
var n_text = this.text_[0], n_attr = this.attr_[0],
ht = this.ht_, wd = this.wd_;
for (var r = 1; r < ht; ++r) {
this.text_[r - 1] = this.text_[r];
this.attr_[r - 1] = this.attr_[r];
}
this.text_[ht - 1] = n_text;
this.attr_[ht - 1] = n_attr;
for (var c = 0; c < wd; ++c) {
n_text[c] = ' ';
n_attr[c] = this.bkgd_;
}
}
VT100.prototype.standend = function()
{
//this.debug("standend");
this.attrset(0);
}
VT100.prototype.standout = function()
{
//this.debug("standout");
this.attron(VT100.A_STANDOUT);
}
VT100.prototype.write = function(stuff)
{
var ch, x, r, c, i, j, yx, myx;
for (i = 0; i < stuff.length; ++i) {
ch = stuff.charAt(i);
if (ch == '\x0D') {
this.debug("write:: ch: " + ch.charCodeAt(0) + ", '\\x0D'");
} else {
this.debug("write:: ch: " + ch.charCodeAt(0) + ", '" + (ch == '\x1b' ? "ESC" : ch) + "'");
}
//dump("ch: " + ch.charCodeAt(0) + ", '" + (ch == '\x1b' ? "ESC" : ch) + "'\n");
switch (ch) {
case '\x00':
case '\x7f':
case '\x07': /* bell, ignore it */
this.debug("write:: ignoring bell character: " + ch);
continue;
case '\a':
case '\b':
case '\t':
case '\r':
this.addch(ch);
continue;
case '\n':
case '\v':
case '\f': // what a mess
yx = this.getyx();
myx = this.getmaxyx();
if (yx.y >= myx.y) {
this.scroll();
this.move(myx.y, 0);
} else
this.move(yx.y + 1, 0);
continue;
case '\x18':
case '\x1a':
this.esc_state_ = 0;
this.debug("write:: set escape state: 0");
continue;
case '\x1b':
this.esc_state_ = 1;
this.debug("write:: set escape state: 1");
continue;
case '\x9b':
this.esc_state_ = 2;
this.debug("write:: set escape state: 2");
continue;
}
// not a recognized control character
switch (this.esc_state_) {
case 0: // not in escape sequence
this.addch(ch);
break;
case 1: // just saw ESC
switch (ch) {
case '[':
this.esc_state_ = 2;
this.debug("write:: set escape state: 2");
break;
case '=':
/* Set keypade mode (ignored) */
this.debug("write:: set keypade mode: ignored");
this.esc_state_ = 0;
break;
case '>':
/* Reset keypade mode (ignored) */
this.debug("write:: reset keypade mode: ignored");
this.esc_state_ = 0;
break;
case 'H':
/* Set tab at cursor column (ignored) */
this.debug("write:: set tab cursor column: ignored");
this.esc_state_ = 0;
break;
}
break;
case 2: // just saw CSI
switch (ch) {
case 'K':
/* Erase in Line */
this.esc_state_ = 0;
this.clrtoeol();
continue;
case 'H':
/* Move to (0,0). */
this.esc_state_ = 0;
this.move(0, 0);
continue;
case 'J':
/* Clear to the bottom. */
this.esc_state_ = 0;
this.clrtobot();
continue;
case '?':
/* Special VT100 mode handling. */
this.esc_state_ = 5;
this.debug("write:: special vt100 mode");
continue;
}
// Drop through to next case.
this.csi_parms_ = [0];
this.debug("write:: set escape state: 3");
this.esc_state_ = 3;
case 3: // saw CSI and parameters
switch (ch) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
x = this.csi_parms_.pop();
this.csi_parms_.push(x * 10 + ch * 1);
this.debug("csi_parms_: " + this.csi_parms_);
continue;
case ';':
if (this.csi_parms_.length < 17)
this.csi_parms_.push(0);
continue;
}
this.esc_state_ = 0;
switch (ch) {
case 'A':
// Cursor Up <ESC>[{COUNT}A
this.move(this.row_ - Math.max(1, this.csi_parms_[0]),
this.col_);
break;
case 'B':
// Cursor Down <ESC>[{COUNT}B
this.move(this.row_ + Math.max(1, this.csi_parms_[0]),
this.col_);
break;
case 'C':
// Cursor Forward <ESC>[{COUNT}C
this.move(this.row_,
this.col_ + Math.max(1, this.csi_parms_[0]));
break;
case 'c':
this.warn("write:: got TERM query");
break;
case 'D':
// Cursor Backward <ESC>[{COUNT}D
this.move(this.row_,
this.col_ - Math.max(1, this.csi_parms_[0]));
break;
case 'f':
case 'H':
// Cursor Home <ESC>[{ROW};{COLUMN}H
this.csi_parms_.push(0);
this.move(this.csi_parms_[0] - 1,
this.csi_parms_[1] - 1);
break;
case 'J':
switch (this.csi_parms_[0]) {
case 0:
this.clrtobot();
break;
case 2:
this.clear();
this.move(0, 0);
}
break;
case 'm':
for (j=0; j<this.csi_parms_.length; ++j) {
x = this.csi_parms_[j];
switch (x) {
case 0:
this.standend();
this.fgset(this.bkgd_.fg);
this.bgset(this.bkgd_.bg);
break;
case 1:
this.attron(VT100.A_BOLD);
break;
case 30:
this.fgset(VT100.COLOR_BLACK);
break;
case 31:
this.fgset(VT100.COLOR_RED);
break;
case 32:
this.fgset(VT100.COLOR_GREEN);
break;
case 33:
this.fgset(VT100.COLOR_YELLOW);
break;
case 34:
this.fgset(VT100.COLOR_BLUE);
break;
case 35:
this.fgset(VT100.COLOR_MAGENTA);
break;
case 36:
this.fgset(VT100.COLOR_CYAN);
break;
case 37:
this.fgset(VT100.COLOR_WHITE);
break;
case 40:
this.bgset(VT100.COLOR_BLACK);
break;
case 41:
this.bgset(VT100.COLOR_RED);
break;
case 42:
this.bgset(VT100.COLOR_GREEN);
break;
case 44:
this.bgset(VT100.COLOR_YELLOW);
break;
case 44:
this.bgset(VT100.COLOR_BLUE);
break;
case 45:
this.bgset(VT100.COLOR_MAGENTA);
break;
case 46:
this.bgset(VT100.COLOR_CYAN);
break;
case 47:
this.bgset(VT100.COLOR_WHITE);
break;
}
}
break;
case 'r':
// 1,24r - set scrolling region (ignored)
break;
case '[':
this.debug("write:: set escape state: 4");
this.esc_state_ = 4;
break;
case 'g':
// 0g: clear tab at cursor (ignored)
// 3g: clear all tabs (ignored)
break;
default:
this.warn("write:: unknown command: " + ch);
this.csi_parms_ = [];
break;
}
break;
case 4: // saw CSI [
this.esc_state_ = 0; // gobble char.
break;
case 5: // Special mode handling, saw <ESC>[?
// Expect a number - the reset type
this.csi_parms_ = [ch];
this.esc_state_ = 6;
break;
case 6: // Reset mode handling, saw <ESC>[?1
// Expect a letter - the mode target, example:
// <ESC>[?1l : cursor key mode = cursor
// <ESC>[?1h : save current screen, create new empty
// screen and position at 0,0
// <ESC>[?5l : White on blk
// XXX: Ignored for now.
//dump("Saw reset mode: <ESC>[?" + this.csi_parms_[0] + ch + "\n");
this.esc_state_ = 0;
this.debug("write:: set escape state: 0");
break;
}
}
this.refresh();
}
VT100.prototype.debug = function(message) {
if (this.debug_) {
dump(message + "\n");
}
}
VT100.prototype.warn = function(message) {
dump(message + "\n");
}
../include
\ No newline at end of file
/* Functions to compute MD5 message digest of files or memory blocks.
according to the definition of MD5 in RFC 1321 from April 1992.
Copyright (C) 1995,1996,1997,1999,2000,2001,2005
Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <sys/types.h>
#if STDC_HEADERS || defined _LIBC
# include <stdlib.h>
# include <string.h>
#else
# ifndef HAVE_MEMCPY
# define memcpy(d, s, n) (bcopy ((s), (d), (n)), (d))
# endif
#endif
#ifndef __THROW
#define __THROW
#endif
#include "md5.h"
#ifdef _LIBC
# include <endian.h>
# if __BYTE_ORDER == __BIG_ENDIAN
# define WORDS_BIGENDIAN 1
# endif
/* We need to keep the namespace clean so define the MD5 function
protected using leading __ . */
# define md5_init_ctx __md5_init_ctx
# define md5_process_block __md5_process_block
# define md5_process_bytes __md5_process_bytes
# define md5_finish_ctx __md5_finish_ctx
# define md5_read_ctx __md5_read_ctx
# define md5_stream __md5_stream
# define md5_buffer __md5_buffer
#else
/* Squelch compiler complaints */
void md5_process_bytes (const void *buffer, size_t len, struct md5_ctx *ctx);
void md5_process_block (const void *buffer, size_t len, struct md5_ctx *ctx);
#endif
#ifdef WORDS_BIGENDIAN
# define SWAP(n) \
(((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
#else
# define SWAP(n) (n)
#endif
/* This array contains the bytes used to pad the buffer to the next
64-byte boundary. (RFC 1321, 3.1: Step 1) */
static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ };
/* Initialize structure containing state of computation.
(RFC 1321, 3.3: Step 3) */
void
md5_init_ctx (ctx)
struct md5_ctx *ctx;
{
ctx->A = 0x67452301;
ctx->B = 0xefcdab89;
ctx->C = 0x98badcfe;
ctx->D = 0x10325476;
ctx->total[0] = ctx->total[1] = 0;
ctx->buflen = 0;
}
/* Put result from CTX in first 16 bytes following RESBUF. The result
must be in little endian byte order.
IMPORTANT: On some systems it is required that RESBUF is correctly
aligned for a 32 bits value. */
void *
md5_read_ctx (ctx, resbuf)
const struct md5_ctx *ctx;
void *resbuf;
{
((md5_uint32 *) resbuf)[0] = SWAP (ctx->A);
((md5_uint32 *) resbuf)[1] = SWAP (ctx->B);
((md5_uint32 *) resbuf)[2] = SWAP (ctx->C);
((md5_uint32 *) resbuf)[3] = SWAP (ctx->D);
return resbuf;
}
/* Process the remaining bytes in the internal buffer and the usual
prolog according to the standard and write the result to RESBUF.
IMPORTANT: On some systems it is required that RESBUF is correctly
aligned for a 32 bits value. */
void *
md5_finish_ctx (ctx, resbuf)
struct md5_ctx *ctx;
void *resbuf;
{
/* Take yet unprocessed bytes into account. */
md5_uint32 bytes = ctx->buflen;
size_t pad;
/* Now count remaining bytes. */
ctx->total[0] += bytes;
if (ctx->total[0] < bytes)
++ctx->total[1];
pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
memcpy (&ctx->buffer[bytes], fillbuf, pad);
/* Put the 64-bit file length in *bits* at the end of the buffer. */
*(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3);
*(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) |
(ctx->total[0] >> 29));
/* Process last bytes. */
md5_process_block (ctx->buffer, bytes + pad + 8, ctx);
return md5_read_ctx (ctx, resbuf);
}
/* Compute MD5 message digest for bytes read from STREAM. The
resulting message digest number will be written into the 16 bytes
beginning at RESBLOCK. */
int
md5_stream (stream, resblock)
FILE *stream;
void *resblock;
{
/* Important: BLOCKSIZE must be a multiple of 64. */
#define BLOCKSIZE 4096
struct md5_ctx ctx;
char buffer[BLOCKSIZE + 72];
size_t sum;
/* Initialize the computation context. */
md5_init_ctx (&ctx);
/* Iterate over full file contents. */
while (1)
{
/* We read the file in blocks of BLOCKSIZE bytes. One call of the
computation function processes the whole buffer so that with the
next round of the loop another block can be read. */
size_t n;
sum = 0;
/* Read block. Take care for partial reads. */
do
{
n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);
sum += n;
}
while (sum < BLOCKSIZE && n != 0);
if (n == 0 && ferror (stream))
return 1;
/* If end of file is reached, end the loop. */
if (n == 0)
break;
/* Process buffer with BLOCKSIZE bytes. Note that
BLOCKSIZE % 64 == 0
*/
md5_process_block (buffer, BLOCKSIZE, &ctx);
}
/* Add the last bytes if necessary. */
if (sum > 0)
md5_process_bytes (buffer, sum, &ctx);
/* Construct result in desired memory. */
md5_finish_ctx (&ctx, resblock);
return 0;
}
/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The
result is always in little endian byte order, so that a byte-wise
output yields to the wanted ASCII representation of the message
digest. */
void *
md5_buffer (buffer, len, resblock)
const char *buffer;
size_t len;
void *resblock;
{
struct md5_ctx ctx;
/* Initialize the computation context. */
md5_init_ctx (&ctx);
/* Process whole buffer but last len % 64 bytes. */
md5_process_bytes (buffer, len, &ctx);
/* Put result in desired memory area. */
return md5_finish_ctx (&ctx, resblock);
}
void
md5_process_bytes (buffer, len, ctx)
const void *buffer;
size_t len;
struct md5_ctx *ctx;
{
/* When we already have some bits in our internal buffer concatenate
both inputs first. */
if (ctx->buflen != 0)
{
size_t left_over = ctx->buflen;
size_t add = 128 - left_over > len ? len : 128 - left_over;
memcpy (&ctx->buffer[left_over], buffer, add);
ctx->buflen += add;
if (ctx->buflen > 64)
{
md5_process_block (ctx->buffer, ctx->buflen & ~63, ctx);
ctx->buflen &= 63;
/* The regions in the following copy operation cannot overlap. */
memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
ctx->buflen);
}
buffer = (const char *) buffer + add;
len -= add;
}
/* Process available complete blocks. */
if (len >= 64)
{
#if !_STRING_ARCH_unaligned
/* To check alignment gcc has an appropriate operator. Other
compilers don't. */
# if __GNUC__ >= 2
# define UNALIGNED_P(p) (((md5_uintptr) p) % __alignof__ (md5_uint32) != 0)
# else
# define UNALIGNED_P(p) (((md5_uintptr) p) % sizeof (md5_uint32) != 0)
# endif
if (UNALIGNED_P (buffer))
while (len > 64)
{
md5_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx);
buffer = (const char *) buffer + 64;
len -= 64;
}
else
#endif
{
md5_process_block (buffer, len & ~63, ctx);
buffer = (const char *) buffer + (len & ~63);
len &= 63;
}
}
/* Move remaining bytes in internal buffer. */
if (len > 0)
{
size_t left_over = ctx->buflen;
memcpy (&ctx->buffer[left_over], buffer, len);
left_over += len;
if (left_over >= 64)
{
md5_process_block (ctx->buffer, 64, ctx);
left_over -= 64;
memcpy (ctx->buffer, &ctx->buffer[64], left_over);
}
ctx->buflen = left_over;
}
}
/* These are the four functions used in the four steps of the MD5 algorithm
and defined in the RFC 1321. The first function is a little bit optimized
(as found in Colin Plumbs public domain implementation). */
/* #define FF(b, c, d) ((b & c) | (~b & d)) */
#define FF(b, c, d) (d ^ (b & (c ^ d)))
#define FG(b, c, d) FF (d, b, c)
#define FH(b, c, d) (b ^ c ^ d)
#define FI(b, c, d) (c ^ (b | ~d))
/* Process LEN bytes of BUFFER, accumulating context into CTX.
It is assumed that LEN % 64 == 0. */
void
md5_process_block (buffer, len, ctx)
const void *buffer;
size_t len;
struct md5_ctx *ctx;
{
md5_uint32 correct_words[16];
const md5_uint32 *words = buffer;
size_t nwords = len / sizeof (md5_uint32);
const md5_uint32 *endp = words + nwords;
md5_uint32 A = ctx->A;
md5_uint32 B = ctx->B;
md5_uint32 C = ctx->C;
md5_uint32 D = ctx->D;
/* First increment the byte count. RFC 1321 specifies the possible
length of the file up to 2^64 bits. Here we only compute the
number of bytes. Do a double word increment. */
ctx->total[0] += len;
if (ctx->total[0] < len)
++ctx->total[1];
/* Process all bytes in the buffer with 64 bytes in each round of
the loop. */
while (words < endp)
{
md5_uint32 *cwp = correct_words;
md5_uint32 A_save = A;
md5_uint32 B_save = B;
md5_uint32 C_save = C;
md5_uint32 D_save = D;
/* First round: using the given function, the context and a constant
the next context is computed. Because the algorithms processing
unit is a 32-bit word and it is determined to work on words in
little endian byte order we perhaps have to change the byte order
before the computation. To reduce the work for the next steps
we store the swapped words in the array CORRECT_WORDS. */
#define OP(a, b, c, d, s, T) \
do \
{ \
a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \
++words; \
CYCLIC (a, s); \
a += b; \
} \
while (0)
/* It is unfortunate that C does not provide an operator for
cyclic rotation. Hope the C compiler is smart enough. */
#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
/* Before we start, one word to the strange constants.
They are defined in RFC 1321 as
T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
*/
/* Round 1. */
OP (A, B, C, D, 7, 0xd76aa478);
OP (D, A, B, C, 12, 0xe8c7b756);
OP (C, D, A, B, 17, 0x242070db);
OP (B, C, D, A, 22, 0xc1bdceee);
OP (A, B, C, D, 7, 0xf57c0faf);
OP (D, A, B, C, 12, 0x4787c62a);
OP (C, D, A, B, 17, 0xa8304613);
OP (B, C, D, A, 22, 0xfd469501);
OP (A, B, C, D, 7, 0x698098d8);
OP (D, A, B, C, 12, 0x8b44f7af);
OP (C, D, A, B, 17, 0xffff5bb1);
OP (B, C, D, A, 22, 0x895cd7be);
OP (A, B, C, D, 7, 0x6b901122);
OP (D, A, B, C, 12, 0xfd987193);
OP (C, D, A, B, 17, 0xa679438e);
OP (B, C, D, A, 22, 0x49b40821);
/* For the second to fourth round we have the possibly swapped words
in CORRECT_WORDS. Redefine the macro to take an additional first
argument specifying the function to use. */
#undef OP
#define OP(f, a, b, c, d, k, s, T) \
do \
{ \
a += f (b, c, d) + correct_words[k] + T; \
CYCLIC (a, s); \
a += b; \
} \
while (0)
/* Round 2. */
OP (FG, A, B, C, D, 1, 5, 0xf61e2562);
OP (FG, D, A, B, C, 6, 9, 0xc040b340);
OP (FG, C, D, A, B, 11, 14, 0x265e5a51);
OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
OP (FG, A, B, C, D, 5, 5, 0xd62f105d);
OP (FG, D, A, B, C, 10, 9, 0x02441453);
OP (FG, C, D, A, B, 15, 14, 0xd8a1e681);
OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
OP (FG, A, B, C, D, 9, 5, 0x21e1cde6);
OP (FG, D, A, B, C, 14, 9, 0xc33707d6);
OP (FG, C, D, A, B, 3, 14, 0xf4d50d87);
OP (FG, B, C, D, A, 8, 20, 0x455a14ed);
OP (FG, A, B, C, D, 13, 5, 0xa9e3e905);
OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8);
OP (FG, C, D, A, B, 7, 14, 0x676f02d9);
OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
/* Round 3. */
OP (FH, A, B, C, D, 5, 4, 0xfffa3942);
OP (FH, D, A, B, C, 8, 11, 0x8771f681);
OP (FH, C, D, A, B, 11, 16, 0x6d9d6122);
OP (FH, B, C, D, A, 14, 23, 0xfde5380c);
OP (FH, A, B, C, D, 1, 4, 0xa4beea44);
OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9);
OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60);
OP (FH, B, C, D, A, 10, 23, 0xbebfbc70);
OP (FH, A, B, C, D, 13, 4, 0x289b7ec6);
OP (FH, D, A, B, C, 0, 11, 0xeaa127fa);
OP (FH, C, D, A, B, 3, 16, 0xd4ef3085);
OP (FH, B, C, D, A, 6, 23, 0x04881d05);
OP (FH, A, B, C, D, 9, 4, 0xd9d4d039);
OP (FH, D, A, B, C, 12, 11, 0xe6db99e5);
OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8);
OP (FH, B, C, D, A, 2, 23, 0xc4ac5665);
/* Round 4. */
OP (FI, A, B, C, D, 0, 6, 0xf4292244);
OP (FI, D, A, B, C, 7, 10, 0x432aff97);
OP (FI, C, D, A, B, 14, 15, 0xab9423a7);
OP (FI, B, C, D, A, 5, 21, 0xfc93a039);
OP (FI, A, B, C, D, 12, 6, 0x655b59c3);
OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92);
OP (FI, C, D, A, B, 10, 15, 0xffeff47d);
OP (FI, B, C, D, A, 1, 21, 0x85845dd1);
OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f);
OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
OP (FI, C, D, A, B, 6, 15, 0xa3014314);
OP (FI, B, C, D, A, 13, 21, 0x4e0811a1);
OP (FI, A, B, C, D, 4, 6, 0xf7537e82);
OP (FI, D, A, B, C, 11, 10, 0xbd3af235);
OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
OP (FI, B, C, D, A, 9, 21, 0xeb86d391);
/* Add the starting values of the context. */
A += A_save;
B += B_save;
C += C_save;
D += D_save;
}
/* Put checksum in context given as argument. */
ctx->A = A;
ctx->B = B;
ctx->C = C;
ctx->D = D;
}
/* Declaration of functions and data types used for MD5 sum computing
library functions.
Copyright (C) 1995-1997,1999,2000,2001,2004,2005
Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#ifndef _MD5_H
#define _MD5_H 1
#include <stdio.h>
#if defined HAVE_LIMITS_H || _LIBC
# include <limits.h>
#endif
#define MD5_DIGEST_SIZE 16
#define MD5_BLOCK_SIZE 64
/* The following contortions are an attempt to use the C preprocessor
to determine an unsigned integral type that is 32 bits wide. An
alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but
doing that would require that the configure script compile and *run*
the resulting executable. Locally running cross-compiled executables
is usually not possible. */
#ifdef _LIBC
# include <stdint.h>
typedef uint32_t md5_uint32;
typedef uintptr_t md5_uintptr;
#else
# if defined __STDC__ && __STDC__
# define UINT_MAX_32_BITS 4294967295U
# else
# define UINT_MAX_32_BITS 0xFFFFFFFF
# endif
/* If UINT_MAX isn't defined, assume it's a 32-bit type.
This should be valid for all systems GNU cares about because
that doesn't include 16-bit systems, and only modern systems
(that certainly have <limits.h>) have 64+-bit integral types. */
# ifndef UINT_MAX
# define UINT_MAX UINT_MAX_32_BITS
# endif
# if UINT_MAX == UINT_MAX_32_BITS
typedef unsigned int md5_uint32;
# else
# if USHRT_MAX == UINT_MAX_32_BITS
typedef unsigned short md5_uint32;
# else
# if ULONG_MAX == UINT_MAX_32_BITS
typedef unsigned long md5_uint32;
# else
/* The following line is intended to evoke an error.
Using #error is not portable enough. */
"Cannot determine unsigned 32-bit data type."
# endif
# endif
# endif
/* We have to make a guess about the integer type equivalent in size
to pointers which should always be correct. */
typedef unsigned long int md5_uintptr;
#endif
/* Structure to save state of computation between the single steps. */
struct md5_ctx
{
md5_uint32 A;
md5_uint32 B;
md5_uint32 C;
md5_uint32 D;
md5_uint32 total[2];
md5_uint32 buflen;
char buffer[128] __attribute__ ((__aligned__ (__alignof__ (md5_uint32))));
};
/*
* The following three functions are build up the low level used in
* the functions `md5_stream' and `md5_buffer'.
*/
/* Initialize structure containing state of computation.
(RFC 1321, 3.3: Step 3) */
extern void __md5_init_ctx (struct md5_ctx *ctx) __THROW;
/* Starting with the result of former calls of this function (or the
initialization function update the context for the next LEN bytes
starting at BUFFER.
It is necessary that LEN is a multiple of 64!!! */
extern void __md5_process_block (const void *buffer, size_t len,
struct md5_ctx *ctx) __THROW;
/* Starting with the result of former calls of this function (or the
initialization function update the context for the next LEN bytes
starting at BUFFER.
It is NOT required that LEN is a multiple of 64. */
extern void __md5_process_bytes (const void *buffer, size_t len,
struct md5_ctx *ctx) __THROW;
/* Process the remaining bytes in the buffer and put result from CTX
in first 16 bytes following RESBUF. The result is always in little
endian byte order, so that a byte-wise output yields to the wanted
ASCII representation of the message digest.
IMPORTANT: On some systems it is required that RESBUF is correctly
aligned for a 32 bits value. */
extern void *__md5_finish_ctx (struct md5_ctx *ctx, void *resbuf) __THROW;
/* Put result from CTX in first 16 bytes following RESBUF. The result is
always in little endian byte order, so that a byte-wise output yields
to the wanted ASCII representation of the message digest.
IMPORTANT: On some systems it is required that RESBUF is correctly
aligned for a 32 bits value. */
extern void *__md5_read_ctx (const struct md5_ctx *ctx, void *resbuf) __THROW;
/* Compute MD5 message digest for bytes read from STREAM. The
resulting message digest number will be written into the 16 bytes
beginning at RESBLOCK. */
extern int __md5_stream (FILE *stream, void *resblock) __THROW;
/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The
result is always in little endian byte order, so that a byte-wise
output yields to the wanted ASCII representation of the message
digest. */
extern void *__md5_buffer (const char *buffer, size_t len,
void *resblock) __THROW;
#endif /* md5.h */
int main () {
printf("hello world\n");
}
/*
* WebSocket lib with support for "wss://" encryption.
* Copyright 2010 Joel Martin
* Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
*
* You can make a cert/key with openssl using:
* openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem
* as taken from http://docs.python.org/dev/library/ssl.html#certificates
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <signal.h> // daemonizing
#include <fcntl.h> // daemonizing
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <resolv.h> /* base64 encode/decode */
#include "websocket.h"
const char server_handshake[] = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n\
Upgrade: WebSocket\r\n\
Connection: Upgrade\r\n\
%sWebSocket-Origin: %s\r\n\
%sWebSocket-Location: %s://%s%s\r\n\
%sWebSocket-Protocol: sample\r\n\
\r\n%s";
const char policy_response[] = "<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>\n";
/*
* Global state
*
* Warning: not thread safe
*/
int ssl_initialized = 0;
int pipe_error = 0;
char *tbuf, *cbuf, *tbuf_tmp, *cbuf_tmp;
unsigned int bufsize, dbufsize;
settings_t settings;
void traffic(char * token) {
if ((settings.verbose) && (! settings.daemon)) {
fprintf(stdout, "%s", token);
fflush(stdout);
}
}
void error(char *msg)
{
perror(msg);
}
void fatal(char *msg)
{
perror(msg);
exit(1);
}
/* resolve host with also IP address parsing */
int resolve_host(struct in_addr *sin_addr, const char *hostname)
{
if (!inet_aton(hostname, sin_addr)) {
struct addrinfo *ai, *cur;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
if (getaddrinfo(hostname, NULL, &hints, &ai))
return -1;
for (cur = ai; cur; cur = cur->ai_next) {
if (cur->ai_family == AF_INET) {
*sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
freeaddrinfo(ai);
return 0;
}
}
freeaddrinfo(ai);
return -1;
}
return 0;
}
/*
* SSL Wrapper Code
*/
ssize_t ws_recv(ws_ctx_t *ctx, void *buf, size_t len) {
if (ctx->ssl) {
//handler_msg("SSL recv\n");
return SSL_read(ctx->ssl, buf, len);
} else {
return recv(ctx->sockfd, buf, len, 0);
}
}
ssize_t ws_send(ws_ctx_t *ctx, const void *buf, size_t len) {
if (ctx->ssl) {
//handler_msg("SSL send\n");
return SSL_write(ctx->ssl, buf, len);
} else {
return send(ctx->sockfd, buf, len, 0);
}
}
ws_ctx_t *ws_socket(int socket) {
ws_ctx_t *ctx;
ctx = malloc(sizeof(ws_ctx_t));
ctx->sockfd = socket;
ctx->ssl = NULL;
ctx->ssl_ctx = NULL;
return ctx;
}
ws_ctx_t *ws_socket_ssl(int socket, char * certfile, char * keyfile) {
int ret;
char msg[1024];
char * use_keyfile;
ws_ctx_t *ctx;
ctx = ws_socket(socket);
if (keyfile && (keyfile[0] != '\0')) {
// Separate key file
use_keyfile = keyfile;
} else {
// Combined key and cert file
use_keyfile = certfile;
}
// Initialize the library
if (! ssl_initialized) {
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
ssl_initialized = 1;
}
ctx->ssl_ctx = SSL_CTX_new(TLSv1_server_method());
if (ctx->ssl_ctx == NULL) {
ERR_print_errors_fp(stderr);
fatal("Failed to configure SSL context");
}
if (SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, use_keyfile,
SSL_FILETYPE_PEM) <= 0) {
sprintf(msg, "Unable to load private key file %s\n", use_keyfile);
fatal(msg);
}
if (SSL_CTX_use_certificate_file(ctx->ssl_ctx, certfile,
SSL_FILETYPE_PEM) <= 0) {
sprintf(msg, "Unable to load certificate file %s\n", certfile);
fatal(msg);
}
// if (SSL_CTX_set_cipher_list(ctx->ssl_ctx, "DEFAULT") != 1) {
// sprintf(msg, "Unable to set cipher\n");
// fatal(msg);
// }
// Associate socket and ssl object
ctx->ssl = SSL_new(ctx->ssl_ctx);
SSL_set_fd(ctx->ssl, socket);
ret = SSL_accept(ctx->ssl);
if (ret < 0) {
ERR_print_errors_fp(stderr);
return NULL;
}
return ctx;
}
int ws_socket_free(ws_ctx_t *ctx) {
if (ctx->ssl) {
SSL_free(ctx->ssl);
ctx->ssl = NULL;
}
if (ctx->ssl_ctx) {
SSL_CTX_free(ctx->ssl_ctx);
ctx->ssl_ctx = NULL;
}
if (ctx->sockfd) {
shutdown(ctx->sockfd, SHUT_RDWR);
close(ctx->sockfd);
ctx->sockfd = 0;
}
free(ctx);
}
/* ------------------------------------------------------- */
int encode(u_char const *src, size_t srclength, char *target, size_t targsize) {
int i, sz = 0, len = 0;
unsigned char chr;
target[sz++] = '\x00';
len = b64_ntop(src, srclength, target+sz, targsize-sz);
if (len < 0) {
return len;
}
sz += len;
target[sz++] = '\xff';
return sz;
}
int decode(char *src, size_t srclength, u_char *target, size_t targsize) {
char *start, *end, cntstr[4];
int i, len, framecount = 0, retlen = 0;
unsigned char chr;
if ((src[0] != '\x00') || (src[srclength-1] != '\xff')) {
handler_emsg("WebSocket framing error\n");
return -1;
}
start = src+1; // Skip '\x00' start
do {
/* We may have more than one frame */
end = memchr(start, '\xff', srclength);
*end = '\x00';
len = b64_pton(start, target+retlen, targsize-retlen);
if (len < 0) {
return len;
}
retlen += len;
start = end + 2; // Skip '\xff' end and '\x00' start
framecount++;
} while (end < (src+srclength-1));
if (framecount > 1) {
snprintf(cntstr, 3, "%d", framecount);
traffic(cntstr);
}
return retlen;
}
int parse_handshake(char *handshake, headers_t *headers) {
char *start, *end;
if ((strlen(handshake) < 92) || (bcmp(handshake, "GET ", 4) != 0)) {
return 0;
}
start = handshake+4;
end = strstr(start, " HTTP/1.1");
if (!end) { return 0; }
strncpy(headers->path, start, end-start);
headers->path[end-start] = '\0';
start = strstr(handshake, "\r\nHost: ");
if (!start) { return 0; }
start += 8;
end = strstr(start, "\r\n");
strncpy(headers->host, start, end-start);
headers->host[end-start] = '\0';
start = strstr(handshake, "\r\nOrigin: ");
if (!start) { return 0; }
start += 10;
end = strstr(start, "\r\n");
strncpy(headers->origin, start, end-start);
headers->origin[end-start] = '\0';
start = strstr(handshake, "\r\n\r\n");
if (!start) { return 0; }
start += 4;
if (strlen(start) == 8) {
strncpy(headers->key3, start, 8);
headers->key3[8] = '\0';
start = strstr(handshake, "\r\nSec-WebSocket-Key1: ");
if (!start) { return 0; }
start += 22;
end = strstr(start, "\r\n");
strncpy(headers->key1, start, end-start);
headers->key1[end-start] = '\0';
start = strstr(handshake, "\r\nSec-WebSocket-Key2: ");
if (!start) { return 0; }
start += 22;
end = strstr(start, "\r\n");
strncpy(headers->key2, start, end-start);
headers->key2[end-start] = '\0';
} else {
headers->key1[0] = '\0';
headers->key2[0] = '\0';
headers->key3[0] = '\0';
}
return 1;
}
int gen_md5(headers_t *headers, char *target) {
unsigned int i, spaces1 = 0, spaces2 = 0;
unsigned long num1 = 0, num2 = 0;
unsigned char buf[17];
for (i=0; i < strlen(headers->key1); i++) {
if (headers->key1[i] == ' ') {
spaces1 += 1;
}
if ((headers->key1[i] >= 48) && (headers->key1[i] <= 57)) {
num1 = num1 * 10 + (headers->key1[i] - 48);
}
}
num1 = num1 / spaces1;
for (i=0; i < strlen(headers->key2); i++) {
if (headers->key2[i] == ' ') {
spaces2 += 1;
}
if ((headers->key2[i] >= 48) && (headers->key2[i] <= 57)) {
num2 = num2 * 10 + (headers->key2[i] - 48);
}
}
num2 = num2 / spaces2;
/* Pack it big-endian */
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;
strncpy(buf+8, headers->key3, 8);
buf[16] = '\0';
md5_buffer(buf, 16, target);
target[16] = '\0';
return 1;
}
ws_ctx_t *do_handshake(int sock) {
char handshake[4096], response[4096], trailer[17];
char *scheme, *pre;
headers_t headers;
int len, ret;
ws_ctx_t * ws_ctx;
// Peek, but don't read the data
len = recv(sock, handshake, 1024, MSG_PEEK);
handshake[len] = 0;
if (len == 0) {
handler_msg("ignoring empty handshake\n");
return NULL;
} else if (bcmp(handshake, "<policy-file-request/>", 22) == 0) {
len = recv(sock, handshake, 1024, 0);
handshake[len] = 0;
handler_msg("sending flash policy response\n");
send(sock, policy_response, sizeof(policy_response), 0);
return NULL;
} else if ((bcmp(handshake, "\x16", 1) == 0) ||
(bcmp(handshake, "\x80", 1) == 0)) {
// SSL
if (!settings.cert) {
handler_msg("SSL connection but no cert specified\n");
return NULL;
} else if (access(settings.cert, R_OK) != 0) {
handler_msg("SSL connection but '%s' not found\n",
settings.cert);
return NULL;
}
ws_ctx = ws_socket_ssl(sock, settings.cert, settings.key);
if (! ws_ctx) { return NULL; }
scheme = "wss";
handler_msg("using SSL socket\n");
} else if (settings.ssl_only) {
handler_msg("non-SSL connection disallowed\n");
return NULL;
} else {
ws_ctx = ws_socket(sock);
if (! ws_ctx) { return NULL; }
scheme = "ws";
handler_msg("using plain (not SSL) socket\n");
}
len = ws_recv(ws_ctx, handshake, 4096);
if (len == 0) {
handler_emsg("Client closed during handshake\n");
return NULL;
}
handshake[len] = 0;
if (!parse_handshake(handshake, &headers)) {
handler_emsg("Invalid WS request\n");
return NULL;
}
if (headers.key3[0] != '\0') {
gen_md5(&headers, trailer);
pre = "Sec-";
handler_msg("using protocol version 76\n");
} else {
trailer[0] = '\0';
pre = "";
handler_msg("using protocol version 75\n");
}
sprintf(response, server_handshake, pre, headers.origin, pre, scheme,
headers.host, headers.path, pre, trailer);
//handler_msg("response: %s\n", response);
ws_send(ws_ctx, response, strlen(response));
return ws_ctx;
}
void signal_handler(sig) {
switch (sig) {
case SIGHUP: break; // ignore for now
case SIGPIPE: pipe_error = 1; break; // handle inline
case SIGTERM: exit(0); break;
}
}
void daemonize(int keepfd) {
int pid, i;
umask(0);
chdir('/');
setgid(getgid());
setuid(getuid());
/* Double fork to daemonize */
pid = fork();
if (pid<0) { fatal("fork error"); }
if (pid>0) { exit(0); } // parent exits
setsid(); // Obtain new process group
pid = fork();
if (pid<0) { fatal("fork error"); }
if (pid>0) { exit(0); } // parent exits
/* Signal handling */
signal(SIGHUP, signal_handler); // catch HUP
signal(SIGTERM, signal_handler); // catch kill
/* Close open files */
for (i=getdtablesize(); i>=0; --i) {
if (i != keepfd) {
close(i);
} else if (settings.verbose) {
printf("keeping fd %d\n", keepfd);
}
}
i=open("/dev/null", O_RDWR); // Redirect stdin
dup(i); // Redirect stdout
dup(i); // Redirect stderr
}
void start_server() {
int lsock, csock, pid, clilen, sopt = 1, i;
struct sockaddr_in serv_addr, cli_addr;
ws_ctx_t *ws_ctx;
/* Initialize buffers */
bufsize = 65536;
if (! (tbuf = malloc(bufsize)) )
{ fatal("malloc()"); }
if (! (cbuf = malloc(bufsize)) )
{ fatal("malloc()"); }
if (! (tbuf_tmp = malloc(bufsize)) )
{ fatal("malloc()"); }
if (! (cbuf_tmp = malloc(bufsize)) )
{ fatal("malloc()"); }
lsock = socket(AF_INET, SOCK_STREAM, 0);
if (lsock < 0) { error("ERROR creating listener socket"); }
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(settings.listen_port);
/* Resolve listen address */
if (settings.listen_host && (settings.listen_host[0] != '\0')) {
if (resolve_host(&serv_addr.sin_addr, settings.listen_host) < -1) {
fatal("Could not resolve listen address");
}
} else {
serv_addr.sin_addr.s_addr = INADDR_ANY;
}
setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, (char *)&sopt, sizeof(sopt));
if (bind(lsock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
fatal("ERROR on binding listener socket");
}
listen(lsock,100);
signal(SIGPIPE, signal_handler); // catch pipe
if (settings.daemon) {
daemonize(lsock);
}
// Reep zombies
signal(SIGCHLD, SIG_IGN);
printf("Waiting for connections on %s:%d\n",
settings.listen_host, settings.listen_port);
while (1) {
clilen = sizeof(cli_addr);
pipe_error = 0;
pid = 0;
csock = accept(lsock,
(struct sockaddr *) &cli_addr,
&clilen);
if (csock < 0) {
error("ERROR on accept");
continue;
}
handler_msg("got client connection from %s\n",
inet_ntoa(cli_addr.sin_addr));
/* base64 is 4 bytes for every 3
* 20 for WS '\x00' / '\xff' and good measure */
dbufsize = (bufsize * 3)/4 - 20;
handler_msg("forking handler process\n");
pid = fork();
if (pid == 0) { // handler process
ws_ctx = do_handshake(csock);
if (ws_ctx == NULL) {
handler_msg("No connection after handshake\n");
break; // Child process exits
}
settings.handler(ws_ctx);
if (pipe_error) {
handler_emsg("Closing due to SIGPIPE\n");
}
break; // Child process exits
} else { // parent process
settings.handler_id += 1;
}
}
if (pid == 0) {
if (ws_ctx) {
ws_socket_free(ws_ctx);
} else {
shutdown(csock, SHUT_RDWR);
close(csock);
}
handler_msg("handler exit\n");
} else {
handler_msg("wsproxy exit\n");
}
}
#include <openssl/ssl.h>
typedef struct {
int sockfd;
SSL_CTX *ssl_ctx;
SSL *ssl;
} ws_ctx_t;
typedef struct {
int verbose;
char listen_host[256];
int listen_port;
void (*handler)(ws_ctx_t*);
int handler_id;
char *cert;
char *key;
int ssl_only;
int daemon;
} settings_t;
typedef struct {
char path[1024+1];
char host[1024+1];
char origin[1024+1];
char key1[1024+1];
char key2[1024+1];
char key3[8+1];
} headers_t;
ssize_t ws_recv(ws_ctx_t *ctx, void *buf, size_t len);
ssize_t ws_send(ws_ctx_t *ctx, const void *buf, size_t len);
/* base64.c declarations */
//int b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize);
//int b64_pton(char const *src, u_char *target, size_t targsize);
#define gen_handler_msg(stream, ...) \
if (! settings.daemon) { \
fprintf(stream, " %d: ", settings.handler_id); \
fprintf(stream, __VA_ARGS__); \
}
#define handler_msg(...) gen_handler_msg(stdout, __VA_ARGS__);
#define handler_emsg(...) gen_handler_msg(stderr, __VA_ARGS__);
#!/usr/bin/python
'''
A WebSocket to TCP socket proxy with support for "wss://" encryption.
Copyright 2010 Joel Martin
Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
You can make a cert/key with openssl using:
openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem
as taken from http://docs.python.org/dev/library/ssl.html#certificates
'''
import socket, optparse, time, os, sys, subprocess
from select import select
from websocket import WebSocketServer
class WebSocketProxy(WebSocketServer):
"""
Proxy traffic to and from a WebSockets client to a normal TCP
socket server target. All traffic to/from the client is base64
encoded/decoded to allow binary data to be sent/received to/from
the target.
"""
buffer_size = 65536
traffic_legend = """
Traffic Legend:
} - Client receive
}. - Client receive partial
{ - Target receive
> - Target send
>. - Target send partial
< - Client send
<. - Client send partial
"""
def __init__(self, *args, **kwargs):
# Save off proxy specific options
self.target_host = kwargs.pop('target_host')
self.target_port = kwargs.pop('target_port')
self.wrap_cmd = kwargs.pop('wrap_cmd')
self.wrap_mode = kwargs.pop('wrap_mode')
# Last 3 timestamps command was run
self.wrap_times = [0, 0, 0]
if self.wrap_cmd:
rebinder_path = ['./', os.path.dirname(sys.argv[0])]
self.rebinder = None
for rdir in rebinder_path:
rpath = os.path.join(rdir, "rebind.so")
if os.path.exists(rpath):
self.rebinder = rpath
break
if not self.rebinder:
raise Exception("rebind.so not found, perhaps you need to run make")
self.target_host = "127.0.0.1" # Loopback
# Find a free high port
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('', 0))
self.target_port = sock.getsockname()[1]
sock.close()
os.environ.update({
"LD_PRELOAD": self.rebinder,
"REBIND_OLD_PORT": str(kwargs['listen_port']),
"REBIND_NEW_PORT": str(self.target_port)})
WebSocketServer.__init__(self, *args, **kwargs)
def run_wrap_cmd(self):
print "Starting '%s'" % " ".join(self.wrap_cmd)
self.wrap_times.append(time.time())
self.wrap_times.pop(0)
self.cmd = subprocess.Popen(
self.wrap_cmd, env=os.environ)
self.spawn_message = True
def started(self):
"""
Called after Websockets server startup (i.e. after daemonize)
"""
# Need to call wrapped command after daemonization so we can
# know when the wrapped command exits
if self.wrap_cmd:
print " - proxying from %s:%s to '%s' (port %s)\n" % (
self.listen_host, self.listen_port,
" ".join(self.wrap_cmd), self.target_port)
self.run_wrap_cmd()
else:
print " - proxying from %s:%s to %s:%s\n" % (
self.listen_host, self.listen_port,
self.target_host, self.target_port)
def poll(self):
# If we are wrapping a command, check it's status
if self.wrap_cmd and self.cmd:
ret = self.cmd.poll()
if ret != None:
self.vmsg("Wrapped command exited (or daemon). Returned %s" % ret)
self.cmd = None
if self.wrap_cmd and self.cmd == None:
# Response to wrapped command being gone
if self.wrap_mode == "ignore":
pass
elif self.wrap_mode == "exit":
sys.exit(ret)
elif self.wrap_mode == "respawn":
now = time.time()
avg = sum(self.wrap_times)/len(self.wrap_times)
if (now - avg) < 10:
# 3 times in the last 10 seconds
if self.spawn_message:
print "Command respawning too fast"
self.spawn_message = False
else:
self.run_wrap_cmd()
#
# Routines above this point are run in the master listener
# process.
#
#
# Routines below this point are connection handler routines and
# will be run in a separate forked process for each connection.
#
def new_client(self, client):
"""
Called after a new WebSocket connection has been established.
"""
self.rec = None
if self.record:
# Record raw frame data as a JavaScript compatible file
fname = "%s.%s" % (self.record,
self.handler_id)
self.msg("opening record file: %s" % fname)
self.rec = open(fname, 'w+')
self.rec.write("var VNC_frame_data = [\n")
# Connect to the target
self.msg("connecting to: %s:%s" % (
self.target_host, self.target_port))
tsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tsock.connect((self.target_host, self.target_port))
if self.verbose and not self.daemon:
print self.traffic_legend
# Stat proxying
try:
self.do_proxy(client, tsock)
except:
if tsock: tsock.close()
if self.rec:
self.rec.write("'EOF']\n")
self.rec.close()
raise
def do_proxy(self, client, target):
"""
Proxy client WebSocket to normal target socket.
"""
cqueue = []
cpartial = ""
tqueue = []
rlist = [client, target]
tstart = int(time.time()*1000)
while True:
wlist = []
tdelta = int(time.time()*1000) - tstart
if tqueue: wlist.append(target)
if cqueue: wlist.append(client)
ins, outs, excepts = select(rlist, wlist, [], 1)
if excepts: raise Exception("Socket exception")
if target in outs:
# Send queued client data to the target
dat = tqueue.pop(0)
sent = target.send(dat)
if sent == len(dat):
self.traffic(">")
else:
# requeue the remaining data
tqueue.insert(0, dat[sent:])
self.traffic(".>")
if client in outs:
# Send queued target data to the client
dat = cqueue.pop(0)
sent = client.send(dat)
if sent == len(dat):
self.traffic("<")
if self.rec:
self.rec.write("%s,\n" %
repr("{%s{" % tdelta + dat[1:-1]))
else:
cqueue.insert(0, dat[sent:])
self.traffic("<.")
if target in ins:
# Receive target data, encode it and queue for client
buf = target.recv(self.buffer_size)
if len(buf) == 0: raise self.EClose("Target closed")
cqueue.append(self.encode(buf))
self.traffic("{")
if client in ins:
# Receive client data, decode it, and queue for target
buf = client.recv(self.buffer_size)
if len(buf) == 0: raise self.EClose("Client closed")
if buf == '\xff\x00':
raise self.EClose("Client sent orderly close frame")
elif buf[-1] == '\xff':
if buf.count('\xff') > 1:
self.traffic(str(buf.count('\xff')))
self.traffic("}")
if self.rec:
self.rec.write("%s,\n" %
(repr("}%s}" % tdelta + buf[1:-1])))
if cpartial:
# Prepend saved partial and decode frame(s)
tqueue.extend(self.decode(cpartial + buf))
cpartial = ""
else:
# decode frame(s)
tqueue.extend(self.decode(buf))
else:
# Save off partial WebSockets frame
self.traffic(".}")
cpartial = cpartial + buf
if __name__ == '__main__':
usage = "\n %prog [options]"
usage += " [source_addr:]source_port target_addr:target_port"
usage += "\n %prog [options]"
usage += " [source_addr:]source_port -- WRAP_COMMAND_LINE"
parser = optparse.OptionParser(usage=usage)
parser.add_option("--verbose", "-v", action="store_true",
help="verbose messages and per frame traffic")
parser.add_option("--record",
help="record sessions to FILE.[session_number]", metavar="FILE")
parser.add_option("--daemon", "-D",
dest="daemon", action="store_true",
help="become a daemon (background process)")
parser.add_option("--cert", default="self.pem",
help="SSL certificate file")
parser.add_option("--key", default=None,
help="SSL key file (if separate from cert)")
parser.add_option("--ssl-only", action="store_true",
help="disallow non-encrypted connections")
parser.add_option("--web", default=None, metavar="DIR",
help="run webserver on same port. Serve files from DIR.")
parser.add_option("--wrap-mode", default="exit", metavar="MODE",
choices=["exit", "ignore", "respawn"],
help="action to take when the wrapped program exits "
"or daemonizes: exit (default), ignore, respawn")
(opts, args) = parser.parse_args()
# Sanity checks
if len(args) < 2:
parser.error("Too few arguments")
if sys.argv.count('--'):
opts.wrap_cmd = args[1:]
else:
opts.wrap_cmd = None
if len(args) > 2:
parser.error("Too many arguments")
if opts.ssl_only and not os.path.exists(opts.cert):
parser.error("SSL only and %s not found" % opts.cert)
# Parse host:port and convert ports to numbers
if args[0].count(':') > 0:
opts.listen_host, opts.listen_port = args[0].split(':')
else:
opts.listen_host, opts.listen_port = '', args[0]
try: opts.listen_port = int(opts.listen_port)
except: parser.error("Error parsing listen port")
if opts.wrap_cmd:
opts.target_host = None
opts.target_port = None
else:
if args[1].count(':') > 0:
opts.target_host, opts.target_port = args[1].split(':')
else:
parser.error("Error parsing target")
try: opts.target_port = int(opts.target_port)
except: parser.error("Error parsing target port")
# Create and start the WebSockets proxy
server = WebSocketProxy(**opts.__dict__)
server.start_server()
<html>
<head>
<title>WebSockets Echo Test</title>
<script src="include/base64.js"></script>
<script src="include/util.js"></script>
<script src="include/webutil.js"></script>
<!-- Uncomment to activate firebug lite -->
<!--
<script type='text/javascript'
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
-->
</head>
<body>
Host: <input id='host' style='width:100'>&nbsp;
Port: <input id='port' style='width:50'>&nbsp;
Encrypt: <input id='encrypt' type='checkbox'>&nbsp;
<input id='connectButton' type='button' value='Start' style='width:100px'
onclick="connect();">&nbsp;
<br>
Log:<br>
<textarea id="messages" style="font-size: 9;" cols=80 rows=25></textarea>
</body>
<script>
var ws, host = null, port = null,
msg_cnt = 0, send_cnt = 1, echoDelay = 500,
echo_ref;
function message(str) {
console.log(str);
cell = $D('messages');
cell.innerHTML += msg_cnt + ": " + str + "\n";
cell.scrollTop = cell.scrollHeight;
msg_cnt++;
}
Array.prototype.pushStr = function (str) {
var n = str.length;
for (var i=0; i < n; i++) {
this.push(str.charCodeAt(i));
}
}
function send_msg() {
if (ws.bufferedAmount > 0) {
console.log("Delaying send");
return;
}
var str = "Message #" + send_cnt, arr = [];
arr.pushStr(str)
ws.send(Base64.encode(arr));
message("Sent message: '" + str + "'");
send_cnt++;
}
function update_stats() {
$D('sent').innerHTML = sent;
$D('received').innerHTML = received;
$D('errors').innerHTML = errors;
}
function init_ws() {
console.log(">> init_ws");
console.log("<< init_ws");
}
function connect() {
var host = $D('host').value,
port = $D('port').value,
scheme = "ws://", uri;
console.log(">> connect");
if ((!host) || (!port)) {
console.log("must set host and port");
return;
}
if (ws) {
ws.close();
}
if ($D('encrypt').checked) {
scheme = "wss://";
}
uri = scheme + host + ":" + port;
message("connecting to " + uri);
ws = new WebSocket(uri);
ws.onmessage = function(e) {
//console.log(">> WebSockets.onmessage");
var arr = Base64.decode(e.data), str = "", i;
for (i = 0; i < arr.length; i++) {
str = str + String.fromCharCode(arr[i]);
}
message("Received message '" + str + "'");
//console.log("<< WebSockets.onmessage");
};
ws.onopen = function(e) {
console.log(">> WebSockets.onopen");
echo_ref = setInterval(send_msg, echoDelay);
console.log("<< WebSockets.onopen");
};
ws.onclose = function(e) {
console.log(">> WebSockets.onclose");
if (echo_ref) {
clearInterval(echo_ref);
echo_ref = null;
}
console.log("<< WebSockets.onclose");
};
ws.onerror = function(e) {
console.log(">> WebSockets.onerror");
if (echo_ref) {
clearInterval(echo_ref);
echo_ref = null;
}
console.log("<< WebSockets.onerror");
};
$D('connectButton').value = "Stop";
$D('connectButton').onclick = disconnect;
console.log("<< connect");
}
function disconnect() {
console.log(">> disconnect");
if (ws) {
ws.close();
}
if (echo_ref) {
clearInterval(echo_ref);
}
$D('connectButton').value = "Start";
$D('connectButton').onclick = connect;
console.log("<< disconnect");
}
/* If no builtin websockets then load web_socket.js */
if (window.WebSocket) {
VNC_native_ws = true;
} else {
VNC_native_ws = false;
console.log("Loading web-socket-js flash bridge");
var extra = "<script src='include/web-socket-js/swfobject.js'><\/script>";
extra += "<script src='include/web-socket-js/FABridge.js'><\/script>";
extra += "<script src='include/web-socket-js/web_socket.js'><\/script>";
document.write(extra);
}
window.onload = function() {
console.log("onload");
if (!VNC_native_ws) {
console.log("initializing web-socket-js flash bridge");
WebSocket.__swfLocation = "include/web-socket-js/WebSocketMain.swf";
WebSocket.__initialize();
}
var url = document.location.href;
$D('host').value = (url.match(/host=([^&#]*)/) || ['',''])[1];
$D('port').value = (url.match(/port=([^&#]*)/) || ['',''])[1];
}
</script>
</html>
#!/usr/bin/python
'''
A WebSocket server that echos back whatever it receives from the client.
Copyright 2010 Joel Martin
Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
You can make a cert/key with openssl using:
openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem
as taken from http://docs.python.org/dev/library/ssl.html#certificates
'''
import sys, socket, select
from websocket import WebSocketServer
class WebSocketEcho(WebSocketServer):
"""
WebSockets server that echo back whatever is received from the
client. All traffic to/from the client is base64
encoded/decoded.
"""
buffer_size = 8096
def new_client(self, client):
"""
Echo back whatever is received.
"""
cqueue = []
cpartial = ""
rlist = [client]
while True:
wlist = []
if cqueue: wlist.append(client)
ins, outs, excepts = select.select(rlist, wlist, [], 1)
if excepts: raise Exception("Socket exception")
if client in outs:
# Send queued target data to the client
dat = cqueue.pop(0)
sent = client.send(dat)
self.vmsg("Sent %s/%s bytes of frame: '%s'" % (
sent, len(dat), self.decode(dat)[0]))
if sent != len(dat):
# requeue the remaining data
cqueue.insert(0, dat[sent:])
if client in ins:
# Receive client data, decode it, and send it back
buf = client.recv(self.buffer_size)
if len(buf) == 0: raise self.EClose("Client closed")
if buf == '\xff\x00':
raise self.EClose("Client sent orderly close frame")
elif buf[-1] == '\xff':
if cpartial:
# Prepend saved partial and decode frame(s)
frames = self.decode(cpartial + buf)
cpartial = ""
else:
# decode frame(s)
frames = self.decode(buf)
for frame in frames:
self.vmsg("Received frame: %s" % repr(frame))
cqueue.append(self.encode(frame))
else:
# Save off partial WebSockets frame
self.vmsg("Received partial frame")
cpartial = cpartial + buf
if __name__ == '__main__':
try:
if len(sys.argv) < 1: raise
listen_port = int(sys.argv[1])
except:
print "Usage: %s <listen_port>" % sys.argv[0]
sys.exit(1)
server = WebSocketEcho(
listen_port=listen_port,
verbose=True,
cert='self.pem',
web='.')
server.start_server()
/*
* A WebSocket to TCP socket proxy with support for "wss://" encryption.
* Copyright 2010 Joel Martin
* Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
*
* You can make a cert/key with openssl using:
* openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem
* as taken from http://docs.python.org/dev/library/ssl.html#certificates
*/
#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include <getopt.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/select.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "websocket.h"
char traffic_legend[] = "\n\
Traffic Legend:\n\
} - Client receive\n\
}. - Client receive partial\n\
{ - Target receive\n\
\n\
> - Target send\n\
>. - Target send partial\n\
< - Client send\n\
<. - Client send partial\n\
";
char USAGE[] = "Usage: [options] " \
"[source_addr:]source_port target_addr:target_port\n\n" \
" --verbose|-v verbose messages and per frame traffic\n" \
" --daemon|-D become a daemon (background process)\n" \
" --cert CERT SSL certificate file\n" \
" --key KEY SSL key file (if separate from cert)\n" \
" --ssl-only disallow non-encrypted connections";
#define usage(fmt, args...) \
fprintf(stderr, "%s\n\n", USAGE); \
fprintf(stderr, fmt , ## args); \
exit(1);
char target_host[256];
int target_port;
extern pipe_error;
extern settings_t settings;
extern char *tbuf, *cbuf, *tbuf_tmp, *cbuf_tmp;
extern unsigned int bufsize, dbufsize;
void do_proxy(ws_ctx_t *ws_ctx, int target) {
fd_set rlist, wlist, elist;
struct timeval tv;
int i, maxfd, client = ws_ctx->sockfd;
unsigned int tstart, tend, cstart, cend, ret;
ssize_t len, bytes;
tstart = tend = cstart = cend = 0;
maxfd = client > target ? client+1 : target+1;
while (1) {
tv.tv_sec = 1;
tv.tv_usec = 0;
FD_ZERO(&rlist);
FD_ZERO(&wlist);
FD_ZERO(&elist);
FD_SET(client, &elist);
FD_SET(target, &elist);
if (tend == tstart) {
// Nothing queued for target, so read from client
FD_SET(client, &rlist);
} else {
// Data queued for target, so write to it
FD_SET(target, &wlist);
}
if (cend == cstart) {
// Nothing queued for client, so read from target
FD_SET(target, &rlist);
} else {
// Data queued for client, so write to it
FD_SET(client, &wlist);
}
ret = select(maxfd, &rlist, &wlist, &elist, &tv);
if (pipe_error) { break; }
if (FD_ISSET(target, &elist)) {
handler_emsg("target exception\n");
break;
}
if (FD_ISSET(client, &elist)) {
handler_emsg("client exception\n");
break;
}
if (ret == -1) {
handler_emsg("select(): %s\n", strerror(errno));
break;
} else if (ret == 0) {
//handler_emsg("select timeout\n");
continue;
}
if (FD_ISSET(target, &wlist)) {
len = tend-tstart;
bytes = send(target, tbuf + tstart, len, 0);
if (pipe_error) { break; }
if (bytes < 0) {
handler_emsg("target connection error: %s\n",
strerror(errno));
break;
}
tstart += bytes;
if (tstart >= tend) {
tstart = tend = 0;
traffic(">");
} else {
traffic(">.");
}
}
if (FD_ISSET(client, &wlist)) {
len = cend-cstart;
bytes = ws_send(ws_ctx, cbuf + cstart, len);
if (pipe_error) { break; }
if (len < 3) {
handler_emsg("len: %d, bytes: %d: %d\n", len, bytes, *(cbuf + cstart));
}
cstart += bytes;
if (cstart >= cend) {
cstart = cend = 0;
traffic("<");
} else {
traffic("<.");
}
}
if (FD_ISSET(target, &rlist)) {
bytes = recv(target, cbuf_tmp, dbufsize , 0);
if (pipe_error) { break; }
if (bytes <= 0) {
handler_emsg("target closed connection\n");
break;
}
cstart = 0;
cend = encode(cbuf_tmp, bytes, cbuf, bufsize);
/*
printf("encoded: ");
for (i=0; i< cend; i++) {
printf("%u,", (unsigned char) *(cbuf+i));
}
printf("\n");
*/
if (cend < 0) {
handler_emsg("encoding error\n");
break;
}
traffic("{");
}
if (FD_ISSET(client, &rlist)) {
bytes = ws_recv(ws_ctx, tbuf_tmp, bufsize-1);
if (pipe_error) { break; }
if (bytes <= 0) {
handler_emsg("client closed connection\n");
break;
} else if ((bytes == 2) &&
(tbuf_tmp[0] == '\xff') &&
(tbuf_tmp[1] == '\x00')) {
handler_emsg("client sent orderly close frame\n");
break;
}
/*
printf("before decode: ");
for (i=0; i< bytes; i++) {
printf("%u,", (unsigned char) *(tbuf_tmp+i));
}
printf("\n");
*/
len = decode(tbuf_tmp, bytes, tbuf, bufsize-1);
/*
printf("decoded: ");
for (i=0; i< len; i++) {
printf("%u,", (unsigned char) *(tbuf+i));
}
printf("\n");
*/
if (len < 0) {
handler_emsg("decoding error\n");
break;
}
traffic("}");
tstart = 0;
tend = len;
}
}
}
void proxy_handler(ws_ctx_t *ws_ctx) {
int tsock = 0;
struct sockaddr_in taddr;
handler_msg("connecting to: %s:%d\n", target_host, target_port);
tsock = socket(AF_INET, SOCK_STREAM, 0);
if (tsock < 0) {
handler_emsg("Could not create target socket: %s\n",
strerror(errno));
return;
}
bzero((char *) &taddr, sizeof(taddr));
taddr.sin_family = AF_INET;
taddr.sin_port = htons(target_port);
/* Resolve target address */
if (resolve_host(&taddr.sin_addr, target_host) < -1) {
handler_emsg("Could not resolve target address: %s\n",
strerror(errno));
}
if (connect(tsock, (struct sockaddr *) &taddr, sizeof(taddr)) < 0) {
handler_emsg("Could not connect to target: %s\n",
strerror(errno));
close(tsock);
return;
}
if ((settings.verbose) && (! settings.daemon)) {
printf("%s", traffic_legend);
}
do_proxy(ws_ctx, tsock);
close(tsock);
}
int main(int argc, char *argv[])
{
int fd, c, option_index = 0;
static int ssl_only = 0, daemon = 0, verbose = 0;
char *found;
static struct option long_options[] = {
{"verbose", no_argument, &verbose, 'v'},
{"ssl-only", no_argument, &ssl_only, 1 },
{"daemon", no_argument, &daemon, 'D'},
/* ---- */
{"cert", required_argument, 0, 'c'},
{"key", required_argument, 0, 'k'},
{0, 0, 0, 0}
};
settings.cert = realpath("self.pem", NULL);
if (!settings.cert) {
/* Make sure it's always set to something */
settings.cert = "self.pem";
}
settings.key = "";
while (1) {
c = getopt_long (argc, argv, "vDc:k:",
long_options, &option_index);
/* Detect the end */
if (c == -1) { break; }
switch (c) {
case 0:
break; // ignore
case 1:
break; // ignore
case 'v':
verbose = 1;
break;
case 'D':
daemon = 1;
break;
case 'c':
settings.cert = realpath(optarg, NULL);
if (! settings.cert) {
usage("No cert file at %s\n", optarg);
}
break;
case 'k':
settings.key = realpath(optarg, NULL);
if (! settings.key) {
usage("No key file at %s\n", optarg);
}
break;
default:
usage("");
}
}
settings.verbose = verbose;
settings.ssl_only = ssl_only;
settings.daemon = daemon;
if ((argc-optind) != 2) {
usage("Invalid number of arguments\n");
}
found = strstr(argv[optind], ":");
if (found) {
memcpy(settings.listen_host, argv[optind], found-argv[optind]);
settings.listen_port = strtol(found+1, NULL, 10);
} else {
settings.listen_host[0] = '\0';
settings.listen_port = strtol(argv[optind], NULL, 10);
}
optind++;
if (settings.listen_port == 0) {
usage("Could not parse listen_port\n");
}
found = strstr(argv[optind], ":");
if (found) {
memcpy(target_host, argv[optind], found-argv[optind]);
target_port = strtol(found+1, NULL, 10);
} else {
usage("Target argument must be host:port\n");
}
if (target_port == 0) {
usage("Could not parse target port\n");
}
if (ssl_only) {
if (!access(settings.cert, R_OK)) {
usage("SSL only and cert file '%s' not found\n", settings.cert);
}
} else if (access(settings.cert, R_OK) != 0) {
fprintf(stderr, "Warning: '%s' not found\n", settings.cert);
}
//printf(" verbose: %d\n", settings.verbose);
//printf(" ssl_only: %d\n", settings.ssl_only);
//printf(" daemon: %d\n", settings.daemon);
//printf(" cert: %s\n", settings.cert);
//printf(" key: %s\n", settings.key);
settings.handler = proxy_handler;
start_server();
free(tbuf);
free(cbuf);
free(tbuf_tmp);
free(cbuf_tmp);
}
// A WebSocket to TCP socket proxy
// Copyright 2010 Joel Martin
// Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
var net = require('net'),
sys = require('sys'),
crypto = require('crypto'),
source_arg, source_host, source_port,
target_arg, target_host, target_port;
// md5 calculation borrowed from Socket.IO (MIT license)
function gen_md5(headers, k3) {
var k1 = headers['sec-websocket-key1'],
k2 = headers['sec-websocket-key2'],
md5 = crypto.createHash('md5');
[k1, k2].forEach(function(k){
var n = parseInt(k.replace(/[^\d]/g, '')),
spaces = k.replace(/[^ ]/g, '').length;
if (spaces === 0 || n % spaces !== 0){
return false;
}
n /= spaces;
md5.update(String.fromCharCode(
n >> 24 & 0xFF,
n >> 16 & 0xFF,
n >> 8 & 0xFF,
n & 0xFF));
});
md5.update(k3.toString('binary'));
return md5.digest('binary');
}
function encode(buf) {
return String.fromCharCode(0) +
buf.toString('base64', 0) +
String.fromCharCode(255);
}
function decode(data) {
var i, len = 0, strs, retstrs = [],
buf = new Buffer(data.length),
str = data.toString('binary', 1, data.length-1);
if (str.indexOf('\xff') > -1) {
// We've gotten multiple frames at once
strs = str.split('\xff\x00')
for (i = 0; i < strs.length; i++) {
len = buf.write(strs[i], 0, 'base64');
retstrs.push(buf.toString('binary', 0, len));
}
return retstrs.join("");
} else {
len = buf.write(str, 0, 'base64');
return buf.toString('binary', 0, len);
}
}
var server = net.createServer(function (client) {
var handshake = "", headers = {}, header,
version, path, k1, k2, k3, target = null;
function cleanup() {
client.end();
if (target) {
target.end();
target = null;
}
}
function do_handshake(data) {
var i, idx, dlen = data.length, lines, location, rheaders,
sec_hdr;
//sys.log("received handshake data: " + data);
handshake += data.toString('utf8');
if ((data[dlen-12] != 13) ||
(data[dlen-11] != 10) ||
(data[dlen-10] != 13) ||
(data[dlen-9] != 10)) {
//sys.log("Got partial handshake");
return;
}
//sys.log("Got whole handshake");
if (handshake.indexOf('GET ') != 0) {
sys.error("Got invalid handshake");
client.end();
return;
}
lines = handshake.split('\r\n');
path = lines[0].split(' ')[1];
//sys.log("path: " + path);
k3 = data.slice(dlen-8, dlen);
for (i = 1; i < lines.length; i++) {
//sys.log("lines[i]: " + lines[i]);
if (lines[i].length == 0) { break; }
idx = lines[i].indexOf(': ');
if (idx < 0) {
sys.error("Got invalid handshake header");
client.end();
return;
}
header = lines[i].slice(0, idx).toLowerCase();
headers[header] = lines[i].slice(idx+2);
}
//console.dir(headers);
//sys.log("k3: " + k3 + ", k3.length: " + k3.length);
if (headers.upgrade !== 'WebSocket') {
sys.error("Upgrade header is not 'WebSocket'");
client.end();
return;
}
location = (headers.origin.substr(0, 5) == 'https' ? 'wss' : 'ws')
+ '://' + headers.host + path;
//sys.log("location: " + location);
if ('sec-websocket-key1' in headers) {
version = 76;
sec_hdr = "Sec-";
} else {
version = 75;
sec_hdr = "";
}
sys.log("using protocol version " + version);
rheaders = [
'HTTP/1.1 101 WebSocket Protocol Handshake',
'Upgrade: WebSocket',
'Connection: Upgrade',
sec_hdr + 'WebSocket-Origin: ' + headers.origin,
sec_hdr + 'WebSocket-Location: ' + location
];
if ('sec-websocket-protocol' in headers) {
rheaders.push('Sec-WebSocket-Protocol: ' + headers['sec-websocket-protocol']);
}
rheaders.push('');
if (version === 76) {
rheaders.push(gen_md5(headers, k3));
}
// Switch listener to normal data path
client.on('data', client_data);
//client.setEncoding('utf8');
client.removeListener('data', do_handshake);
// Do not delay writes
client.setNoDelay(true);
// Send the handshake response
try {
//sys.log("response: " + rheaders.join('\r\n'));
client.write(rheaders.join('\r\n'), 'binary');
} catch(e) {
sys.error("Failed to send handshake response");
client.end();
return;
}
// Create a connection to the target
target = net.createConnection(target_port, target_host);
target.on('data', target_data);
target.on('end', function () {
sys.log("received target end");
cleanup();
});
target.on('error', function (exc) {
sys.log("received target error: " + exc);
cleanup();
});
}
function client_data(data) {
var ret;
//sys.log("received client data: " + data);
//sys.log(" decoded: " + decode(data));
try {
ret = target.write(decode(data), 'binary');
if (! ret) {
sys.log("target write returned false");
}
} catch(e) {
sys.log("fatal error writing to target");
cleanup();
}
}
function target_data(data) {
//sys.log("received target data: " + data);
//sys.log(" encoded: " + encode(data));
try {
client.write(encode(data), 'binary');
} catch(e) {
sys.log("fatal error writing to client");
cleanup();
}
}
client.on('connect', function () {
sys.log("Got client connection");
});
client.on('data', do_handshake);
client.on('end', function () {
sys.log("recieved client end");
cleanup();
});
client.on('error', function (exc) {
sys.log("recieved client error: " + exc);
cleanup();
});
});
// parse source and target into parts
source_arg = process.argv[2];
target_arg = process.argv[3];
try {
var idx;
idx = source_arg.indexOf(":");
if (idx >= 0) {
source_host = source_arg.slice(0, idx);
source_port = parseInt(source_arg.slice(idx+1), 10);
} else {
source_host = "";
source_port = parseInt(source_arg, 10);
}
idx = target_arg.indexOf(":");
if (idx < 0) {
throw("target must be host:port");
}
target_host = target_arg.slice(0, idx);
target_port = parseInt(target_arg.slice(idx+1), 10);
if (isNaN(source_port) || isNaN(target_port)) {
throw("illegal port");
}
} catch(e) {
console.error("wsproxy.py [source_addr:]source_port target_addr:target_port");
process.exit(2);
}
sys.log("source: " + source_host + ":" + source_port);
sys.log("target: " + target_host + ":" + target_port);
server.listen(source_port, source_host);
#!/usr/bin/python
'''
A WebSocket to TCP socket proxy with support for "wss://" encryption.
Copyright 2010 Joel Martin
Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
You can make a cert/key with openssl using:
openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem
as taken from http://docs.python.org/dev/library/ssl.html#certificates
'''
import socket, optparse, time, os, sys, subprocess
from select import select
from websocket import WebSocketServer
class WebSocketProxy(WebSocketServer):
"""
Proxy traffic to and from a WebSockets client to a normal TCP
socket server target. All traffic to/from the client is base64
encoded/decoded to allow binary data to be sent/received to/from
the target.
"""
buffer_size = 65536
traffic_legend = """
Traffic Legend:
} - Client receive
}. - Client receive partial
{ - Target receive
> - Target send
>. - Target send partial
< - Client send
<. - Client send partial
"""
def __init__(self, *args, **kwargs):
# Save off proxy specific options
self.target_host = kwargs.pop('target_host')
self.target_port = kwargs.pop('target_port')
self.wrap_cmd = kwargs.pop('wrap_cmd')
self.wrap_mode = kwargs.pop('wrap_mode')
# Last 3 timestamps command was run
self.wrap_times = [0, 0, 0]
if self.wrap_cmd:
rebinder_path = ['./', os.path.dirname(sys.argv[0])]
self.rebinder = None
for rdir in rebinder_path:
rpath = os.path.join(rdir, "rebind.so")
if os.path.exists(rpath):
self.rebinder = rpath
break
if not self.rebinder:
raise Exception("rebind.so not found, perhaps you need to run make")
self.target_host = "127.0.0.1" # Loopback
# Find a free high port
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('', 0))
self.target_port = sock.getsockname()[1]
sock.close()
os.environ.update({
"LD_PRELOAD": self.rebinder,
"REBIND_OLD_PORT": str(kwargs['listen_port']),
"REBIND_NEW_PORT": str(self.target_port)})
WebSocketServer.__init__(self, *args, **kwargs)
def run_wrap_cmd(self):
print "Starting '%s'" % " ".join(self.wrap_cmd)
self.wrap_times.append(time.time())
self.wrap_times.pop(0)
self.cmd = subprocess.Popen(
self.wrap_cmd, env=os.environ)
self.spawn_message = True
def started(self):
"""
Called after Websockets server startup (i.e. after daemonize)
"""
# Need to call wrapped command after daemonization so we can
# know when the wrapped command exits
if self.wrap_cmd:
print " - proxying from %s:%s to '%s' (port %s)\n" % (
self.listen_host, self.listen_port,
" ".join(self.wrap_cmd), self.target_port)
self.run_wrap_cmd()
else:
print " - proxying from %s:%s to %s:%s\n" % (
self.listen_host, self.listen_port,
self.target_host, self.target_port)
def poll(self):
# If we are wrapping a command, check it's status
if self.wrap_cmd and self.cmd:
ret = self.cmd.poll()
if ret != None:
self.vmsg("Wrapped command exited (or daemon). Returned %s" % ret)
self.cmd = None
if self.wrap_cmd and self.cmd == None:
# Response to wrapped command being gone
if self.wrap_mode == "ignore":
pass
elif self.wrap_mode == "exit":
sys.exit(ret)
elif self.wrap_mode == "respawn":
now = time.time()
avg = sum(self.wrap_times)/len(self.wrap_times)
if (now - avg) < 10:
# 3 times in the last 10 seconds
if self.spawn_message:
print "Command respawning too fast"
self.spawn_message = False
else:
self.run_wrap_cmd()
#
# Routines above this point are run in the master listener
# process.
#
#
# Routines below this point are connection handler routines and
# will be run in a separate forked process for each connection.
#
def new_client(self, client):
"""
Called after a new WebSocket connection has been established.
"""
self.rec = None
if self.record:
# Record raw frame data as a JavaScript compatible file
fname = "%s.%s" % (self.record,
self.handler_id)
self.msg("opening record file: %s" % fname)
self.rec = open(fname, 'w+')
self.rec.write("var VNC_frame_data = [\n")
# Connect to the target
self.msg("connecting to: %s:%s" % (
self.target_host, self.target_port))
tsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tsock.connect((self.target_host, self.target_port))
if self.verbose and not self.daemon:
print self.traffic_legend
# Stat proxying
try:
self.do_proxy(client, tsock)
except:
if tsock: tsock.close()
if self.rec:
self.rec.write("'EOF']\n")
self.rec.close()
raise
def do_proxy(self, client, target):
"""
Proxy client WebSocket to normal target socket.
"""
cqueue = []
cpartial = ""
tqueue = []
rlist = [client, target]
tstart = int(time.time()*1000)
while True:
wlist = []
tdelta = int(time.time()*1000) - tstart
if tqueue: wlist.append(target)
if cqueue: wlist.append(client)
ins, outs, excepts = select(rlist, wlist, [], 1)
if excepts: raise Exception("Socket exception")
if target in outs:
# Send queued client data to the target
dat = tqueue.pop(0)
sent = target.send(dat)
if sent == len(dat):
self.traffic(">")
else:
# requeue the remaining data
tqueue.insert(0, dat[sent:])
self.traffic(".>")
if client in outs:
# Send queued target data to the client
dat = cqueue.pop(0)
sent = client.send(dat)
if sent == len(dat):
self.traffic("<")
if self.rec:
self.rec.write("%s,\n" %
repr("{%s{" % tdelta + dat[1:-1]))
else:
cqueue.insert(0, dat[sent:])
self.traffic("<.")
if target in ins:
# Receive target data, encode it and queue for client
buf = target.recv(self.buffer_size)
if len(buf) == 0: raise self.EClose("Target closed")
cqueue.append(self.encode(buf))
self.traffic("{")
if client in ins:
# Receive client data, decode it, and queue for target
buf = client.recv(self.buffer_size)
if len(buf) == 0: raise self.EClose("Client closed")
if buf == '\xff\x00':
raise self.EClose("Client sent orderly close frame")
elif buf[-1] == '\xff':
if buf.count('\xff') > 1:
self.traffic(str(buf.count('\xff')))
self.traffic("}")
if self.rec:
self.rec.write("%s,\n" %
(repr("}%s}" % tdelta + buf[1:-1])))
if cpartial:
# Prepend saved partial and decode frame(s)
tqueue.extend(self.decode(cpartial + buf))
cpartial = ""
else:
# decode frame(s)
tqueue.extend(self.decode(buf))
else:
# Save off partial WebSockets frame
self.traffic(".}")
cpartial = cpartial + buf
if __name__ == '__main__':
usage = "\n %prog [options]"
usage += " [source_addr:]source_port target_addr:target_port"
usage += "\n %prog [options]"
usage += " [source_addr:]source_port -- WRAP_COMMAND_LINE"
parser = optparse.OptionParser(usage=usage)
parser.add_option("--verbose", "-v", action="store_true",
help="verbose messages and per frame traffic")
parser.add_option("--record",
help="record sessions to FILE.[session_number]", metavar="FILE")
parser.add_option("--daemon", "-D",
dest="daemon", action="store_true",
help="become a daemon (background process)")
parser.add_option("--cert", default="self.pem",
help="SSL certificate file")
parser.add_option("--key", default=None,
help="SSL key file (if separate from cert)")
parser.add_option("--ssl-only", action="store_true",
help="disallow non-encrypted connections")
parser.add_option("--web", default=None, metavar="DIR",
help="run webserver on same port. Serve files from DIR.")
parser.add_option("--wrap-mode", default="exit", metavar="MODE",
choices=["exit", "ignore", "respawn"],
help="action to take when the wrapped program exits "
"or daemonizes: exit (default), ignore, respawn")
(opts, args) = parser.parse_args()
# Sanity checks
if len(args) < 2:
parser.error("Too few arguments")
if sys.argv.count('--'):
opts.wrap_cmd = args[1:]
else:
opts.wrap_cmd = None
if len(args) > 2:
parser.error("Too many arguments")
if opts.ssl_only and not os.path.exists(opts.cert):
parser.error("SSL only and %s not found" % opts.cert)
# Parse host:port and convert ports to numbers
if args[0].count(':') > 0:
opts.listen_host, opts.listen_port = args[0].split(':')
else:
opts.listen_host, opts.listen_port = '', args[0]
try: opts.listen_port = int(opts.listen_port)
except: parser.error("Error parsing listen port")
if opts.wrap_cmd:
opts.target_host = None
opts.target_port = None
else:
if args[1].count(':') > 0:
opts.target_host, opts.target_port = args[1].split(':')
else:
parser.error("Error parsing target")
try: opts.target_port = int(opts.target_port)
except: parser.error("Error parsing target port")
# Create and start the WebSockets proxy
server = WebSocketProxy(**opts.__dict__)
server.start_server()
websockify
\ No newline at end of file
<html>
<head>
<title>Telnet Client using WebSockets</title>
<script src="include/base64.js"></script>
<script src="include/util.js"></script>
<script src="include/webutil.js"></script>
<script src="include/canvas.js"></script>
<script src="VT100.js"></script>
<script src="wstelnet.js"></script>
<!-- Uncomment to activate firebug lite -->
<!--
<script type='text/javascript'
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
-->
</head>
<body>
Host: <input id='host' style='width:100'>&nbsp;
Port: <input id='port' style='width:50'>&nbsp;
Encrypt: <input id='encrypt' type='checkbox'>&nbsp;
<input id='connectButton' type='button' value='Connect' style='width:100px'
onclick="connect();">&nbsp;
<br><br>
<pre id="terminal"></pre>
<script>
var telnet;
function connect() {
telnet.connect($D('host').value,
$D('port').value,
$D('encrypt').checked);
$D('connectButton').disabled = true;
$D('connectButton').value = "Connecting";
}
function disconnect() {
$D('connectButton').disabled = true;
$D('connectButton').value = "Disconnecting";
telnet.disconnect();
}
function connected() {
$D('connectButton').disabled = false;
$D('connectButton').value = "Disconnect";
$D('connectButton').onclick = disconnect;
}
function disconnected() {
$D('connectButton').disabled = false;
$D('connectButton').value = "Connect";
$D('connectButton').onclick = connect;
}
/* If no builtin websockets then load web_socket.js */
if (window.WebSocket) {
VNC_native_ws = true;
} else {
VNC_native_ws = false;
console.log("Loading web-socket-js flash bridge");
var extra = "<script src='include/web-socket-js/swfobject.js'><\/script>";
extra += "<script src='include/web-socket-js/FABridge.js'><\/script>";
extra += "<script src='include/web-socket-js/web_socket.js'><\/script>";
document.write(extra);
}
window.onload = function() {
console.log("onload");
if (!VNC_native_ws) {
console.log("initializing web-socket-js flash bridge");
WebSocket.__swfLocation = "include/web-socket-js/WebSocketMain.swf";
WebSocket.__initialize();
}
var url = document.location.href;
$D('host').value = (url.match(/host=([^&#]*)/) || ['',''])[1];
$D('port').value = (url.match(/port=([^&#]*)/) || ['',''])[1];
telnet = Telnet('terminal', connected, disconnected);
}
</script>
</body>
</html>
/*
* WebSockets telnet client
* Copyright (C) 2011 Joel Martin
* Licensed under LGPL-3 (see LICENSE.txt)
*
* Includes VT100.js from:
* http://code.google.com/p/sshconsole
* Which was modified from:
* http://fzort.org/bi/o.php#vt100_js
*
* Telnet protocol:
* http://www.networksorcery.com/enp/protocol/telnet.htm
* http://www.networksorcery.com/enp/rfc/rfc1091.txt
*
* ANSI escape sequeneces:
* http://en.wikipedia.org/wiki/ANSI_escape_code
* http://ascii-table.com/ansi-escape-sequences-vt-100.php
* http://www.termsys.demon.co.uk/vtansi.htm
* http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
*
* ASCII codes:
* http://en.wikipedia.org/wiki/ASCII
* http://www.hobbyprojects.com/ascii-table/ascii-table.html
*
* Other web consoles:
* http://stackoverflow.com/questions/244750/ajax-console-window-with-ansi-vt100-support
*/
function Telnet(target, connect_callback, disconnect_callback) {
var that = {}, // Public API interface
vt100, ws, sQ = [];
termType = "VT100";
Array.prototype.pushStr = function (str) {
var n = str.length;
for (var i=0; i < n; i++) {
this.push(str.charCodeAt(i));
}
}
function do_send() {
if (sQ.length > 0) {
Util.Debug("Sending " + sQ);
ws.send(Base64.encode(sQ));
sQ = [];
}
}
function do_recv(e) {
//console.log(">> do_recv");
var arr = Base64.decode(e.data), str = "",
chr, cmd, code, value;
Util.Debug("Received array '" + arr + "'");
while (arr.length > 0) {
chr = arr.shift();
switch (chr) {
case 255: // IAC
cmd = chr;
code = arr.shift();
value = arr.shift();
switch (code) {
case 254: // DONT
Util.Debug("Got Cmd DONT '" + value + "', ignoring");
break;
case 253: // DO
Util.Debug("Got Cmd DO '" + value + "'");
if (value === 24) {
// Terminal type
Util.Info("Send WILL '" + value + "' (TERM-TYPE)");
sQ.push(255, 251, value);
} else {
// Refuse other DO requests with a WONT
Util.Debug("Send WONT '" + value + "'");
sQ.push(255, 252, value);
}
break;
case 252: // WONT
Util.Debug("Got Cmd WONT '" + value + "', ignoring");
break;
case 251: // WILL
Util.Debug("Got Cmd WILL '" + value + "'");
if (value === 1) {
// Affirm echo with DO
Util.Info("Send Cmd DO '" + value + "' (echo)");
sQ.push(255, 253, value);
} else {
// Reject other WILL offers with a DONT
Util.Debug("Send Cmd DONT '" + value + "'");
sQ.push(255, 254, value);
}
break;
case 250: // SB (subnegotiation)
if (value === 24) {
Util.Info("Got IAC SB TERM-TYPE SEND(1) IAC SE");
// TERM-TYPE subnegotiation
if (arr[0] === 1 &&
arr[1] === 255 &&
arr[2] === 240) {
arr.shift(); arr.shift(); arr.shift();
Util.Info("Send IAC SB TERM-TYPE IS(0) '" +
termType + "' IAC SE");
sQ.push(255, 250, 24, 0);
sQ.pushStr(termType);
sQ.push(255, 240);
} else {
Util.Info("Invalid subnegotiation received" + arr);
}
} else {
Util.Info("Ignoring SB " + value);
}
break;
default:
Util.Info("Got Cmd " + cmd + " " + value + ", ignoring"); }
continue;
case 242: // Data Mark (Synch)
cmd = chr;
code = arr.shift();
value = arr.shift();
Util.Info("Ignoring Data Mark (Synch)");
break;
default: // everything else
str += String.fromCharCode(chr);
}
}
if (sQ) {
do_send();
}
if (str) {
vt100.write(str);
}
//console.log("<< do_recv");
}
that.connect = function(host, port, encrypt) {
var host = host,
port = port,
scheme = "ws://", uri;
Util.Debug(">> connect");
if ((!host) || (!port)) {
console.log("must set host and port");
return;
}
if (ws) {
ws.close();
}
if (encrypt) {
scheme = "wss://";
}
uri = scheme + host + ":" + port;
Util.Info("connecting to " + uri);
ws = new WebSocket(uri);
ws.onmessage = do_recv;
ws.onopen = function(e) {
Util.Info(">> WebSockets.onopen");
vt100.curs_set(true, true);
connect_callback();
Util.Info("<< WebSockets.onopen");
};
ws.onclose = function(e) {
Util.Info(">> WebSockets.onclose");
that.disconnect();
Util.Info("<< WebSockets.onclose");
};
ws.onerror = function(e) {
Util.Info(">> WebSockets.onerror");
that.disconnect();
Util.Info("<< WebSockets.onerror");
};
Util.Debug("<< connect");
}
that.disconnect = function() {
Util.Debug(">> disconnect");
if (ws) {
ws.close();
}
vt100.curs_set(true, false);
disconnect_callback();
Util.Debug("<< disconnect");
}
function constructor() {
/* Initialize the terminal emulator/renderer */
vt100 = new VT100(80, 24, target);
// Turn off local echo
vt100.noecho();
/*
* Override VT100 I/O routines
*/
// Set handler for sending characters
vt100.getch(
function send_chr(chr, vt) {
var i;
Util.Debug(">> send_chr: " + chr);
for (i = 0; i < chr.length; i++) {
sQ.push(chr.charCodeAt(i));
}
do_send();
vt100.getch(send_chr);
}
);
vt100.debug = function(message) {
Util.Debug(message + "\n");
}
vt100.warn = function(message) {
Util.Warn(message + "\n");
}
vt100.curs_set = function(vis, grab, eventist)
{
this.debug("curs_set:: vis: " + vis + ", grab: " + grab);
if (vis !== undefined)
this.cursor_vis_ = (vis > 0);
if (eventist === undefined)
eventist = window;
if (grab === true || grab === false) {
if (grab === this.grab_events_)
return;
if (grab) {
this.grab_events_ = true;
VT100.the_vt_ = this;
Util.addEvent(eventist, 'keydown', vt100.key_down);
Util.addEvent(eventist, 'keyup', vt100.key_up);
} else {
Util.removeEvent(eventist, 'keydown', vt100.key_down);
Util.removeEvent(eventist, 'keyup', vt100.key_up);
this.grab_events_ = false;
VT100.the_vt_ = undefined;
}
}
}
vt100.key_down = function(e) {
var vt = VT100.the_vt_, keysym, ch, str = "";
if (vt === undefined)
return true;
keysym = getKeysym(e);
if (keysym < 128) {
if (e.ctrlKey) {
if (keysym == 64) {
// control 0
ch = 0;
} else if ((keysym >= 97) && (keysym <= 122)) {
// control codes 1-26
ch = keysym - 96;
} else if ((keysym >= 91) && (keysym <= 95)) {
// control codes 27-31
ch = keysym - 64;
} else {
Util.Info("Debug unknown control keysym: " + keysym);
}
} else {
ch = keysym;
}
str = String.fromCharCode(ch);
} else {
switch (keysym) {
case 65505: // Shift, do not send directly
break;
case 65507: // Ctrl, do not send directly
break;
case 65293: // Carriage return, line feed
str = '\n'; break;
case 65288: // Backspace
str = '\b'; break;
case 65307: // Escape
str = '\x1b'; break;
case 65361: // Left arrow
str = '\x1b[D'; break;
case 65362: // Up arrow
str = '\x1b[A'; break;
case 65363: // Right arrow
str = '\x1b[C'; break;
case 65364: // Down arrow
str = '\x1b[B'; break;
default:
Util.Info("Unrecoginized keysym " + keysym);
}
}
if (str) {
vt.key_buf_.push(str);
setTimeout(VT100.go_getch_, 0);
}
Util.stopEvent(e);
return false;
}
vt100.key_up = function(e) {
var vt = VT100.the_vt_;
if (vt === undefined)
return true;
Util.stopEvent(e);
return false;
}
return that;
}
return constructor(); // Return the public API interface
} // End of Telnet()
<html>
<head>
<title>WebSockets Test</title>
<script src="include/base64.js"></script>
<script src="include/util.js"></script>
<script src="include/webutil.js"></script>
<!-- Uncomment to activate firebug lite -->
<!--
<script type='text/javascript'
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
-->
</head>
<body>
Host: <input id='host' style='width:100'>&nbsp;
Port: <input id='port' style='width:50'>&nbsp;
Encrypt: <input id='encrypt' type='checkbox'>&nbsp;
Send Delay (ms): <input id='sendDelay' style='width:50' value="100">&nbsp;
<input id='connectButton' type='button' value='Start' style='width:100px'
onclick="connect();">&nbsp;
<br><br>
<table border=1>
<tr>
<th align="right">Packets sent:</th>
<td align="right"><div id='sent'>0</div></td>
</tr><tr>
<th align="right">Good Packets Received:</th>
<td align="right"><div id='received'>0</div></td>
</tr><tr>
<th align="right">Errors (Bad Packets Received:)</th>
<td align="right"><div id='errors'>0</div></td>
</tr>
</table>
<br>
Errors:<br>
<textarea id="error" style="font-size: 9;" cols=80 rows=25></textarea>
</body>
<script>
function error(str) {
console.error(str);
cell = $D('error');
cell.innerHTML += errors + ": " + str + "\n";
cell.scrollTop = cell.scrollHeight;
}
var host = null, port = null, sendDelay = 0;
var ws = null, update_ref = null, send_ref = null;
var sent = 0, received = 0, errors = 0;
var max_send = 2000;
var recv_seq = 0, send_seq = 0;
Array.prototype.pushStr = function (str) {
var n = str.length;
for (var i=0; i < n; i++) {
this.push(str.charCodeAt(i));
}
}
function add (x,y) {
return parseInt(x,10)+parseInt(y,10);
}
function check_respond(data) {
//console.log(">> check_respond");
var decoded, first, last, str, length, chksum, nums, arr;
decoded = Base64.decode(data);
first = String.fromCharCode(decoded.shift());
last = String.fromCharCode(decoded.pop());
if (first != "^") {
errors++;
error("Packet missing start char '^'");
return;
}
if (last != "$") {
errors++;
error("Packet missing end char '$'");
return;
}
arr = decoded.map(function(num) {
return String.fromCharCode(num);
} ).join('').split(':');
seq = arr[0];
length = arr[1];
chksum = arr[2];
nums = arr[3];
//console.log(" length:" + length + " chksum:" + chksum + " nums:" + nums);
if (seq != recv_seq) {
errors++;
error("Expected seq " + recv_seq + " but got " + seq);
recv_seq = parseInt(seq,10) + 1; // Back on track
return;
}
recv_seq++;
if (nums.length != length) {
errors++;
error("Expected length " + length + " but got " + nums.length);
return;
}
//real_chksum = nums.reduce(add);
real_chksum = 0;
for (var i=0; i < nums.length; i++) {
real_chksum += parseInt(nums.charAt(i), 10);
}
if (real_chksum != chksum) {
errors++
error("Expected chksum " + chksum + " but real chksum is " + real_chksum);
return;
}
received++;
//console.log(" Packet checks out: length:" + length + " chksum:" + chksum);
//console.log("<< check_respond");
}
function send() {
if (ws.bufferedAmount > 0) {
console.log("Delaying send");
return;
}
var length = Math.floor(Math.random()*(max_send-9)) + 10; // 10 - max_send
var numlist = [], arr = [];
for (var i=0; i < length; i++) {
numlist.push( Math.floor(Math.random()*10) );
}
//chksum = numlist.reduce(add);
chksum = 0;
for (var i=0; i < numlist.length; i++) {
chksum += parseInt(numlist[i], 10);
}
var nums = numlist.join('');
arr.pushStr("^" + send_seq + ":" + length + ":" + chksum + ":" + nums + "$")
send_seq ++;
ws.send(Base64.encode(arr));
sent++;
}
function update_stats() {
$D('sent').innerHTML = sent;
$D('received').innerHTML = received;
$D('errors').innerHTML = errors;
}
function init_ws() {
console.log(">> init_ws");
var scheme = "ws://";
if ($D('encrypt').checked) {
scheme = "wss://";
}
var uri = scheme + host + ":" + port;
console.log("connecting to " + uri);
ws = new WebSocket(uri);
ws.onmessage = function(e) {
//console.log(">> WebSockets.onmessage");
check_respond(e.data);
//console.log("<< WebSockets.onmessage");
};
ws.onopen = function(e) {
console.log(">> WebSockets.onopen");
send_ref = setInterval(send, sendDelay);
console.log("<< WebSockets.onopen");
};
ws.onclose = function(e) {
console.log(">> WebSockets.onclose");
clearInterval(send_ref);
console.log("<< WebSockets.onclose");
};
ws.onerror = function(e) {
console.log(">> WebSockets.onerror");
console.log(" " + e);
console.log("<< WebSockets.onerror");
};
console.log("<< init_ws");
}
function connect() {
console.log(">> connect");
host = $D('host').value;
port = $D('port').value;
sendDelay = parseInt($D('sendDelay').value, 10);
if ((!host) || (!port)) {
console.log("must set host and port");
return;
}
if (ws) {
ws.close();
}
init_ws();
update_ref = setInterval(update_stats, 1);
$D('connectButton').value = "Stop";
$D('connectButton').onclick = disconnect;
console.log("<< connect");
}
function disconnect() {
console.log(">> disconnect");
if (ws) {
ws.close();
}
clearInterval(update_ref);
update_stats(); // Final numbers
recv_seq = 0;
send_seq = 0;
$D('connectButton').value = "Start";
$D('connectButton').onclick = connect;
console.log("<< disconnect");
}
/* If no builtin websockets then load web_socket.js */
if (window.WebSocket) {
VNC_native_ws = true;
} else {
VNC_native_ws = false;
console.log("Loading web-socket-js flash bridge");
var extra = "<script src='include/web-socket-js/swfobject.js'><\/script>";
extra += "<script src='include/web-socket-js/FABridge.js'><\/script>";
extra += "<script src='include/web-socket-js/web_socket.js'><\/script>";
document.write(extra);
}
window.onload = function() {
console.log("onload");
if (!VNC_native_ws) {
console.log("initializing web-socket-js flash bridge");
WebSocket.__swfLocation = "include/web-socket-js/WebSocketMain.swf";
WebSocket.__initialize();
}
var url = document.location.href;
$D('host').value = (url.match(/host=([^&#]*)/) || ['',''])[1];
$D('port').value = (url.match(/port=([^&#]*)/) || ['',''])[1];
}
</script>
</html>
#!/usr/bin/python
'''
WebSocket server-side load test program. Sends and receives traffic
that has a random payload (length and content) that is checksummed and
given a sequence number. Any errors are reported and counted.
'''
import sys, os, socket, ssl, time, traceback
import random, time
from select import select
sys.path.insert(0,os.path.dirname(__file__) + "/../utils/")
from websocket import WebSocketServer
class WebSocketTest(WebSocketServer):
buffer_size = 65536
max_packet_size = 10000
recv_cnt = 0
send_cnt = 0
def __init__(self, *args, **kwargs):
self.errors = 0
self.delay = kwargs.pop('delay')
print "Prepopulating random array"
self.rand_array = []
for i in range(0, self.max_packet_size):
self.rand_array.append(random.randint(0, 9))
WebSocketServer.__init__(self, *args, **kwargs)
def new_client(self, client):
self.send_cnt = 0
self.recv_cnt = 0
try:
self.responder(client)
except:
print "accumulated errors:", self.errors
self.errors = 0
raise
def responder(self, client):
cqueue = []
cpartial = ""
socks = [client]
last_send = time.time() * 1000
while True:
ins, outs, excepts = select(socks, socks, socks, 1)
if excepts: raise Exception("Socket exception")
if client in ins:
buf = client.recv(self.buffer_size)
if len(buf) == 0:
raise self.EClose("Client closed")
#print "Client recv: %s (%d)" % (repr(buf[1:-1]), len(buf))
if buf[-1] == '\xff':
if cpartial:
err = self.check(cpartial + buf)
cpartial = ""
else:
err = self.check(buf)
if err:
self.traffic("}")
self.errors = self.errors + 1
print err
else:
self.traffic(">")
else:
self.traffic(".>")
cpartial = cpartial + buf
now = time.time() * 1000
if client in outs and now > (last_send + self.delay):
last_send = now
#print "Client send: %s" % repr(cqueue[0])
client.send(self.generate())
self.traffic("<")
def generate(self):
length = random.randint(10, self.max_packet_size)
numlist = self.rand_array[self.max_packet_size-length:]
# Error in length
#numlist.append(5)
chksum = sum(numlist)
# Error in checksum
#numlist[0] = 5
nums = "".join( [str(n) for n in numlist] )
data = "^%d:%d:%d:%s$" % (self.send_cnt, length, chksum, nums)
self.send_cnt += 1
return WebSocketServer.encode(data)
def check(self, buf):
try:
data_list = WebSocketServer.decode(buf)
except:
print "\n<BOF>" + repr(buf) + "<EOF>"
return "Failed to decode"
err = ""
for data in data_list:
if data.count('$') > 1:
raise Exception("Multiple parts within single packet")
if len(data) == 0:
self.traffic("_")
continue
if data[0] != "^":
err += "buf did not start with '^'\n"
continue
try:
cnt, length, chksum, nums = data[1:-1].split(':')
cnt = int(cnt)
length = int(length)
chksum = int(chksum)
except:
print "\n<BOF>" + repr(data) + "<EOF>"
err += "Invalid data format\n"
continue
if self.recv_cnt != cnt:
err += "Expected count %d but got %d\n" % (self.recv_cnt, cnt)
self.recv_cnt = cnt + 1
continue
self.recv_cnt += 1
if len(nums) != length:
err += "Expected length %d but got %d\n" % (length, len(nums))
continue
inv = nums.translate(None, "0123456789")
if inv:
err += "Invalid characters found: %s\n" % inv
continue
real_chksum = 0
for num in nums:
real_chksum += int(num)
if real_chksum != chksum:
err += "Expected checksum %d but real chksum is %d\n" % (chksum, real_chksum)
return err
if __name__ == '__main__':
try:
if len(sys.argv) < 2: raise
listen_port = int(sys.argv[1])
if len(sys.argv) == 3:
delay = int(sys.argv[2])
else:
delay = 10
except:
print "Usage: %s <listen_port> [delay_ms]" % sys.argv[0]
sys.exit(1)
server = WebSocketTest(
listen_port=listen_port,
verbose=True,
cert='self.pem',
web='.',
delay=delay)
server.start_server()
#!/bin/bash
usage() {
echo "Usage: $(basename $0) PORT CMDLINE"
echo
echo " PORT Port to wrap with WebSockets support"
echo " CMDLINE Command line to wrap"
exit 2
}
# Parameter defaults
mydir=$(readlink -f $(dirname ${0}))
# Process parameters
#while [ "${1}" != "${1#-}" ]; do
# param=$1; shift
#done
export WSWRAP_PORT="${1}"; shift
LD_PRELOAD=${mydir}/wswrapper.so "${@}"
/*
* wswrap/wswrapper: Add WebSockets support to any service.
* Copyright 2010 Joel Martin
* Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
*
* 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
* - 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.
*
* **********************************************************************
* 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) {
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;
}
#ifdef DO_TRACE
TRACE(">> _WS_select(%d, %d, _, _, _, _)\n", mode, nfds);
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 */
if (timeptr && mode == 1) {
memcpy(timeptr, &savetv, sizeof(savetv));
}
#ifdef DO_TRACE
TRACE("<< _WS_select(%d, %d, _, _, _, _) ret %d, errno %d\n",
mode, nfds, ret, errno);
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);
}
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");
TRACE(">> dup2(%d, %d) called\n", oldfd, newfd);
ret = (int) func(oldfd, newfd);
if ((! _WS_connections[oldfd]) && (! _WS_connections[newfd])) {
return ret;
}
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);
}
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++;
TRACE("<< dup2(%d, %d) ret %d\n", oldfd, newfd, ret);
return ret;
}
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;
}
/*
* wswrap/wswrapper: Add WebSockets support to any service.
* Copyright 2010 Joel Martin
* Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
*/
#ifdef DO_MSG
#define MSG(...) \
fprintf(stderr, "wswrapper: "); \
fprintf(stderr, __VA_ARGS__);
#else
#define MSG(...)
#endif
#ifdef DO_DEBUG
#define DEBUG(...) \
if (DO_DEBUG) { \
fprintf(stderr, "wswrapper: "); \
fprintf(stderr, __VA_ARGS__); \
}
#else
#define DEBUG(...)
#endif
#ifdef DO_TRACE
#define TRACE(...) \
if (DO_TRACE) { \
fprintf(stderr, "wswrapper: "); \
fprintf(stderr, __VA_ARGS__); \
}
#else
#define TRACE(...)
#endif
#define RET_ERROR(eno, ...) \
fprintf(stderr, "wswrapper error: "); \
fprintf(stderr, __VA_ARGS__); \
errno = eno; \
return -1;
const char _WS_response[] = "\
HTTP/1.1 101 Web Socket Protocol Handshake\r\n\
Upgrade: WebSocket\r\n\
Connection: Upgrade\r\n\
%sWebSocket-Origin: %s\r\n\
%sWebSocket-Location: %s://%s%s\r\n\
%sWebSocket-Protocol: sample\r\n\
\r\n%s";
#define WS_BUFSIZE 65536
#define WS_MAX_FDS 1024
/* Buffers and state for each wrapped WebSocket connection */
typedef struct {
char rbuf[WS_BUFSIZE];
char sbuf[WS_BUFSIZE];
int rcarry_cnt;
char rcarry[3];
int newframe;
int refcnt;
} _WS_connection;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment