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
metadata
CHANGED
@@ -1,50 +1,94 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: coderrr-rtunnel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- coderrr
|
8
|
+
- costan
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
12
|
|
12
|
-
date:
|
13
|
+
date: 2009-01-11 00:00:00 -08:00
|
13
14
|
default_executable:
|
14
15
|
dependencies:
|
15
16
|
- !ruby/object:Gem::Dependency
|
16
|
-
name:
|
17
|
+
name: eventmachine
|
17
18
|
version_requirement:
|
18
19
|
version_requirements: !ruby/object:Gem::Requirement
|
19
20
|
requirements:
|
20
21
|
- - ">="
|
21
22
|
- !ruby/object:Gem::Version
|
22
|
-
version:
|
23
|
+
version: 0.12.2
|
23
24
|
version:
|
24
|
-
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: net-ssh
|
27
|
+
version_requirement:
|
28
|
+
version_requirements: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.0.4
|
33
|
+
version:
|
34
|
+
description: ""
|
25
35
|
email: coderrr.contact@gmail.com
|
26
|
-
executables:
|
27
|
-
|
36
|
+
executables:
|
37
|
+
- rtunnel_client
|
38
|
+
- rtunnel_server
|
28
39
|
extensions: []
|
29
40
|
|
30
41
|
extra_rdoc_files: []
|
31
42
|
|
32
43
|
files:
|
33
|
-
-
|
34
|
-
-
|
35
|
-
-
|
36
|
-
- lib/rtunnel_client_cmd.rb
|
37
|
-
- lib/server.rb
|
38
|
-
- lib/cmds.rb
|
39
|
-
- bin/rtunnel_server
|
40
|
-
- bin/rtunnel_client
|
41
|
-
- ab_test.rb
|
44
|
+
- CHANGELOG
|
45
|
+
- LICENSE
|
46
|
+
- Manifest
|
42
47
|
- README.markdown
|
43
|
-
- rtunnel.gemspec
|
44
|
-
- stress_test.rb
|
45
48
|
- Rakefile
|
46
|
-
- rtunnel_client
|
47
|
-
- rtunnel_server
|
49
|
+
- bin/rtunnel_client
|
50
|
+
- bin/rtunnel_server
|
51
|
+
- lib/rtunnel.rb
|
52
|
+
- lib/rtunnel/client.rb
|
53
|
+
- lib/rtunnel/command_protocol.rb
|
54
|
+
- lib/rtunnel/commands.rb
|
55
|
+
- lib/rtunnel/core.rb
|
56
|
+
- lib/rtunnel/crypto.rb
|
57
|
+
- lib/rtunnel/frame_protocol.rb
|
58
|
+
- lib/rtunnel/io_extensions.rb
|
59
|
+
- lib/rtunnel/leak.rb
|
60
|
+
- lib/rtunnel/rtunnel_client_cmd.rb
|
61
|
+
- lib/rtunnel/rtunnel_server_cmd.rb
|
62
|
+
- lib/rtunnel/server.rb
|
63
|
+
- lib/rtunnel/socket_factory.rb
|
64
|
+
- lib/rtunnel/connection_id.rb
|
65
|
+
- lib/rtunnel/command_processor.rb
|
66
|
+
- spec/client_spec.rb
|
67
|
+
- spec/cmds_spec.rb
|
68
|
+
- spec/integration_spec.rb
|
69
|
+
- spec/server_spec.rb
|
70
|
+
- spec/spec_helper.rb
|
71
|
+
- test/command_stubs.rb
|
72
|
+
- test/protocol_mocks.rb
|
73
|
+
- test/scenario_connection.rb
|
74
|
+
- test/test_client.rb
|
75
|
+
- test/test_command_protocol.rb
|
76
|
+
- test/test_commands.rb
|
77
|
+
- test/test_crypto.rb
|
78
|
+
- test/test_frame_protocol.rb
|
79
|
+
- test/test_io_extensions.rb
|
80
|
+
- test/test_server.rb
|
81
|
+
- test/test_socket_factory.rb
|
82
|
+
- test/test_tunnel.rb
|
83
|
+
- test/test_connection_id.rb
|
84
|
+
- test_data/known_hosts
|
85
|
+
- test_data/ssh_host_rsa_key
|
86
|
+
- test_data/random_rsa_key
|
87
|
+
- test_data/ssh_host_dsa_key
|
88
|
+
- test_data/authorized_keys2
|
89
|
+
- tests/_ab_test.rb
|
90
|
+
- tests/_stress_test.rb
|
91
|
+
- tests/lo_http_server.rb
|
48
92
|
has_rdoc: false
|
49
93
|
homepage: http://github.com/coderrr/rtunnel
|
50
94
|
post_install_message:
|
@@ -70,10 +114,6 @@ rubyforge_project:
|
|
70
114
|
rubygems_version: 1.2.0
|
71
115
|
signing_key:
|
72
116
|
specification_version: 2
|
73
|
-
summary:
|
74
|
-
test_files:
|
75
|
-
|
76
|
-
- spec/spec_helper.rb
|
77
|
-
- spec/client_spec.rb
|
78
|
-
- spec/cmds_spec.rb
|
79
|
-
- spec/server_spec.rb
|
117
|
+
summary: Reverse tunnel server and client.
|
118
|
+
test_files: []
|
119
|
+
|
data/ab_test.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
#require 'facets'
|
2
|
-
require 'net/http'
|
3
|
-
require 'logger'
|
4
|
-
require 'stringio'
|
5
|
-
require 'webrick'
|
6
|
-
|
7
|
-
require 'lib/core'
|
8
|
-
|
9
|
-
puts "make sure a fast responding webserver is started on port 4000"
|
10
|
-
|
11
|
-
base_dir = File.dirname(__FILE__)
|
12
|
-
|
13
|
-
fork{ exec "ruby #{base_dir}/rtunnel_server.rb > /dev/null 2>&1" }
|
14
|
-
fork{ exec "ruby #{base_dir}/rtunnel_client.rb -c localhost -f 5000 -t 4000 > /dev/null" }
|
15
|
-
|
16
|
-
sleep 2
|
17
|
-
|
18
|
-
(puts "you need ab (apache bench) to run this stress test" ; exit) if %x{which ab}.empty?
|
19
|
-
|
20
|
-
Process.wait fork { exec("ab -c 500 -n 10000 http://localhost:5000/") }
|
21
|
-
|
22
|
-
puts "done, hit ^C"
|
23
|
-
sleep 999999
|
data/lib/client.rb
DELETED
@@ -1,150 +0,0 @@
|
|
1
|
-
require 'core'
|
2
|
-
require 'cmds'
|
3
|
-
require 'leak'
|
4
|
-
|
5
|
-
require 'gserver'
|
6
|
-
require 'timeout'
|
7
|
-
|
8
|
-
module RTunnel
|
9
|
-
class Client
|
10
|
-
CONNECTIONS = {}
|
11
|
-
|
12
|
-
def initialize(options = {})
|
13
|
-
@control_address = options[:control_address]
|
14
|
-
@control_address << ":#{DEFAULT_CONTROL_PORT}" if @control_address !~ /:\d+$/
|
15
|
-
@control_host = @control_address.split(/:/).first
|
16
|
-
|
17
|
-
@remote_listen_address = options[:remote_listen_address]
|
18
|
-
@remote_listen_address.insert 0, "0.0.0.0:" if @remote_listen_address =~ /^\d+$/
|
19
|
-
|
20
|
-
@tunnel_to_address = options[:tunnel_to_address]
|
21
|
-
@tunnel_to_address.insert 0, "localhost:" if @tunnel_to_address =~ /^\d+$/
|
22
|
-
|
23
|
-
[@control_address, @remote_listen_address, @tunnel_to_address].each do |addr|
|
24
|
-
addr.replace_with_ip!
|
25
|
-
end
|
26
|
-
|
27
|
-
@ping_timeout = options[:ping_timeout] || PING_TIMEOUT
|
28
|
-
end
|
29
|
-
|
30
|
-
def start
|
31
|
-
@threads = []
|
32
|
-
|
33
|
-
@last_ping = Time.now
|
34
|
-
|
35
|
-
@threads << Thread.safe do
|
36
|
-
loop do
|
37
|
-
if @check_ping and (Time.now - @last_ping) > @ping_timeout
|
38
|
-
D "control connection timeout"
|
39
|
-
@control_sock.close rescue nil
|
40
|
-
end
|
41
|
-
|
42
|
-
sleep 1
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
# Memory leak testing
|
47
|
-
LeakTracker.start
|
48
|
-
|
49
|
-
@main_thread = Thread.safe do
|
50
|
-
loop do
|
51
|
-
stop_ping_check
|
52
|
-
D "connecting to control address (#{@control_address})"
|
53
|
-
@control_sock = begin
|
54
|
-
timeout(5) { TCPSocket.new(*@control_address.split(/:/)) }
|
55
|
-
rescue Exception
|
56
|
-
D "fail"
|
57
|
-
sleep 1
|
58
|
-
next
|
59
|
-
end
|
60
|
-
|
61
|
-
start_ping_check
|
62
|
-
|
63
|
-
write_to_control_sock RemoteListenCommand.new(@remote_listen_address)
|
64
|
-
|
65
|
-
cmd_queue = ""
|
66
|
-
while data = (@control_sock.readpartial(16384) rescue (D "control sock read error: #{$!.inspect}"; nil))
|
67
|
-
cmd_queue << data
|
68
|
-
while Command.match(cmd_queue)
|
69
|
-
case command = Command.parse(cmd_queue)
|
70
|
-
when PingCommand
|
71
|
-
@last_ping = Time.now
|
72
|
-
when CreateConnectionCommand
|
73
|
-
begin
|
74
|
-
# TODO: this currently blocks, but if we put it in thread, a SendDataCommand may try to get run for this connection before the connection exists
|
75
|
-
CONNECTIONS[command.conn_id] = TCPSocket.new(*@tunnel_to_address.split(/:/))
|
76
|
-
|
77
|
-
Thread.safe do
|
78
|
-
cmd = command
|
79
|
-
conn = CONNECTIONS[cmd.conn_id]
|
80
|
-
|
81
|
-
begin
|
82
|
-
while localdata = conn.readpartial(16834)
|
83
|
-
write_to_control_sock SendDataCommand.new(cmd.conn_id, localdata)
|
84
|
-
end
|
85
|
-
rescue Exception
|
86
|
-
begin
|
87
|
-
D "to tunnel closed, closing from tunnel"
|
88
|
-
conn.close
|
89
|
-
CONNECTIONS.delete cmd.conn_id
|
90
|
-
write_to_control_sock CloseConnectionCommand.new(cmd.conn_id)
|
91
|
-
rescue
|
92
|
-
p $!
|
93
|
-
puts $!.backtrace.join("\n")
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
rescue Exception
|
98
|
-
D "error connecting to local port"
|
99
|
-
write_to_control_sock CloseConnectionCommand.new(command.conn_id)
|
100
|
-
end
|
101
|
-
when CloseConnectionCommand
|
102
|
-
D "closing connection #{command.conn_id}"
|
103
|
-
if connection = CONNECTIONS[command.conn_id]
|
104
|
-
# TODO: how the hell do u catch a .close error?
|
105
|
-
connection.close_read
|
106
|
-
#connection.close unless connection.closed?
|
107
|
-
CONNECTIONS.delete(command.conn_id)
|
108
|
-
end
|
109
|
-
when SendDataCommand
|
110
|
-
if connection = CONNECTIONS[command.conn_id]
|
111
|
-
connection.write(command.data)
|
112
|
-
else
|
113
|
-
puts "WARNING: received data for non existant connection!"
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
@threads << @main_thread
|
122
|
-
end
|
123
|
-
|
124
|
-
def join
|
125
|
-
@main_thread.join
|
126
|
-
end
|
127
|
-
|
128
|
-
def stop
|
129
|
-
@threads.each { |t| t.kill! rescue nil}
|
130
|
-
@control_sock.close rescue nil
|
131
|
-
end
|
132
|
-
|
133
|
-
private
|
134
|
-
|
135
|
-
def write_to_control_sock(data)
|
136
|
-
(@control_sock_mutex ||= Mutex.new).synchronize do
|
137
|
-
@control_sock.write data
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
def start_ping_check
|
142
|
-
@last_ping = Time.now
|
143
|
-
@check_ping = true
|
144
|
-
end
|
145
|
-
|
146
|
-
def stop_ping_check
|
147
|
-
@check_ping = false
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
data/lib/cmds.rb
DELETED
@@ -1,166 +0,0 @@
|
|
1
|
-
module RTunnel
|
2
|
-
class Command
|
3
|
-
|
4
|
-
def to_s
|
5
|
-
CLASSES_TO_CODES[self.class].dup
|
6
|
-
end
|
7
|
-
|
8
|
-
class << self
|
9
|
-
def parse(data)
|
10
|
-
klass = class_from_code(data[0..0])
|
11
|
-
|
12
|
-
new_data = data[1..-1]
|
13
|
-
cmd = klass.parse(new_data)
|
14
|
-
|
15
|
-
data.replace(new_data)
|
16
|
-
|
17
|
-
cmd
|
18
|
-
end
|
19
|
-
|
20
|
-
def match(data)
|
21
|
-
return false if ! (klass = class_from_code(data[0..0]))
|
22
|
-
|
23
|
-
klass.match(data[1..-1])
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
def class_from_code(code)
|
29
|
-
CODES_TO_CLASSES[code]
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
class ConnectionCommand < Command
|
35
|
-
attr_reader :conn_id
|
36
|
-
|
37
|
-
def initialize(conn_id)
|
38
|
-
@conn_id = conn_id
|
39
|
-
end
|
40
|
-
|
41
|
-
def to_s
|
42
|
-
super + "#{conn_id}|"
|
43
|
-
end
|
44
|
-
|
45
|
-
class << self
|
46
|
-
RE = %r{^([^|]+)\|}
|
47
|
-
|
48
|
-
def parse(data)
|
49
|
-
data =~ RE
|
50
|
-
conn_id = $1
|
51
|
-
|
52
|
-
cmd = self.new(conn_id)
|
53
|
-
|
54
|
-
data.sub! RE, ''
|
55
|
-
|
56
|
-
cmd
|
57
|
-
end
|
58
|
-
|
59
|
-
def match(data)
|
60
|
-
!! (data =~ RE)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
end
|
65
|
-
|
66
|
-
class CreateConnectionCommand < ConnectionCommand
|
67
|
-
end
|
68
|
-
|
69
|
-
class CloseConnectionCommand < ConnectionCommand
|
70
|
-
end
|
71
|
-
|
72
|
-
class SendDataCommand < Command
|
73
|
-
attr_reader :conn_id
|
74
|
-
attr_reader :data
|
75
|
-
|
76
|
-
def initialize(conn_id, data)
|
77
|
-
@conn_id = conn_id
|
78
|
-
@data = data
|
79
|
-
end
|
80
|
-
|
81
|
-
def to_s
|
82
|
-
super + "#{conn_id}|#{data.size}|#{data}"
|
83
|
-
end
|
84
|
-
|
85
|
-
class << self
|
86
|
-
RE = %r{^([^|]+)\|([^|]+)\|}
|
87
|
-
|
88
|
-
def parse(data)
|
89
|
-
data =~ RE
|
90
|
-
|
91
|
-
conn_id = $1
|
92
|
-
data_size = $2.to_i
|
93
|
-
|
94
|
-
new_data = data.sub(RE, '')
|
95
|
-
cmd_data = new_data[0,data_size]
|
96
|
-
|
97
|
-
cmd = SendDataCommand.new(conn_id, cmd_data)
|
98
|
-
|
99
|
-
data.replace(new_data[data_size..-1])
|
100
|
-
|
101
|
-
cmd
|
102
|
-
end
|
103
|
-
|
104
|
-
def match(data)
|
105
|
-
return false if ! (data =~ RE)
|
106
|
-
|
107
|
-
data_size = $2.to_i
|
108
|
-
|
109
|
-
data.sub(RE, '').size >= data_size
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
class RemoteListenCommand < Command
|
115
|
-
attr_reader :address
|
116
|
-
|
117
|
-
def initialize(address)
|
118
|
-
@address = address
|
119
|
-
end
|
120
|
-
|
121
|
-
def to_s
|
122
|
-
super + "#{address}|"
|
123
|
-
end
|
124
|
-
|
125
|
-
class << self
|
126
|
-
RE = %r{^([^|]+)\|}
|
127
|
-
|
128
|
-
def parse(data)
|
129
|
-
data =~ RE
|
130
|
-
address = $1
|
131
|
-
|
132
|
-
cmd = self.new(address)
|
133
|
-
|
134
|
-
data.sub! RE, ''
|
135
|
-
|
136
|
-
cmd
|
137
|
-
end
|
138
|
-
|
139
|
-
def match(data)
|
140
|
-
!! (data =~ RE)
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
end
|
145
|
-
|
146
|
-
class PingCommand < Command
|
147
|
-
def self.parse(data)
|
148
|
-
PingCommand.new
|
149
|
-
end
|
150
|
-
|
151
|
-
def self.match(data)
|
152
|
-
true
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
class Command
|
157
|
-
CODES_TO_CLASSES = {
|
158
|
-
"C" => CreateConnectionCommand,
|
159
|
-
"X" => CloseConnectionCommand,
|
160
|
-
"D" => SendDataCommand,
|
161
|
-
"P" => PingCommand,
|
162
|
-
"L" => RemoteListenCommand,
|
163
|
-
}
|
164
|
-
CLASSES_TO_CODES = CODES_TO_CLASSES.invert
|
165
|
-
end
|
166
|
-
end
|