From 2c027d464bedcf0c4d7792b99515cb3f3c3e67ba Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Wed, 12 Jan 2011 20:31:04 -0600 Subject: [PATCH] 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. --- tests/wsecho.html | 1 - tests/wsencoding.html | 151 ------ tests/wsencoding.py | 87 ---- tests/wstest.html | 1 - tests/wstest.py | 1 - utils/Makefile | 18 +- utils/README.md | 166 +----- utils/VT100.js | 919 -------------------------------- utils/include | 1 - utils/md5.c | 466 ----------------- utils/md5.h | 148 ------ utils/md5_test.c | 3 - utils/websocket.c | 556 -------------------- utils/websocket.h | 47 -- utils/websockify | 308 +++++++++++ utils/wsecho.html | 176 ------- utils/wsecho.py | 90 ---- utils/wsproxy.c | 353 ------------- utils/wsproxy.js | 253 --------- utils/wsproxy.py | 309 +---------- utils/wstelnet.html | 90 ---- utils/wstelnet.js | 333 ------------ utils/wstest.html | 252 --------- utils/wstest.py | 171 ------ utils/wswrap | 22 - utils/wswrapper.c | 1156 ----------------------------------------- utils/wswrapper.h | 64 --- 27 files changed, 318 insertions(+), 5824 deletions(-) delete mode 120000 tests/wsecho.html delete mode 100644 tests/wsencoding.html delete mode 100755 tests/wsencoding.py delete mode 120000 tests/wstest.html delete mode 120000 tests/wstest.py delete mode 100644 utils/VT100.js delete mode 120000 utils/include delete mode 100644 utils/md5.c delete mode 100644 utils/md5.h delete mode 100644 utils/md5_test.c delete mode 100644 utils/websocket.c delete mode 100644 utils/websocket.h create mode 100755 utils/websockify delete mode 100644 utils/wsecho.html delete mode 100755 utils/wsecho.py delete mode 100644 utils/wsproxy.c delete mode 100644 utils/wsproxy.js mode change 100755 => 120000 utils/wsproxy.py delete mode 100644 utils/wstelnet.html delete mode 100644 utils/wstelnet.js delete mode 100644 utils/wstest.html delete mode 100755 utils/wstest.py delete mode 100755 utils/wswrap delete mode 100644 utils/wswrapper.c delete mode 100644 utils/wswrapper.h diff --git a/tests/wsecho.html b/tests/wsecho.html deleted file mode 120000 index 28c636a..0000000 --- a/tests/wsecho.html +++ /dev/null @@ -1 +0,0 @@ -../utils/wsecho.html \ No newline at end of file diff --git a/tests/wsencoding.html b/tests/wsencoding.html deleted file mode 100644 index 846645e..0000000 --- a/tests/wsencoding.html +++ /dev/null @@ -1,151 +0,0 @@ - - - WebSockets Test - - - - Host:   - Port:   - Encrypt:   -   - -
- Messages:
- - - - - - - - - - - - - - diff --git a/tests/wsencoding.py b/tests/wsencoding.py deleted file mode 100755 index c44b934..0000000 --- a/tests/wsencoding.py +++ /dev/null @@ -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: " - sys.exit(1) - - settings['listen_port'] = listen_port - settings['daemon'] = False - settings['handler'] = responder - start_server() diff --git a/tests/wstest.html b/tests/wstest.html deleted file mode 120000 index 13d6ff0..0000000 --- a/tests/wstest.html +++ /dev/null @@ -1 +0,0 @@ -../utils/wstest.html \ No newline at end of file diff --git a/tests/wstest.py b/tests/wstest.py deleted file mode 120000 index 2c5b2b3..0000000 --- a/tests/wstest.py +++ /dev/null @@ -1 +0,0 @@ -../utils/wstest.py \ No newline at end of file diff --git a/utils/Makefile b/utils/Makefile index 008d45c..7dc1bc4 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -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 diff --git a/utils/README.md b/utils/README.md index 7aeebe6..0abbd0d 100644 --- a/utils/README.md +++ b/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: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProgramLanguageMultiprocessDaemonizeSSL/wssFlash Policy ServerSession RecordWeb ServerProgram Wrap
wsproxy.pypythonyesyesyes 1yesyesyesyes
wsproxyCyesyesyesyesnonono
wsproxy.jsNode (node.js)yesnononononono
- - -* 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). diff --git a/utils/VT100.js b/utils/VT100.js deleted file mode 100644 index 6a325e1..0000000 --- a/utils/VT100.js +++ /dev/null @@ -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 -// -// 2007-08-12 - refresh(): -// - factor out colour code to html_colours_() -// - fix handling of A_REVERSE | A_DIM -// - simplify initial
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
...
  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 ... 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 -//
