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.
This commit is contained in:
parent
aba09b9071
commit
2c027d464b
@ -1 +0,0 @@
|
||||
../utils/wsecho.html
|
@ -1,151 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head><title>WebSockets Test</title></head>
|
||||
|
||||
<body>
|
||||
|
||||
Host: <input id='host' style='width:100'>
|
||||
Port: <input id='port' style='width:50'>
|
||||
Encrypt: <input id='encrypt' type='checkbox'>
|
||||
<input id='connectButton' type='button' value='Start' style='width:100px'
|
||||
onclick="connect();">
|
||||
|
||||
<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>
|
@ -1,87 +0,0 @@
|
||||
#!/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()
|
@ -1 +0,0 @@
|
||||
../utils/wstest.html
|
@ -1 +0,0 @@
|
||||
../utils/wstest.py
|
@ -1,25 +1,11 @@
|
||||
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
|
||||
|
||||
|
166
utils/README.md
166
utils/README.md
@ -1,163 +1,11 @@
|
||||
## WebSockets Proxy
|
||||
|
||||
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).
|
||||
|
||||
### 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`
|
||||
For more detailed description and usage information please refer to
|
||||
the [websockify README](https://github.com/kanaka/websockify/blob/master/README.md).
|
||||
|
||||
|
919
utils/VT100.js
919
utils/VT100.js
@ -1,919 +0,0 @@
|
||||
// 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 /> 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 += '&'; break;
|
||||
case '<':
|
||||
stuff += '<'; break;
|
||||
case '>':
|
||||
stuff += '>'; break;
|
||||
case ' ':
|
||||
//stuff += ' '; 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");
|
||||
}
|
@ -1 +0,0 @@
|
||||
../include
|
466
utils/md5.c
466
utils/md5.c
@ -1,466 +0,0 @@
|
||||
/* 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;
|
||||
}
|
148
utils/md5.h
148
utils/md5.h
@ -1,148 +0,0 @@
|
||||
/* 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 */
|
@ -1,3 +0,0 @@
|
||||
int main () {
|
||||
printf("hello world\n");
|
||||
}
|
@ -1,556 +0,0 @@
|
||||
/*
|
||||
* 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");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,47 +0,0 @@
|
||||
#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__);
|
||||
|
308
utils/websockify
Executable file
308
utils/websockify
Executable file
@ -0,0 +1,308 @@
|
||||
#!/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()
|
@ -1,176 +0,0 @@
|
||||
<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'>
|
||||
Port: <input id='port' style='width:50'>
|
||||
Encrypt: <input id='encrypt' type='checkbox'>
|
||||
<input id='connectButton' type='button' value='Start' style='width:100px'
|
||||
onclick="connect();">
|
||||
|
||||
|
||||
<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>
|
@ -1,90 +0,0 @@
|
||||
#!/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()
|
||||
|
353
utils/wsproxy.c
353
utils/wsproxy.c
@ -1,353 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
253
utils/wsproxy.js
253
utils/wsproxy.js
@ -1,253 +0,0 @@
|
||||
// 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);
|
308
utils/wsproxy.py
308
utils/wsproxy.py
@ -1,308 +0,0 @@
|
||||
#!/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()
|
1
utils/wsproxy.py
Symbolic link
1
utils/wsproxy.py
Symbolic link
@ -0,0 +1 @@
|
||||
websockify
|
@ -1,90 +0,0 @@
|
||||
<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'>
|
||||
Port: <input id='port' style='width:50'>
|
||||
Encrypt: <input id='encrypt' type='checkbox'>
|
||||
<input id='connectButton' type='button' value='Connect' style='width:100px'
|
||||
onclick="connect();">
|
||||
|
||||
<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>
|
@ -1,333 +0,0 @@
|
||||
/*
|
||||
* 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()
|
@ -1,252 +0,0 @@
|
||||
<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'>
|
||||
Port: <input id='port' style='width:50'>
|
||||
Encrypt: <input id='encrypt' type='checkbox'>
|
||||
Send Delay (ms): <input id='sendDelay' style='width:50' value="100">
|
||||
<input id='connectButton' type='button' value='Start' style='width:100px'
|
||||
onclick="connect();">
|
||||
|
||||
<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>
|
171
utils/wstest.py
171
utils/wstest.py
@ -1,171 +0,0 @@
|
||||
#!/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()
|
22
utils/wswrap
22
utils/wswrap
@ -1,22 +0,0 @@
|
||||
#!/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 "${@}"
|
||||
|
1156
utils/wswrapper.c
1156
utils/wswrapper.c
File diff suppressed because it is too large
Load Diff
@ -1,64 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user