Class: RTunnel::Server::ControlConnection

Inherits:
EventMachine::Connection
  • Object
show all
Includes:
RTunnel, CommandProcessor, CommandProtocol, Logging
Defined in:
lib/rtunnel/server.rb

Overview

A client connection to the server’s control port.

Constant Summary

Constants included from RTunnel

DEFAULT_CONTROL_PORT, KEEP_ALIVE_INTERVAL, TUNNEL_TIMEOUT

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Logging

#D, #E, #F, #I, #W, #init_log

Methods included from CommandProtocol

#incoming_command_hasher=, #outgoing_command_hasher=, #receive_frame

Methods included from FrameProtocol

#receive_data, #send_frame

Methods included from CommandProcessor

#process_create_connection, #process_keep_alive, #process_set_session_key, #receive_command, #unexpected_command

Methods included from RTunnel

resolve_address, run_client, run_server

Constructor Details

#initialize(server) ⇒ ControlConnection

Returns a new instance of ControlConnection.



150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/rtunnel/server.rb', line 150

def initialize(server)
  super()
  
  @server = server
  @tunnel_connections = server.tunnel_connections
  @listener = nil
  @keep_alive_timer = nil
  @keep_alive_interval = server.keep_alive_interval
  @in_hasher = @out_hasher = nil
  
  init_log :to => @server
end

Instance Attribute Details

#listenerObject (readonly)

Returns the value of attribute listener.



148
149
150
# File 'lib/rtunnel/server.rb', line 148

def listener
  @listener
end

#serverObject (readonly)

Returns the value of attribute server.



148
149
150
# File 'lib/rtunnel/server.rb', line 148

def server
  @server
end

Instance Method Details

#disable_keep_alivesObject

Disables sending KeepAlives.



298
299
300
301
302
# File 'lib/rtunnel/server.rb', line 298

def disable_keep_alives
  return unless @keep_alive_timer
  @keep_alive_timer.cancel
  @keep_alive_timer = nil
end

#enable_keep_alivesObject

Enables sending KeepAliveCommands every few seconds.



282
283
284
285
286
287
288
# File 'lib/rtunnel/server.rb', line 282

def enable_keep_alives
  @last_command_time = Time.now
  @keep_alive_timer =
      EventMachine::PeriodicTimer.new(@keep_alive_interval / 2) do
    keep_alive_if_needed
  end
end

#keep_alive_if_neededObject

Sends a KeepAlive command if no command was sent recently.



291
292
293
294
295
# File 'lib/rtunnel/server.rb', line 291

def keep_alive_if_needed
  if Time.now - @last_command_time >= @keep_alive_interval
    send_command KeepAliveCommand.new
  end
end

#post_initObject



163
164
165
166
167
# File 'lib/rtunnel/server.rb', line 163

def post_init
  @client_port, @client_host = *Socket.unpack_sockaddr_in(get_peername)
  D "Established connection with #{@client_host} port #{@client_port}"
  enable_keep_alives
end

#process_close_connection(tunnel_connection_id) ⇒ Object



227
228
229
230
231
232
233
234
235
# File 'lib/rtunnel/server.rb', line 227

def process_close_connection(tunnel_connection_id)
  tunnel_connection = @tunnel_connections[tunnel_connection_id]
  if tunnel_connection
    D "Closed from tunneled end: #{tunnel_connection_id}"
    tunnel_connection.close_from_tunnel
  else
    W "Asked to close unknown connection #{tunnel_connection_id}"
  end
end

#process_generate_session_key(public_key_fp) ⇒ Object



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/rtunnel/server.rb', line 237

def process_generate_session_key(public_key_fp)
  if @server.authorized_keys
    if public_key = @server.authorized_keys[public_key_fp]
      D "Authorized client key received, generating session key"
      @out_hasher, @in_hasher = Crypto::Hasher.new, Crypto::Hasher.new
      
      iokeys = StringIO.new
      iokeys.write_varstring @in_hasher.key
      iokeys.write_varstring @out_hasher.key
      encrypted_keys = Crypto.encrypt_with_key public_key, iokeys.string
    else
      D("Rejecting unauthorized client key (%s authorized keys)" %
        @server.authorized_keys.length)
      encrypted_keys = 'NO'
    end
  else
    D "Asked to generate session key, but no authorized keys set"
    encrypted_keys = ''
  end
  send_command SetSessionKeyCommand.new(encrypted_keys)
  self.incoming_command_hasher = @in_hasher if @in_hasher
  self.outgoing_command_hasher = @out_hasher if @out_hasher
end

#process_remote_listen(address) ⇒ Object

Command processing



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/rtunnel/server.rb', line 177

def process_remote_listen(address)
  listen_host = SocketFactory.host_from_address address
  listen_port = SocketFactory.port_from_address address

  unless validate_remote_listen listen_host, listen_port
    send_command SetSessionKeyCommand.new('NO')
    return      
  end
  
  @server.create_tunnel_listener listen_port, self do
    D "Creating listener for #{listen_host} port #{listen_port}"
    begin
      @listener = EventMachine.start_server listen_host, listen_port,
                                             Server::TunnelConnection, self,
                                             listen_host, listen_port
    rescue RuntimeError => e
      # EventMachine raises 'no acceptor' if the listen address is invalid
      E "Invalid listen address #{listen_host}"        
      @listener = nil
    end
  end
  
  D "Listening on #{listen_host} port #{listen_port}"
end

#process_send_data(tunnel_connection_id, data) ⇒ Object



217
218
219
220
221
222
223
224
225
# File 'lib/rtunnel/server.rb', line 217

def process_send_data(tunnel_connection_id, data)
  tunnel_connection = @tunnel_connections[tunnel_connection_id]
  if tunnel_connection
    D "Data: #{data.length} bytes coming from #{tunnel_connection_id}"
    tunnel_connection.send_data data
  else
    W "Asked to send to unknown connection #{tunnel_connection_id}"
  end
end

#receive_bad_frame(frame, exception) ⇒ Object



261
262
263
264
265
266
267
268
269
270
# File 'lib/rtunnel/server.rb', line 261

def receive_bad_frame(frame, exception)
  case exception
  when :bad_signature
    D "Ignoring command with invalid signature"
  when Exception
    D "Ignoring malformed command."
    D "Decoding exception: #{exception.class.name} - #{exception}\n" +
      "#{exception.backtrace.join("\n")}\n"
  end
end

#send_command(command) ⇒ Object

:nodoc:



276
277
278
279
# File 'lib/rtunnel/server.rb', line 276

def send_command(command)
  @last_command_time = Time.now
  super
end

#unbindObject



169
170
171
172
# File 'lib/rtunnel/server.rb', line 169

def unbind
  D "Lost connection from #{@client_host} port #{@client_port}"
  disable_keep_alives
end

#validate_remote_listen(host, port) ⇒ Object

Verifies if a RemoteListenCommand should be honored.



203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/rtunnel/server.rb', line 203

def validate_remote_listen(host, port)
  if @server.authorized_keys and @out_hasher.nil?
    D "Asked to open listen socket by unauthorized client"
    return false
  end
  
  if port < @server.lowest_listen_port or port > @server.highest_listen_port
    D "Asked to listen to forbidden port"
    return false
  end
  
  true
end