...
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 = ""; - end_tag = "" + end_tag; - } - if (n_at.mode & VT100.A_STANDOUT) - n_at.mode |= VT100.A_BOLD; - pair = this.html_colours_(n_at); - start_tag += ''; - stuff += start_tag; - end_tag = "" + 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 = "" + stuff + "\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 [{COUNT}A - this.move(this.row_ - Math.max(1, this.csi_parms_[0]), - this.col_); - break; - case 'B': - // Cursor Down [{COUNT}B - this.move(this.row_ + Math.max(1, this.csi_parms_[0]), - this.col_); - break; - case 'C': - // Cursor Forward [{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 [{COUNT}D - this.move(this.row_, - this.col_ - Math.max(1, this.csi_parms_[0])); - break; - case 'f': - case 'H': - // Cursor Home [{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[? - // Expect a number - the reset type - this.csi_parms_ = [ch]; - this.esc_state_ = 6; - break; - case 6: // Reset mode handling, saw [?1 - // Expect a letter - the mode target, example: - // [?1l : cursor key mode = cursor - // [?1h : save current screen, create new empty - // screen and position at 0,0 - // [?5l : White on blk - // XXX: Ignored for now. - //dump("Saw reset mode: [?" + 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"); -} diff --git a/utils/include b/utils/include deleted file mode 120000 index f5030fe..0000000 --- a/utils/include +++ /dev/null @@ -1 +0,0 @@ -../include \ No newline at end of file diff --git a/utils/md5.c b/utils/md5.c deleted file mode 100644 index dcdc368..0000000 --- a/utils/md5.c +++ /dev/null @@ -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 , 1995. */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include - -#if STDC_HEADERS || defined _LIBC -# include -# include -#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 -# 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; -} diff --git a/utils/md5.h b/utils/md5.h deleted file mode 100644 index b48545b..0000000 --- a/utils/md5.h +++ /dev/null @@ -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 - -#if defined HAVE_LIMITS_H || _LIBC -# include -#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 -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 ) 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 */ diff --git a/utils/md5_test.c b/utils/md5_test.c deleted file mode 100644 index 81c0f0a..0000000 --- a/utils/md5_test.c +++ /dev/null @@ -1,3 +0,0 @@ -int main () { - printf("hello world\n"); -} diff --git a/utils/websocket.c b/utils/websocket.c deleted file mode 100644 index f73bb22..0000000 --- a/utils/websocket.c +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include -#include -#include // daemonizing -#include // daemonizing -#include -#include -#include /* 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[] = "\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, "", 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"); - } - -} - diff --git a/utils/websocket.h b/utils/websocket.h deleted file mode 100644 index c428fa5..0000000 --- a/utils/websocket.h +++ /dev/null @@ -1,47 +0,0 @@ -#include - -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__); - diff --git a/utils/websockify b/utils/websockify new file mode 100755 index 0000000..fed4c04 --- /dev/null +++ b/utils/websockify @@ -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() diff --git a/utils/wsecho.html b/utils/wsecho.html deleted file mode 100644 index 9e3c6d6..0000000 --- a/utils/wsecho.html +++ /dev/null @@ -1,176 +0,0 @@ - - - - WebSockets Echo Test - - - - - - - - - - - - Host:   - Port:   - Encrypt:   -   - - -
- Log:
- - - - - - - diff --git a/utils/wsecho.py b/utils/wsecho.py deleted file mode 100755 index 15e2ef7..0000000 --- a/utils/wsecho.py +++ /dev/null @@ -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 " % sys.argv[0] - sys.exit(1) - - server = WebSocketEcho( - listen_port=listen_port, - verbose=True, - cert='self.pem', - web='.') - server.start_server() - diff --git a/utils/wsproxy.c b/utils/wsproxy.c deleted file mode 100644 index 42bb45e..0000000 --- a/utils/wsproxy.c +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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); -} diff --git a/utils/wsproxy.js b/utils/wsproxy.js deleted file mode 100644 index c85c506..0000000 --- a/utils/wsproxy.js +++ /dev/null @@ -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); diff --git a/utils/wsproxy.py b/utils/wsproxy.py deleted file mode 100755 index fed4c04..0000000 --- a/utils/wsproxy.py +++ /dev/null @@ -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() diff --git a/utils/wsproxy.py b/utils/wsproxy.py new file mode 120000 index 0000000..05b5af4 --- /dev/null +++ b/utils/wsproxy.py @@ -0,0 +1 @@ +websockify \ No newline at end of file diff --git a/utils/wstelnet.html b/utils/wstelnet.html deleted file mode 100644 index 7380248..0000000 --- a/utils/wstelnet.html +++ /dev/null @@ -1,90 +0,0 @@ - - - - Telnet Client using WebSockets - - - - - - - - - - - - - - - Host:   - Port:   - Encrypt:   -   - -

- -

-
-        
-
-    
-
-
diff --git a/utils/wstelnet.js b/utils/wstelnet.js
deleted file mode 100644
index 2f4ab0e..0000000
--- a/utils/wstelnet.js
+++ /dev/null
@@ -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()
diff --git a/utils/wstest.html b/utils/wstest.html
deleted file mode 100644
index c56c4c7..0000000
--- a/utils/wstest.html
+++ /dev/null
@@ -1,252 +0,0 @@
-
-
-    
-        WebSockets Test
-        
-        
-         
-        
-        
-
-
-    
-
-    
-
-        Host:  
-        Port:  
-        Encrypt:  
-        Send Delay (ms):  
-         
-
-        

