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/env 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_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
|