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.
- data/CHANGELOG +13 -0
- data/LICENSE +21 -0
- data/Manifest +48 -0
- data/README.markdown +84 -0
- data/Rakefile +45 -0
- data/bin/rtunnel_client +2 -1
- data/bin/rtunnel_server +2 -1
- 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/lib/rtunnel.rb +20 -0
- data/rtunnel.gemspec +51 -0
- data/spec/client_spec.rb +47 -0
- data/spec/cmds_spec.rb +127 -0
- data/spec/integration_spec.rb +105 -0
- data/spec/server_spec.rb +21 -0
- data/spec/spec_helper.rb +3 -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 +127 -31
- data/History.txt +0 -3
- data/Manifest.txt +0 -13
- data/README.txt +0 -362
- data/lib/client.rb +0 -185
- data/lib/cmds.rb +0 -166
- data/lib/core.rb +0 -53
- data/lib/rtunnel_client_cmd.rb +0 -25
- data/lib/rtunnel_server_cmd.rb +0 -20
- data/lib/server.rb +0 -181
- data/rtunnel_client.rb +0 -3
- 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
|
data/lib/rtunnel_client_cmd.rb
DELETED
@@ -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
|
data/lib/rtunnel_server_cmd.rb
DELETED
@@ -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
data/rtunnel_server.rb
DELETED