coderrr-rtunnel 0.3.9 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +13 -0
- data/LICENSE +21 -0
- data/Manifest +48 -0
- data/README.markdown +40 -15
- data/Rakefile +31 -4
- data/bin/rtunnel_client +2 -1
- data/bin/rtunnel_server +2 -1
- data/lib/rtunnel.rb +20 -0
- data/lib/rtunnel/client.rb +308 -0
- data/lib/rtunnel/command_processor.rb +62 -0
- data/lib/rtunnel/command_protocol.rb +50 -0
- data/lib/rtunnel/commands.rb +233 -0
- data/lib/rtunnel/connection_id.rb +24 -0
- data/lib/rtunnel/core.rb +58 -0
- data/lib/rtunnel/crypto.rb +106 -0
- data/lib/rtunnel/frame_protocol.rb +34 -0
- data/lib/rtunnel/io_extensions.rb +54 -0
- data/lib/rtunnel/leak.rb +35 -0
- data/lib/rtunnel/rtunnel_client_cmd.rb +41 -0
- data/lib/rtunnel/rtunnel_server_cmd.rb +32 -0
- data/lib/rtunnel/server.rb +351 -0
- data/lib/rtunnel/socket_factory.rb +119 -0
- data/test/command_stubs.rb +77 -0
- data/test/protocol_mocks.rb +43 -0
- data/test/scenario_connection.rb +109 -0
- data/test/test_client.rb +48 -0
- data/test/test_command_protocol.rb +82 -0
- data/test/test_commands.rb +49 -0
- data/test/test_connection_id.rb +30 -0
- data/test/test_crypto.rb +127 -0
- data/test/test_frame_protocol.rb +109 -0
- data/test/test_io_extensions.rb +70 -0
- data/test/test_server.rb +70 -0
- data/test/test_socket_factory.rb +42 -0
- data/test/test_tunnel.rb +186 -0
- data/test_data/authorized_keys2 +4 -0
- data/test_data/known_hosts +4 -0
- data/test_data/random_rsa_key +27 -0
- data/test_data/ssh_host_dsa_key +12 -0
- data/test_data/ssh_host_rsa_key +27 -0
- data/tests/_ab_test.rb +16 -0
- data/tests/_stress_test.rb +96 -0
- data/tests/lo_http_server.rb +55 -0
- metadata +67 -27
- data/ab_test.rb +0 -23
- data/lib/client.rb +0 -150
- data/lib/cmds.rb +0 -166
- data/lib/core.rb +0 -58
- data/lib/rtunnel_client_cmd.rb +0 -23
- data/lib/rtunnel_server_cmd.rb +0 -18
- data/lib/server.rb +0 -197
- data/rtunnel.gemspec +0 -18
- data/rtunnel_client.rb +0 -3
- data/rtunnel_server.rb +0 -3
- data/stress_test.rb +0 -68
data/lib/core.rb
DELETED
@@ -1,58 +0,0 @@
|
|
1
|
-
require 'resolv'
|
2
|
-
|
3
|
-
module RTunnel
|
4
|
-
VERSION = '0.3.9'
|
5
|
-
|
6
|
-
DEFAULT_CONTROL_PORT = 19050
|
7
|
-
PING_TIMEOUT = 10
|
8
|
-
end
|
9
|
-
|
10
|
-
if ENV['RTUNNEL_DEBUG']
|
11
|
-
def D(msg)
|
12
|
-
puts msg
|
13
|
-
end
|
14
|
-
else
|
15
|
-
def D(*a);end
|
16
|
-
end
|
17
|
-
|
18
|
-
class << Thread
|
19
|
-
def safe(*a)
|
20
|
-
Thread.new(*a) do
|
21
|
-
begin
|
22
|
-
yield
|
23
|
-
rescue Exception
|
24
|
-
puts $!.inspect
|
25
|
-
puts $!.backtrace.join("\n")
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
class String
|
32
|
-
def replace_with_ip!
|
33
|
-
host = self.split(/:/).first
|
34
|
-
|
35
|
-
ip = timeout(5) { Resolv.getaddress(host) }
|
36
|
-
|
37
|
-
self.replace(self.gsub(host, ip))
|
38
|
-
rescue Exception
|
39
|
-
puts "Error resolving #{host}"
|
40
|
-
raise
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
class IO
|
45
|
-
def while_reading(data = nil, &b)
|
46
|
-
while buf = readpartial_rescued(1024)
|
47
|
-
data << buf if data
|
48
|
-
yield buf if block_given?
|
49
|
-
end
|
50
|
-
data
|
51
|
-
end
|
52
|
-
|
53
|
-
def readpartial_rescued(size)
|
54
|
-
readpartial(size)
|
55
|
-
rescue EOFError
|
56
|
-
nil
|
57
|
-
end
|
58
|
-
end
|
data/lib/rtunnel_client_cmd.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
#!/usr/bin/ruby
|
2
|
-
|
3
|
-
$LOAD_PATH << 'lib'
|
4
|
-
require 'optparse'
|
5
|
-
require 'client'
|
6
|
-
|
7
|
-
control_address = tunnel_from_address = tunnel_to_address = remote_listen_address = nil
|
8
|
-
|
9
|
-
(opts = OptionParser.new do |o|
|
10
|
-
o.on("-c", "--control-address ADDRESS") { |a| control_address = a }
|
11
|
-
o.on("-f", "--remote-listen-port ADDRESS") { |a| remote_listen_address = a }
|
12
|
-
o.on("-t", "--tunnel-to ADDRESS") { |a| tunnel_to_address = a }
|
13
|
-
end).parse! rescue (puts opts; exit)
|
14
|
-
|
15
|
-
(puts opts; exit) if [control_address, remote_listen_address, tunnel_to_address].include? nil
|
16
|
-
|
17
|
-
client = RTunnel::Client.new(
|
18
|
-
:control_address => control_address,
|
19
|
-
:remote_listen_address => remote_listen_address,
|
20
|
-
:tunnel_to_address => tunnel_to_address
|
21
|
-
)
|
22
|
-
client.start
|
23
|
-
client.join
|
data/lib/rtunnel_server_cmd.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
#!/usr/bin/ruby
|
2
|
-
|
3
|
-
$LOAD_PATH << 'lib'
|
4
|
-
require 'optparse'
|
5
|
-
require 'server'
|
6
|
-
|
7
|
-
control_address = tunnel_port = nil
|
8
|
-
|
9
|
-
(opts = OptionParser.new do |o|
|
10
|
-
o.on("-c", "--control ADDRESS") { |a| control_address = a }
|
11
|
-
end).parse! rescue (puts opts; exit)
|
12
|
-
|
13
|
-
server = RTunnel::Server.new(
|
14
|
-
:control_address => control_address
|
15
|
-
)
|
16
|
-
|
17
|
-
server.start
|
18
|
-
server.join
|
data/lib/server.rb
DELETED
@@ -1,197 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
gem 'uuidtools', '>=1.0.2'
|
3
|
-
require 'uuidtools'
|
4
|
-
|
5
|
-
require 'core'
|
6
|
-
require 'cmds'
|
7
|
-
|
8
|
-
require 'gserver'
|
9
|
-
|
10
|
-
Socket.do_not_reverse_lookup = true
|
11
|
-
|
12
|
-
module RTunnel
|
13
|
-
# listens for incoming connections to tunnel
|
14
|
-
class RemoteListenServer < GServer
|
15
|
-
CONNECTIONS = {}
|
16
|
-
CONTROL_CONNECTION_MAPPING = {}
|
17
|
-
|
18
|
-
def initialize(port, host, control_connection)
|
19
|
-
super(port, host, 10)
|
20
|
-
@control_connection = control_connection
|
21
|
-
@maxConnections = 1024
|
22
|
-
end
|
23
|
-
|
24
|
-
def serve(sock)
|
25
|
-
D "new incoming connection"
|
26
|
-
|
27
|
-
conn_id = UUID.timestamp_create.hexdigest
|
28
|
-
CONNECTIONS[conn_id] = sock
|
29
|
-
CONTROL_CONNECTION_MAPPING[conn_id] = @control_connection
|
30
|
-
begin
|
31
|
-
ControlServer.new_tunnel(conn_id)
|
32
|
-
|
33
|
-
sock.while_reading do |buf|
|
34
|
-
begin
|
35
|
-
ControlServer.send_data(conn_id, buf)
|
36
|
-
rescue Exception
|
37
|
-
D "error talking on control connection, dropping incoming connection: #{$!.inspect}"
|
38
|
-
break
|
39
|
-
end
|
40
|
-
end
|
41
|
-
rescue IOError
|
42
|
-
raise unless $!.message =~ /stream closed/i
|
43
|
-
end
|
44
|
-
|
45
|
-
ControlServer.close_tunnel(conn_id)
|
46
|
-
|
47
|
-
CONNECTIONS.delete conn_id
|
48
|
-
CONTROL_CONNECTION_MAPPING.delete conn_id
|
49
|
-
|
50
|
-
D "sock closed"
|
51
|
-
rescue
|
52
|
-
p $!
|
53
|
-
puts $!.backtrace.join("\n")
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
class ControlServer < GServer
|
58
|
-
@@control_connections = []
|
59
|
-
@@remote_listen_servers = []
|
60
|
-
|
61
|
-
@@m = Mutex.new
|
62
|
-
|
63
|
-
attr_accessor :ping_interval
|
64
|
-
|
65
|
-
def initialize(*args)
|
66
|
-
super
|
67
|
-
@maxConnections = 1024
|
68
|
-
end
|
69
|
-
|
70
|
-
class << self
|
71
|
-
def new_tunnel(conn_id)
|
72
|
-
D "sending create connection command: #{conn_id}"
|
73
|
-
|
74
|
-
@@m.synchronize { control_connection_for(conn_id).write CreateConnectionCommand.new(conn_id) }
|
75
|
-
end
|
76
|
-
|
77
|
-
def send_data(conn_id, data)
|
78
|
-
@@m.synchronize { control_connection_for(conn_id).write SendDataCommand.new(conn_id, data) }
|
79
|
-
end
|
80
|
-
|
81
|
-
def close_tunnel(conn_id)
|
82
|
-
D "sending close connection command"
|
83
|
-
|
84
|
-
@@m.synchronize { control_connection_for(conn_id).write CloseConnectionCommand.new(conn_id) }
|
85
|
-
end
|
86
|
-
|
87
|
-
private
|
88
|
-
|
89
|
-
def control_connection_for(conn_id)
|
90
|
-
RemoteListenServer::CONTROL_CONNECTION_MAPPING[conn_id]
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def starting
|
95
|
-
start_pinging
|
96
|
-
end
|
97
|
-
|
98
|
-
def serve(sock)
|
99
|
-
D "new control connection"
|
100
|
-
@@control_connections << sock
|
101
|
-
sock.sync = true
|
102
|
-
|
103
|
-
cmd_queue = ""
|
104
|
-
sock.while_reading(cmd_queue = '') do
|
105
|
-
while Command.match(cmd_queue)
|
106
|
-
case cmd = Command.parse(cmd_queue)
|
107
|
-
when RemoteListenCommand
|
108
|
-
@@m.synchronize do
|
109
|
-
addr, port = cmd.address.split(/:/)
|
110
|
-
if rls = @@remote_listen_servers.detect {|s| s.port == port.to_i }
|
111
|
-
rls.stop
|
112
|
-
@@remote_listen_servers.delete rls
|
113
|
-
end
|
114
|
-
(new_rls = RemoteListenServer.new(port, addr, sock)).start
|
115
|
-
@@remote_listen_servers << new_rls
|
116
|
-
end
|
117
|
-
D "listening for remote connections on #{cmd.address}"
|
118
|
-
when SendDataCommand
|
119
|
-
conn = RemoteListenServer::CONNECTIONS[cmd.conn_id]
|
120
|
-
begin
|
121
|
-
conn.write(cmd.data) if conn
|
122
|
-
rescue Errno::EPIPE
|
123
|
-
D "broken pipe on #{cmd.conn_id}"
|
124
|
-
end
|
125
|
-
when CloseConnectionCommand
|
126
|
-
if connection = RemoteListenServer::CONNECTIONS[cmd.conn_id]
|
127
|
-
D "closing remote connection: #{cmd.conn_id}"
|
128
|
-
connection.close
|
129
|
-
end
|
130
|
-
else
|
131
|
-
D "bad command received: #{cmd.inspect}"
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
rescue Errno::ECONNRESET
|
136
|
-
D "client disconnected (conn reset)"
|
137
|
-
rescue
|
138
|
-
D $!.inspect
|
139
|
-
D $@*"\n"
|
140
|
-
raise
|
141
|
-
ensure
|
142
|
-
@@control_connections.delete sock
|
143
|
-
end
|
144
|
-
|
145
|
-
def stopping
|
146
|
-
@ping_thread.kill
|
147
|
-
@@remote_listen_server.stop
|
148
|
-
end
|
149
|
-
|
150
|
-
private
|
151
|
-
|
152
|
-
def start_pinging
|
153
|
-
@ping_thread = Thread.safe do
|
154
|
-
loop do
|
155
|
-
sleep @ping_interval
|
156
|
-
|
157
|
-
@@m.synchronize do
|
158
|
-
@@control_connections.each {|cc| cc.write PingCommand.new }
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
class Server
|
166
|
-
def initialize(options = {})
|
167
|
-
@control_address = options[:control_address]
|
168
|
-
if ! @control_address
|
169
|
-
@control_address = "0.0.0.0:#{DEFAULT_CONTROL_PORT}" if ! @control_address
|
170
|
-
elsif @control_address =~ /^\d+$/
|
171
|
-
@control_address.insert 0, "0.0.0.0:"
|
172
|
-
elsif @control_address !~ /:\d+$/
|
173
|
-
@control_address << ":#{DEFAULT_CONTROL_PORT}"
|
174
|
-
end
|
175
|
-
@control_host = @control_address.split(/:/).first
|
176
|
-
|
177
|
-
@ping_interval = options[:ping_interval] || 2.0
|
178
|
-
end
|
179
|
-
|
180
|
-
def start
|
181
|
-
@control_server = ControlServer.new(*@control_address.split(/:/).reverse)
|
182
|
-
@control_server.ping_interval = @ping_interval
|
183
|
-
@control_server.audit = true
|
184
|
-
|
185
|
-
@control_server.start
|
186
|
-
end
|
187
|
-
|
188
|
-
def join
|
189
|
-
@control_server.join
|
190
|
-
end
|
191
|
-
|
192
|
-
def stop
|
193
|
-
@control_server.shutdown
|
194
|
-
ControlServer.stop(@control_server.port, @control_server.host)
|
195
|
-
end
|
196
|
-
end
|
197
|
-
end
|
data/rtunnel.gemspec
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
Gem::Specification.new do |s|
|
2
|
-
s.name = "rtunnel"
|
3
|
-
s.version = "0.3.9"
|
4
|
-
s.summary = "reverse tunnel server and client"
|
5
|
-
s.email = "coderrr.contact@gmail.com"
|
6
|
-
s.homepage = "http://github.com/coderrr/rtunnel"
|
7
|
-
s.description = "reverse tunnel server and client"
|
8
|
-
s.has_rdoc = false
|
9
|
-
s.authors = ["coderrr"]
|
10
|
-
s.files = [
|
11
|
-
"lib/rtunnel_server_cmd.rb", "lib/client.rb", "lib/core.rb", "lib/rtunnel_client_cmd.rb", "lib/server.rb", "lib/cmds.rb", "bin/rtunnel_server", "bin/rtunnel_client",
|
12
|
-
"ab_test.rb", "README.markdown", "rtunnel.gemspec", "stress_test.rb", "Rakefile", "rtunnel_client.rb", "rtunnel_server.rb"
|
13
|
-
]
|
14
|
-
s.test_files = [
|
15
|
-
"spec/integration_spec.rb", "spec/spec_helper.rb", "spec/client_spec.rb", "spec/cmds_spec.rb", "spec/server_spec.rb"
|
16
|
-
]
|
17
|
-
s.add_dependency("uuidtools", [">= 1.0.2"])
|
18
|
-
end
|
data/rtunnel_client.rb
DELETED
data/rtunnel_server.rb
DELETED
data/stress_test.rb
DELETED
@@ -1,68 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'thin'
|
3
|
-
|
4
|
-
require 'lib/core'
|
5
|
-
|
6
|
-
require 'logger'
|
7
|
-
require 'stringio'
|
8
|
-
|
9
|
-
|
10
|
-
CONCURRENT_CONNECTIONS = 25
|
11
|
-
DISPLAY_RTUNNEL_OUTPUT = false
|
12
|
-
TUNNEL_PORT = 5000
|
13
|
-
HTTP_PORT = 4444
|
14
|
-
|
15
|
-
#################
|
16
|
-
|
17
|
-
pids = []
|
18
|
-
at_exit do
|
19
|
-
pids.each {|pid| Process.kill 9, pid }
|
20
|
-
p $!
|
21
|
-
puts "done, hit ^C"
|
22
|
-
sleep 999999
|
23
|
-
end
|
24
|
-
|
25
|
-
module Enumerable
|
26
|
-
def parallel_map
|
27
|
-
self.map do |e|
|
28
|
-
Thread.new(e) do |element|
|
29
|
-
yield element
|
30
|
-
end
|
31
|
-
end.map {|thread| thread.value }
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
TUNNEL_URI = "http://localhost:#{TUNNEL_PORT}"
|
36
|
-
EXPECTED_DATA = (0..10*1024).map{0until(c=rand(?z).chr)=~/(?!_)\w/;c}*'' # gen random string GOLF FTW!
|
37
|
-
puts EXPECTED_DATA
|
38
|
-
|
39
|
-
app = lambda { |env| [200, {}, EXPECTED_DATA] }
|
40
|
-
server = ::Thin::Server.new('localhost', HTTP_PORT, app)
|
41
|
-
Thread.new { server.start }
|
42
|
-
|
43
|
-
base_dir = File.dirname(__FILE__)
|
44
|
-
|
45
|
-
d = !DISPLAY_RTUNNEL_OUTPUT
|
46
|
-
pids << fork{ exec "ruby #{base_dir}/rtunnel_server.rb #{d && '> /dev/null'} 2>&1" }
|
47
|
-
pids << fork{ exec "ruby #{base_dir}/rtunnel_client.rb -c localhost -f #{TUNNEL_PORT} -t #{HTTP_PORT} #{d &&' > /dev/null'} 2>&1" }
|
48
|
-
|
49
|
-
puts 'wait 2 secs'
|
50
|
-
sleep 2
|
51
|
-
|
52
|
-
STDOUT.sync = true
|
53
|
-
999999999.times do |i|
|
54
|
-
puts i if i%10 == 0
|
55
|
-
threads = []
|
56
|
-
CONCURRENT_CONNECTIONS.times do
|
57
|
-
threads << Thread.new do
|
58
|
-
text = %x{curl --silent #{TUNNEL_URI}}
|
59
|
-
if text != EXPECTED_DATA
|
60
|
-
puts "BAD!!!!"*1000
|
61
|
-
puts "response: #{text.inspect}"
|
62
|
-
exit
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
threads.parallel_map {|t| t.join; print '.'; STDOUT.flush }
|
68
|
-
end
|