- - - - - - - - - - - -
Packets sent:
0
Good Packets Received:
0
Errors (Bad Packets Received:)
0
- -
- Errors:
- - - - - - - diff --git a/utils/wstest.py b/utils/wstest.py deleted file mode 100755 index 9442b04..0000000 --- a/utils/wstest.py +++ /dev/null @@ -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" + repr(buf) + "" - 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" + repr(data) + "" - 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 [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() diff --git a/utils/wswrap b/utils/wswrap deleted file mode 100755 index 565d8ed..0000000 --- a/utils/wswrap +++ /dev/null @@ -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 "${@}" - diff --git a/utils/wswrapper.c b/utils/wswrapper.c deleted file mode 100644 index bd4e6f0..0000000 --- a/utils/wswrapper.c +++ /dev/null @@ -1,1156 +0,0 @@ -/* - * wswrap/wswrapper: Add WebSockets support to any service. - * Copyright 2010 Joel Martin - * Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3) - * - * wswrapper is an LD_PRELOAD library that converts a TCP listen socket of an - * existing program to a be a WebSockets socket. The `wswrap` script can be - * used to easily launch a program using wswrapper. Here is an example of - * using wswrapper with vncserver. wswrapper will convert the socket listening - * on port 5901 to be a WebSockets port: - * - * cd noVNC/utils - * ./wswrap 5901 vncserver -geometry 640x480 :1 - * - * This is tricky a subtle process so there are some serious limitations: - * - multi-threaded programs may not work - * - programs that fork may behave in strange and mysterious ways (such as - * fork bombing your system) - * - programs using ppoll or epoll will not work correctly - * - doesn't support fopencookie, streams, putc, etc. - * - * ********************************************************************** - * WARNING: - * Due to the above limitations, this code should be considered an experiment - * only. Consider using the program wrap mode of wsproxy.py instead. - * ********************************************************************** - */ - -#define DO_MSG 1 -#define DO_DEBUG 1 -#define DO_TRACE 1 - -#include -#include - -#define __USE_GNU 1 // Pull in RTLD_NEXT -#include - -#include -#include -#include -#include -#include -#include /* base64 encode/decode */ -#include -#include "md5.h" -#include "wswrapper.h" - -/* - * If WSWRAP_PORT environment variable is set then listen to the bind fd that - * matches WSWRAP_PORT - */ -int _WS_listen_fd = -1; -int _WS_nfds = 0; -int _WS_fds[WS_MAX_FDS]; -_WS_connection *_WS_connections[65536]; - - -/* - * Utillity routines - */ - -/* - * Subtract the `struct timeval' values X and Y, storing the - * result in RESULT. If TS is set then RESULT and X are really - * type-cast `struct timespec` so scale them appropriately. - * Return 1 if the difference is negative or 0, otherwise 0. - */ -int _WS_subtract_time (result, x, y, ts) - struct timeval *result, *x, *y; -{ - int scale = ts ? 1000 : 1; - /* Perform the carry for the later subtraction by updating y. */ - if ((x->tv_usec / scale) < y->tv_usec) { - int sec = (y->tv_usec - (x->tv_usec / scale)) / 1000000 + 1; - y->tv_usec -= 1000000 * sec; - y->tv_sec += sec; - } - if ((x->tv_usec / scale) - y->tv_usec > 1000000) { - int sec = ((x->tv_usec / scale) - y->tv_usec) / 1000000; - y->tv_usec += 1000000 * sec; - y->tv_sec -= sec; - } - - /* Compute the time remaining to wait. - * tv_usec is certainly positive. */ - result->tv_sec = x->tv_sec - y->tv_sec; - result->tv_usec = x->tv_usec - (y->tv_usec * scale); - - /* Return 1 if result is negative or 0. */ - return x->tv_sec <= y->tv_sec; -} - -int _WS_alloc(int fd) { - if (_WS_connections[fd]) { - RET_ERROR(ENOMEM, "Memory already allocated for fd %d\n", fd); - } - if (! (_WS_connections[fd] = malloc(sizeof(_WS_connection)))) { - RET_ERROR(ENOMEM, "Could not allocate interposer memory\n"); - } - _WS_connections[fd]->rcarry_cnt = 0; - _WS_connections[fd]->rcarry[0] = '\0'; - _WS_connections[fd]->newframe = 1; - _WS_connections[fd]->refcnt = 1; - - /* Add to search list for select/pselect */ - _WS_fds[_WS_nfds] = fd; - _WS_nfds++; - - return 0; -} - -int _WS_free(int fd) { - int i; - _WS_connection * wsptr; - wsptr = _WS_connections[fd]; - if (wsptr) { - TRACE(">> _WS_free(%d)\n", fd); - - wsptr->refcnt--; - if (wsptr->refcnt <= 0) { - free(wsptr); - DEBUG("freed memory for fd %d\n", fd); - } - _WS_connections[fd] = NULL; - - /* Remove from the search list for select/pselect */ - for (i = 0; i < _WS_nfds; i++) { - if (_WS_fds[i] == fd) { - break; - } - } - if (_WS_nfds - i - 1 > 0) { - memmove(_WS_fds + i, _WS_fds + i + 1, _WS_nfds - i - 1); - } - _WS_nfds--; - - MSG("finished interposing on fd %d\n", fd); - TRACE("<< _WS_free(%d)\n", fd); - } -} - - -/* - * WebSocket handshake routines - */ - -/* For WebSockets v76, use key1, key2 and key3 to generate md5 hash */ -int _WS_gen_md5(char *key1, char *key2, char *key3, char *target) { - unsigned int i, spaces1 = 0, spaces2 = 0; - unsigned long num1 = 0, num2 = 0; - unsigned char buf[17]; - - /* Parse number 1 from key 1 */ - for (i=0; i < strlen(key1); i++) { - if (key1[i] == ' ') { - spaces1 += 1; - } - if ((key1[i] >= 48) && (key1[i] <= 57)) { - num1 = num1 * 10 + (key1[i] - 48); - } - } - num1 = num1 / spaces1; - - /* Parse number 2 from key 2 */ - for (i=0; i < strlen(key2); i++) { - if (key2[i] == ' ') { - spaces2 += 1; - } - if ((key2[i] >= 48) && (key2[i] <= 57)) { - num2 = num2 * 10 + (key2[i] - 48); - } - } - num2 = num2 / spaces2; - - /* Pack it big-endian as the first 8 bytes */ - buf[0] = (num1 & 0xff000000) >> 24; - buf[1] = (num1 & 0xff0000) >> 16; - buf[2] = (num1 & 0xff00) >> 8; - buf[3] = num1 & 0xff; - - buf[4] = (num2 & 0xff000000) >> 24; - buf[5] = (num2 & 0xff0000) >> 16; - buf[6] = (num2 & 0xff00) >> 8; - buf[7] = num2 & 0xff; - - /* Add key 3 as the last 8 bytes */ - strncpy(buf+8, key3, 8); - buf[16] = '\0'; - - /* md5 hash all 16 bytes to generate 16 byte target */ - md5_buffer(buf, 16, target); - target[16] = '\0'; - - return 1; -} - -/* Do v75 and v76 handshake on WebSocket connection */ -int _WS_handshake(int sockfd) -{ - int sz = 0, len, idx; - int ret = -1, save_errno = EPROTO; - char *last, *start, *end; - long flags; - char handshake[4096], response[4096], - path[1024], prefix[5] = "", scheme[10] = "ws", host[1024], - origin[1024], key1[100], key2[100], key3[9], chksum[17]; - - static void * (*rfunc)(), * (*wfunc)(); - if (!rfunc) rfunc = (void *(*)()) dlsym(RTLD_NEXT, "recv"); - if (!wfunc) wfunc = (void *(*)()) dlsym(RTLD_NEXT, "send"); - DEBUG("_WS_handshake starting\n"); - - /* Disable NONBLOCK if set */ - flags = fcntl(sockfd, F_GETFL, 0); - if (flags & O_NONBLOCK) { - fcntl(sockfd, F_SETFL, flags^O_NONBLOCK); - } - - while (1) { - len = (int) rfunc(sockfd, handshake+sz, 4095, 0); - if (len < 1) { - ret = len; - save_errno = errno; - break; - } - sz += len; - handshake[sz] = '\x00'; - if (sz < 4) { - // Not enough yet - continue; - } - if (strstr(handshake, "GET ") != handshake) { - MSG("Got non-WebSockets client connection\n"); - break; - } - last = strstr(handshake, "\r\n\r\n"); - if (! last) { - continue; - } - if (! strstr(handshake, "Upgrade: WebSocket\r\n")) { - MSG("Invalid WebSockets handshake\n"); - break; - } - - /* Now parse out the data elements */ - start = handshake+4; - end = strstr(start, " HTTP/1.1"); - if (!end) { break; } - snprintf(path, end-start+1, "%s", start); - - start = strstr(handshake, "\r\nHost: "); - if (!start) { break; } - start += 8; - end = strstr(start, "\r\n"); - snprintf(host, end-start+1, "%s", start); - - start = strstr(handshake, "\r\nOrigin: "); - if (!start) { break; } - start += 10; - end = strstr(start, "\r\n"); - snprintf(origin, end-start+1, "%s", start); - - start = strstr(handshake, "\r\n\r\n") + 4; - if (strlen(start) == 8) { - sprintf(prefix, "Sec-"); - - snprintf(key3, 8+1, "%s", start); - - start = strstr(handshake, "\r\nSec-WebSocket-Key1: "); - if (!start) { break; } - start += 22; - end = strstr(start, "\r\n"); - snprintf(key1, end-start+1, "%s", start); - - start = strstr(handshake, "\r\nSec-WebSocket-Key2: "); - if (!start) { break; } - start += 22; - end = strstr(start, "\r\n"); - snprintf(key2, end-start+1, "%s", start); - - _WS_gen_md5(key1, key2, key3, chksum); - - //DEBUG("Got handshake (v76): %s\n", handshake); - MSG("New WebSockets client (v76)\n"); - - } else { - sprintf(prefix, ""); - sprintf(key1, ""); - sprintf(key2, ""); - sprintf(key3, ""); - sprintf(chksum, ""); - - //DEBUG("Got handshake (v75): %s\n", handshake); - MSG("New WebSockets client (v75)\n"); - } - sprintf(response, _WS_response, prefix, origin, prefix, scheme, - host, path, prefix, chksum); - //DEBUG("Handshake response: %s\n", response); - wfunc(sockfd, response, strlen(response), 0); - save_errno = 0; - ret = 0; - break; - } - - /* Re-enable NONBLOCK if it was set */ - if (flags & O_NONBLOCK) { - fcntl(sockfd, F_SETFL, flags); - } - errno = save_errno; - return ret; -} - -/* - * Strip empty WebSockets frames and return a positive value if there is - * enough data to base64 decode (a 4 byte chunk). If nonblock is not set then - * it will block until there is enough data (or until an error occurs). - */ -ssize_t _WS_ready(int sockfd, int nonblock) -{ - _WS_connection *ws = _WS_connections[sockfd]; - char buf[6]; - int count, len, flags, i; - static void * (*rfunc)(); - if (!rfunc) rfunc = (void *(*)()) dlsym(RTLD_NEXT, "recv"); - - TRACE(">> _WS_ready(%d, %d)\n", sockfd, nonblock); - - count = 4 + ws->newframe; - flags = MSG_PEEK; - if (nonblock) { - flags |= MSG_DONTWAIT; - } - while (1) { - len = (int) rfunc(sockfd, buf, count, flags); - if (len < 1) { - TRACE("<< _WS_ready(%d, %d) len %d, errno: %d\n", - sockfd, nonblock, len, errno); - return len; - } - if (len >= 2 && buf[0] == '\x00' && buf[1] == '\xff') { - /* Strip emtpy frame */ - DEBUG("_WS_ready(%d, %d), strip empty\n", sockfd, nonblock); - len = (int) rfunc(sockfd, buf, 2, 0); - if (len < 2) { - MSG("Failed to strip empty frame headers\n"); - TRACE("<< _WS_ready: failed to strip empty frame headers\n"); - return len; - } else if (len == 2 && nonblock) { - errno = EAGAIN; - TRACE("<< _WS_ready(%d, %d), len == 2, EAGAIN\n", - sockfd, nonblock); - return -1; - } - continue; - } - if (len < count) { - if (nonblock) { - errno = EAGAIN; - TRACE("<< _WS_ready(%d, %d), len < count, EAGAIN\n", - sockfd, nonblock); - return -1; - } else { - fprintf(stderr, "_WS_ready(%d, %d), loop: len %d, buf:", - sockfd, nonblock, len, (unsigned char) buf[0]); - for (i = 0; i < len; i++) { - fprintf(stderr, "%d", (unsigned char) buf[i]); - } - fprintf(stderr, "\n"); - - continue; - } - } - TRACE("<< _WS_ready(%d, %d) len: %d\n", sockfd, nonblock, len); - return len; - } -} - -/* - * WebSockets recv/read interposer routine - */ -ssize_t _WS_recv(int recvf, int sockfd, const void *buf, - size_t len, int flags) -{ - _WS_connection *ws = _WS_connections[sockfd]; - int rawcount, deccount, left, striplen, decodelen, ready; - ssize_t retlen, rawlen; - int sockflags; - int i; - char *fstart, *fend, *cstart; - - static void * (*rfunc)(), * (*rfunc2)(); - if (!rfunc) rfunc = (void *(*)()) dlsym(RTLD_NEXT, "recv"); - if (!rfunc2) rfunc2 = (void *(*)()) dlsym(RTLD_NEXT, "read"); - - if (! ws) { - // Not our file descriptor, just pass through - if (recvf) { - return (ssize_t) rfunc(sockfd, buf, len, flags); - } else { - return (ssize_t) rfunc2(sockfd, buf, len); - } - } - TRACE(">> _WS_recv(%d)\n", sockfd); - - if (len == 0) { - TRACE("<< _WS_recv(%d) len == 0\n", sockfd); - return 0; - } - - sockflags = fcntl(sockfd, F_GETFL, 0); - if (sockflags & O_NONBLOCK) { - TRACE("_WS_recv(%d, _, %d) with O_NONBLOCK\n", sockfd, len); - } else { - TRACE("_WS_recv(%d, _, %d) without O_NONBLOCK\n", sockfd, len); - } - - left = len; - retlen = 0; - - /* first copy in carry-over bytes from previous recv/read */ - if (ws->rcarry_cnt) { - if (ws->rcarry_cnt == 1) { - DEBUG("Using carry byte: %u (", ws->rcarry[0]); - } else if (ws->rcarry_cnt == 2) { - DEBUG("Using carry bytes: %u,%u (", ws->rcarry[0], - ws->rcarry[1]); - } else { - RET_ERROR(EIO, "Too many carry-over bytes\n"); - } - if (len <= ws->rcarry_cnt) { - DEBUG("final)\n"); - memcpy((char *) buf, ws->rcarry, len); - ws->rcarry_cnt -= len; - return len; - } else { - DEBUG("prepending)\n"); - memcpy((char *) buf, ws->rcarry, ws->rcarry_cnt); - retlen += ws->rcarry_cnt; - left -= ws->rcarry_cnt; - ws->rcarry_cnt = 0; - } - } - - /* Determine the number of base64 encoded bytes needed */ - rawcount = (left * 4) / 3 + 3; - rawcount -= rawcount%4; - - if (rawcount > WS_BUFSIZE - 1) { - RET_ERROR(ENOMEM, "recv of %d bytes is larger than buffer\n", rawcount); - } - - ready = _WS_ready(sockfd, 0); - if (ready < 1) { - if (retlen) { - /* We had some carry over, don't error until next call */ - errno = 0; - } else { - retlen = ready; - } - TRACE("<< _WS_recv(%d, _, %d) retlen %d\n", sockfd, len, retlen); - return retlen; - } - - /* We have enough data to return something */ - - /* Peek at everything available */ - rawlen = (ssize_t) rfunc(sockfd, ws->rbuf, WS_BUFSIZE-1, - flags | MSG_PEEK); - if (rawlen <= 0) { - RET_ERROR(EPROTO, "Socket was ready but then had failure"); - } - fstart = ws->rbuf; - fstart[rawlen] = '\x00'; - - - if (ws->newframe) { - if (fstart[0] != '\x00') { - RET_ERROR(EPROTO, "Missing frame start\n"); - } - fstart++; - rawlen--; - ws->newframe = 0; - } - - fend = memchr(fstart, '\xff', rawlen); - - if (fend) { - ws->newframe = 1; - if ((fend - fstart) % 4) { - RET_ERROR(EPROTO, "Frame length is not multiple of 4\n"); - } - } else { - fend = fstart + rawlen - (rawlen % 4); - if (fend - fstart < 4) { - RET_ERROR(EPROTO, "Frame too short\n"); - } - } - - /* Determine amount to consume */ - if (rawcount < fend - fstart) { - ws->newframe = 0; - deccount = rawcount; - } else { - deccount = fend - fstart; - } - - /* Now consume what was processed (if not MSG_PEEK) */ - if (flags & MSG_PEEK) { - DEBUG("_WS_recv(%d, _, %d) MSG_PEEK, not consuming\n", sockfd, len); - } else { - rfunc(sockfd, ws->rbuf, fstart - ws->rbuf + deccount + ws->newframe, flags); - } - - fstart[deccount] = '\x00'; // base64 terminator - - /* Do base64 decode into the return buffer */ - decodelen = b64_pton(fstart, (char *) buf + retlen, deccount); - if (decodelen <= 0) { - RET_ERROR(EPROTO, "Base64 decode error\n"); - } - - /* Calculate return length and carry-over */ - if (decodelen <= left) { - retlen += decodelen; - } else { - retlen += left; - - if (! (flags & MSG_PEEK)) { - /* Add anything left over to the carry-over */ - ws->rcarry_cnt = decodelen - left; - if (ws->rcarry_cnt > 2) { - RET_ERROR(EPROTO, "Got too much base64 data\n"); - } - memcpy(ws->rcarry, buf + retlen, ws->rcarry_cnt); - if (ws->rcarry_cnt == 1) { - DEBUG("Saving carry byte: %u\n", ws->rcarry[0]); - } else if (ws->rcarry_cnt == 2) { - DEBUG("Saving carry bytes: %u,%u\n", ws->rcarry[0], - ws->rcarry[1]); - } else { - RET_ERRO(EPROTO, "Too many carry bytes!\n"); - } - } - } - ((char *) buf)[retlen] = '\x00'; - - TRACE("<< _WS_recv(%d) retlen %d\n", sockfd, retlen); - return retlen; -} - -/* - * WebSockets send and write interposer routine - */ -ssize_t _WS_send(int sendf, int sockfd, const void *buf, - size_t len, int flags) -{ - _WS_connection *ws = _WS_connections[sockfd]; - int rawlen, enclen, rlen, over, left, clen, retlen, dbufsize; - int sockflags; - char * target; - int i; - static void * (*sfunc)(), * (*sfunc2)(); - if (!sfunc) sfunc = (void *(*)()) dlsym(RTLD_NEXT, "send"); - if (!sfunc2) sfunc2 = (void *(*)()) dlsym(RTLD_NEXT, "write"); - - if (! ws) { - // Not our file descriptor, just pass through - if (sendf) { - return (ssize_t) sfunc(sockfd, buf, len, flags); - } else { - return (ssize_t) sfunc2(sockfd, buf, len); - } - } - TRACE(">> _WS_send(%d, _, %d)\n", sockfd, len); - - sockflags = fcntl(sockfd, F_GETFL, 0); - - dbufsize = (WS_BUFSIZE * 3)/4 - 2; - if (len > dbufsize) { - RET_ERROR(ENOMEM, "send of %d bytes is larger than send buffer\n", len); - } - - /* base64 encode and add frame markers */ - rawlen = 0; - ws->sbuf[rawlen++] = '\x00'; - enclen = b64_ntop(buf, len, ws->sbuf+rawlen, WS_BUFSIZE-rawlen); - if (enclen < 0) { - RET_ERROR(EPROTO, "Base64 encoding error\n"); - } - rawlen += enclen; - ws->sbuf[rawlen++] = '\xff'; - - rlen = (int) sfunc(sockfd, ws->sbuf, rawlen, flags); - - if (rlen <= 0) { - TRACE("<< _WS_send(%d, _, %d) send failed, returning\n", sockfd, len); - return rlen; - } else if (rlen < rawlen) { - /* Spin until we can send a whole base64 chunck and frame end */ - over = (rlen - 1) % 4; - left = (4 - over) % 4 + 1; // left to send - DEBUG("_WS_send: rlen: %d (over: %d, left: %d), rawlen: %d\n", - rlen, over, left, rawlen); - rlen += left; - ws->sbuf[rlen-1] = '\xff'; - i = 0; - do { - i++; - clen = (int) sfunc(sockfd, ws->sbuf + rlen - left, left, flags); - if (clen > 0) { - left -= clen; - } else if (clen == 0) { - MSG("_WS_send: got clen %d\n", clen); - } else if (!(sockflags & O_NONBLOCK)) { - MSG("<< _WS_send: clen %d\n", clen); - return clen; - } - if (i > 1000000) { - RET_ERROR(EIO, "Could not send final part of frame\n"); - } - } while (left > 0); - //DEBUG("_WS_send: spins until finished %d\n", i); - DEBUG("_WS_send: spins until finished %d\n", i); - } - - - /* - * Report back the number of original characters sent, - * not the raw number sent - */ - - /* Adjust for framing */ - retlen = rlen - 2; - - /* Adjust for base64 padding */ - if (ws->sbuf[rlen-1] == '=') { retlen --; } - if (ws->sbuf[rlen-2] == '=') { retlen --; } - - /* Scale return value for base64 encoding size */ - retlen = (retlen*3)/4; - - TRACE(">> _WS_send(%d, _, %d) retlen %d\n", sockfd, len, retlen); - return (ssize_t) retlen; -} - -/* - * Interpose select/pselect/poll. - * - * WebSocket descriptors are not ready until we have received a frame start - * ('\x00') and at least 4 bytes of base64 encoded data. In addition we may - * have carry-over data from the last 4 bytes of base64 data in which case the - * WebSockets socket is ready even though there might not be data in the raw - * socket itself. - */ - -/* Interpose on select (mode==0) and pselect (mode==1) */ -int _WS_select(int mode, int nfds, fd_set *readfds, - fd_set *writefds, fd_set *exceptfds, - void *timeptr, const sigset_t *sigmask) -{ - _WS_connection *ws; - fd_set carryfds, savefds; - /* Assumes timeptr is two longs whether timeval or timespec */ - struct timeval savetv, starttv, nowtv, difftv; - int carrycnt = 0, less = 0; - int ret, i, ready, fd; - static void * (*func0)(), * (*func1)(); - if (!func0) func0 = (void *(*)()) dlsym(RTLD_NEXT, "select"); - if (!func1) func1 = (void *(*)()) dlsym(RTLD_NEXT, "pselect"); - - if ((_WS_listen_fd == -1) || (_WS_nfds == 0)) { - if (mode == 0) { - ret = (int) func0(nfds, readfds, writefds, exceptfds, - timeptr); - } else if (mode == 1) { - ret = (int) func1(nfds, readfds, writefds, exceptfds, - timeptr, sigmask); - } - return ret; - } - -#ifdef DO_TRACE - TRACE(">> _WS_select(%d, %d, _, _, _, _)\n", mode, nfds); - for (i = 0; i < _WS_nfds; i++) { - fd = _WS_fds[i]; - if (readfds && (FD_ISSET(fd, readfds))) { - TRACE(" WS %d is in readfds\n", fd, nfds); - } - if (writefds && (FD_ISSET(fd, writefds))) { - TRACE(" WS %d is in writefds\n", fd, nfds); - } - if (exceptfds && (FD_ISSET(fd, exceptfds))) { - TRACE(" WS %d is in exceptfds\n", fd, nfds); - } - } -#endif - if (timeptr) { - memcpy(&savetv, timeptr, sizeof(savetv)); - gettimeofday(&starttv, NULL); - } - - /* If we have carry-over return it right away */ - FD_ZERO(&carryfds); - if (readfds) { - memcpy(&savefds, readfds, sizeof(savefds)); - for (i = 0; i < _WS_nfds; i++) { - fd = _WS_fds[i]; - ws = _WS_connections[fd]; - if ((ws->rcarry_cnt) && (FD_ISSET(fd, readfds))) { - FD_SET(fd, &carryfds); - carrycnt++; - } - } - } - if (carrycnt) { - if (writefds) { - FD_ZERO(writefds); - } - if (exceptfds) { - FD_ZERO(writefds); - } - memcpy(readfds, &carryfds, sizeof(carryfds)); - TRACE("<< _WS_select(%d, %d, _, _, _, _) carrycnt %d\n", - mode, nfds, carrycnt); - return carrycnt; - } - - do { - if (timeptr) { - TRACE(" _WS_select tv/ts: %ld:%ld\n", - ((struct timeval *) timeptr)->tv_sec, - ((struct timeval *) timeptr)->tv_usec); - } - if (mode == 0) { - ret = (int) func0(nfds, readfds, writefds, exceptfds, - timeptr); - } else if (mode == 1) { - ret = (int) func1(nfds, readfds, writefds, exceptfds, - timeptr, sigmask); - } - if (! readfds) { - break; - } - if (ret <= 0) { - break; - } - - for (i = 0; i < _WS_nfds; i++) { - fd = _WS_fds[i]; - ws = _WS_connections[fd]; - if (FD_ISSET(fd, readfds)) { - ready = _WS_ready(fd, 1); - if (ready == 0) { - /* 0 means EOF which is also a ready condition */ - DEBUG("_WS_select: detected %d is closed\n", fd); - } else if (ready < 0) { - DEBUG("_WS_select: FD_CLR(%d,readfds) - not enough to decode\n", fd); - FD_CLR(fd, readfds); - ret--; - } - } - } - errno = 0; /* errno could be set by _WS_ready */ - - if (ret == 0) { - /* - * If all the ready readfds were WebSockets, but none of - * them were really ready (empty frames) then we select again. But - * first restore original values less passage of time. - */ - if (! timeptr) { - /* No timeout, spin forever */ - continue; - } - memcpy(readfds, &savefds, sizeof(savefds)); - gettimeofday(&nowtv, NULL); - /* Amount of time that has passed */ - _WS_subtract_time(&difftv, &nowtv, &starttv, 0); - /* Subtract from original timout */ - less = _WS_subtract_time((struct timeval *) timeptr, - &savetv, &difftv, mode); - if (less) { - /* Timer has expired */ - TRACE(" _WS_select expired timer\n", mode, nfds); - break; - } - } - } while (ret == 0); - - /* Restore original time value for pselect glibc does */ - if (timeptr && mode == 1) { - memcpy(timeptr, &savetv, sizeof(savetv)); - } - -#ifdef DO_TRACE - TRACE("<< _WS_select(%d, %d, _, _, _, _) ret %d, errno %d\n", - mode, nfds, ret, errno); - for (i = 0; i < _WS_nfds; i++) { - fd = _WS_fds[i]; - if (readfds && (FD_ISSET(fd, readfds))) { - TRACE(" WS %d is set in readfds\n", fd, nfds); - } - if (writefds && (FD_ISSET(fd, writefds))) { - TRACE(" WS %d is set in writefds\n", fd, nfds); - } - if (exceptfds && (FD_ISSET(fd, exceptfds))) { - TRACE(" WS %d is set in exceptfds\n", fd, nfds); - } - } -#endif - return ret; -} - -/* Interpose on poll (mode==0) and ppoll (mode==1) */ -int _WS_poll(int mode, struct pollfd *fds, nfds_t nfds, int timeout, - struct timespec *ptimeout, sigset_t *sigmask) -{ - _WS_connection *ws; - int savetimeout; - struct timespec savets; - struct timeval starttv, nowtv, difftv; - struct pollfd *pfd; - int carrycnt = 0, less = 0; - int ret, i, ready, fd; - static void * (*func0)(), * (*func1)(); - if (!func0) func0 = (void *(*)()) dlsym(RTLD_NEXT, "poll"); - if (!func1) func1 = (void *(*)()) dlsym(RTLD_NEXT, "ppoll"); - - if ((_WS_listen_fd == -1) || (_WS_nfds == 0)) { - if (mode == 0) { - ret = (int) func0(fds, nfds, timeout); - } else if (mode == 1) { - ret = (int) func1(fds, nfds, ptimeout, sigmask); - } - return ret; - } - - TRACE(">> _WS_poll(%d, %ld, _, _, _, _)\n", mode, nfds); - if (mode == 0) { - savetimeout = timeout; - } else if (mode == 1) { - memcpy(&savets, ptimeout, sizeof(savets)); - } - gettimeofday(&starttv, NULL); - - do { - TRACE(" _WS_poll(%d, %ld, _, _, _, _) tv/ts: %ld:%ld\n", mode, nfds, - ptimeout->tv_sec, ptimeout->tv_nsec); - - if (mode == 0) { - ret = (int) func0(fds, nfds, timeout); - } else if (mode == 1) { - ret = (int) func1(fds, nfds, ptimeout, sigmask); - } - if (ret <= 0) { - break; - } - - for (i = 0; i < nfds; i++) { - pfd = &fds[i]; - if (! (pfd->events & POLLIN)) { - continue; - } - ws = _WS_connections[pfd->fd]; - if (! ws) { - continue; - } - if (ws->rcarry_cnt) { - if (! (pfd->revents & POLLIN)) { - pfd->revents |= POLLIN; - ret++; - } - } else if (pfd->revents & POLLIN) { - ready = _WS_ready(pfd->fd, 1); - if (ready == 0) { - /* 0 means EOF which is also a ready condition */ - DEBUG("_WS_poll: detected %d is closed\n", fd); - } else if (ready < 0) { - DEBUG("_WS_poll: not enough to decode\n", fd); - pfd->revents -= POLLIN; - ret--; - } - } - } - errno = 0; /* errno could be set by _WS_ready */ - - if (ret == 0) { - /* - * If all the ready readfds were WebSockets, but none of - * them were really ready (empty frames) then we select again. But - * first restore original values less passage of time. - */ - gettimeofday(&nowtv, NULL); - /* Amount of time that has passed */ - _WS_subtract_time(&difftv, &nowtv, &starttv, 0); - if (mode == 0) { - if (timeout < 0) { - /* Negative timeout means infinite */ - continue; - } - timeout -= difftv.tv_sec * 1000 + difftv.tv_usec / 1000; - if (timeout <= 0) { - less = 1; - } - } else if (mode == 1) { - /* Subtract from original timout */ - less = _WS_subtract_time((struct timeval *) ptimeout, - (struct timeval *) &savets, - &difftv, 1); - } - if (less) { - /* Timer has expired */ - TRACE(" _WS_poll expired timer\n", mode, nfds); - break; - } - } - } while (ret == 0); - - /* Restore original time value for pselect glibc does */ - if (mode == 1) { - memcpy(ptimeout, &savets, sizeof(savets)); - } - - TRACE("<< _WS_poll(%d, %ld, _, _, _, _) ret %d, errno %d\n", - mode, nfds, ret, errno); - return ret; -} - - -/* - * Overload (LD_PRELOAD) standard library network routines - */ - -int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) -{ - static void * (*func)(); - struct sockaddr_in * addr_in = (struct sockaddr_in *)addr; - char * WSWRAP_PORT, * end; - int ret, envport, bindport = htons(addr_in->sin_port); - if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "bind"); - TRACE(">> bind(%d, _, %d)\n", sockfd, addrlen); - - ret = (int) func(sockfd, addr, addrlen); - - if (addr_in->sin_family != AF_INET) { - // TODO: handle IPv6 - TRACE("<< bind, ignoring non-IPv4 socket\n"); - return ret; - } - - WSWRAP_PORT = getenv("WSWRAP_PORT"); - if ((! WSWRAP_PORT) || (*WSWRAP_PORT == '\0')) { - // TODO: interpose on all sockets when WSWRAP_PORT not set - TRACE("<< bind, not interposing: WSWRAP_PORT is not set\n"); - return ret; - } - - envport = strtol(WSWRAP_PORT, &end, 10); - if ((envport == 0) || (*end != '\0')) { - TRACE("<< bind, not interposing: WSWRAP_PORT is not a number\n"); - return ret; - } - - if (envport != bindport) { - TRACE("<< bind, not interposing on port: %d (fd %d)\n", bindport, sockfd); - return ret; - } - - _WS_listen_fd = sockfd; - - TRACE("<< bind, listening for WebSockets connections on port: %d (fd %d)\n", envport, sockfd); - return ret; -} - -int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) -{ - int fd, ret, envfd; - static void * (*func)(); - if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "accept"); - TRACE("<< accept(%d, _, _)\n", sockfd); - - fd = (int) func(sockfd, addr, addrlen); - - if (_WS_listen_fd == -1) { - TRACE("<< accept: not interposing\n"); - return fd; - } - - if (_WS_listen_fd != sockfd) { - TRACE("<< accept: not interposing on fd %d\n", sockfd); - return fd; - } - - - if (_WS_connections[fd]) { - RET_ERROR(EINVAL, "already interposing on fd %d\n", fd); - } else { - /* It's a port we're interposing on so allocate memory for it */ - if (_WS_nfds >= WS_MAX_FDS) { - RET_ERROR(ENOMEM, "Too many interposer fds\n"); - } - if (_WS_alloc(fd) < 0) { - return -1; - } - - ret = _WS_handshake(fd); - if (ret < 0) { - _WS_free(fd); - errno = EPROTO; - TRACE("<< accept(%d, _, _): ret %d\n", sockfd, ret); - return ret; - } - MSG("interposing on fd %d (allocated memory)\n", fd); - } - - return fd; -} - -int close(int fd) -{ - static void * (*func)(); - if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "close"); - - TRACE("close(%d) called\n", fd); - - _WS_free(fd); - - return (int) func(fd); -} - - -ssize_t read(int fd, void *buf, size_t count) -{ - TRACE("read(%d, _, %d) called\n", fd, count); - return (ssize_t) _WS_recv(0, fd, buf, count, 0); -} - -ssize_t write(int fd, const void *buf, size_t count) -{ - TRACE("write(%d, _, %d) called\n", fd, count); - return (ssize_t) _WS_send(0, fd, buf, count, 0); -} - -ssize_t recv(int sockfd, void *buf, size_t len, int flags) -{ - TRACE("recv(%d, _, %d, %d) called\n", sockfd, len, flags); - return (ssize_t) _WS_recv(1, sockfd, buf, len, flags); -} - -ssize_t send(int sockfd, const void *buf, size_t len, int flags) -{ - TRACE("send(%d, _, %d, %d) called\n", sockfd, len, flags); - return (ssize_t) _WS_send(1, sockfd, buf, len, flags); -} - -int select(int nfds, fd_set *readfds, fd_set *writefds, - fd_set *exceptfds, struct timeval *timeout) -{ - TRACE("select(%d, _, _, _, _) called\n", nfds); - return _WS_select(0, nfds, readfds, writefds, exceptfds, - (void *) timeout, NULL); -} - -int pselect(int nfds, fd_set *readfds, fd_set *writefds, - fd_set *exceptfds, const struct timespec *timeout, - const sigset_t *sigmask) -{ - TRACE("pselect(%d, _, _, _, _, _) called\n", nfds); - return _WS_select(1, nfds, readfds, writefds, exceptfds, - (void *) timeout, sigmask); -} - -int poll(struct pollfd *fds, nfds_t nfds, int timeout) -{ - TRACE("poll(_, %ld, %d) called\n", nfds, timeout); - return _WS_poll(0, fds, nfds, timeout, NULL, NULL); -} - -int ppoll(struct pollfd *fds, nfds_t nfds, - const struct timespec *timeout, const sigset_t *sigmask) -{ - TRACE("ppoll(_, %ld, _, _) called\n", nfds); - return _WS_poll(0, fds, nfds, 0, (struct timespec *)timeout, - (sigset_t *)sigmask); -} - -int dup(int oldfd) { - int ret; - static void * (*func)(); - if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "dup"); - - TRACE(">> dup(%d) called\n", oldfd); - - ret = (int) func(oldfd); - - TRACE("<< dup(%d) ret %d\n", oldfd, ret); - return ret; -} - -int dup2(int oldfd, int newfd) { - int ret; - static void * (*func)(); - if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "dup2"); - - TRACE(">> dup2(%d, %d) called\n", oldfd, newfd); - - ret = (int) func(oldfd, newfd); - if ((! _WS_connections[oldfd]) && (! _WS_connections[newfd])) { - return ret; - } - - if ((ret < 0) || (oldfd == newfd) || - (_WS_connections[oldfd] == _WS_connections[newfd])) { - TRACE("<< dup2(%d, %d) ret %d\n", oldfd, newfd, ret); - return ret; - } - - /* dup2 behavior is to close newfd if it's open */ - if (_WS_connections[newfd]) { - _WS_free(newfd); - } - - if (! _WS_connections[oldfd]) { - TRACE("<< dup2(%d, %d) ret %d\n", oldfd, newfd, ret); - return ret; - } - - MSG("interposing on duplicated fd %d\n", newfd); - /* oldfd and newfd are now descriptors for the same socket, - * re-use the same context memory area */ - _WS_connections[newfd] = _WS_connections[oldfd]; - _WS_connections[newfd]->refcnt++; - - /* Add to search list for select/pselect */ - _WS_fds[_WS_nfds] = newfd; - _WS_nfds++; - - TRACE("<< dup2(%d, %d) ret %d\n", oldfd, newfd, ret); - return ret; - -} - -int dup3(int oldfd, int newfd, int flags) { - int ret; - static void * (*func)(); - if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "dup3"); - - TRACE(">> dup3(%d, %d, %d) called\n", oldfd, newfd, flags); - - ret = (int) func(oldfd, newfd, flags); - - TRACE("<< dup3(%d, %d, %d) ret %d\n", oldfd, newfd, flags, ret); - return ret; -} - diff --git a/utils/wswrapper.h b/utils/wswrapper.h deleted file mode 100644 index 412b17a..0000000 --- a/utils/wswrapper.h +++ /dev/null @@ -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; - -