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
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'rtunnel'
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
class SocketFactoryTest < Test::Unit::TestCase
|
6
|
+
SF = RTunnel::SocketFactory
|
7
|
+
|
8
|
+
def setup
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
def teardown
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_host_from_address
|
17
|
+
assert_equal nil, SF.host_from_address(nil)
|
18
|
+
assert_equal '127.0.0.1', SF.host_from_address('127.0.0.1')
|
19
|
+
assert_equal '127.0.0.1', SF.host_from_address('127.0.0.1:1234')
|
20
|
+
assert_equal 'fe80::1%lo0', SF.host_from_address('fe80::1%lo0')
|
21
|
+
assert_equal 'fe80::1%lo0', SF.host_from_address('fe80::1%lo0:19020')
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_port_from_address
|
25
|
+
assert_equal nil, SF.port_from_address(nil)
|
26
|
+
assert_equal nil, SF.port_from_address('127.0.0.1')
|
27
|
+
assert_equal 1234, SF.port_from_address('127.0.0.1:1234')
|
28
|
+
assert_equal nil, SF.port_from_address('fe80::1%lo0')
|
29
|
+
assert_equal 19020, SF.port_from_address('fe80::1%lo0:19020')
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_inbound
|
33
|
+
assert SF.inbound?(:in_port => 1)
|
34
|
+
assert SF.inbound?(:in_host => '1')
|
35
|
+
assert SF.inbound?(:in_addr => '1')
|
36
|
+
assert SF.inbound?(:inbound => true)
|
37
|
+
assert !SF.inbound?(:out_port => 1)
|
38
|
+
assert !SF.inbound?(:out_host => '1')
|
39
|
+
assert !SF.inbound?(:out_addr => '1')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
data/test/test_tunnel.rb
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'rtunnel'
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'eventmachine'
|
7
|
+
|
8
|
+
require 'test/scenario_connection.rb'
|
9
|
+
|
10
|
+
# Integration tests ensuring that we can start a tunnel.
|
11
|
+
class TunnelTest < Test::Unit::TestCase
|
12
|
+
def setup
|
13
|
+
super
|
14
|
+
|
15
|
+
@connection_time = 0.001
|
16
|
+
@secure_connection_time = 0.5
|
17
|
+
@log_level = 'debug'
|
18
|
+
@local_host = '127.0.0.1'
|
19
|
+
@listen_port = 21335
|
20
|
+
@tunnel_port = 21336
|
21
|
+
@control_port = 21337
|
22
|
+
@key_file = 'test_data/ssh_host_rsa_key'
|
23
|
+
@hosts_file = 'test_data/known_hosts'
|
24
|
+
|
25
|
+
@tunnel_server = new_server
|
26
|
+
@tunnel_client = new_client
|
27
|
+
end
|
28
|
+
|
29
|
+
def new_server(extra_options = {})
|
30
|
+
RTunnel::Server.new({
|
31
|
+
:control_address => "#{@local_host}:#{@control_port}",
|
32
|
+
:log_level => @log_level
|
33
|
+
}.merge(extra_options))
|
34
|
+
end
|
35
|
+
|
36
|
+
def new_client(extra_options = {})
|
37
|
+
RTunnel::Client.new({
|
38
|
+
:control_address => "#{@local_host}:#{@control_port}",
|
39
|
+
:remote_listen_address => "#{@local_host}:#{@listen_port}",
|
40
|
+
:tunnel_to_address => "#{@local_host}:#{@tunnel_port}",
|
41
|
+
:log_level => @log_level
|
42
|
+
}.merge(extra_options))
|
43
|
+
end
|
44
|
+
|
45
|
+
def tunnel_test(connection_time = nil)
|
46
|
+
@stop_proc = proc do
|
47
|
+
@tunnel_client.stop
|
48
|
+
@tunnel_server.stop
|
49
|
+
@stop_proc = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
EventMachine::run do
|
53
|
+
@tunnel_server.start
|
54
|
+
@tunnel_client.start
|
55
|
+
|
56
|
+
if connection_time
|
57
|
+
EventMachine.add_timer(connection_time) { yield }
|
58
|
+
else
|
59
|
+
EventMachine.next_tick { yield }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_client_driven_tunnel
|
65
|
+
tunnel_test do
|
66
|
+
@tunnel_server.on_remote_listen do
|
67
|
+
EventMachine::start_server @local_host, @tunnel_port,
|
68
|
+
ScenarioConnection, self, [[:recv, 'Hello'], [:send, 'World'],
|
69
|
+
[:unbind], [:stop, @stop_proc]]
|
70
|
+
|
71
|
+
EventMachine::connect @local_host, @listen_port,
|
72
|
+
ScenarioConnection, self, [[:send, 'Hello'], [:recv, 'World'],
|
73
|
+
[:close]]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_server_driven_tunnel
|
79
|
+
tunnel_test do
|
80
|
+
@tunnel_server.on_remote_listen do
|
81
|
+
EventMachine::start_server @local_host, @tunnel_port,
|
82
|
+
ScenarioConnection, self, [[:send, 'Hello'], [:recv, 'World'],
|
83
|
+
[:close]]
|
84
|
+
|
85
|
+
EventMachine::connect @local_host, @listen_port,
|
86
|
+
ScenarioConnection, self, [[:recv, 'Hello'], [:send, 'World'],
|
87
|
+
[:unbind], [:stop]]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_two_tunnels
|
93
|
+
start_second = lambda do
|
94
|
+
@tunnel_client.stop
|
95
|
+
@tunnel_client.start
|
96
|
+
@tunnel_server.on_remote_listen do
|
97
|
+
EventMachine::connect @local_host, @listen_port,
|
98
|
+
ScenarioConnection, self, [[:send, 'Hello'], [:recv, 'World'],
|
99
|
+
[:unbind], [:stop, @stop_proc]]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
tunnel_test do
|
104
|
+
@tunnel_server.on_remote_listen do
|
105
|
+
EventMachine::start_server @local_host, @tunnel_port,
|
106
|
+
ScenarioConnection, self, [[:recv, 'Hello'], [:send, 'World'],
|
107
|
+
[:close], [:recv, 'Hello'], [:send, 'World'], [:close]]
|
108
|
+
|
109
|
+
EventMachine::connect @local_host, @listen_port,
|
110
|
+
ScenarioConnection, self, [[:send, 'Hello'], [:recv, 'World'],
|
111
|
+
[:proc, start_second], [:unbind]]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_secure_tunnel
|
117
|
+
@tunnel_server = new_server :authorized_keys => @hosts_file
|
118
|
+
@tunnel_client = new_client :private_key => @key_file
|
119
|
+
tunnel_test do
|
120
|
+
@tunnel_server.on_remote_listen do
|
121
|
+
EventMachine::start_server @local_host, @tunnel_port,
|
122
|
+
ScenarioConnection, self, [[:recv, 'Hello'], [:send, 'World'],
|
123
|
+
[:unbind], [:stop, @stop_proc]]
|
124
|
+
|
125
|
+
EventMachine::connect @local_host, @listen_port,
|
126
|
+
ScenarioConnection, self, [[:send, 'Hello'], [:recv, 'World'],
|
127
|
+
[:close]]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_secure_async_tunnel
|
133
|
+
@tunnel_server = new_server :authorized_keys => @hosts_file
|
134
|
+
@tunnel_client = new_client :private_key => @key_file
|
135
|
+
tunnel_test do
|
136
|
+
@tunnel_server.on_remote_listen do
|
137
|
+
EventMachine::start_server @local_host, @tunnel_port,
|
138
|
+
ScenarioConnection, self, [[:send, 'World'], [:recv, 'Hello'],
|
139
|
+
[:unbind], [:stop, @stop_proc]]
|
140
|
+
|
141
|
+
EventMachine::connect @local_host, @listen_port,
|
142
|
+
ScenarioConnection, self, [[:send, 'Hello'], [:recv, 'World'],
|
143
|
+
[:close]]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_bad_listen_address
|
149
|
+
@tunnel_server = new_server
|
150
|
+
@tunnel_client = new_client :remote_listen_address =>
|
151
|
+
"18.70.0.160:#{@listen_port}"
|
152
|
+
|
153
|
+
tunnel_test do
|
154
|
+
EventMachine::start_server @local_host, @tunnel_port,
|
155
|
+
ScenarioConnection, self, []
|
156
|
+
|
157
|
+
EventMachine::connect @local_host, @listen_port,
|
158
|
+
ScenarioConnection, self, [[:unbind], [:stop, @stop_proc]]
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# TODO: fix this
|
163
|
+
def test_secure_server_rejects_unsecure_client
|
164
|
+
@tunnel_server = new_server :authorized_keys => @hosts_file
|
165
|
+
tunnel_test(@secure_connection_time) do
|
166
|
+
EventMachine::start_server @local_host, @tunnel_port,
|
167
|
+
ScenarioConnection, self, []
|
168
|
+
|
169
|
+
EventMachine::connect @local_host, @listen_port,
|
170
|
+
ScenarioConnection, self, [[:unbind], [:stop, @stop_proc]]
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# hrmf... this is testing security and its not 100% reliable... but I can't think of a better way
|
175
|
+
def test_secure_server_rejects_unauthorized_key
|
176
|
+
@tunnel_server = new_server :authorized_keys => @hosts_file
|
177
|
+
@tunnel_client = new_client :private_key => 'test_data/random_rsa_key'
|
178
|
+
tunnel_test(@secure_connection_time) do
|
179
|
+
EventMachine::start_server @local_host, @tunnel_port,
|
180
|
+
ScenarioConnection, self, []
|
181
|
+
|
182
|
+
EventMachine::connect @local_host, @listen_port,
|
183
|
+
ScenarioConnection, self, [[:unbind], [:stop, @stop_proc]]
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,4 @@
|
|
1
|
+
no-port-forwarding,no-agent-forwarding,command="/usr/NX/bin/nxnode" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== stub@github.org
|
2
|
+
no-port-forwarding,no-agent-forwarding,command="/usr/NX/bin/nxnode" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq7VciLo2wB+/2ThHXejC/kgkN205zlyS5cvN40Mqi3qvRVS75X1RawDoLot8eJ9KCYqZFr2Dr73d/EQNltN7dKJoPIj1IKxoraWkyNFbhhzpYuOltg9oO5UBSTNLupqla7zcdj4IwCCkBYk4+TS0dwmi20buOJ0FPY5PgbzmMnUiV9ipBqeJSdZB+TePH1gqlt7AP/6ti/0gxb2K7F69dZl/BSxMEzRCfBlTFC3f/4n8IdCuSJvNxxY+TtRnLL5CKUhj9QaIBan6JCkdRvVOBY7wmsNT8nGDzfDFSDD3KKn93g4LRkyMeaYlSDLxKy8PnNhjWgBNH1YNYyicsGfBKQ== costan@obsidian.local
|
3
|
+
no-port-forwarding,no-agent-forwarding,command="/usr/NX/bin/nxnode" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA3tPhdPUFGAYyrT1quRSOevLbAdKAJ6Ovwqw0m99R0QkqUwMUh09pgedWZeij7HtAHtoWPrNFev8FrwcwnL14NgA/gwNnXxbqd4twC1HyFShUf7POry8bz3Qk+84STHeMY8++hhn8LgNyfuVQswHoW661aqieLM6pF8q8xIUtkXA7daNpJAL4nTN1TxgUoCDpCa0EbUPkGpwFPNtGPuokRXNOCR9g8T6LmPQbzGUTc4CzFfQ9rrHaimqkEmRWJbBOaik1bdQNqOh6MUDDuUSpkJV7fwu3bl4fF5/1kw2HCREEjJmESOYnZhOS+MCp1qAUcuXqpMqXD8ATNsuXqrIhTQ== stub@rubyforge.org
|
4
|
+
no-port-forwarding,no-agent-forwarding,command="/usr/NX/bin/nxnode" ssh-dss AAAAB3NzaC1kc3MAAACBAPdWp4HhWUoNawosyEBvrhPSbjhCJiKnVWiUS6bo0BCGzTHhugrkv2HgtlKhWo8nqTw5E4YxzFVyZ0YQt4m7NYDLTZVjrqbIpL/3F5qNXco127O/im0cG27AKC8Jf7knmUTjd8EBhtK65tNDmxPtzKQtemlNTVPX1VccOn6eLtn1AAAAFQDtQC21TJrf/p5WNsU9UIJzO9/hIwAAAIEAgwTrIfleQMEAK9N3xeMVZpAGfSAoX6owLtk3z+iQ3rM9FRvM/CgezOgezLowJghkw/bcQDmMuudBUuijrM3zZWdr6eqoNbFTR/KKiUx3cYf0LAHNPbXfVz+P7BXqjcEj75qnwuHQMp7vNMg+dmV40UA2TiC5/8QlaZVwkOSPN6gAAACAPMGJEFkoR0ayfkd0S/tnY9ilO17T6rdoDuF25ATtNUd6Zji6tslxBkQFWtTeinO3rGkqJRPndq0wp3E33AOHhJE/FOIlWl4Tf6aeU95Y4enYujKQDiImSTXmdiw5wq/LFdc3a2waOUvuI+647wxgHhqTmD7xI2biGZLYN9Oasy0= costan@obsidian.local
|
@@ -0,0 +1,4 @@
|
|
1
|
+
github.com,65.74.177.129 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
|
2
|
+
obsidian.local ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq7VciLo2wB+/2ThHXejC/kgkN205zlyS5cvN40Mqi3qvRVS75X1RawDoLot8eJ9KCYqZFr2Dr73d/EQNltN7dKJoPIj1IKxoraWkyNFbhhzpYuOltg9oO5UBSTNLupqla7zcdj4IwCCkBYk4+TS0dwmi20buOJ0FPY5PgbzmMnUiV9ipBqeJSdZB+TePH1gqlt7AP/6ti/0gxb2K7F69dZl/BSxMEzRCfBlTFC3f/4n8IdCuSJvNxxY+TtRnLL5CKUhj9QaIBan6JCkdRvVOBY7wmsNT8nGDzfDFSDD3KKn93g4LRkyMeaYlSDLxKy8PnNhjWgBNH1YNYyicsGfBKQ==
|
3
|
+
rubyforge.org,205.234.109.19 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA3tPhdPUFGAYyrT1quRSOevLbAdKAJ6Ovwqw0m99R0QkqUwMUh09pgedWZeij7HtAHtoWPrNFev8FrwcwnL14NgA/gwNnXxbqd4twC1HyFShUf7POry8bz3Qk+84STHeMY8++hhn8LgNyfuVQswHoW661aqieLM6pF8q8xIUtkXA7daNpJAL4nTN1TxgUoCDpCa0EbUPkGpwFPNtGPuokRXNOCR9g8T6LmPQbzGUTc4CzFfQ9rrHaimqkEmRWJbBOaik1bdQNqOh6MUDDuUSpkJV7fwu3bl4fF5/1kw2HCREEjJmESOYnZhOS+MCp1qAUcuXqpMqXD8ATNsuXqrIhTQ==
|
4
|
+
obsidian.local ssh-dss AAAAB3NzaC1kc3MAAACBAPdWp4HhWUoNawosyEBvrhPSbjhCJiKnVWiUS6bo0BCGzTHhugrkv2HgtlKhWo8nqTw5E4YxzFVyZ0YQt4m7NYDLTZVjrqbIpL/3F5qNXco127O/im0cG27AKC8Jf7knmUTjd8EBhtK65tNDmxPtzKQtemlNTVPX1VccOn6eLtn1AAAAFQDtQC21TJrf/p5WNsU9UIJzO9/hIwAAAIEAgwTrIfleQMEAK9N3xeMVZpAGfSAoX6owLtk3z+iQ3rM9FRvM/CgezOgezLowJghkw/bcQDmMuudBUuijrM3zZWdr6eqoNbFTR/KKiUx3cYf0LAHNPbXfVz+P7BXqjcEj75qnwuHQMp7vNMg+dmV40UA2TiC5/8QlaZVwkOSPN6gAAACAPMGJEFkoR0ayfkd0S/tnY9ilO17T6rdoDuF25ATtNUd6Zji6tslxBkQFWtTeinO3rGkqJRPndq0wp3E33AOHhJE/FOIlWl4Tf6aeU95Y4enYujKQDiImSTXmdiw5wq/LFdc3a2waOUvuI+647wxgHhqTmD7xI2biGZLYN9Oasy0=
|
@@ -0,0 +1,27 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIIEowIBAAKCAQEAoSWzcqViB/JUz6Zd80c3km114/OWfxbGo/xeZqJm4s9+kR8o
|
3
|
+
xtFjfbj9oDiuckSkAPNQW5VUWCeeNdpNjLmBe4Jm40R+FfibLOR5lw5I/7jVozBZ
|
4
|
+
gQKuF395ODgbOwGXMsmRc1xViDYsOzo4A/f+5WhbpG20AtBz6+mg3yDLWXDffhat
|
5
|
+
ecRf7PvWvlkj0QtL6538a7OE4MY75pCiTLWWbHJnBoQgNmYFZZdBaarHsenlKSQ9
|
6
|
+
P6xL6ENyDOom3/Xk9bvqHQNNvgHyN4e4PRAtZUqsjyoRMm8sfDIJhK+cvpvqqWWm
|
7
|
+
QK1ef1Fb1yXnmjkmUfx15UXxGgB79v3/vYWmJwIBIwKCAQBugECJIPLDntsSC64j
|
8
|
+
KYUxNR2jn7enmpbW2PBGYLuUNoKsp6buu3dsJxRQm+VVq2klOSEo220JP7zxukPO
|
9
|
+
Ng+lJjfsTDkzoyiTz95Y89pI88WxusEIAdZ2g0vOx2MhJamB6U3LcoPLHdUv7Wg9
|
10
|
+
PFcDtUYnm66i2BT5ikJtHc1EpBTte71pZVRQsHYEsgcnNfsIc9hpXZacQ8CxF3Cc
|
11
|
+
gM35GRSC7z/UVouyMbhQCjeKrlR/aZIE1whpnWpdOa+AeYtdndwxn6gNeplV8lA1
|
12
|
+
8J0mpQlsy9i8cl2Nx/euN+dAW87zI3kwm1znj4pHF5e12v+SziZqnjuI4PhxNJO1
|
13
|
+
CSRLAoGBAM9WfBRkVuWv+RdVm5VBpSwvBDytJpqcSbTxkypmV7sN6I4A2rBcNws9
|
14
|
+
IaJ7Bf+ZxG4R8THXg5jCt1yfjIwMYrQ1Jg7aVALwF+JzeZgGTWFYJwCcC5VXCvy7
|
15
|
+
gsJhnboBPVuM3Vxhz98nHKUngbLDbV8Sl+DVp1NDJ+tDJqY6tVZNAoGBAMb38M+G
|
16
|
+
ZF9HVr33Oe7LjLxQRiQk5G6gmbidZfOszK797/BxYd1vMMPEl8d56zcYkeaIDmvL
|
17
|
+
xHGLhoT6vtA4N0WCuk96USBk7JrRYhXoGTw0yFOJyopzPWdR66iaVbF/g1aknzki
|
18
|
+
pZfb2LeyocztlxKJTUe0IocEkHu5aqzFeFBDAoGBAI4srNrIdhnXwL+LKNtgNr82
|
19
|
+
LsqFXEzC8LaXBdPuaAs8vLklKD5rHm4bSkOHjxWrRN3DKQw8Andg8sMrk5M7sWWg
|
20
|
+
yaPI6SaWAb/aJ34wNQ+M54tjsCvj6kbm+pPrKlOpFCGFKhN2RWXRrT4MdjFwEHu0
|
21
|
+
+m5JXMthP/HHpXlM1B3rAoGBAMFIoMmYfsL01+u89nLxnqhN+v6KPP3AlVRBITXb
|
22
|
+
D/pzBldJkkTSaeK++de4Q5SbhmrqkaqLbl+sHEaqcDf8GG9pDMI8Ts8C9HkjLBVH
|
23
|
+
3f/4wppLVwomze9W5OztslSnwWocQucLts4Iw+WmNscSsANgzrqu/PgwUddGZ6CF
|
24
|
+
UE33AoGBAJ3VCQx0hwDSIQuuakjUb5ouPup+8B1A7wspGirkQNlTZ1F2q4Zug/Fp
|
25
|
+
dovyzPZuV3/0dVDNbQzz21gRuiE2I/l9uF4Bz2LqY1Jg1R9WuQH5sdRG0ncf6Ikn
|
26
|
+
1LRLgE2tCGGfhLUi2vDMwORx1o46p4hboF8ZaOBK1+WIwNnfRGNl
|
27
|
+
-----END RSA PRIVATE KEY-----
|
@@ -0,0 +1,12 @@
|
|
1
|
+
-----BEGIN DSA PRIVATE KEY-----
|
2
|
+
MIIBvAIBAAKBgQD3VqeB4VlKDWsKLMhAb64T0m44QiYip1VolEum6NAQhs0x4boK
|
3
|
+
5L9h4LZSoVqPJ6k8OROGMcxVcmdGELeJuzWAy02VY66myKS/9xeajV3KNduzv4pt
|
4
|
+
HBtuwCgvCX+5J5lE43fBAYbSuubTQ5sT7cykLXppTU1T19VXHDp+ni7Z9QIVAO1A
|
5
|
+
LbVMmt/+nlY2xT1QgnM73+EjAoGBAIME6yH5XkDBACvTd8XjFWaQBn0gKF+qMC7Z
|
6
|
+
N8/okN6zPRUbzPwoHszoHsy6MCYIZMP23EA5jLrnQVLoo6zN82Vna+nqqDWxU0fy
|
7
|
+
iolMd3GH9CwBzT2131c/j+wV6o3BI++ap8Lh0DKe7zTIPnZleNFANk4guf/EJWmV
|
8
|
+
cJDkjzeoAoGAPMGJEFkoR0ayfkd0S/tnY9ilO17T6rdoDuF25ATtNUd6Zji6tslx
|
9
|
+
BkQFWtTeinO3rGkqJRPndq0wp3E33AOHhJE/FOIlWl4Tf6aeU95Y4enYujKQDiIm
|
10
|
+
STXmdiw5wq/LFdc3a2waOUvuI+647wxgHhqTmD7xI2biGZLYN9Oasy0CFQDL/9Z1
|
11
|
+
fSLn88m1sUeWAZ4Ys2IIxw==
|
12
|
+
-----END DSA PRIVATE KEY-----
|
@@ -0,0 +1,27 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIIEoQIBAAKCAQEAq7VciLo2wB+/2ThHXejC/kgkN205zlyS5cvN40Mqi3qvRVS7
|
3
|
+
5X1RawDoLot8eJ9KCYqZFr2Dr73d/EQNltN7dKJoPIj1IKxoraWkyNFbhhzpYuOl
|
4
|
+
tg9oO5UBSTNLupqla7zcdj4IwCCkBYk4+TS0dwmi20buOJ0FPY5PgbzmMnUiV9ip
|
5
|
+
BqeJSdZB+TePH1gqlt7AP/6ti/0gxb2K7F69dZl/BSxMEzRCfBlTFC3f/4n8IdCu
|
6
|
+
SJvNxxY+TtRnLL5CKUhj9QaIBan6JCkdRvVOBY7wmsNT8nGDzfDFSDD3KKn93g4L
|
7
|
+
RkyMeaYlSDLxKy8PnNhjWgBNH1YNYyicsGfBKQIBIwKCAQA/xwUc13Nr7okWKtit
|
8
|
+
2hyKVU9HyXve7y8/aPSzf1jx+l5blIBN7LfXSXrPdaNCvtJbULyEygxXN+S8yNHY
|
9
|
+
73b/b4XNV3D9gd281x/y0WsjL09fPpyitUP435LDatL8Ks+6TXZ1D7obedaFtqAi
|
10
|
+
DEMHpH5Rch33xUsW3RY3gK1F8GHzYh+zMd0llH8w6IVBv0hrWttyp+4By1dhXA3L
|
11
|
+
alYYOlGbu5cNuJfz50v4T/uB9F63JqHtAxEjqD7SvjCUbogGPxEH1k4Or+3xGuvR
|
12
|
+
yTPaiuR9kehBstjbqc91rTLnGOr5a1VUIq6++0tCVOXQa+x5J5zCu/baGTdfkgRU
|
13
|
+
loILAoGBAOHQcGIEgD1xHwFX9AafarF+1j4pPkM1p2u+9DNTIl1f2Rk6yOHlHZGF
|
14
|
+
mfozMyh6l4HpvPTT7nflM4YR0JWJNbQIpCY8CdYKMXBVWcH4PXuqwyGKJBZMXWkn
|
15
|
+
hKvelJ0UZUwx5+n8fTmSxLcIIlz5uKEE5k23/3cKkP7P6+Z6YuIZAoGBAMKpYBkM
|
16
|
+
A4JMJ3QJ5TVsdFOoO0b1wThRgQTZR6ic9rNeXnZiL0mmr44DX2T1rTnEm6KZv2f2
|
17
|
+
kWLQp5jx8BPFI+TUv16j4xrnnuHPhuBordCYQizSPcxXqL8b8a/KU+S5xofRjwB6
|
18
|
+
+8szd/JmGFDY8WgWrtIL32TdRhcIFg0Dg1mRAoGAE1sCUYtbcvsRSUINmirr41QD
|
19
|
+
vC9rvJ4y6/pss/Eu1M2zhdHWtEbWphLEDiGlTJzLKGR96Rl61xOlVKJw9t/gCB3/
|
20
|
+
cP3U9RbRCaDqb7YxJ9tvz6zBQ71nF6RNM02XtbFKgt+0yunBlzh3QuNwqOI0ZZK0
|
21
|
+
p5Ntqx4pr3DoVZV2MKMCgYBprGdeDdYE54MhvDqZWCHkRWIB8x+/fLPAzbkvpap+
|
22
|
+
oPFzdyD8GKhxqg82zoKbs991hqm8GCMJwboRMuFp0WtBtVHxjCrUF1ZAEZJckJje
|
23
|
+
85GjTY9DCwPVdZHUdSY6VjiS33mD6v23c7YkgJDbbnRrtIrJy+5MsqJkRjfbLcr2
|
24
|
+
GwKBgQCWDKVM8mZJcmM3KJwzBWh+b5T7tIbOBFaCvOBq4G0UUZGytWVUaddSbdh5
|
25
|
+
YJ+1CoLDB+Wp3U95B1USQuSYwwr0wOYy42HBII7HnkaphT9HgDIhgvttFhX162oC
|
26
|
+
bRizr31NgiGtgJbHJ9QPhaLvn3mZtZAFfRaEEdZslUzqRSACww==
|
27
|
+
-----END RSA PRIVATE KEY-----
|
data/tests/_ab_test.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
base_dir = File.dirname(__FILE__)
|
2
|
+
lo_server_file = File.join(base_dir, '../tests/lo_http_server.rb')
|
3
|
+
lo_port = 4000
|
4
|
+
Kernel.system "ruby #{lo_server_file} start #{lo_port}"
|
5
|
+
|
6
|
+
fork{ exec "ruby #{base_dir}/../lib/rtunnel/rtunnel_server.rb > /dev/null 2>&1" }
|
7
|
+
fork{ exec "ruby #{base_dir}/../lib/rtunnel/rtunnel_client.rb -c localhost -f 5000 -t 4000 > /dev/null" }
|
8
|
+
|
9
|
+
sleep 2
|
10
|
+
|
11
|
+
(puts "you need ab (apache bench) to run this stress test" ; exit) if %x{which ab}.empty?
|
12
|
+
|
13
|
+
Process.wait fork { exec("ab -c 500 -n 10000 http://localhost:5000/") }
|
14
|
+
|
15
|
+
puts "done, hit ^C"
|
16
|
+
sleep 999999
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'logger'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
CONCURRENT_CONNECTIONS = 50
|
7
|
+
DISPLAY_RTUNNEL_OUTPUT = true
|
8
|
+
TUNNEL_PORT = 5000
|
9
|
+
HTTP_PORT = 4444
|
10
|
+
|
11
|
+
#################
|
12
|
+
|
13
|
+
TUNNEL_URI = "http://localhost:#{TUNNEL_PORT}"
|
14
|
+
srand(0) # deterministic random data
|
15
|
+
EXPECTED_DATA = Array.new(16*1024) { rand(?z).chr }*''
|
16
|
+
|
17
|
+
$pids = [$$]
|
18
|
+
|
19
|
+
def cleanup
|
20
|
+
puts $!, $@ if $!
|
21
|
+
|
22
|
+
# move the current process to the end of the kill list
|
23
|
+
$pids << $pids.delete($$)
|
24
|
+
$pids.each {|pid| Process.kill 9, pid rescue nil }
|
25
|
+
|
26
|
+
exit!
|
27
|
+
end
|
28
|
+
|
29
|
+
at_exit { cleanup }
|
30
|
+
|
31
|
+
ENV['RTUNNEL_DEBUG'] = '1' if DISPLAY_RTUNNEL_OUTPUT
|
32
|
+
|
33
|
+
base_dir = File.join(File.dirname(__FILE__), '..', 'bin')
|
34
|
+
$pids << fork{ exec "ruby", "-Ilib", "#{base_dir}/rtunnel_server" }
|
35
|
+
$pids << fork{ exec "ruby", "-Ilib", "#{base_dir}/rtunnel_client", '-c', '127.0.0.1', '-f', TUNNEL_PORT.to_s, '-t', "127.0.0.1:#{HTTP_PORT}" }
|
36
|
+
|
37
|
+
$pids << fork do
|
38
|
+
require 'thin'
|
39
|
+
|
40
|
+
app = lambda do |env|
|
41
|
+
body = env['rack.input'].string
|
42
|
+
if body != EXPECTED_DATA
|
43
|
+
p body, EXPECTED_DATA
|
44
|
+
puts "server received BAD DATA!"
|
45
|
+
|
46
|
+
cleanup
|
47
|
+
end
|
48
|
+
|
49
|
+
[200, {}, EXPECTED_DATA]
|
50
|
+
end
|
51
|
+
|
52
|
+
Thin::Server.new('localhost', HTTP_PORT, app).start
|
53
|
+
end
|
54
|
+
|
55
|
+
puts 'wait 2 secs...'
|
56
|
+
sleep 2
|
57
|
+
|
58
|
+
module Stresser
|
59
|
+
@@open_connections = 0
|
60
|
+
|
61
|
+
def post_init
|
62
|
+
@@open_connections += 1
|
63
|
+
send_data "POST / HTTP/1.0\r\nContent-Length: #{EXPECTED_DATA.size}\r\n\r\n#{EXPECTED_DATA}"
|
64
|
+
@data = ''
|
65
|
+
print '('; $stdout.flush
|
66
|
+
end
|
67
|
+
|
68
|
+
def receive_data(data)
|
69
|
+
@data << data
|
70
|
+
print '.'; $stdout.flush
|
71
|
+
end
|
72
|
+
|
73
|
+
def unbind
|
74
|
+
if @data.gsub(/\A.+\r\n\r\n/m,'') != EXPECTED_DATA
|
75
|
+
p EXPECTED_DATA, @data
|
76
|
+
puts "client received BAD DATA!"
|
77
|
+
|
78
|
+
cleanup
|
79
|
+
end
|
80
|
+
|
81
|
+
print ')'; $stdout.flush
|
82
|
+
|
83
|
+
@@open_connections -= 1
|
84
|
+
EventMachine.stop_event_loop if @@open_connections == 0
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
loop do
|
89
|
+
EventMachine.run do
|
90
|
+
CONCURRENT_CONNECTIONS.times do
|
91
|
+
EventMachine.connect 'localhost', TUNNEL_PORT, Stresser
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
puts
|
96
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'socket'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'simple-daemon'
|
7
|
+
|
8
|
+
class LoHttpServer < SimpleDaemon::Base
|
9
|
+
def send_headers(socket, status, length = 0, mime_type = 'text/html', headers = {})
|
10
|
+
reason = headers[:reason] || {200 => 'OK', 404 => 'File not found'}[status]
|
11
|
+
|
12
|
+
response_line = "HTTP/1.1 #{status} #{reason}\r\n"
|
13
|
+
|
14
|
+
response_headers = { 'Connection' => 'close', 'Server' => 'Rubylicious/1.0', 'Date' => Time.now.rfc2822,
|
15
|
+
'Content-Type' => mime_type, 'Content-Length' => length }.merge headers
|
16
|
+
|
17
|
+
socket.write response_line
|
18
|
+
socket.write response_headers.to_a.map { |k,v| k.to_s + ': ' + v.to_s }.join("\r\n") + "\r\n\r\n"
|
19
|
+
end
|
20
|
+
|
21
|
+
def send_html(socket, html_data = "Loopback page\n")
|
22
|
+
send_headers(socket, 200, html_data.length)
|
23
|
+
socket.write html_data
|
24
|
+
socket.close
|
25
|
+
end
|
26
|
+
|
27
|
+
def run(listen_port)
|
28
|
+
listen_socket = TCPServer.new listen_port
|
29
|
+
|
30
|
+
loop do
|
31
|
+
client_socket = listen_socket.accept
|
32
|
+
Thread.new do
|
33
|
+
begin
|
34
|
+
print client_socket.readpartial(16384), "\n"
|
35
|
+
send_html client_socket
|
36
|
+
rescue
|
37
|
+
print "#{$!.class.name}: #{$!}\n"
|
38
|
+
print $!.backtrace.join("\n"), "\n"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
SimpleDaemon::WORKING_DIRECTORY = File.dirname(__FILE__)
|
45
|
+
|
46
|
+
def self.start
|
47
|
+
port = ARGV[1] || 3000
|
48
|
+
self.new.run(port)
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.stop
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
LoHttpServer.daemonize
|