Class: HrrRbSsh::Connection

Inherits:
Object
  • Object
show all
Includes:
Loggable
Defined in:
lib/hrr_rb_ssh/connection.rb,
lib/hrr_rb_ssh/connection/channel.rb,
lib/hrr_rb_ssh/connection/request_handler.rb,
lib/hrr_rb_ssh/connection/channel/channel_type.rb,
lib/hrr_rb_ssh/connection/global_request_handler.rb,
lib/hrr_rb_ssh/connection/channel/channel_type/session.rb,
lib/hrr_rb_ssh/connection/channel/channel_type/direct_tcpip.rb,
lib/hrr_rb_ssh/connection/channel/channel_type/forwarded_tcpip.rb,
lib/hrr_rb_ssh/connection/channel/channel_type/session/proc_chain.rb,
lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type.rb,
lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/env.rb,
lib/hrr_rb_ssh/connection/request_handler/reference_env_request_handler.rb,
lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/exec.rb,
lib/hrr_rb_ssh/connection/request_handler/reference_exec_request_handler.rb,
lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/shell.rb,
lib/hrr_rb_ssh/connection/request_handler/reference_shell_request_handler.rb,
lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/pty_req.rb,
lib/hrr_rb_ssh/connection/request_handler/reference_pty_req_request_handler.rb,
lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/subsystem.rb,
lib/hrr_rb_ssh/connection/channel/channel_type/session/proc_chain/chain_context.rb,
lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/env/context.rb,
lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/exec/context.rb,
lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/shell/context.rb,
lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/window_change.rb,
lib/hrr_rb_ssh/connection/request_handler/reference_window_change_request_handler.rb,
lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/pty_req/context.rb,
lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/subsystem/context.rb,
lib/hrr_rb_ssh/connection/channel/channel_type/session/request_type/window_change/context.rb

Defined Under Namespace

Classes: Channel, GlobalRequestHandler, RequestHandler

Instance Attribute Summary collapse

Attributes included from Loggable

#log_key, #logger

Instance Method Summary collapse

Methods included from Loggable

#log_debug, #log_error, #log_fatal, #log_info, #log_warn

Constructor Details

#initialize(authentication, mode, options = {}, logger: nil) ⇒ Connection

Returns a new instance of Connection.



19
20
21
22
23
24
25
26
27
28
29
# File 'lib/hrr_rb_ssh/connection.rb', line 19

def initialize authentication, mode, options={}, logger: nil
  self.logger = logger
  @authentication = authentication
  @mode = mode
  @options = options
  @global_request_handler = GlobalRequestHandler.new self, logger: logger
  @channels = Hash.new
  @username = nil
  @variables = nil
  @closed = nil
end

Instance Attribute Details

#modeObject (readonly)

Returns the value of attribute mode.



13
14
15
# File 'lib/hrr_rb_ssh/connection.rb', line 13

def mode
  @mode
end

#optionsObject (readonly)

Returns the value of attribute options.



13
14
15
# File 'lib/hrr_rb_ssh/connection.rb', line 13

def options
  @options
end

#usernameObject (readonly)

Returns the value of attribute username.



13
14
15
# File 'lib/hrr_rb_ssh/connection.rb', line 13

def username
  @username
end

#variablesObject (readonly)

Returns the value of attribute variables.



13
14
15
# File 'lib/hrr_rb_ssh/connection.rb', line 13

def variables
  @variables
end

Instance Method Details

#assign_channelObject



40
41
42
43
44
45
46
47
48
# File 'lib/hrr_rb_ssh/connection.rb', line 40

def assign_channel
  i = 0
  res = nil
  while true
    break unless @channels.keys.include?(i)
    i += 1
  end
  i
end

#channel_close(payload) ⇒ Object



258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/hrr_rb_ssh/connection.rb', line 258

