rtunnel 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|