aboutsummaryrefslogtreecommitdiff
path: root/third_party/websockify/other/websockify.rb
blob: bdc61f16fd05b8242934b31b57ae2476ead74196 (plain)
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