def channel_close payload
  log_info { 'received ' + Message::SSH_MSG_CHANNEL_CLOSE::ID }
  message = Message::SSH_MSG_CHANNEL_CLOSE.new(logger: logger).decode payload
  local_channel = message[:'recipient channel']
  channel = @channels[local_channel]
  channel.close
  log_info { "wait until threads closed in channel" }
  channel.wait_until_closed
  log_info { "channel closed" }
  log_info { "deleting channel" }
  @channels.delete local_channel
  log_info { "channel deleted" }
end

#channel_data(payload) ⇒ Object



237
238
239
240
241
242
# File 'lib/hrr_rb_ssh/connection.rb', line 237

def channel_data payload
  log_info { 'received ' + Message::SSH_MSG_CHANNEL_DATA::ID }
  message = Message::SSH_MSG_CHANNEL_DATA.new(logger: logger).decode payload
  local_channel = message[:'recipient channel']
  @channels[local_channel].receive_message_queue.enq message if @channels.has_key? local_channel
end

#channel_eof(payload) ⇒ Object



251
252
253
254
255
256
# File 'lib/hrr_rb_ssh/connection.rb', line 251

def channel_eof payload
  log_info { 'received ' + Message::SSH_MSG_CHANNEL_EOF::ID }
  message = Message::SSH_MSG_CHANNEL_EOF.new(logger: logger).decode payload
  local_channel = message[:'recipient channel']
  @channels[local_channel].receive_message_queue.enq message if @channels.has_key? local_channel
end

#channel_extended_data(payload) ⇒ Object



244
245
246
247
248
249
# File 'lib/hrr_rb_ssh/connection.rb', line 244

def channel_extended_data payload
  log_info { 'received ' + Message::SSH_MSG_CHANNEL_EXTENDED_DATA::ID }
  message = Message::SSH_MSG_CHANNEL_EXTENDED_DATA.new(logger: logger).decode payload
  local_channel = message[:'recipient channel']
  @channels[local_channel].receive_message_queue.enq message if @channels.has_key? local_channel
end

#channel_open(payload) ⇒ Object



172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/hrr_rb_ssh/connection.rb', line 172

def channel_open payload
  log_info { 'received ' + Message::SSH_MSG_CHANNEL_OPEN::ID }
  message = Message::SSH_MSG_CHANNEL_OPEN.new(logger: logger).decode payload
  begin
    channel = Channel.new self, message, logger: logger
    @channels[channel.local_channel] = channel
    channel.start
    send_channel_open_confirmation channel
  rescue => e
    log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
    recipient_channel = message[:'sender channel']
    send_channel_open_failure recipient_channel, Message::SSH_MSG_CHANNEL_OPEN_FAILURE::ReasonCode::SSH_OPEN_CONNECT_FAILED, e.message
  end
end

#channel_open_confirmation(payload) ⇒ Object



215
216
217
218
219
220
221
# File 'lib/hrr_rb_ssh/connection.rb', line 215

def channel_open_confirmation payload
  log_info { 'received ' + Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION::ID }
  message = Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION.new(logger: logger).decode payload
  channel = @channels[message[:'recipient channel']]
  channel.set_remote_parameters message
  channel.start
end

#channel_open_start(address, port, socket) ⇒ Object



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/hrr_rb_ssh/connection.rb', line 153

def channel_open_start address, port, socket
  log_info { 'channel open start' }
  channel = Channel.new self, {:'channel type' => "forwarded-tcpip"}, socket, logger: logger
  @channels[channel.local_channel] = channel
  log_info { 'channel opened' }
  message = {
    :'message number'             => Message::SSH_MSG_CHANNEL_OPEN::VALUE,
    :'channel type'               => "forwarded-tcpip",
    :'sender channel'             => channel.local_channel,
    :'initial window size'        => channel.local_window_size,
    :'maximum packet size'        => channel.local_maximum_packet_size,
    :'address that was connected' => address,
    :'port that was connected'    => port,
    :'originator IP address'      => socket.remote_address.ip_address,
    :'originator port'            => socket.remote_address.ip_port,
  }
  send_channel_open message
end

#channel_request(payload) ⇒ Object



223
224
225
226
227
228
# File 'lib/hrr_rb_ssh/connection.rb', line 223

