Class: RTunnel::Client::ServerConnection

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

Overview

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 RTunnel::CommandProtocol

#incoming_command_hasher=, #outgoing_command_hasher=, #receive_frame, #send_command

Methods included from FrameProtocol

#receive_data, #send_frame

Methods included from RTunnel::CommandProcessor

#process_generate_session_key, #process_remote_listen, #unexpected_command

Methods included from Logging

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

Methods included from RTunnel

resolve_address, run_client, run_server

Constructor Details

#initialize(client) ⇒ ServerConnection

Returns a new instance of ServerConnection.



99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/rtunnel/client.rb', line 99

def initialize(client)
  super()
  
  @client = client
  @tunnel_to_address = client.tunnel_to_address
  @tunnel_to_host = SocketFactory.host_from_address @tunnel_to_address
  @tunnel_to_port = SocketFactory.port_from_address @tunnel_to_address
  @timeout_timer = nil
  @hasher = nil
  @connections = @client.connections
  init_log :to => @client
end

Instance Attribute Details

#clientObject (readonly)

Returns the value of attribute client.



97
98
99
# File 'lib/rtunnel/client.rb', line 97

def client
  @client
end

Instance Method Details

#check_tunnel_timeoutObject

Closes the connection if no command has been received for some time.



244
245
246
247
248
249
250
# File 'lib/rtunnel/client.rb', line 244

def check_tunnel_timeout
  if tunnel_timeout?
    W 'Tunnel timeout. Disconnecting from server.'
    disable_tunnel_timeouts
    close_connection_after_writing
  end
end

#data_connection_closed(connection_id) ⇒ Object

Called when a tunnel connection is closed.



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

def data_connection_closed(connection_id)
  return unless @connections.delete(connection_id)
  D "Connection #{connection_id} closed by this end"
  send_command CloseConnectionCommand.new(connection_id)
end

#disable_tunnel_timeoutsObject

Disables timeout checking (so the tunnel will not be torn down if no command is received for some period of time).



254
255
256
257
258
# File 'lib/rtunnel/client.rb', line 254

def disable_tunnel_timeouts
  return unless @timeout_timer
  @timeout_timer.cancel
  @timeout_timer = nil
end

#enable_tunnel_timeoutsObject

After this is called, the control connection will be closed if no command is received within a certain amount of time.



236
237
238
239
240
241
# File 'lib/rtunnel/client.rb', line 236

def enable_tunnel_timeouts
  @last_packet_time = Time.now
  @timeout_timer = EventMachine::PeriodicTimer.new(1.0) do
    check_tunnel_timeout
  end
end

#post_initObject



112
113
114
115
116
117
118
# File 'lib/rtunnel/client.rb', line 112

def post_init
  if @client.private_key
    request_session_key
  else
    request_listen
  end
end

#process_close_connection(connection_id) ⇒ Object

CloseConnectionCommand handler



158
159
160
161
162
163
164
165
166
# File 'lib/rtunnel/client.rb', line 158

def process_close_connection(connection_id)
  if connection = @connections[connection_id]
    I "Closing connection #{connection_id}"
    connection.close_connection_after_writing
    @connections.delete connection_id
  else
    W "Asked to close inexistent connection #{connection_id}"
  end
end

#process_create_connection(connection_id) ⇒ Object

CreateConnectionCommand handler



145
146
147
148
149
150
151
152
153
154
155
# File 'lib/rtunnel/client.rb', line 145

def process_create_connection(connection_id)
  if @connections[connection_id]
    E "asked to create already open connection #{connection_id}"
    return
  end
  
  D "Tunnel #{connection_id} to #{@tunnel_to_host} port #{@tunnel_to_port}"
  connection = EventMachine.connect(@tunnel_to_host, @tunnel_to_port,
      Client::TunnelConnection, connection_id, @client)
  @connections[connection_id] = connection
end

#process_keep_aliveObject

Keep-alive received from the control connection.



225
226
# File 'lib/rtunnel/client.rb', line 225

def process_keep_alive
end

#process_send_data(connection_id, data) ⇒ Object

SendData handler



175
176
177
178
179
180
181
182
# File 'lib/rtunnel/client.rb', line 175

def process_send_data(connection_id, data)
  if connection = @connections[connection_id]
    D "Data: #{data.length} bytes for #{connection_id}"
    connection.tunnel_data data
  else
    W "Received data for non-existent connection #{connection_id}!"
  end
end

#process_set_session_key(encrypted_keys) ⇒ Object

SetSessionKey handler



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/rtunnel/client.rb', line 185

def process_set_session_key(encrypted_keys)
  case encrypted_keys
  when ''
    W "Sent key to open tunnel server"
    request_listen
  when 'NO'
    if @client.private_key
      E "Server refused provided key"
    else
      E "Server requires authentication and no private key was provided"
    end
    close_connection_after_writing
  else
    D "Received server session keys, installing hashers"
    iokeys = StringIO.new Crypto.decrypt_with_key(client.private_key,
                                                  encrypted_keys)
    @out_hasher = Crypto::Hasher.new iokeys.read_varstring
    @in_hasher = Crypto::Hasher.new iokeys.read_varstring
    self.outgoing_command_hasher = @out_hasher
    self.incoming_command_hasher = @in_hasher
    
    D "Hashers installed, opening listen socket on server"
    request_listen
  end
end

#receive_bad_frame(frame, exception) ⇒ Object



211
212
213
214
215
216
217
218
219
220
# File 'lib/rtunnel/client.rb', line 211

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

#receive_command(command) ⇒ Object

:nodoc:



229
230
231
232
# File 'lib/rtunnel/client.rb', line 229

def receive_command(command) 
  @last_packet_time = Time.now
  super
end

#request_listenObject

Asks the server to open a listen socket for this client’s tunnel.



121
122
123
124
# File 'lib/rtunnel/client.rb', line 121

def request_listen
  send_command RemoteListenCommand.new(@client.remote_listen_address)
  enable_tunnel_timeouts    
end

#request_session_keyObject

Asks the server to establish a session key with this client.



127
128
129
130
131
# File 'lib/rtunnel/client.rb', line 127

def request_session_key
  D 'Private key provided, asking server for session key'
  key_fp = Crypto.key_fingerprint @client.private_key
  send_command GenerateSessionKeyCommand.new(key_fp)    
end

#tunnel_timeout?Boolean

If true, a tunnel timeout has occured.

Returns:

  • (Boolean)


261
262
263
# File 'lib/rtunnel/client.rb', line 261

def tunnel_timeout?
  Time.now - @last_packet_time > client.tunnel_timeout
end

#unbindObject



133
134
135
136
137
138
139
# File 'lib/rtunnel/client.rb', line 133

def unbind
  # wait for a second, then try connecting again
  W 'Lost server connection, will reconnect in 1s'
  EventMachine.add_timer(1.0) { client.connect_to_server }
  @connections.each { |conn_id, conn| conn.close_connection_after_writing }
  @connections.clear
end