1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
|
#!/usr/bin/ruby
# A WebSocket to TCP socket proxy
# Copyright 2011 Joel Martin
# Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
require 'socket'
$: << "other"
$: << "../other"
require 'websocket'
require 'optparse'
# 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.
class WebSocketProxy < WebSocketServer
@@Traffic_legend = "
Traffic Legend:
} - Client receive
}. - Client receive partial
{ - Target receive
> - Target send
>. - Target send partial
< - Client send
<. - Client send partial
"
def initialize(opts)
vmsg "in WebSocketProxy.initialize"
super(opts)
@target_host = opts["target_host"]
@target_port = opts["target_port"]
end
# Echo back whatever is received
def new_websocket_client(client)
msg "connecting to: %s:%s" % [@target_host, @target_port]
tsock = TCPSocket.open(@target_host, @target_port)
if @verbose then puts @@Traffic_legend end
begin
do_proxy(client, tsock)
rescue
tsock.shutdown(Socket::SHUT_RDWR)
tsock.close
raise
end
end
# Proxy client WebSocket to normal target socket.
def do_proxy(client, target)
cqueue = []
c_pend = 0
tqueue = []
rlist = [client, target]
loop do
wlist = []
if tqueue.length > 0
wlist << target
end
if cqueue.length > 0 || c_pend > 0
wlist << client
end
ins, outs, excepts = IO.select(rlist, wlist, nil, 0.001)
if excepts && excepts.length > 0
raise Exception, "Socket exception"
end
# Send queued client data to the target
if outs && outs.include?(target)
dat = tqueue.shift
sent = target.send(dat, 0)
if sent == dat.length
traffic ">"
else
tqueue.unshift(dat[sent...dat.length])
traffic ".>"
end
end
# Receive target data and queue for the client
if ins && ins.include?(target)
buf = target.recv(@@Buffer_size)
if buf.length == 0
raise EClose, "Target closed"
end
cqueue << buf
traffic "{"
end
# Encode and send queued data to the client
if outs && outs.include?(client)
c_pend = send_frames(cqueue)
cqueue = []
end
# Receive client data, decode it, and send it back
if ins && ins.include?(client)
frames, closed = recv_frames
tqueue += frames
if closed
send_close
raise EClose, closed
end
end
end # loop
end
end
# Parse parameters
opts = {}
parser = OptionParser.new do |o|
o.on('--verbose', '-v') { |b| opts['verbose'] = b }
o.parse!
end
if ARGV.length < 2
puts "Too few arguments"
exit 2
end
# Parse host:port and convert ports to numbers
if ARGV[0].count(":") > 0
opts['listen_host'], _, opts['listen_port'] = ARGV[0].rpartition(':')
else
opts['listen_host'], opts['listen_port'] = nil, ARGV[0]
end
begin
opts['listen_port'] = opts['listen_port'].to_i
rescue
puts "Error parsing listen port"
exit 2
end
if ARGV[1].count(":") > 0
opts['target_host'], _, opts['target_port'] = ARGV[1].rpartition(':')
else
puts "Error parsing target"
exit 2
end
begin
opts['target_port'] = opts['target_port'].to_i
rescue
puts "Error parsing target port"
exit 2
end
puts "Starting server on #{opts['listen_host']}:#{opts['listen_port']}"
server = WebSocketProxy.new(opts)
server.start(100)
server.join
puts "Server has been terminated"
# vim: sw=2
|