def channel_request payload
  log_info { 'received ' + Message::SSH_MSG_CHANNEL_REQUEST::ID }
  message = Message::SSH_MSG_CHANNEL_REQUEST.new(logger: logger).decode payload
  local_channel = message[:'recipient channel']
  @channels[local_channel].receive_message_queue.enq message if @channels.has_key? local_channel
end

#channel_window_adjust(payload) ⇒ Object



230
231
232
233
234
235
# File 'lib/hrr_rb_ssh/connection.rb', line 230

def channel_window_adjust payload
  log_info { 'received ' + Message::SSH_MSG_CHANNEL_WINDOW_ADJUST::ID }
  message = Message::SSH_MSG_CHANNEL_WINDOW_ADJUST.new(logger: logger).decode payload
  local_channel = message[:'recipient channel']
  @channels[local_channel].receive_message_queue.enq message if @channels.has_key? local_channel
end

#closeObject



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/hrr_rb_ssh/connection.rb', line 69

def close
  return if @closed
  log_info { "close connection" }
  @closed = true
  @authentication.close
  @channels.values.each do |channel|
    begin
      channel.close
    rescue => e
      log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
    end
  end
  @channels.clear
  @global_request_handler.close
  @connection_loop_thread.join if @connection_loop_thread && @connection_loop_thread != Thread.current
  log_info { "connection closed" }
end

#closed?Boolean

Returns:

  • (Boolean)


87
88
89
# File 'lib/hrr_rb_ssh/connection.rb', line 87

def closed?
  @closed
end

#connection_loop_threadObject



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/hrr_rb_ssh/connection.rb', line 91

def connection_loop_thread
  log_info { "start connection loop" }
  Thread.new do
    begin
      while true
        begin
          payload = @authentication.receive
        rescue Error::ClosedAuthentication => e
          log_info { "authentication closed" }
          break
        end
        @username ||= @authentication.username
        @variables ||= @authentication.variables
        case payload[0,1].unpack("C")[0]
        when Message::SSH_MSG_GLOBAL_REQUEST::VALUE
          global_request payload
        when Message::SSH_MSG_CHANNEL_OPEN::VALUE
          channel_open payload
        when Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION::VALUE
          channel_open_confirmation payload
        when Message::SSH_MSG_CHANNEL_REQUEST::VALUE
          channel_request payload
        when Message::SSH_MSG_CHANNEL_WINDOW_ADJUST::VALUE
          channel_window_adjust payload
        when Message::SSH_MSG_CHANNEL_DATA::VALUE
          channel_data payload
        when Message::SSH_MSG_CHANNEL_EXTENDED_DATA::VALUE
          channel_extended_data payload
        when Message::SSH_MSG_CHANNEL_EOF::VALUE
          channel_eof payload
        when Message::SSH_MSG_CHANNEL_CLOSE::VALUE
          channel_close payload
        else
          log_warn { "received unsupported message: id: #{payload[0,1].unpack("C")[0]}" }
        end
      end
    rescue => e
      log_error { [e.backtrace[0], ": ", e.message, " (", e.class.to_s, ")\n\t", e.backtrace[1..-1].join("\n\t")].join }
    ensure
      log_info { "closing connection loop" }
      close
      log_info { "connection loop closed" }
    end
  end
end

#global_request(payload) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/hrr_rb_ssh/connection.rb', line 137

def global_request payload
  log_info { 'received ' + Message::SSH_MSG_GLOBAL_REQUEST::ID }
  message = Message::SSH_MSG_GLOBAL_REQUEST.new(logger: logger).decode payload
  begin
    @global_request_handler.request message
  rescue
    if message[:'want reply']
      send_request_failure
    end
  else
    if message[:'want reply']
      send_request_success
    end
  end
end

#loopObject



65
66
67
# File 'lib/hrr_rb_ssh/connection.rb', line 65

def loop
  @connection_loop_thread.join
end

#request_channel_open(channel_type, channel_specific_message = {}, wait_response = true) ⇒ Object



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/hrr_rb_ssh/connection.rb', line 187

