rtunnel 0.2.3 → 0.3.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/lib/client.rb +89 -40
- data/lib/core.rb +7 -3
- data/lib/rtunnel_client_cmd.rb +0 -1
- data/lib/rtunnel_server_cmd.rb +1 -3
- data/lib/server.rb +100 -86
- data/rtunnel_client.rb +1 -1
- metadata +2 -2
data/lib/client.rb
CHANGED
@@ -3,10 +3,18 @@ require 'optparse'
|
|
3
3
|
require 'timeout'
|
4
4
|
require 'resolv'
|
5
5
|
|
6
|
+
require 'facets'
|
7
|
+
require 'synchash'
|
8
|
+
|
6
9
|
require 'core'
|
10
|
+
require 'cmds'
|
11
|
+
|
12
|
+
$debug = true
|
7
13
|
|
8
14
|
module RTunnel
|
9
15
|
class Client
|
16
|
+
CONNECTIONS = SyncHash.new
|
17
|
+
|
10
18
|
def initialize(options = {})
|
11
19
|
@control_address = options[:control_address]
|
12
20
|
@control_address << ":#{DEFAULT_CONTROL_PORT}" if @control_address !~ /:\d+$/
|
@@ -18,9 +26,7 @@ module RTunnel
|
|
18
26
|
@tunnel_to_address = options[:tunnel_to_address]
|
19
27
|
@tunnel_to_address.insert 0, "localhost:" if @tunnel_to_address =~ /^\d+$/
|
20
28
|
|
21
|
-
@
|
22
|
-
|
23
|
-
[@control_address, @remote_listen_address, @tunnel_to_address, @tunnel_from_address].each do |addr|
|
29
|
+
[@control_address, @remote_listen_address, @tunnel_to_address].each do |addr|
|
24
30
|
addr.replace_with_ip!
|
25
31
|
end
|
26
32
|
|
@@ -43,6 +49,39 @@ module RTunnel
|
|
43
49
|
end
|
44
50
|
end
|
45
51
|
|
52
|
+
=begin
|
53
|
+
# Memory leak testing
|
54
|
+
Thread.safe do
|
55
|
+
sleep 10
|
56
|
+
begin
|
57
|
+
objects = Hash.new 0
|
58
|
+
|
59
|
+
while true
|
60
|
+
last_objects = objects.dup
|
61
|
+
ObjectSpace.each_object do |o|
|
62
|
+
next if ! o.respond_to? :class
|
63
|
+
objects[o.class] += 1
|
64
|
+
end
|
65
|
+
|
66
|
+
objects.reject!{|k,v| ! last_objects.has_key? k } unless last_objects.empty?
|
67
|
+
|
68
|
+
new_objects = objects.dup
|
69
|
+
objects.each do |(klass, count)|
|
70
|
+
new_objects.delete klass if count < last_objects[klass] # has been GC'ed, "cant be leaking"
|
71
|
+
end
|
72
|
+
objects = new_objects
|
73
|
+
|
74
|
+
PP.pp objects.sort_by{|(k,cnt)| cnt }.reverse[0..10], STDERR
|
75
|
+
|
76
|
+
sleep 10
|
77
|
+
end
|
78
|
+
rescue Object
|
79
|
+
STDERR.puts $!.inspect
|
80
|
+
STDERR.puts $!.backtrace.join("\n")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
=end
|
84
|
+
|
46
85
|
@main_thread = Thread.safe do
|
47
86
|
while true
|
48
87
|
stop_ping_check
|
@@ -57,46 +96,50 @@ module RTunnel
|
|
57
96
|
|
58
97
|
start_ping_check
|
59
98
|
|
60
|
-
@
|
61
|
-
|
62
|
-
|
63
|
-
while
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
from.close
|
82
|
-
to.close
|
83
|
-
|
84
|
-
Thread.exit
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
if ready.include? from
|
89
|
-
begin
|
90
|
-
to.write(from.readpartial(1024))
|
91
|
-
rescue EOFError
|
92
|
-
D "from tunnel close, closing to tunnel"
|
93
|
-
from.close
|
94
|
-
to.close
|
95
|
-
|
96
|
-
Thread.exit
|
99
|
+
write_to_control_sock RemoteListenCommand.new(@remote_listen_address)
|
100
|
+
|
101
|
+
cmd_queue = ""
|
102
|
+
while data = (@control_sock.readpartial(16384) rescue (D "#{$!.inspect}"; nil))
|
103
|
+
cmd_queue << data
|
104
|
+
while Command.match(cmd_queue)
|
105
|
+
case command = Command.parse(cmd_queue)
|
106
|
+
when PingCommand
|
107
|
+
@last_ping = Time.now
|
108
|
+
when CreateConnectionCommand
|
109
|
+
# TODO: this currently blocks, but if we put it in thread, a SendDataCommand may try to get run for this connection before the connection exists
|
110
|
+
CONNECTIONS[command.conn_id] = TCPSocket.new(*@tunnel_to_address.split(/:/))
|
111
|
+
Thread.safe do
|
112
|
+
cmd = command
|
113
|
+
conn = CONNECTIONS[cmd.conn_id]
|
114
|
+
|
115
|
+
begin
|
116
|
+
D "reading local data"
|
117
|
+
while localdata = conn.readpartial(16834)
|
118
|
+
D "sending data back to server: #{localdata.size}"
|
119
|
+
write_to_control_sock SendDataCommand.new(cmd.conn_id, localdata)
|
97
120
|
end
|
121
|
+
rescue EOFError
|
122
|
+
D "to tunnel closed, closing from tunnel"
|
123
|
+
conn.close
|
124
|
+
CONNECTIONS.delete cmd.conn_id
|
125
|
+
write_to_control_sock CloseConnectionCommand.new(cmd.conn_id)
|
98
126
|
end
|
99
127
|
end
|
128
|
+
when CloseConnectionCommand
|
129
|
+
D "close " + command.conn_id
|
130
|
+
if connection = CONNECTIONS[command.conn_id]
|
131
|
+
# TODO: how the hell do u catch a .close error?
|
132
|
+
connection.close_read
|
133
|
+
#connection.close unless connection.closed?
|
134
|
+
CONNECTIONS.delete(command.conn_id)
|
135
|
+
end
|
136
|
+
when SendDataCommand
|
137
|
+
D "send to local " + command.conn_id + " " + command.inspect
|
138
|
+
if connection = CONNECTIONS[command.conn_id]
|
139
|
+
connection.write(command.data)
|
140
|
+
else
|
141
|
+
puts "WARNING: received data for non existant connection!"
|
142
|
+
end
|
100
143
|
end
|
101
144
|
end
|
102
145
|
end
|
@@ -117,6 +160,12 @@ module RTunnel
|
|
117
160
|
|
118
161
|
private
|
119
162
|
|
163
|
+
def write_to_control_sock(data)
|
164
|
+
(@control_sock_mutex ||= Mutex.new).synchronize do
|
165
|
+
@control_sock.write data
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
120
169
|
def start_ping_check
|
121
170
|
@last_ping = Time.now
|
122
171
|
@check_ping = true
|
data/lib/core.rb
CHANGED
@@ -1,13 +1,11 @@
|
|
1
1
|
module RTunnel
|
2
|
-
VERSION = '0.
|
2
|
+
VERSION = '0.3.0'
|
3
3
|
|
4
4
|
DEFAULT_CONTROL_PORT = 19050
|
5
|
-
DEFAULT_TUNNEL_PORT = 19051
|
6
5
|
PING_TIMEOUT = 10
|
7
6
|
end
|
8
7
|
|
9
8
|
|
10
|
-
|
11
9
|
def D(msg)
|
12
10
|
puts msg if $debug
|
13
11
|
end
|
@@ -47,3 +45,9 @@ class IO
|
|
47
45
|
nil
|
48
46
|
end
|
49
47
|
end
|
48
|
+
|
49
|
+
class UUID
|
50
|
+
def self.t
|
51
|
+
timestamp_create.hexdigest
|
52
|
+
end
|
53
|
+
end
|
data/lib/rtunnel_client_cmd.rb
CHANGED
@@ -11,7 +11,6 @@ control_address = tunnel_from_address = tunnel_to_address = remote_listen_addres
|
|
11
11
|
(opts = OptionParser.new do |o|
|
12
12
|
o.on("-c", "--control-address ADDRESS") { |a| control_address = a }
|
13
13
|
o.on("-f", "--remote-listen-port ADDRESS") { |a| remote_listen_address = a }
|
14
|
-
o.on("-p", "--tunnel-port ADDRESS") { |a| tunnel_port = a }
|
15
14
|
o.on("-t", "--tunnel-to ADDRESS") { |a| tunnel_to_address = a }
|
16
15
|
end).parse! rescue (puts opts; exit)
|
17
16
|
|
data/lib/rtunnel_server_cmd.rb
CHANGED
@@ -10,12 +10,10 @@ control_address = tunnel_port = nil
|
|
10
10
|
|
11
11
|
(opts = OptionParser.new do |o|
|
12
12
|
o.on("-c", "--control ADDRESS") { |a| control_address = a }
|
13
|
-
o.on("-t", "--tunnel-port PORT") { |a| tunnel_port = a }
|
14
13
|
end).parse! rescue (puts opts; exit)
|
15
14
|
|
16
15
|
server = RTunnel::Server.new(
|
17
|
-
:control_address => control_address
|
18
|
-
:tunnel_port => tunnel_port
|
16
|
+
:control_address => control_address
|
19
17
|
)
|
20
18
|
|
21
19
|
server.start
|
data/lib/server.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
require 'gserver'
|
2
2
|
require 'optparse'
|
3
|
+
require 'uuidtools'
|
4
|
+
|
5
|
+
require 'facets'
|
6
|
+
require 'synchash'
|
3
7
|
|
4
8
|
require 'core'
|
9
|
+
require 'cmds'
|
5
10
|
|
6
11
|
Socket.do_not_reverse_lookup = true
|
7
12
|
|
@@ -10,36 +15,29 @@ $debug = true
|
|
10
15
|
module RTunnel
|
11
16
|
# listens for incoming connections to tunnel
|
12
17
|
class RemoteListenServer < GServer
|
13
|
-
|
14
|
-
@@incoming_connections = []
|
15
|
-
@@cnt = 0
|
18
|
+
CONNECTIONS = SyncHash.new
|
16
19
|
|
17
20
|
def initialize(port, host = DEFAULT_HOST)
|
18
21
|
super(port, host, 10)
|
19
22
|
end
|
20
23
|
|
21
|
-
def self.next_incoming_connection
|
22
|
-
@@m.synchronize { @@incoming_connections.pop }
|
23
|
-
end
|
24
|
-
|
25
24
|
def serve(sock)
|
26
|
-
m = Mutex.new
|
27
|
-
cv = ConditionVariable.new
|
28
|
-
|
29
25
|
D "new incoming connection"
|
30
|
-
incoming = [cv, sock]
|
31
|
-
@@m.synchronize { @@incoming_connections << incoming }
|
32
26
|
|
27
|
+
conn_id = UUID.t
|
28
|
+
CONNECTIONS[conn_id] = sock
|
33
29
|
begin
|
34
|
-
ControlServer.new_tunnel
|
35
|
-
rescue
|
36
|
-
D "error talking on control connection, dropping incoming connection"
|
30
|
+
ControlServer.new_tunnel(conn_id)
|
37
31
|
|
38
|
-
|
39
|
-
|
32
|
+
while data = (sock.readpartial(16384) rescue nil)
|
33
|
+
ControlServer.send_data(conn_id, data)
|
34
|
+
end
|
35
|
+
rescue
|
36
|
+
D "error talking on control connection, dropping incoming connection: #{$!.inspect}"
|
40
37
|
end
|
41
|
-
|
42
|
-
|
38
|
+
|
39
|
+
CONNECTIONS.delete conn_id
|
40
|
+
ControlServer.close_tunnel(conn_id)
|
43
41
|
|
44
42
|
D "sock closed"
|
45
43
|
rescue
|
@@ -47,52 +45,52 @@ module RTunnel
|
|
47
45
|
puts $!.backtrace.join("\n")
|
48
46
|
end
|
49
47
|
end
|
50
|
-
|
51
|
-
class TunnelListenServer < GServer
|
52
|
-
@@tunneled_connections = []
|
53
|
-
|
54
|
-
def initialize(port, host = DEFAULT_HOST)
|
55
|
-
super(port, host, 10)
|
56
|
-
end
|
57
|
-
|
58
|
-
def serve(sock)
|
59
|
-
D "new tunneled connection"
|
60
|
-
cv, incoming_connection = RemoteListenServer.next_incoming_connection
|
61
|
-
if ! incoming_connection
|
62
|
-
D "no incoming connections for this tunneled connection, closing"
|
63
|
-
return
|
64
|
-
end
|
65
|
-
|
66
|
-
while ready = (IO.select([incoming_connection, sock], nil, nil, 0.5).first rescue [])
|
67
|
-
if ready.include? incoming_connection
|
68
|
-
begin
|
69
|
-
sock.write(incoming_connection.readpartial(1024))
|
70
|
-
rescue EOFError
|
71
|
-
D "incoming socket closed, closing tunneled socket"
|
72
|
-
sock.close
|
73
|
-
cv.signal
|
74
|
-
|
75
|
-
return
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
if ready.include? sock
|
80
|
-
begin
|
81
|
-
incoming_connection.write(sock.readpartial(1024))
|
82
|
-
rescue EOFError
|
83
|
-
D "tunneled socket closed, closing incoming connection socket"
|
84
|
-
incoming_connection.close
|
85
|
-
cv.signal
|
86
|
-
|
87
|
-
return
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
rescue Exception
|
92
|
-
p $!
|
93
|
-
puts $!.backtrace.join("\n")
|
94
|
-
end
|
95
|
-
end
|
48
|
+
#
|
49
|
+
# class TunnelListenServer < GServer
|
50
|
+
# @@tunneled_connections = []
|
51
|
+
#
|
52
|
+
# def initialize(port, host = DEFAULT_HOST)
|
53
|
+
# super(port, host, 10)
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# def serve(sock)
|
57
|
+
# D "new tunneled connection"
|
58
|
+
# cv, incoming_connection = RemoteListenServer.next_incoming_connection
|
59
|
+
# if ! incoming_connection
|
60
|
+
# D "no incoming connections for this tunneled connection, closing"
|
61
|
+
# return
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# while ready = (IO.select([incoming_connection, sock], nil, nil, 0.5).first rescue [])
|
65
|
+
# if ready.include? incoming_connection
|
66
|
+
# begin
|
67
|
+
# sock.write(incoming_connection.readpartial(1024))
|
68
|
+
# rescue EOFError
|
69
|
+
# D "incoming socket closed, closing tunneled socket"
|
70
|
+
# sock.close
|
71
|
+
# cv.signal
|
72
|
+
#
|
73
|
+
# return
|
74
|
+
# end
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# if ready.include? sock
|
78
|
+
# begin
|
79
|
+
# incoming_connection.write(sock.readpartial(1024))
|
80
|
+
# rescue EOFError
|
81
|
+
# D "tunneled socket closed, closing incoming connection socket"
|
82
|
+
# incoming_connection.close
|
83
|
+
# cv.signal
|
84
|
+
#
|
85
|
+
# return
|
86
|
+
# end
|
87
|
+
# end
|
88
|
+
# end
|
89
|
+
# rescue Exception
|
90
|
+
# p $!
|
91
|
+
# puts $!.backtrace.join("\n")
|
92
|
+
# end
|
93
|
+
# end
|
96
94
|
|
97
95
|
class ControlServer < GServer
|
98
96
|
@@control_connection = nil
|
@@ -102,10 +100,21 @@ module RTunnel
|
|
102
100
|
|
103
101
|
attr_accessor :ping_interval
|
104
102
|
|
105
|
-
def self.new_tunnel
|
106
|
-
D "sending
|
103
|
+
def self.new_tunnel(conn_id)
|
104
|
+
D "sending create connection command: #{conn_id}"
|
107
105
|
|
108
|
-
@@m.synchronize { @@control_connection.write
|
106
|
+
@@m.synchronize { @@control_connection.write CreateConnectionCommand.new(conn_id) }
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.send_data(conn_id, data)
|
110
|
+
D "send to client: #{conn_id}"
|
111
|
+
@@m.synchronize { @@control_connection.write SendDataCommand.new(conn_id, data) }
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.close_tunnel(conn_id)
|
115
|
+
D "sending close connection command"
|
116
|
+
|
117
|
+
@@m.synchronize { @@control_connection.write CloseConnectionCommand.new(conn_id) }
|
109
118
|
end
|
110
119
|
|
111
120
|
def serve(sock)
|
@@ -113,20 +122,32 @@ module RTunnel
|
|
113
122
|
@@control_connection = sock
|
114
123
|
sock.sync = true
|
115
124
|
|
125
|
+
cmd_queue = ""
|
116
126
|
while true
|
117
|
-
if IO.select([sock], nil, nil, @ping_interval)
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
+
if r = IO.select([sock], nil, nil, @ping_interval)
|
128
|
+
cmd_queue << sock.readpartial(16384) rescue (D "#{$!.inspect}"; break)
|
129
|
+
while Command.match(cmd_queue)
|
130
|
+
cmd = Command.parse(cmd_queue)
|
131
|
+
case cmd
|
132
|
+
when RemoteListenCommand
|
133
|
+
@@remote_listen_server.stop if @@remote_listen_server
|
134
|
+
(@@remote_listen_server = RemoteListenServer.new(*cmd.address.split(/:/).reverse)).start
|
135
|
+
D "listening for remote connections on #{cmd.address}"
|
136
|
+
when SendDataCommand
|
137
|
+
D "send data to remote conn #{cmd.conn_id}"
|
138
|
+
RemoteListenServer::CONNECTIONS[cmd.conn_id].write cmd.data
|
139
|
+
when CloseConnectionCommand
|
140
|
+
if connection = RemoteListenServer::CONNECTIONS[cmd.conn_id]
|
141
|
+
D "closing remote connection: #{cmd.conn_id}"
|
142
|
+
connection.close
|
143
|
+
end
|
144
|
+
else
|
145
|
+
D "bad command received: #{cmd.inspect}"
|
146
|
+
end
|
127
147
|
end
|
128
148
|
end
|
129
|
-
|
149
|
+
D "ping"
|
150
|
+
@@m.synchronize { @@control_connection.write PingCommand.new }
|
130
151
|
end
|
131
152
|
rescue
|
132
153
|
D $!.inspect
|
@@ -151,8 +172,6 @@ module RTunnel
|
|
151
172
|
@control_host = @control_address.split(/:/).first
|
152
173
|
|
153
174
|
@ping_interval = options[:ping_interval] || 2.0
|
154
|
-
|
155
|
-
@tunnel_address = @control_host + ":#{options[:tunnel_port] || DEFAULT_TUNNEL_PORT}"
|
156
175
|
end
|
157
176
|
|
158
177
|
def start
|
@@ -160,9 +179,6 @@ module RTunnel
|
|
160
179
|
@control_server.ping_interval = @ping_interval
|
161
180
|
@control_server.audit = true
|
162
181
|
|
163
|
-
@tunnel_server = TunnelListenServer.new(*@tunnel_address.split(/:/).reverse)
|
164
|
-
|
165
|
-
@tunnel_server.start
|
166
182
|
@control_server.start
|
167
183
|
end
|
168
184
|
|
@@ -172,9 +188,7 @@ module RTunnel
|
|
172
188
|
|
173
189
|
def stop
|
174
190
|
@control_server.shutdown
|
175
|
-
@tunnel_server.shutdown
|
176
191
|
ControlServer.stop(@control_server.port, @control_server.host)
|
177
|
-
TunnelListenServer.stop(@tunnel_server.port, @tunnel_server.host)
|
178
192
|
end
|
179
193
|
end
|
180
194
|
end
|
data/rtunnel_client.rb
CHANGED
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: rtunnel
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2007-11-
|
6
|
+
version: 0.3.0
|
7
|
+
date: 2007-11-27 00:00:00 +07:00
|
8
8
|
summary: The author was too lazy to write a summary
|
9
9
|
require_paths:
|
10
10
|
- lib
|