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



252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/hrr_rb_ssh/connection.rb', line 252

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



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

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
end

#channel_eof(payload) ⇒ Object



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

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
end

#channel_extended_data(payload) ⇒ Object



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

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
end

#channel_open(payload) ⇒ Object



166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/hrr_rb_ssh/connection.rb', line 166

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



209
210
211
212
213
214
215
# File 'lib/hrr_rb_ssh/connection.rb', line 209

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



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/hrr_rb_ssh/connection.rb', line 147

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



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

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
end

#channel_window_adjust(payload) ⇒ Object



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

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
end

#closeObject



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/hrr_rb_ssh/connection.rb', line 64

def close
  log_info { "closing 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 unless @connection_loop_thread == Thread.current
  log_info { "connection closed" }
end

#closed?Boolean

Returns:

  • (Boolean)


81
82
83
# File 'lib/hrr_rb_ssh/connection.rb', line 81

def closed?
  @closed
end

#connection_loop_threadObject



85
86
87
88
89
90
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
# File 'lib/hrr_rb_ssh/connection.rb', line 85

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



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/hrr_rb_ssh/connection.rb', line 131

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



60
61
62
# File 'lib/hrr_rb_ssh/connection.rb', line 60

def loop
  @connection_loop_thread.join
end

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



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/hrr_rb_ssh/connection.rb', line 181

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



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

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



287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/hrr_rb_ssh/connection.rb', line 287

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



300
301
302
303
304
305
306
307
308
309
310
# File 'lib/hrr_rb_ssh/connection.rb', line 300

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



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

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



266
267
268
269
270
271
272
# File 'lib/hrr_rb_ssh/connection.rb', line 266

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
# File 'lib/hrr_rb_ssh/connection.rb', line 50

def start foreground: true
  log_info { "start connection" }
  @authentication.start
  @closed = false
  @connection_loop_thread = connection_loop_thread
  if foreground
    @connection_loop_thread.join
  end
end