rtunnel 0.3.8 → 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.
Files changed (61) hide show
  1. data/CHANGELOG +13 -0
  2. data/LICENSE +21 -0
  3. data/Manifest +48 -0
  4. data/README.markdown +84 -0
  5. data/Rakefile +45 -0
  6. data/bin/rtunnel_client +2 -1
  7. data/bin/rtunnel_server +2 -1
  8. data/lib/rtunnel/client.rb +308 -0
  9. data/lib/rtunnel/command_processor.rb +62 -0
  10. data/lib/rtunnel/command_protocol.rb +50 -0
  11. data/lib/rtunnel/commands.rb +233 -0
  12. data/lib/rtunnel/connection_id.rb +24 -0
  13. data/lib/rtunnel/core.rb +58 -0
  14. data/lib/rtunnel/crypto.rb +106 -0
  15. data/lib/rtunnel/frame_protocol.rb +34 -0
  16. data/lib/rtunnel/io_extensions.rb +54 -0
  17. data/lib/rtunnel/leak.rb +35 -0
  18. data/lib/rtunnel/rtunnel_client_cmd.rb +41 -0
  19. data/lib/rtunnel/rtunnel_server_cmd.rb +32 -0
  20. data/lib/rtunnel/server.rb +351 -0
  21. data/lib/rtunnel/socket_factory.rb +119 -0
  22. data/lib/rtunnel.rb +20 -0
  23. data/rtunnel.gemspec +51 -0
  24. data/spec/client_spec.rb +47 -0
  25. data/spec/cmds_spec.rb +127 -0
  26. data/spec/integration_spec.rb +105 -0
  27. data/spec/server_spec.rb +21 -0
  28. data/spec/spec_helper.rb +3 -0
  29. data/test/command_stubs.rb +77 -0
  30. data/test/protocol_mocks.rb +43 -0
  31. data/test/scenario_connection.rb +109 -0
  32. data/test/test_client.rb +48 -0
  33. data/test/test_command_protocol.rb +82 -0
  34. data/test/test_commands.rb +49 -0
  35. data/test/test_connection_id.rb +30 -0
  36. data/test/test_crypto.rb +127 -0
  37. data/test/test_frame_protocol.rb +109 -0
  38. data/test/test_io_extensions.rb +70 -0
  39. data/test/test_server.rb +70 -0
  40. data/test/test_socket_factory.rb +42 -0
  41. data/test/test_tunnel.rb +186 -0
  42. data/test_data/authorized_keys2 +4 -0
  43. data/test_data/known_hosts +4 -0
  44. data/test_data/random_rsa_key +27 -0
  45. data/test_data/ssh_host_dsa_key +12 -0
  46. data/test_data/ssh_host_rsa_key +27 -0
  47. data/tests/_ab_test.rb +16 -0
  48. data/tests/_stress_test.rb +96 -0
  49. data/tests/lo_http_server.rb +55 -0
  50. metadata +127 -31
  51. data/History.txt +0 -3
  52. data/Manifest.txt +0 -13
  53. data/README.txt +0 -362
  54. data/lib/client.rb +0 -185
  55. data/lib/cmds.rb +0 -166
  56. data/lib/core.rb +0 -53
  57. data/lib/rtunnel_client_cmd.rb +0 -25
  58. data/lib/rtunnel_server_cmd.rb +0 -20
  59. data/lib/server.rb +0 -181
  60. data/rtunnel_client.rb +0 -3
  61. data/rtunnel_server.rb +0 -3
