reverse-tunnel 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,140 @@
1
+ module ReverseTunnel
2
+ class Message
3
+
4
+ def self.type
5
+ name.split("::").last.gsub(/^([A-Z])/) {$1.downcase}.gsub(/([A-Z])/) { "_#{$1.downcase}" }.to_sym
6
+ end
7
+ def type
8
+ self.class.type
9
+ end
10
+
11
+ @@types = [:open_tunnel, :open_session, :data, :ping]
12
+ def self.types
13
+ @@types
14
+ end
15
+
16
+ types.each do |type|
17
+ define_method "#{type}?" do
18
+ self.type == type
19
+ end
20
+ end
21
+
22
+ def self.type_id
23
+ types.index(type)
24
+ end
25
+ def type_id
26
+ self.class.type_id
27
+ end
28
+
29
+ def pack
30
+ [type_id, *payload].to_msgpack
31
+ end
32
+
33
+ def self.create(type)
34
+ type = types.at(type) if Fixnum === type
35
+ const_get(type.to_s.gsub(/(^|_)(.)/) { $2.capitalize }).new
36
+ end
37
+
38
+ class Unpacker
39
+
40
+ attr_reader :unpacker
41
+
42
+ def initialize
43
+ @unpacker = MessagePack::Unpacker.new
44
+ end
45
+
46
+ def feed(data)
47
+ unpacker.feed data
48
+ end
49
+
50
+ include Enumerable
51
+ def each(&block)
52
+ unpacker.each do |data|
53
+ type_id = data.shift
54
+ payload = data
55
+
56
+ Message.create(type_id).tap do |message|
57
+ message.load(payload)
58
+
59
+ yield message
60
+ end
61
+ end
62
+ end
63
+
64
+ end
65
+
66
+ def self.unpack(data)
67
+ Unpacker.new.tap do |packer|
68
+ packer.feed data
69
+ end.first
70
+ end
71
+
72
+ class Data < Message
73
+ attr_accessor :session_id, :data
74
+
75
+ def initialize(session_id = nil, data = nil)
76
+ self.session_id = session_id
77
+ self.data = data
78
+ end
79
+
80
+ def payload
81
+ [session_id, data]
82
+ end
83
+
84
+ def load(payload)
85
+ self.session_id, self.data = payload
86
+ end
87
+ end
88
+
89
+ class OpenSession < Message
90
+ attr_accessor :session_id
91
+
92
+ def initialize(session_id = nil)
93
+ self.session_id = session_id
94
+ end
95
+
96
+ def payload
97
+ [session_id]
98
+ end
99
+
100
+ def load(payload)
101
+ self.session_id = payload.first
102
+ end
103
+
104
+ end
105
+
106
+ class OpenTunnel < Message
107
+ attr_accessor :token
108
+
109
+ def initialize(token = nil)
110
+ self.token = token
111
+ end
112
+
113
+ def payload
114
+ [token]
115
+ end
116
+
117
+ def load(payload)
118
+ self.token = payload.first
119
+ end
120
+ end
121
+
122
+ class Ping < Message
123
+ attr_accessor :sequence_number
124
+
125
+ def initialize(sequence_number = nil)
126
+ self.sequence_number = sequence_number
127
+ end
128
+
129
+ def payload
130
+ [sequence_number]
131
+ end
132
+
133
+ def load(payload)
134
+ self.sequence_number = payload.first
135
+ end
136
+ end
137
+
138
+ end
139
+ end
140
+
@@ -0,0 +1,406 @@
1
+ require 'evma_httpserver'
2
+ require 'json'
3
+
4
+ module ReverseTunnel
5
+ class Server
6
+
7
+ class ApiServer < EM::Connection
8
+ include EM::HttpServer
9
+
10
+ attr_accessor :server
11
+
12
+ def initialize(server)
13
+ @server = server
14
+ end
15
+
16
+ def post_init
17
+ super
18
+ no_environment_strings
19
+ end
20
+
21
+ def process_http_request
22
+ # the http request details are available via the following instance variables:
23
+ # @http_protocol
24
+ # @http_request_method
25
+ # @http_cookie
26
+ # @http_if_none_match
27
+ # @http_content_type
28
+ # @http_path_info
29
+ # @http_request_uri
30
+ # @http_query_string
31
+ # @http_post_content
32
+ # @http_headers
33
+
34
+ ReverseTunnel.logger.debug "Process http request #{@http_request_uri}"
35
+
36
+ response = EM::DelegatedHttpResponse.new(self)
37
+ response.status = 200
38
+ response.content_type 'application/json'
39
+
40
+ begin
41
+
42
+ case @http_request_uri
43
+ when %r{^/tunnels(.json)?$}
44
+ case @http_request_method
45
+ when "GET"
46
+ response.content = server.tunnels.to_json
47
+ when "POST"
48
+ params = @http_post_content ? JSON.parse(@http_post_content) : {}
49
+ tunnel = server.tunnels.create params
50
+ response.content = tunnel.to_json
51
+ end
52
+ when %r{^/tunnels/([0-9A-F]+)(.json)?$}
53
+ tunnel_id = $1
54
+ tunnel = server.tunnels.find(tunnel_id)
55
+
56
+ if tunnel
57
+ case @http_request_method
58
+ when "GET"
59
+ response.content = tunnel.to_json
60
+ when "DELETE"
61
+ tunnel = server.tunnels.destroy(tunnel_id)
62
+ response.content = tunnel.to_json
63
+ end
64
+ end
65
+ else
66
+ end
67
+ rescue => e
68
+ ReverseTunnel.logger.error "Error in http request processing: #{e}"
69
+ response.status = 500
70
+ end
71
+
72
+ if response.content.nil?
73
+ response.status = 404
74
+ end
75
+
76
+ response.send_response
77
+ end
78
+
79
+ end
80
+
81
+ class Tunnel
82
+
83
+ attr_accessor :token, :local_port, :local_host
84
+
85
+ def initialize(attributes)
86
+ attributes.each { |k,v| send "#{k}=", v }
87
+ end
88
+
89
+ def local_host
90
+ @local_host ||= "127.0.0.1"
91
+ end
92
+
93
+ attr_accessor :connection
94
+
95
+ def connection=(connection)
96
+ if @connection and @connection != connection
97
+ @connection.close_connection
98
+ local_connections.each(&:close_connection)
99
+ end
100
+
101
+ @connection = connection
102
+
103
+ if @connection
104
+ open
105
+ end
106
+ end
107
+
108
+ def connection_closed(connection)
109
+ self.connection = nil if self.connection == connection
110
+ end
111
+
112
+ attr_accessor :local_server
113
+
114
+ def close
115
+ if local_server
116
+ ReverseTunnel.logger.info "Close local connections on #{local_port}"
117
+ EventMachine.stop_server local_server
118
+ self.local_server = nil
119
+ end
120
+
121
+ if connection
122
+ ReverseTunnel.logger.info "Close tunnel connection #{token}"
123
+ self.connection.tap do |connection|
124
+ @connection = nil
125
+ connection.close_connection
126
+ end
127
+ end
128
+ end
129
+
130
+ def open
131
+ unless local_server
132
+ ReverseTunnel.logger.info "Listen on #{local_host}:#{local_port} for #{token}"
133
+ self.local_server = EventMachine.start_server local_host, local_port, LocalConnection, self
134
+ end
135
+ rescue => e
136
+ ReverseTunnel.logger.error "Can't listen on #{local_host}:#{local_port} for #{token} : #{e}"
137
+ end
138
+
139
+ def open_session(session_id)
140
+ if connection
141
+ ReverseTunnel.logger.debug "Send open session #{session_id}"
142
+ connection.send_data Message::OpenSession.new(session_id).pack
143
+ end
144
+ end
145
+
146
+ def ping_received(ping)
147
+ ReverseTunnel.logger.debug "Receive ping #{token}/#{ping.sequence_number}"
148
+ connection.send_data Message::Ping.new(ping.sequence_number).pack
149
+ end
150
+
151
+ def send_data(session_id, data)
152
+ if connection
153
+ ReverseTunnel.logger.debug "Send data to local connection #{session_id}"
154
+ connection.send_data Message::Data.new(session_id,data).pack
155
+ end
156
+ end
157
+
158
+ def local_connections
159
+ @local_connections ||= []
160
+ end
161
+
162
+ def receive_data(session_id, data)
163
+ local_connection = local_connections.find { |c| c.session_id == session_id }
164
+ if local_connection
165
+ ReverseTunnel.logger.debug "Send data for local connection #{session_id}"
166
+ local_connection.send_data data
167
+ end
168
+ end
169
+
170
+ def next_session_id
171
+ @next_session_id ||= 0
172
+ @next_session_id += 1
173
+ end
174
+
175
+ def to_json(*args)
176
+ { :token => token, :local_port => local_port }.tap do |attributes|
177
+ attributes[:connection] = connection.to_json if connection
178
+ end.to_json(*args)
179
+ end
180
+
181
+ end
182
+
183
+ class TunnelConnection < EventMachine::Connection
184
+ attr_accessor :server, :created_at
185
+
186
+ def initialize(server)
187
+ @server = server
188
+ end
189
+
190
+ def post_init
191
+ ReverseTunnel.logger.info "New tunnel connection from #{peer}"
192
+ self.created_at = Time.now
193
+
194
+ EventMachine.add_timer(10) do
195
+ unless open? or closed?
196
+ ReverseTunnel.logger.info "Force close of unopened tunnel connection from #{peer}"
197
+ close_connection
198
+ end
199
+ end
200
+ end
201
+
202
+ def message_unpacker
203
+ @message_unpacker ||= Message::Unpacker.new
204
+ end
205
+
206
+ def receive_data(data)
207
+ message_unpacker.feed data
208
+
209
+ message_unpacker.each do |message|
210
+ if message.data?
211
+ tunnel.receive_data message.session_id, message.data
212
+ elsif message.open_tunnel?
213
+ open_tunnel message.token
214
+ elsif message.ping?
215
+ tunnel.ping_received message
216
+ end
217
+ end
218
+ end
219
+
220
+ attr_accessor :tunnel
221
+
222
+ def open?
223
+ !!tunnel
224
+ end
225
+
226
+ def closed?
227
+ @closed ||= false
228
+ end
229
+
230
+ def close_connection(after_writing = false)
231
+ super
232
+ @closed = true
233
+ end
234
+
235
+ def open_tunnel(token)
236
+ self.tunnel = server.tunnels.find token
237
+ if tunnel
238
+ ReverseTunnel.logger.info "Open tunnel #{token}"
239
+ tunnel.connection = self
240
+ else
241
+ ReverseTunnel.logger.warn "Refuse tunnel connection #{token}"
242
+ close_connection
243
+ end
244
+ end
245
+
246
+ def unbind
247
+ tunnel.connection_closed self if tunnel
248
+ end
249
+
250
+ def peer
251
+ @peer ||=
252
+ begin
253
+ port, ip = Socket.unpack_sockaddr_in(get_peername)
254
+ "#{ip}:#{port}"
255
+ end
256
+ end
257
+
258
+ def to_json(*args)
259
+ { :peer => peer, :created_at => created_at }.json
260
+ end
261
+
262
+ end
263
+
264
+ class LocalConnection < EventMachine::Connection
265
+ attr_accessor :tunnel
266
+
267
+ def initialize(tunnel)
268
+ @tunnel = tunnel
269
+ end
270
+
271
+ def post_init
272
+ ReverseTunnel.logger.debug "New local connection"
273
+ tunnel.local_connections << self
274
+ tunnel.open_session(session_id)
275
+ end
276
+
277
+ def receive_data(data)
278
+ ReverseTunnel.logger.debug "Received data in local #{session_id}"
279
+ tunnel.send_data session_id, data
280
+ end
281
+
282
+ def session_id
283
+ @session_id ||= tunnel.next_session_id
284
+ end
285
+
286
+ def unbind
287
+ ReverseTunnel.logger.debug "Close local connection"
288
+ tunnel.local_connections.delete self
289
+ end
290
+
291
+ end
292
+
293
+ def tunnels
294
+ @tunnels ||= Tunnels.new
295
+ end
296
+
297
+ def local_host=(local_host)
298
+ tunnels.local_host = local_host
299
+ end
300
+
301
+ def local_port_range=(local_port_range)
302
+ tunnels.local_port_range = local_port_range
303
+ end
304
+
305
+ class Tunnels
306
+
307
+ def tunnels
308
+ @tunnels ||= []
309
+ end
310
+
311
+ attr_accessor :local_host, :local_port_range
312
+
313
+ def local_port_range
314
+ @local_port_range ||= 10000..10200
315
+ end
316
+
317
+ def find(token)
318
+ tunnels.find { |t| t.token == token }
319
+ end
320
+
321
+ def create(attributes = {})
322
+ attributes = default_attributes.merge(attributes).merge(:local_host => local_host)
323
+ Tunnel.new(attributes).tap do |tunnel|
324
+ ReverseTunnel.logger.info "Create tunnel #{tunnel.inspect}"
325
+ tunnels << tunnel
326
+ end
327
+ end
328
+
329
+ def destroy(token)
330
+ tunnel = find(token)
331
+ if tunnel
332
+ tunnel.close
333
+ tunnels.delete tunnel
334
+ tunnel
335
+ end
336
+ end
337
+
338
+ def default_attributes
339
+ { "token" => create_token, "local_port" => available_local_port }
340
+ end
341
+
342
+ def create_token
343
+ rand(10e32).to_s(16).ljust(28, '0').upcase
344
+ end
345
+
346
+ def used_local_ports
347
+ tunnels.map(&:local_port)
348
+ end
349
+
350
+ def available_local_ports
351
+ local_port_range.to_a - used_local_ports
352
+ end
353
+
354
+ def available_local_port
355
+ available_local_ports.tap do |ports|
356
+ ports.shuffle if respond_to?(:shuffle)
357
+ end.first
358
+ end
359
+
360
+ def to_json(*args)
361
+ tunnels.to_json(*args)
362
+ end
363
+
364
+ end
365
+
366
+ def start
367
+ tunnels.create "token" => "6B833D3F561369156820B4240C7C2657", "local_port" => 10000
368
+
369
+ EventMachine.run do
370
+ start_server
371
+ start_api
372
+ end
373
+ end
374
+
375
+ attr_accessor :server_host, :server_port
376
+
377
+ def server_host
378
+ @server_host ||= "0.0.0.0"
379
+ end
380
+
381
+ def server_port
382
+ @server_port ||= 4893
383
+ end
384
+
385
+ def start_server
386
+ ReverseTunnel.logger.info "Wait tunnel connections on #{server_host}:#{server_port}"
387
+ EventMachine.start_server server_host, server_port, TunnelConnection, self
388
+ end
389
+
390
+ attr_accessor :api_host, :api_port
391
+
392
+ def api_host
393
+ @api_host ||= "127.0.0.1"
394
+ end
395
+
396
+ def api_port
397
+ @api_port ||= 4894
398
+ end
399
+
400
+ def start_api
401
+ ReverseTunnel.logger.info "Wait api requests #{api_host}:#{api_port}"
402
+ EventMachine.start_server api_host, api_port, ApiServer, self
403
+ end
404
+
405
+ end
406
+ end