def request_channel_open channel_type, channel_specific_message={}, wait_response=true
  log_info { 'request channel open' }
  case channel_type
  when "session"
    channel = Channel.new self, {:'channel type' => channel_type}, logger: logger
    @channels[channel.local_channel] = channel
  end
  message = {
    :'message number'             => Message::SSH_MSG_CHANNEL_OPEN::VALUE,
    :'channel type'               => channel_type,
    :'sender channel'             => channel.local_channel,
    :'initial window size'        => channel.local_window_size,
    :'maximum packet size'        => channel.local_maximum_packet_size,
  }
  send_channel_open message.merge(channel_specific_message)
  log_info { 'sent channel open' }
  if wait_response
    log_info { 'wait response' }
    channel.wait_until_started
  end
  unless channel.closed?
    log_info { 'channel opened' }
    channel
  else
    raise "Faild opening channel"
  end
end

#send(payload) ⇒ Object



31
32
33
34
35
36
37
38
# File 'lib/hrr_rb_ssh/connection.rb', line 31

def send payload
  raise Error::ClosedConnection if @closed
  begin
    @authentication.send payload
  rescue Error::ClosedAuthentication
    raise Error::ClosedConnection
  end
end

#send_channel_open(message) ⇒ Object



288
289
290
291
# File 'lib/hrr_rb_ssh/connection.rb', line 288

def send_channel_open message
  payload = Message::SSH_MSG_CHANNEL_OPEN.new(logger: logger).encode message
  @authentication.send payload
end

#send_channel_open_confirmation(channel) ⇒ Object



293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/hrr_rb_ssh/connection.rb', line 293

def send_channel_open_confirmation channel
  message = {
    :'message number'      => Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION::VALUE,
    :'channel type'        => channel.channel_type,
    :'recipient channel'   => channel.remote_channel,
    :'sender channel'      => channel.local_channel,
    :'initial window size' => channel.local_window_size,
    :'maximum packet size' => channel.local_maximum_packet_size,
  }
  payload = Message::SSH_MSG_CHANNEL_OPEN_CONFIRMATION.new(logger: logger).encode message
  @authentication.send payload
end

#send_channel_open_failure(recipient_channel, reason_code, description) ⇒ Object



306
307
308
309
310
311
312
313
314
315
316
# File 'lib/hrr_rb_ssh/connection.rb', line 306

def send_channel_open_failure recipient_channel, reason_code, description
  message = {
    :'message number'      => Message::SSH_MSG_CHANNEL_OPEN_FAILURE::VALUE,
    :'recipient channel'   => recipient_channel,
    :'reason code'         => reason_code,
    :'description'         => description,
    :'language tag'        => "",
  }
  payload = Message::SSH_MSG_CHANNEL_OPEN_FAILURE.new(logger: logger).encode message
  @authentication.send payload
end

#send_request_failureObject



280
281
282
283
284
285
286
# File 'lib/hrr_rb_ssh/connection.rb', line 280

def send_request_failure
  message = {
    :'message number' => Message::SSH_MSG_REQUEST_FAILURE::VALUE,
  }
  payload = Message::SSH_MSG_REQUEST_FAILURE.new(logger: logger).encode message
  @authentication.send payload
end

#send_request_successObject



272
273
274
275
276
277
278
# File 'lib/hrr_rb_ssh/connection.rb', line 272

def send_request_success
  message = {
    :'message number' => Message::SSH_MSG_REQUEST_SUCCESS::VALUE,
  }
  payload = Message::SSH_MSG_REQUEST_SUCCESS.new(logger: logger).encode message
  @authentication.send payload
end

#start(foreground: true) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/hrr_rb_ssh/connection.rb', line 50

def start foreground: true
  log_info { "start connection" }
  begin
    @authentication.start
  rescue Error::ClosedAuthentication
    close
    raise Error::ClosedConnection
  end
  @closed = false
  @connection_loop_thread = connection_loop_thread
  if foreground
    @connection_loop_thread.join
  end
end