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
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
data/lib/core.rb DELETED
@@ -1,53 +0,0 @@
1
- module RTunnel
2
- VERSION = '0.3.8'
3
-
4
- DEFAULT_CONTROL_PORT = 19050
5
- PING_TIMEOUT = 10
6
- end
7
-
8
-
9
- def D(msg)
10
- puts msg if $debug
11
- end
12
-
13
- class << Thread
14
- def safe(*a)
15
- Thread.new(*a) do
16
- begin
17
- yield
18
- rescue Exception
19
- puts $!.inspect
20
- puts $!.backtrace.join("\n")
21
- end
22
- end
23
- end
24
- end
25
-
26
- class String
27
- def replace_with_ip!
28
- host = self.split(/:/).first
29
-
30
- ip = timeout(5) { Resolv.getaddress(host) }
31
-
32
- self.replace(self.gsub(host, ip))
33
- rescue Exception
34
- puts "Error resolving #{host}"
35
- exit
36
- end
37
- end
38
-
39
- class IO
40
- def read_or_timeout(timeout = 5, read_size = 1024)
41
- data = timeout(timeout) { self.read(read_size) }
42
- rescue Timeout::Error
43
- ''
44
- rescue
45
- nil
46
- end
47
- end
48
-
49
- class UUID
50
- def self.t
51
- timestamp_create.hexdigest
52
- end
53
- end
@@ -1,25 +0,0 @@
1
- #!/usr/bin/ruby
2
-
3
- $LOAD_PATH << 'lib'
4
-
5
- require 'client'
6
-
7
- $debug = true
8
-
9
- control_address = tunnel_from_address = tunnel_to_address = remote_listen_address = nil
10
-
11
- (opts = OptionParser.new do |o|
12
- o.on("-c", "--control-address ADDRESS") { |a| control_address = a }
13
- o.on("-f", "--remote-listen-port ADDRESS") { |a| remote_listen_address = a }
14
- o.on("-t", "--tunnel-to ADDRESS") { |a| tunnel_to_address = a }
15
- end).parse! rescue (puts opts; exit)
16
-
17
- (puts opts; exit) if [control_address, remote_listen_address, tunnel_to_address].include? nil
18
-
19
- client = RTunnel::Client.new(
20
- :control_address => control_address,
21
- :remote_listen_address => remote_listen_address,
22
- :tunnel_to_address => tunnel_to_address
23
- )
24
- client.start
25
- client.join
@@ -1,20 +0,0 @@
1
- #!/usr/bin/ruby
2
-
3
- $LOAD_PATH << 'lib'
4
-
5
- require 'server'
6
-
7
- $debug = true
8
-
9
- control_address = tunnel_port = nil
10
-
11
- (opts = OptionParser.new do |o|
12
- o.on("-c", "--control ADDRESS") { |a| control_address = a }
13
- end).parse! rescue (puts opts; exit)
14
-
15
- server = RTunnel::Server.new(
16
- :control_address => control_address
17
- )
18
-
19
- server.start
20
- server.join
data/lib/server.rb DELETED
@@ -1,181 +0,0 @@
1
- require 'gserver'
2
- require 'optparse'
3
- require 'uuidtools'
4
-
5
- require 'facets'
6
- require 'facets/synchash'
7
-
8
- require 'core'
9
- require 'cmds'
10
-
11
- Socket.do_not_reverse_lookup = true
12
-
13
- $debug = true
14
-
15
- module RTunnel
16
- # listens for incoming connections to tunnel
17
- class RemoteListenServer < GServer
18
- CONNECTIONS = SyncHash.new
19
- CONTROL_CONNECTION_MAPPING = SyncHash.new
20
-
21
- def initialize(port, host, control_connection)
22
- super(port, host, 10)
23
- @control_connection = control_connection
24
- @maxConnections = 1024
25
- end
26
-
27
- def serve(sock)
28
- D "new incoming connection"
29
-
30
- conn_id = UUID.t
31
- CONNECTIONS[conn_id] = sock
32
- CONTROL_CONNECTION_MAPPING[conn_id] = @control_connection
33
- begin
34
- ControlServer.new_tunnel(conn_id)
35
-
36
- while data = (sock.readpartial(16384) rescue nil)
37
- ControlServer.send_data(conn_id, data)
38
- end
39
- rescue
40
- D "error talking on control connection, dropping incoming connection: #{$!.inspect}"
41
- end
42
-
43
- ControlServer.close_tunnel(conn_id)
44
-
45
- CONNECTIONS.delete conn_id
46
- CONTROL_CONNECTION_MAPPING.delete conn_id
47
-
48
- D "sock closed"
49
- rescue
50
- p $!
51
- puts $!.backtrace.join("\n")
52
- end
53
- end
54
-
55
- class ControlServer < GServer
56
- @@control_connections = []
57
- @@remote_listen_servers = []
58
-
59
- @@m = Mutex.new
60
-
61
- attr_accessor :ping_interval
62
-
63
- def initialize(*args)
64
- super
65
- @maxConnections = 1024
66
- end
67
-
68
- class << self
69
- def new_tunnel(conn_id)
70
- D "sending create connection command: #{conn_id}"
71
-
72
- @@m.synchronize { control_connection_for(conn_id).write CreateConnectionCommand.new(conn_id) }
73
- end
74
-
75
- def send_data(conn_id, data)
76
- @@m.synchronize { control_connection_for(conn_id).write SendDataCommand.new(conn_id, data) }
77
- end
78
-
79
- def close_tunnel(conn_id)
80
- D "sending close connection command"
81
-
82
- @@m.synchronize { control_connection_for(conn_id).write CloseConnectionCommand.new(conn_id) }
83
- end
84
-
85
- private
86
-
87
- def control_connection_for(conn_id)
88
- RemoteListenServer::CONTROL_CONNECTION_MAPPING[conn_id]
89
- end
90
- end
91
-
92
- def serve(sock)
93
- D "new control connection"
94
- @@control_connections << sock
95
- sock.sync = true
96
-
97
- cmd_queue = ""
98
- while true
99
- if r = IO.select([sock], nil, nil, @ping_interval)
100
- cmd_queue << sock.readpartial(16384) rescue (D "#{$!.inspect}"; break)
101
- while Command.match(cmd_queue)
102
- cmd = Command.parse(cmd_queue)
103
- case cmd
104
- when RemoteListenCommand
105
- @@m.synchronize do
106
- addr, port = cmd.address.split(/:/)
107
- if rls = @@remote_listen_servers.detect {|s| s.port == port.to_i }
108
- rls.stop
109
- @@remote_listen_servers.delete rls
110
- end
111
- (new_rls = RemoteListenServer.new(port, addr, sock)).start
112
- @@remote_listen_servers << new_rls
113
- end
114
- D "listening for remote connections on #{cmd.address}"
115
- when SendDataCommand
116
- conn = RemoteListenServer::CONNECTIONS[cmd.conn_id]
117
- begin
118
- conn.write(cmd.data) if conn
119
- rescue Errno::EPIPE
120
- D "broken pipe on #{cmd.conn_id}"
121
- end
122
- when CloseConnectionCommand
123
- if connection = RemoteListenServer::CONNECTIONS[cmd.conn_id]
124
- D "closing remote connection: #{cmd.conn_id}"
125
- connection.close
126
- end
127
- else
128
- D "bad command received: #{cmd.inspect}"
129
- end
130
- end
131
- end
132
-
133
- # TODO: only do this every ping_interval instead of after each socket read
134
- @@m.synchronize { @@control_connections.each {|cc| cc.write PingCommand.new } }
135
- end
136
- rescue
137
- D $!.inspect
138
- D $!.backtrace.join("\n")
139
- raise
140
- ensure
141
- @@control_connections.delete sock
142
- end
143
-
144
- def stopping
145
- @@remote_listen_server.stop
146
- end
147
- end
148
-
149
- class Server
150
- def initialize(options = {})
151
- @control_address = options[:control_address]
152
- if ! @control_address
153
- @control_address = "0.0.0.0:#{DEFAULT_CONTROL_PORT}" if ! @control_address
154
- elsif @control_address =~ /^\d+$/
155
- @control_address.insert 0, "0.0.0.0:"
156
- elsif @control_address !~ /:\d+$/
157
- @control_address << ":#{DEFAULT_CONTROL_PORT}"
158
- end
159
- @control_host = @control_address.split(/:/).first
160
-
161
- @ping_interval = options[:ping_interval] || 2.0
162
- end
163
-
164
- def start
165
- @control_server = ControlServer.new(*@control_address.split(/:/).reverse)
166
- @control_server.ping_interval = @ping_interval
167
- @control_server.audit = true
168
-
169
- @control_server.start
170
- end
171
-
172
- def join
173
- @control_server.join
174
- end
175
-
176
- def stop
177
- @control_server.shutdown
178
- ControlServer.stop(@control_server.port, @control_server.host)
179
- end
180
- end
181
- end
data/rtunnel_client.rb DELETED
@@ -1,3 +0,0 @@
1
- #!/usr/bin/ruby
2
-
3
- load 'lib/rtunnel_client_cmd.rb'
data/rtunnel_server.rb DELETED
@@ -1,3 +0,0 @@
1
- #!/usr/bin/ruby
2
-
3
- load 'lib/rtunnel_server_cmd.rb'