@@ -0,0 +1,127 @@
1
+ require 'rtunnel'
2
+
3
+ require 'openssl'
4
+ require 'test/unit'
5
+
6
+ class CryptoTest < Test::Unit::TestCase
7
+ C = RTunnel::Crypto
8
+
9
+ @@rsa_key_path = 'test_data/ssh_host_rsa_key'
10
+ @@dsa_key_path = 'test_data/ssh_host_dsa_key'
11
+ @@known_hosts_path = 'test_data/known_hosts'
12
+ @@authorized_keys_path = 'test_data/authorized_keys2'
13
+
14
+ def test_read_private_key
15
+ key = C.read_private_key @@rsa_key_path
16
+ assert_equal File.read(@@rsa_key_path), key.to_pem
17
+ assert_equal OpenSSL::PKey::RSA, key.class
18
+
19
+ key = C.read_private_key @@dsa_key_path
20
+ assert_equal File.read(@@dsa_key_path), key.to_pem
21
+ assert_equal OpenSSL::PKey::DSA, key.class
22
+ end
23
+
24
+ def test_read_known_hosts
25
+ keys = C.read_authorized_keys @@known_hosts_path
26
+ verify_authorized_keys keys
27
+ end
28
+
29
+ def test_read_authorized_keys
30
+ keys = C.read_authorized_keys @@authorized_keys_path
31
+ verify_authorized_keys keys
32
+ end
33
+
34
+ def verify_authorized_keys(keys)
35
+ assert_equal 4, keys.length
36
+ assert_equal [OpenSSL::PKey::RSA] * 3 + [OpenSSL::PKey::DSA],
37
+ keys.map { |k| k.class }
38
+ assert_equal C.read_private_key(@@rsa_key_path).public_key.to_pem,
39
+ keys[1].to_pem
40
+ assert_equal C.read_private_key(@@dsa_key_path).public_key.to_pem,
41
+ keys[3].to_pem
42
+ end
43
+
44
+ def test_key_fingerprint
45
+ keys = C.read_authorized_keys @@known_hosts_path
46
+
47
+ assert_equal 4, keys.map { |k| C.key_fingerprint k }.uniq.length
48
+ keys.each { |k| assert_equal C.key_fingerprint(k), C.key_fingerprint(k) }
49
+ end
50
+
51
+ def test_load_public_keys
52
+ keyset = C.load_public_keys @@known_hosts_path
53
+ rsa_key = C.read_private_key @@rsa_key_path
54
+ dsa_key = C.read_private_key @@dsa_key_path
55
+
56
+ assert_equal rsa_key.public_key.to_pem,
57
+ keyset[C.key_fingerprint(rsa_key)].to_pem
58
+ assert_equal dsa_key.public_key.to_pem,
59
+ keyset[C.key_fingerprint(dsa_key)].to_pem
60
+ end
61
+
62
+ def test_key_encryption
63
+ test_data = 'qwertyuiopasdfghjklzxcvbnm' * 2
64
+ rsa_key = C.read_private_key @@rsa_key_path
65
+ dsa_key = C.read_private_key @@rsa_key_path
66
+
67
+ [rsa_key, dsa_key].each do |key|
68
+ encrypted_data = C.encrypt_with_key key.public_key, test_data
69
+ decrypted_data = C.decrypt_with_key key, encrypted_data
70
+
71
+ assert_equal test_data, decrypted_data
72
+ 0.upto(test_data.length - 4) do |i|
73
+ assert !encrypted_data.index(test_data[i, 4]),
74
+ 'Encryption did not wipe the original pattern'
75
+ end
76
+ end
77
+ end
78
+
79
+ def test_encryption_depends_on_key
80
+ num_keys = 16
81
+ test_data = 'qwertyuiopasdfghjklzxcvbnm' * 2
82
+ keys = (0...num_keys).map { OpenSSL::PKey::RSA.generate 1024, 35 }
83
+ assert_equal num_keys, keys.map { |k| C.encrypt_with_key k, test_data }.
84
+ uniq.length
85
+ end
86
+
87
+ def test_hasher_random_keys_are_random
88
+ num_keys = 1024
89
+ assert_equal num_keys, (0...num_keys).map { C::Hasher.random_key }.
90
+ uniq.length
91
+ end
92
+
93
+ def test_hasher_random_keys_yield_random_results
94
+ num_keys = 1024
95
+ test_data = 'qwertyuiopasdfghjklzxcvbnm' * 2
96
+ assert_equal num_keys, (0...num_keys).map { C::Hasher.new.hash test_data }.
97
+ uniq.length
98
+ end
99
+
100
+ def test_hasher_hashes_are_finite
101
+ num_blocks = 64
102
+ block = 'qwertyuiopasdfghjklzxcvbnm'
103
+
104
+ hasher = C::Hasher.new
105
+ hash_length = hasher.hash('').length
106
+ 1.upto(num_blocks) { |n| assert_equal hash_length, hasher.hash(block * n).
107
+ length }
108
+ end
109
+
110
+ def test_hasher_has_state
111
+ num_blocks = 1024
112
+ block = 'qwertyuiopasdfghjklzxcvbnm'
113
+ hasher = C::Hasher.new
114
+
115
+ assert_equal num_blocks, (0...num_blocks).map { hasher.hash block}.uniq.
116
+ length
117
+ end
118
+
119
+ def test_hasher_is_reproducible
120
+ hasher = C::Hasher.new
121
+ hasher2 = C::Hasher.new hasher.key
122
+
123
+ num_blocks = 128
124
+ block = 'qwertyuiopasdfghjklzxcvbnm'
125
+ 1.upto(num_blocks) { assert_equal hasher.hash(block), hasher2.hash(block) }
126
+ end
127
+ end
@@ -0,0 +1,109 @@
1
+ require 'rtunnel'
2
+
3
+ require 'test/unit'
4
+
5
+ require 'test/protocol_mocks.rb'
6
+
7
+ # Send mock for frames.
8
+ class EmSendFramesMock < EmSendMock
9
+ include RTunnel::FrameProtocol
10
+ end
11
+
12
+ # Receive mock for frames.
13
+ class EmReceiveFramesMock < EmReceiveMock
14
+ include RTunnel::FrameProtocol
15
+ object_name :frame
16
+ end
17
+
18
+ class FrameProtocolTest < Test::Unit::TestCase
19
+ def setup
20
+ super
21
+ @send_mock = EmSendFramesMock.new
22
+ end
23
+
24
+ def teardown
25
+ super
26
+ end
27
+
28
+ def continuous_data_test(frames)
29
+ truncated_data_test frames, []
30
+ end
31
+
32
+ def truncated_data_test(frames, sub_lengths)
33
+ frames.each { |frame| @send_mock.send_frame frame }
34
+ in_string = @send_mock.string
35
+ in_strings, i = [], 0
36
+ sub_lengths.each do |sublen|
37
+ in_strings << in_string[i, sublen]
38
+ i += sublen
39
+ end
40
+ in_strings << in_string[i..-1] if i < in_string.length
41
+ out_frames = EmReceiveFramesMock.new(@send_mock.string).replay.frames
42
+ assert_equal frames, out_frames
43
+ end
44
+
45
+ def test_empty_frame
46
+ continuous_data_test ['']
47
+ end
48
+
49
+ def test_byte_frame
50
+ continuous_data_test ['F']
51
+ end
52
+
53
+ def test_string_frame
54
+ continuous_data_test [(32...128).to_a.pack('C*')]
55
+ end
56
+
57
+ def test_multiple_frames
58
+ continuous_data_test [(32...128).to_a.pack('C*'), '', 'F', '', '1234567890']
59
+ end
60
+
61
+ def test_truncated_border
62
+ truncated_data_test ['A', 'A'], [1, 0, 2, 0]
63
+ end
64
+
65
+ def test_truncated_border_and_joined_data_size
66
+ truncated_data_test ['A', 'A'], [1, 1, 1, 1]
67
+ end
68
+
69
+ def test_truncated_size
70
+ long_frame = (32...128).to_a.pack('C*') * 5
71
+ truncated_data_test [long_frame], [1]
72
+ end
73
+
74
+ def test_truncated_size_and_data
75
+ long_frame = (32...128).to_a.pack('C*') * 5
76
+ truncated_data_test [long_frame], [1, 16]
77
+ end
78
+
79
+ def test_badass
80
+ # TODO(not_me): this test takes 4 seconds; replace with more targeted tests
81
+
82
+ # build the badass string
83
+ s2_frame = 'qwertyuiopasdfgh' * 8 * 128 # 16384 characters, size is 3 bytes
84
+ @send_mock.send_frame s2_frame
85
+ s2_string = @send_mock.string
86
+ s2_count = 3
87
+ send_string = s2_string * s2_count
88
+ ex_frames = [s2_frame] * s2_count
89
+
90
+ # build cut points in a string
91
+ s2_points = [0, 1, 2, 3, 4, 5, 127, 128, 8190, 16381, 16382, 16383]
92
+ cut_points = []
93
+ 0.upto(s2_count - 1) do |i|
94
+ cut_points += s2_points.map { |p| p + i * s2_string.length }
95
+ end
96
+
97
+ # try all combinations of cutting up the string in 4 pieces
98
+ 0.upto(cut_points.length - 1) do |i|
99
+ (i + 1).upto(cut_points.length - 1) do |j|
100
+ (j + 1).upto(cut_points.length - 1) do |k|
101
+ packets = [0...cut_points[i], cut_points[i]...cut_points[j],
102
+ cut_points[j]...cut_points[k], cut_points[k]..-1].
103
+ map { |r| send_string[r] }
104
+ assert_equal ex_frames, EmReceiveFramesMock.new(packets).replay.frames
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,70 @@
1
+ require 'rtunnel'
2
+
3
+ require 'stringio'
4
+ require 'test/unit'
5
+
6
+ class IOExtensionsTest < Test::Unit::TestCase
7
+ def setup
8
+ @test_sizes = [0, 1, 10, 127, 128, (1 << 20), (1 << 45) / 3, (1 << 62) / 5]
9
+ @test_strings = ['', 'a',
10
+ (32..127).to_a.map { |c| c.chr}.join,
11
+ (32..127).to_a.map { |c| c.chr}.join * 511]
12
+ @str = StringIO.new
13
+ end
14
+
15
+ def test_varsizes_independent
16
+ @test_sizes.each do |size|
17
+ @str = StringIO.new
18
+ @str.write_varsize size
19
+ @str.rewind
20
+ assert_equal size, @str.read_varsize
21
+ end
22
+ end
23
+
24
+ def test_varsizes_appended
25
+ @test_sizes.each { |size| @str.write_varsize size }
26
+ @str.rewind
27
+ @test_sizes.each do |size|
28
+ assert_equal size, @str.read_varsize
29
+ end
30
+ end
31
+
32
+ def test_varsize_error
33
+ @str.write_varsize((1 << 62) / 3)
34
+ vs = @str.string
35
+ 0.upto(vs.length - 1) do |truncated_len|
36
+ @str = StringIO.new
37
+ @str.write vs[0, truncated_len]
38
+ @str.rewind
39
+ assert_raise(RTunnel::TruncatedDataError) { @str.read_varsize }
40
+ end
41
+ end
42
+
43
+ def test_varstring_independent
44
+ @test_strings.each do |str|
45
+ @str = StringIO.new
46
+ @str.write_varstring str
47
+ @str.rewind
48
+ assert_equal str, @str.read_varstring
49
+ end
50
+ end
51
+
52
+ def test_varstring_appended
53
+ @test_strings.each { |str| @str.write_varstring str }
54
+ @str.rewind
55
+ @test_strings.each do |str|
56
+ assert_equal str, @str.read_varstring
57
+ end
58
+ end
59
+
60
+ def test_varstring_error
61
+ @str.write_varstring 'This will be truncated'
62
+ vs = @str.string
63
+ 0.upto(vs.length - 1) do |truncated_len|
64
+ @str = StringIO.new
65
+ @str.write vs[0, truncated_len]
66
+ @str.rewind
67
+ assert_raise(RTunnel::TruncatedDataError) { @str.read_varstring }
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,70 @@
1
+ require 'rtunnel'
2
+
3
+ require 'resolv'
4
+ require 'test/unit'
5
+
6
+
7
+ class ServerTest < Test::Unit::TestCase
8
+ def setup
9
+ super
10
+ @server = RTunnel::Server.new(:control_address => 'localhost')
11
+ @localhost_addr = Resolv.getaddress 'localhost'
12
+ end
13
+
14
+ def test_options
15
+ server = RTunnel::Server
16
+ assert_equal "18.241.3.100:#{RTunnel::DEFAULT_CONTROL_PORT}",
17
+ server.extract_control_address('18.241.3.100')
18
+ assert_equal "18.241.3.100:9199",
19
+ server.extract_control_address('18.241.3.100:9199')
20
+ assert_equal "#{@localhost_addr}:#{RTunnel::DEFAULT_CONTROL_PORT}",
21
+ server.extract_control_address('localhost')
22
+ assert_equal "#{@localhost_addr}:9199",
23
+ server.extract_control_address('localhost:9199')
24
+ assert_equal "0.0.0.0:9199",
25
+ server.extract_control_address('9199')
26
+
27
+ assert_equal RTunnel::KEEP_ALIVE_INTERVAL,
28
+ server.extract_keep_alive_interval(nil)
29
+ assert_equal 29, server.extract_keep_alive_interval(29)
30
+
31
+ assert_equal 0, server.extract_lowest_listen_port(nil)
32
+ assert_equal 29, server.extract_lowest_listen_port(29)
33
+ assert_equal 65535, server.extract_highest_listen_port(nil)
34
+ assert_equal 29, server.extract_highest_listen_port(29)
35
+
36
+ assert_equal nil, server.extract_authorized_keys(nil)
37
+ keyset = server.extract_authorized_keys 'test_data/known_hosts'
38
+ assert_equal RTunnel::Crypto::KeySet, keyset.class
39
+ assert keyset.length != 0, 'No key read from the known_hosts file'
40
+ end
41
+
42
+ def test_validate_remote_listen
43
+ # remove Connection's new override, don't want event_machine here
44
+ EventMachine::Connection.class_eval do
45
+ class << self
46
+ alias_method :backup_new, :new
47
+ remove_method :new
48
+ end
49
+ end
50
+
51
+ server = RTunnel::Server.new(:control_address => 'localhost',
52
+ :lowest_listen_port => 91,
53
+ :highest_listen_port => 105)
54
+ connection = RTunnel::Server::ControlConnection.new server
55
+
56
+ assert !connection.validate_remote_listen('localhost', 80)
57
+ assert connection.validate_remote_listen('localhost', 91)
58
+ assert connection.validate_remote_listen('localhost', 105)
59
+ assert !connection.validate_remote_listen('localhost', 90)
60
+ assert !connection.validate_remote_listen('localhost', 106)
61
+
62
+ # re-instate Connection's new override
63
+ EventMachine::Connection.class_eval do
64
+ class << self
65
+ alias_method :new, :backup_new
66
+ remove_method :backup_new
67
+ end
68
+ end
69
+ end
70
+ end
@@ -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-----