coderrr-rtunnel 0.3.9 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/CHANGELOG +13 -0
  2. data/LICENSE +21 -0
  3. data/Manifest +48 -0
  4. data/README.markdown +40 -15
  5. data/Rakefile +31 -4
  6. data/bin/rtunnel_client +2 -1
  7. data/bin/rtunnel_server +2 -1
  8. data/lib/rtunnel.rb +20 -0
  9. data/lib/rtunnel/client.rb +308 -0
  10. data/lib/rtunnel/command_processor.rb +62 -0
  11. data/lib/rtunnel/command_protocol.rb +50 -0
  12. data/lib/rtunnel/commands.rb +233 -0
  13. data/lib/rtunnel/connection_id.rb +24 -0
  14. data/lib/rtunnel/core.rb +58 -0
  15. data/lib/rtunnel/crypto.rb +106 -0
  16. data/lib/rtunnel/frame_protocol.rb +34 -0
  17. data/lib/rtunnel/io_extensions.rb +54 -0
  18. data/lib/rtunnel/leak.rb +35 -0
  19. data/lib/rtunnel/rtunnel_client_cmd.rb +41 -0
  20. data/lib/rtunnel/rtunnel_server_cmd.rb +32 -0
  21. data/lib/rtunnel/server.rb +351 -0
  22. data/lib/rtunnel/socket_factory.rb +119 -0
  23. data/test/command_stubs.rb +77 -0
  24. data/test/protocol_mocks.rb +43 -0
  25. data/test/scenario_connection.rb +109 -0
  26. data/test/test_client.rb +48 -0
  27. data/test/test_command_protocol.rb +82 -0
  28. data/test/test_commands.rb +49 -0
  29. data/test/test_connection_id.rb +30 -0
  30. data/test/test_crypto.rb +127 -0
  31. data/test/test_frame_protocol.rb +109 -0
  32. data/test/test_io_extensions.rb +70 -0
  33. data/test/test_server.rb +70 -0
  34. data/test/test_socket_factory.rb +42 -0
  35. data/test/test_tunnel.rb +186 -0
  36. data/test_data/authorized_keys2 +4 -0
  37. data/test_data/known_hosts +4 -0
  38. data/test_data/random_rsa_key +27 -0
  39. data/test_data/ssh_host_dsa_key +12 -0
  40. data/test_data/ssh_host_rsa_key +27 -0
  41. data/tests/_ab_test.rb +16 -0
  42. data/tests/_stress_test.rb +96 -0
  43. data/tests/lo_http_server.rb +55 -0
  44. metadata +67 -27
  45. data/ab_test.rb +0 -23
  46. data/lib/client.rb +0 -150
  47. data/lib/cmds.rb +0 -166
  48. data/lib/core.rb +0 -58
  49. data/lib/rtunnel_client_cmd.rb +0 -23
  50. data/lib/rtunnel_server_cmd.rb +0 -18
  51. data/lib/server.rb +0 -197
  52. data/rtunnel.gemspec +0 -18
  53. data/rtunnel_client.rb +0 -3
  54. data/rtunnel_server.rb +0 -3
  55. 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
+
@@ -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