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 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
- @tunnel_from_address = @control_host + ":#{options[:tunnel_port] || DEFAULT_TUNNEL_PORT}"
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
- @control_sock.sync
61
- @control_sock.puts "remote_listen #{@remote_listen_address}"
62
-
63
- while cmd = (@control_sock.readline.strip rescue (D "#{$!.inspect}"; nil))
64
- case cmd
65
- when "ping"
66
- @last_ping = Time.now
67
-
68
- when "new_tunnel"
69
- @threads << Thread.safe do
70
- D "connecting to to tunnel"
71
- to = TCPSocket.new(*@tunnel_to_address.split(/:/))
72
- D "connecting to from tunnel"
73
- from = TCPSocket.new(*@tunnel_from_address.split(/:/))
74
-
75
- while ready = (IO.select([to, from], nil, nil, 0.5).first rescue [])
76
- if ready.include? to
77
- begin
78
- from.write(to.readpartial(1024))
79
- rescue EOFError
80
- D "to tunnel closed, closing from tunnel"
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.3'
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
@@ -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
 
@@ -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
- @@m = Mutex.new
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
- @@m.synchronize { @@incoming_connections.delete(incoming) }
39
- return
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
- m.synchronize { cv.wait(m) }
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 new tunnel command"
103
+ def self.new_tunnel(conn_id)
104
+ D "sending create connection command: #{conn_id}"
107
105
 
108
- @@m.synchronize { @@control_connection.write "new_tunnel\n" }
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
- cmd = sock.readline rescue (D "#{$!.inspect}"; break)
119
- case cmd
120
- when /^remote_listen ([^: ]+:\d+)/
121
- remote_listen_address = $1
122
- @@remote_listen_server.stop if @@remote_listen_server
123
- (@@remote_listen_server = RemoteListenServer.new(*remote_listen_address.split(/:/).reverse)).start
124
- D "listening for remote connections on #{remote_listen_address}"
125
- else
126
- D "bad command received: #{cmd.inspect}"
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
- sock.puts "ping"
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
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/ruby
2
2
 
3
- load 'lib/rtunnel_client_cmd.rb'
3
+ load 'lib/rtunnel_client_cmd.rb'
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.2.3
7
- date: 2007-11-25 00:00:00 +07:00
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