reverse-tunnel 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +20 -0
- data/Gemfile +6 -0
- data/Guardfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +99 -0
- data/Rakefile +4 -0
- data/bin/reverse-tunnel +5 -0
- data/lib/reverse-tunnel.rb +35 -0
- data/lib/reverse-tunnel/cli.rb +170 -0
- data/lib/reverse-tunnel/client.rb +304 -0
- data/lib/reverse-tunnel/message.rb +140 -0
- data/lib/reverse-tunnel/server.rb +406 -0
- data/lib/reverse-tunnel/version.rb +3 -0
- data/reverse-tunnel.gemspec +33 -0
- data/spec/reverse-tunnel/cli_spec.rb +118 -0
- data/spec/reverse-tunnel/client/api_server_spec.rb +72 -0
- data/spec/reverse-tunnel/client_spec.rb +79 -0
- data/spec/reverse-tunnel/message_spec.rb +55 -0
- data/spec/reverse-tunnel/server/api_server_spec.rb +125 -0
- data/spec/reverse-tunnel/server/tunnels_spec.rb +66 -0
- data/spec/reverse-tunnel/server_spec.rb +57 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/log.rb +9 -0
- data/tasks/rspec.rake +2 -0
- metadata +280 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Alban Peignier
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# Reverse::Tunnel
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'reverse-tunnel'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install reverse-tunnel
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
reverse-tunnel server --public=88.190.240.120:4893 --api=172.20.11.9:3000 --range=172.20.11.9:10000-10100
|
22
|
+
|
23
|
+
reverse-tunnel client --server=88.190.240.120:4893 --local-port=22 6B833D3F561369156820B4240C7C2657
|
24
|
+
|
25
|
+
reverse-tunnel rt://console.tryphon.eu:4893/6B833D3F561369156820B4240C7C2657/22
|
26
|
+
reverse-tunnel cnQ6Ly9jb25zb2xlLnRyeXBob24uZXU6NDg5My82QjgzM0QzRjU2MTM2OTE1NjgyMEI0MjQwQzdDMjY1Ny8yMg==
|
27
|
+
|
28
|
+
## API
|
29
|
+
|
30
|
+
Create a new tunnel :
|
31
|
+
|
32
|
+
$ curl -X POST -d '{"token":"156820B4240C7C26576B833D3F561369","local_port":10001}' http://localhost:5000/tunnels.json
|
33
|
+
{
|
34
|
+
"token": "156820B4240C7C26576B833D3F561369",
|
35
|
+
"local_port": 10001
|
36
|
+
}
|
37
|
+
|
38
|
+
Retrieve current tunnels :
|
39
|
+
|
40
|
+
$ curl http://localhost:5000/tunnels.json
|
41
|
+
[
|
42
|
+
{
|
43
|
+
"token": "6B833D3F561369156820B4240C7C2657",
|
44
|
+
"local_port": 10000,
|
45
|
+
},
|
46
|
+
{
|
47
|
+
"token": "156820B4240C7C26576B833D3F561369",
|
48
|
+
"local_port": 10001,
|
49
|
+
"connection": {
|
50
|
+
"peer": "127.0.0.1:42782",
|
51
|
+
"created_at": "2012-12-22 17:29:55 +0100"
|
52
|
+
}
|
53
|
+
}
|
54
|
+
]
|
55
|
+
|
56
|
+
|
57
|
+
## Protocol
|
58
|
+
|
59
|
+
### Create tunnel
|
60
|
+
|
61
|
+
* POST /tunnels on server API (with optional token and local port)
|
62
|
+
* API returns token and local port
|
63
|
+
|
64
|
+
### Open tunnel
|
65
|
+
|
66
|
+
* client send tunnel token to server (on public ip:port)
|
67
|
+
* server opens a tcp server on local port (associated to token)
|
68
|
+
* client receives a confirmation message
|
69
|
+
|
70
|
+
### Ping tunnel
|
71
|
+
|
72
|
+
* every 30s (?) client sends a ping message
|
73
|
+
* server responds a pong message
|
74
|
+
|
75
|
+
### Use tunnel
|
76
|
+
|
77
|
+
* server receives a connection on tcp server on local port
|
78
|
+
* server creates a session (with id)
|
79
|
+
* server sends received data to client
|
80
|
+
* client creates local connection to local port (if not exist)
|
81
|
+
* client send received data to local connection
|
82
|
+
* client send back to server data received on local connection
|
83
|
+
|
84
|
+
### Messages
|
85
|
+
|
86
|
+
* OPEN_TUNNEL:tunnel_token
|
87
|
+
* PING
|
88
|
+
* PONG
|
89
|
+
* OPEN_SESSION:session_id
|
90
|
+
* DATA:session_id:data
|
91
|
+
* CLOSE_SESSION:session_id
|
92
|
+
|
93
|
+
## Contributing
|
94
|
+
|
95
|
+
1. Fork it
|
96
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
97
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
98
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
99
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/reverse-tunnel
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require "reverse-tunnel/version"
|
2
|
+
|
3
|
+
require "logger"
|
4
|
+
|
5
|
+
module ReverseTunnel
|
6
|
+
|
7
|
+
def self.default_logger
|
8
|
+
Logger.new($stderr).tap do |logger|
|
9
|
+
logger.level = Logger::INFO
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
@@logger = default_logger
|
14
|
+
def self.logger
|
15
|
+
@@logger
|
16
|
+
end
|
17
|
+
def self.logger=(logger)
|
18
|
+
@@logger = logger
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.reset_logger!
|
22
|
+
@@logger = default_logger
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
require "eventmachine"
|
28
|
+
require "msgpack"
|
29
|
+
require "trollop"
|
30
|
+
require "syslog/logger"
|
31
|
+
|
32
|
+
require "reverse-tunnel/message"
|
33
|
+
require "reverse-tunnel/server"
|
34
|
+
require "reverse-tunnel/client"
|
35
|
+
require "reverse-tunnel/cli"
|
@@ -0,0 +1,170 @@
|
|
1
|
+
module ReverseTunnel
|
2
|
+
class CLI
|
3
|
+
|
4
|
+
attr_accessor :arguments
|
5
|
+
|
6
|
+
def initialize(arguments = [])
|
7
|
+
@arguments = arguments
|
8
|
+
end
|
9
|
+
|
10
|
+
def mode
|
11
|
+
@mode ||= arguments.shift
|
12
|
+
end
|
13
|
+
|
14
|
+
class Base
|
15
|
+
|
16
|
+
attr_reader :arguments
|
17
|
+
|
18
|
+
def initialize(arguments = [])
|
19
|
+
@arguments = arguments
|
20
|
+
end
|
21
|
+
|
22
|
+
def options
|
23
|
+
@options ||= Trollop::with_standard_exception_handling(parser) do
|
24
|
+
parser.parse arguments
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def configure(object = self)
|
29
|
+
options.each do |k,v|
|
30
|
+
unless [:help, :version].include? k or k.to_s =~ /_given$/
|
31
|
+
object.send "#{k.to_s.gsub('-','_')}=", v
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
object
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
class Global < Base
|
41
|
+
|
42
|
+
def parser
|
43
|
+
@parser ||= Trollop::Parser.new do
|
44
|
+
banner <<-EOS
|
45
|
+
Usage:
|
46
|
+
reverse-client [global options] server|client [options]
|
47
|
+
|
48
|
+
where [global options] are:
|
49
|
+
EOS
|
50
|
+
|
51
|
+
opt :debug, "Enable debug messages"
|
52
|
+
opt :syslog, "Send log messages to syslog"
|
53
|
+
|
54
|
+
version ReverseTunnel::VERSION
|
55
|
+
|
56
|
+
stop_on "server", "client"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
class Configurator < Base
|
63
|
+
|
64
|
+
def instance
|
65
|
+
@instance ||= ReverseTunnel.const_get(self.class.name.split("::").last).new
|
66
|
+
end
|
67
|
+
|
68
|
+
def method_missing(name, *args)
|
69
|
+
instance.send name, *args
|
70
|
+
end
|
71
|
+
|
72
|
+
def parse_host_port(string)
|
73
|
+
host, port = string.split(':')
|
74
|
+
[ host, port.to_i ]
|
75
|
+
end
|
76
|
+
|
77
|
+
def parse_host_port_range(string)
|
78
|
+
if string =~ /(.*):(\d+)-(\d+)$/
|
79
|
+
[ $1, ($2.to_i)..($3.to_i) ]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def server=(server)
|
84
|
+
self.server_host, self.server_port = parse_host_port(server)
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.configure(arguments)
|
88
|
+
new(arguments).configure
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
class Client < Configurator
|
94
|
+
|
95
|
+
def parser
|
96
|
+
@parser ||= Trollop::Parser.new do
|
97
|
+
opt :server, "Host and port of ReverseTunnel server", :type => :string
|
98
|
+
opt :api, "Host and port of status HTTP api", :type => :string
|
99
|
+
opt :"local-port", "Port to forward incoming connection", :default => 22
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def token
|
104
|
+
arguments.first
|
105
|
+
end
|
106
|
+
|
107
|
+
def api=(api)
|
108
|
+
self.api_host, self.api_port = parse_host_port(api) if api
|
109
|
+
end
|
110
|
+
|
111
|
+
def configure(object = self)
|
112
|
+
super
|
113
|
+
self.token = token
|
114
|
+
instance
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
class Server < Configurator
|
120
|
+
|
121
|
+
def parser
|
122
|
+
@parser ||= Trollop::Parser.new do
|
123
|
+
opt :server, "Host and port of ReverseTunnel server", :default => "0.0.0.0:4893"
|
124
|
+
opt :api, "Host and port of ReverseTunnel HTTP api", :default => "127.0.0.1:4894"
|
125
|
+
opt :local, "Host and port range to listen forwarded connections", :default => "127.0.0.1:10000-10010"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def api=(api)
|
130
|
+
self.api_host, self.api_port = parse_host_port(api)
|
131
|
+
end
|
132
|
+
|
133
|
+
def local=(local)
|
134
|
+
self.local_host, self.local_port_range = parse_host_port_range(local)
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
def debug=(debug)
|
140
|
+
level = debug ? Logger::DEBUG : Logger::INFO
|
141
|
+
ReverseTunnel.logger.level = level
|
142
|
+
end
|
143
|
+
|
144
|
+
def syslog=(syslog)
|
145
|
+
if syslog
|
146
|
+
syslog_logger = Syslog::Logger.new("rtunnel").tap do |logger|
|
147
|
+
logger.level = ReverseTunnel.logger.level
|
148
|
+
end
|
149
|
+
ReverseTunnel.logger = syslog_logger
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def configurator_class
|
154
|
+
mode == "server" ? ReverseTunnel::CLI::Server : ReverseTunnel::CLI::Client
|
155
|
+
end
|
156
|
+
|
157
|
+
def instance
|
158
|
+
configurator_class.configure(arguments)
|
159
|
+
end
|
160
|
+
|
161
|
+
def configure
|
162
|
+
Global.new(arguments).configure(self)
|
163
|
+
end
|
164
|
+
|
165
|
+
def run
|
166
|
+
configure
|
167
|
+
instance.start
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,304 @@
|
|
1
|
+
module ReverseTunnel
|
2
|
+
class Client
|
3
|
+
|
4
|
+
class ApiServer < EM::Connection
|
5
|
+
include EM::HttpServer
|
6
|
+
|
7
|
+
attr_accessor :tunnel
|
8
|
+
|
9
|
+
def initialize(tunnel)
|
10
|
+
@tunnel = tunnel
|
11
|
+
end
|
12
|
+
|
13
|
+
def post_init
|
14
|
+
super
|
15
|
+
no_environment_strings
|
16
|
+
end
|
17
|
+
|
18
|
+
def process_http_request
|
19
|
+
ReverseTunnel.logger.debug "Process http request #{@http_request_uri}"
|
20
|
+
|
21
|
+
response = EM::DelegatedHttpResponse.new(self)
|
22
|
+
response.status = 200
|
23
|
+
response.content_type 'application/json'
|
24
|
+
|
25
|
+
begin
|
26
|
+
if @http_request_uri =~ %r{^/status(.json)?$} and @http_request_method == "GET"
|
27
|
+
response.content = tunnel.to_json
|
28
|
+
end
|
29
|
+
rescue => e
|
30
|
+
ReverseTunnel.logger.error "Error in http request processing: #{e}"
|
31
|
+
response.status = 500
|
32
|
+
end
|
33
|
+
|
34
|
+
if response.content.nil?
|
35
|
+
response.status = 404
|
36
|
+
end
|
37
|
+
|
38
|
+
response.send_response
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
class Tunnel
|
44
|
+
|
45
|
+
attr_accessor :host, :port
|
46
|
+
attr_accessor :token, :local_port
|
47
|
+
|
48
|
+
def initialize(attributes = {})
|
49
|
+
attributes.each { |k,v| send "#{k}=", v }
|
50
|
+
end
|
51
|
+
|
52
|
+
attr_accessor :connection
|
53
|
+
|
54
|
+
def connection=(connection)
|
55
|
+
if connection.nil?
|
56
|
+
EventMachine.add_timer(30) do
|
57
|
+
start
|
58
|
+
end
|
59
|
+
|
60
|
+
local_connections.close_all
|
61
|
+
@hearbeat.cancel
|
62
|
+
else
|
63
|
+
@hearbeat = EventMachine.add_periodic_timer(5) do
|
64
|
+
ping
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
@connection = connection
|
69
|
+
end
|
70
|
+
|
71
|
+
def start
|
72
|
+
ReverseTunnel.logger.debug "Connect to #{host}:#{port}"
|
73
|
+
EventMachine.connect host, port, TunnelConnection, self
|
74
|
+
end
|
75
|
+
|
76
|
+
def open
|
77
|
+
connection.send_data Message::OpenTunnel.new(token).pack
|
78
|
+
end
|
79
|
+
|
80
|
+
attr_accessor :sequence_number
|
81
|
+
def sequence_number
|
82
|
+
@sequence_number ||= 0
|
83
|
+
end
|
84
|
+
|
85
|
+
def ping
|
86
|
+
next_number = self.sequence_number += 1
|
87
|
+
ReverseTunnel.logger.debug "Send ping #{next_number}"
|
88
|
+
connection.send_data Message::Ping.new(next_number).pack if connection
|
89
|
+
end
|
90
|
+
|
91
|
+
def ping_received(ping)
|
92
|
+
ReverseTunnel.logger.info "Receive ping #{ping.sequence_number}"
|
93
|
+
end
|
94
|
+
|
95
|
+
def open_session(session_id)
|
96
|
+
local_host = "localhost"
|
97
|
+
EventMachine.connect local_host, local_port, LocalConnection, self, session_id
|
98
|
+
end
|
99
|
+
|
100
|
+
def send_data(session_id, data)
|
101
|
+
if connection
|
102
|
+
ReverseTunnel.logger.debug "Send data to local connection #{session_id}"
|
103
|
+
connection.send_data Message::Data.new(session_id,data).pack
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def local_connections
|
108
|
+
@local_connections ||= LocalConnections.new
|
109
|
+
end
|
110
|
+
|
111
|
+
def receive_data(session_id, data)
|
112
|
+
local_connection = local_connections.find(session_id)
|
113
|
+
if local_connection
|
114
|
+
ReverseTunnel.logger.debug "Send data to local connection #{session_id}"
|
115
|
+
local_connection.send_data data
|
116
|
+
else
|
117
|
+
local_connections.bufferize session_id, data
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def to_json
|
122
|
+
{ :token => token,
|
123
|
+
:local_port => local_port,
|
124
|
+
:server_host => host,
|
125
|
+
:server_port => port,
|
126
|
+
:local_connections => local_connections.as_json
|
127
|
+
}.tap do |attributes|
|
128
|
+
attributes[:connection] = connection.as_json if connection
|
129
|
+
end.to_json
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
class LocalConnections
|
135
|
+
|
136
|
+
attr_reader :connections
|
137
|
+
|
138
|
+
def initialize
|
139
|
+
@connections = []
|
140
|
+
end
|
141
|
+
|
142
|
+
def find(session_id)
|
143
|
+
connections.find { |c| c.session_id == session_id }
|
144
|
+
end
|
145
|
+
|
146
|
+
def push(connection)
|
147
|
+
connections << connection
|
148
|
+
|
149
|
+
session_id = connection.session_id
|
150
|
+
ReverseTunnel.logger.debug "Clear buffer for #{session_id}"
|
151
|
+
|
152
|
+
(buffers.delete(session_id) or []).each do |data|
|
153
|
+
connection.send_data data
|
154
|
+
end
|
155
|
+
end
|
156
|
+
alias_method :<<, :push
|
157
|
+
|
158
|
+
def buffers
|
159
|
+
@buffers ||= Hash.new { |h,k| h[k] = [] }
|
160
|
+
end
|
161
|
+
|
162
|
+
def bufferize(session_id, data)
|
163
|
+
ReverseTunnel.logger.debug "Push buffer for #{session_id}"
|
164
|
+
buffers[session_id] << data
|
165
|
+
end
|
166
|
+
|
167
|
+
def delete(connection)
|
168
|
+
connections.delete connection
|
169
|
+
end
|
170
|
+
|
171
|
+
def close_all
|
172
|
+
connections.each(&:close_connection)
|
173
|
+
end
|
174
|
+
|
175
|
+
def as_json
|
176
|
+
connections.map(&:as_json)
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
class TunnelConnection < EventMachine::Connection
|
182
|
+
attr_accessor :tunnel, :created_at
|
183
|
+
|
184
|
+
attr_reader :hearbeat
|
185
|
+
|
186
|
+
def initialize(tunnel)
|
187
|
+
@tunnel = tunnel
|
188
|
+
end
|
189
|
+
|
190
|
+
def post_init
|
191
|
+
ReverseTunnel.logger.debug "New tunnel connection"
|
192
|
+
self.created_at = Time.now
|
193
|
+
|
194
|
+
tunnel.connection = self
|
195
|
+
tunnel.open
|
196
|
+
end
|
197
|
+
|
198
|
+
def message_unpacker
|
199
|
+
@message_unpacker ||= Message::Unpacker.new
|
200
|
+
end
|
201
|
+
|
202
|
+
def as_json
|
203
|
+
{ :created_at => created_at }
|
204
|
+
end
|
205
|
+
|
206
|
+
def receive_data(data)
|
207
|
+
ReverseTunnel.logger.debug "Received data '#{data.unpack('H*').join}'"
|
208
|
+
message_unpacker.feed data
|
209
|
+
|
210
|
+
message_unpacker.each do |message|
|
211
|
+
ReverseTunnel.logger.debug "Received message in tunnel #{message.inspect}"
|
212
|
+
|
213
|
+
if message.data?
|
214
|
+
tunnel.receive_data message.session_id, message.data
|
215
|
+
elsif message.open_session?
|
216
|
+
tunnel.open_session message.session_id
|
217
|
+
elsif message.ping?
|
218
|
+
tunnel.ping_received message
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def unbind
|
224
|
+
ReverseTunnel.logger.debug "Close tunnel connection"
|
225
|
+
tunnel.connection = nil
|
226
|
+
end
|
227
|
+
|
228
|
+
end
|
229
|
+
|
230
|
+
class LocalConnection < EventMachine::Connection
|
231
|
+
attr_accessor :tunnel, :session_id
|
232
|
+
|
233
|
+
attr_reader :created_at, :received_size, :send_size
|
234
|
+
|
235
|
+
def initialize(tunnel, session_id)
|
236
|
+
@tunnel, @session_id = tunnel, session_id
|
237
|
+
@received_size = @send_size = 0
|
238
|
+
end
|
239
|
+
|
240
|
+
def post_init
|
241
|
+
ReverseTunnel.logger.debug "New local connection"
|
242
|
+
@created_at = Time.now
|
243
|
+
tunnel.local_connections << self
|
244
|
+
end
|
245
|
+
|
246
|
+
def receive_data(data)
|
247
|
+
ReverseTunnel.logger.debug "Received data in local connection #{session_id}"
|
248
|
+
@received_size += data.size
|
249
|
+
tunnel.send_data session_id, data
|
250
|
+
end
|
251
|
+
|
252
|
+
def unbind
|
253
|
+
ReverseTunnel.logger.debug "Close local connection #{session_id}"
|
254
|
+
tunnel.local_connections.delete self
|
255
|
+
end
|
256
|
+
|
257
|
+
def send_data(data)
|
258
|
+
ReverseTunnel.logger.debug "Send data '#{data.unpack('H*').join}'"
|
259
|
+
@send_size += data.size
|
260
|
+
super
|
261
|
+
end
|
262
|
+
|
263
|
+
def as_json
|
264
|
+
{ :session_id => session_id, :created_at => created_at, :received_size => received_size, :send_size => send_size }
|
265
|
+
end
|
266
|
+
|
267
|
+
end
|
268
|
+
|
269
|
+
def start
|
270
|
+
EventMachine.run do
|
271
|
+
tunnel.start
|
272
|
+
start_api
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def start_api
|
277
|
+
if api_host
|
278
|
+
ReverseTunnel.logger.info "Wait api requests #{api_host}:#{api_port}"
|
279
|
+
EventMachine.start_server api_host, api_port, ApiServer, tunnel
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
def tunnel
|
284
|
+
@tunnel ||= Tunnel.new(:token => token, :local_port => local_port, :host => server_host, :port => server_port)
|
285
|
+
end
|
286
|
+
|
287
|
+
attr_accessor :token, :local_port
|
288
|
+
attr_accessor :server_host, :server_port
|
289
|
+
attr_accessor :api_host, :api_port
|
290
|
+
|
291
|
+
def server_port
|
292
|
+
@server_port ||= 4893
|
293
|
+
end
|
294
|
+
|
295
|
+
def api_port
|
296
|
+
@api_port ||= 4895
|
297
|
+
end
|
298
|
+
|
299
|
+
def local_port
|
300
|
+
@local_port ||= 22
|
301
|
+
end
|
302
|
+
|
303
|
+
end
|
304
|
+
end
|