net-ssh 6.2.0.rc1 → 7.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.dockerignore +6 -0
- data/.github/config/rubocop_linter_action.yml +4 -0
- data/.github/workflows/ci-with-docker.yml +44 -0
- data/.github/workflows/ci.yml +87 -0
- data/.github/workflows/rubocop.yml +13 -0
- data/.rubocop.yml +12 -1
- data/.rubocop_todo.yml +470 -262
- data/CHANGES.txt +7 -1
- data/Dockerfile +27 -0
- data/Dockerfile.openssl3 +17 -0
- data/Gemfile +2 -0
- data/Gemfile.noed25519 +2 -0
- data/README.md +9 -3
- data/Rakefile +5 -0
- data/docker-compose.yml +23 -0
- data/lib/net/ssh/authentication/agent.rb +17 -15
- data/lib/net/ssh/authentication/certificate.rb +7 -5
- data/lib/net/ssh/authentication/constants.rb +0 -1
- data/lib/net/ssh/authentication/ed25519.rb +10 -6
- data/lib/net/ssh/authentication/ed25519_loader.rb +4 -7
- data/lib/net/ssh/authentication/key_manager.rb +46 -34
- data/lib/net/ssh/authentication/methods/abstract.rb +12 -3
- data/lib/net/ssh/authentication/methods/hostbased.rb +3 -5
- data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +2 -2
- data/lib/net/ssh/authentication/methods/none.rb +6 -9
- data/lib/net/ssh/authentication/methods/password.rb +2 -3
- data/lib/net/ssh/authentication/methods/publickey.rb +56 -16
- data/lib/net/ssh/authentication/pageant.rb +97 -97
- data/lib/net/ssh/authentication/pub_key_fingerprint.rb +2 -2
- data/lib/net/ssh/authentication/session.rb +18 -17
- data/lib/net/ssh/buffer.rb +50 -30
- data/lib/net/ssh/buffered_io.rb +24 -25
- data/lib/net/ssh/config.rb +33 -20
- data/lib/net/ssh/connection/channel.rb +85 -82
- data/lib/net/ssh/connection/constants.rb +0 -4
- data/lib/net/ssh/connection/event_loop.rb +30 -24
- data/lib/net/ssh/connection/keepalive.rb +12 -12
- data/lib/net/ssh/connection/session.rb +108 -107
- data/lib/net/ssh/connection/term.rb +56 -58
- data/lib/net/ssh/errors.rb +12 -12
- data/lib/net/ssh/key_factory.rb +7 -8
- data/lib/net/ssh/known_hosts.rb +84 -15
- data/lib/net/ssh/loggable.rb +8 -9
- data/lib/net/ssh/packet.rb +1 -1
- data/lib/net/ssh/prompt.rb +9 -11
- data/lib/net/ssh/proxy/command.rb +1 -1
- data/lib/net/ssh/proxy/errors.rb +2 -4
- data/lib/net/ssh/proxy/http.rb +18 -20
- data/lib/net/ssh/proxy/https.rb +8 -10
- data/lib/net/ssh/proxy/jump.rb +8 -10
- data/lib/net/ssh/proxy/socks4.rb +2 -4
- data/lib/net/ssh/proxy/socks5.rb +3 -5
- data/lib/net/ssh/service/forward.rb +7 -7
- data/lib/net/ssh/test/channel.rb +23 -25
- data/lib/net/ssh/test/extensions.rb +35 -35
- data/lib/net/ssh/test/kex.rb +6 -8
- data/lib/net/ssh/test/local_packet.rb +0 -2
- data/lib/net/ssh/test/packet.rb +3 -3
- data/lib/net/ssh/test/remote_packet.rb +5 -7
- data/lib/net/ssh/test/script.rb +24 -26
- data/lib/net/ssh/test/socket.rb +12 -15
- data/lib/net/ssh/test.rb +4 -5
- data/lib/net/ssh/transport/algorithms.rb +14 -13
- data/lib/net/ssh/transport/cipher_factory.rb +28 -28
- data/lib/net/ssh/transport/constants.rb +3 -3
- data/lib/net/ssh/transport/ctr.rb +7 -7
- data/lib/net/ssh/transport/hmac/abstract.rb +4 -5
- data/lib/net/ssh/transport/hmac/md5.rb +0 -2
- data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
- data/lib/net/ssh/transport/hmac/none.rb +0 -2
- data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
- data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
- data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
- data/lib/net/ssh/transport/hmac.rb +12 -12
- data/lib/net/ssh/transport/identity_cipher.rb +11 -13
- data/lib/net/ssh/transport/kex/abstract.rb +3 -3
- data/lib/net/ssh/transport/kex/abstract5656.rb +1 -1
- data/lib/net/ssh/transport/kex/curve25519_sha256.rb +2 -1
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +4 -4
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +21 -21
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +1 -2
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +2 -2
- data/lib/net/ssh/transport/kex.rb +8 -6
- data/lib/net/ssh/transport/key_expander.rb +7 -8
- data/lib/net/ssh/transport/openssl.rb +38 -22
- data/lib/net/ssh/transport/packet_stream.rb +2 -3
- data/lib/net/ssh/transport/server_version.rb +17 -16
- data/lib/net/ssh/transport/session.rb +9 -7
- data/lib/net/ssh/transport/state.rb +43 -43
- data/lib/net/ssh/verifiers/accept_new.rb +0 -2
- data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +1 -2
- data/lib/net/ssh/verifiers/always.rb +6 -4
- data/lib/net/ssh/verifiers/never.rb +0 -2
- data/lib/net/ssh/version.rb +3 -3
- data/lib/net/ssh.rb +4 -5
- data/net-ssh-public_cert.pem +8 -8
- data/net-ssh.gemspec +2 -2
- data/support/ssh_tunnel_bug.rb +3 -3
- data.tar.gz.sig +0 -0
- metadata +26 -18
- metadata.gz.sig +0 -0
- data/.travis.yml +0 -52
@@ -5,10 +5,9 @@ require 'net/ssh/service/forward'
|
|
5
5
|
require 'net/ssh/connection/keepalive'
|
6
6
|
require 'net/ssh/connection/event_loop'
|
7
7
|
|
8
|
-
module Net
|
9
|
-
module SSH
|
8
|
+
module Net
|
9
|
+
module SSH
|
10
10
|
module Connection
|
11
|
-
|
12
11
|
# A session class representing the connection service running on top of
|
13
12
|
# the SSH transport layer. It manages the creation of channels (see
|
14
13
|
# #open_channel), and the dispatching of messages to the various channels.
|
@@ -28,50 +27,50 @@ module Net
|
|
28
27
|
class Session
|
29
28
|
include Loggable
|
30
29
|
include Constants
|
31
|
-
|
30
|
+
|
32
31
|
# Default IO.select timeout threshold
|
33
32
|
DEFAULT_IO_SELECT_TIMEOUT = 300
|
34
|
-
|
33
|
+
|
35
34
|
# The underlying transport layer abstraction (see Net::SSH::Transport::Session).
|
36
35
|
attr_reader :transport
|
37
|
-
|
36
|
+
|
38
37
|
# The map of options that were used to initialize this instance.
|
39
38
|
attr_reader :options
|
40
|
-
|
39
|
+
|
41
40
|
# The collection of custom properties for this instance. (See #[] and #[]=).
|
42
41
|
attr_reader :properties
|
43
|
-
|
42
|
+
|
44
43
|
# The map of channels, each key being the local-id for the channel.
|
45
|
-
attr_reader :channels
|
46
|
-
|
44
|
+
attr_reader :channels # :nodoc:
|
45
|
+
|
47
46
|
# The map of listeners that the event loop knows about. See #listen_to.
|
48
|
-
attr_reader :listeners
|
49
|
-
|
47
|
+
attr_reader :listeners # :nodoc:
|
48
|
+
|
50
49
|
# The map of specialized handlers for opening specific channel types. See
|
51
50
|
# #on_open_channel.
|
52
|
-
attr_reader :channel_open_handlers
|
53
|
-
|
51
|
+
attr_reader :channel_open_handlers # :nodoc:
|
52
|
+
|
54
53
|
# The list of callbacks for pending requests. See #send_global_request.
|
55
|
-
attr_reader :pending_requests
|
56
|
-
|
54
|
+
attr_reader :pending_requests # :nodoc:
|
55
|
+
|
57
56
|
class NilChannel
|
58
57
|
def initialize(session)
|
59
58
|
@session = session
|
60
59
|
end
|
61
|
-
|
60
|
+
|
62
61
|
def method_missing(sym, *args)
|
63
62
|
@session.lwarn { "ignoring request #{sym.inspect} for non-existent (closed?) channel; probably ssh server bug" }
|
64
63
|
end
|
65
64
|
end
|
66
|
-
|
65
|
+
|
67
66
|
# Create a new connection service instance atop the given transport
|
68
67
|
# layer. Initializes the listeners to be only the underlying socket object.
|
69
|
-
def initialize(transport, options={})
|
68
|
+
def initialize(transport, options = {})
|
70
69
|
self.logger = transport.logger
|
71
|
-
|
70
|
+
|
72
71
|
@transport = transport
|
73
72
|
@options = options
|
74
|
-
|
73
|
+
|
75
74
|
@channel_id_counter = -1
|
76
75
|
@channels = Hash.new(NilChannel.new(self))
|
77
76
|
@listeners = { transport.socket => nil }
|
@@ -79,34 +78,34 @@ module Net
|
|
79
78
|
@channel_open_handlers = {}
|
80
79
|
@on_global_request = {}
|
81
80
|
@properties = (options[:properties] || {}).dup
|
82
|
-
|
81
|
+
|
83
82
|
@max_pkt_size = (options.key?(:max_pkt_size) ? options[:max_pkt_size] : 0x8000)
|
84
83
|
@max_win_size = (options.key?(:max_win_size) ? options[:max_win_size] : 0x20000)
|
85
|
-
|
84
|
+
|
86
85
|
@keepalive = Keepalive.new(self)
|
87
|
-
|
86
|
+
|
88
87
|
@event_loop = options[:event_loop] || SingleSessionEventLoop.new
|
89
88
|
@event_loop.register(self)
|
90
89
|
end
|
91
|
-
|
90
|
+
|
92
91
|
# Retrieves a custom property from this instance. This can be used to
|
93
92
|
# store additional state in applications that must manage multiple
|
94
93
|
# SSH connections.
|
95
94
|
def [](key)
|
96
95
|
@properties[key]
|
97
96
|
end
|
98
|
-
|
97
|
+
|
99
98
|
# Sets a custom property for this instance.
|
100
99
|
def []=(key, value)
|
101
100
|
@properties[key] = value
|
102
101
|
end
|
103
|
-
|
102
|
+
|
104
103
|
# Returns the name of the host that was given to the transport layer to
|
105
104
|
# connect to.
|
106
105
|
def host
|
107
106
|
transport.host
|
108
107
|
end
|
109
|
-
|
108
|
+
|
110
109
|
# Returns true if the underlying transport has been closed. Note that
|
111
110
|
# this can be a little misleading, since if the remote server has
|
112
111
|
# closed the connection, the local end will still think it is open
|
@@ -115,7 +114,7 @@ module Net
|
|
115
114
|
def closed?
|
116
115
|
transport.closed?
|
117
116
|
end
|
118
|
-
|
117
|
+
|
119
118
|
# Closes the session gracefully, blocking until all channels have
|
120
119
|
# successfully closed, and then closes the underlying transport layer
|
121
120
|
# connection.
|
@@ -129,7 +128,7 @@ module Net
|
|
129
128
|
end
|
130
129
|
transport.close
|
131
130
|
end
|
132
|
-
|
131
|
+
|
133
132
|
# Performs a "hard" shutdown of the connection. In general, this should
|
134
133
|
# never be done, but it might be necessary (in a rescue clause, for instance,
|
135
134
|
# when the connection needs to close but you don't know the status of the
|
@@ -137,10 +136,10 @@ module Net
|
|
137
136
|
def shutdown!
|
138
137
|
transport.shutdown!
|
139
138
|
end
|
140
|
-
|
139
|
+
|
141
140
|
# preserve a reference to Kernel#loop
|
142
141
|
alias :loop_forever :loop
|
143
|
-
|
142
|
+
|
144
143
|
# Returns +true+ if there are any channels currently active on this
|
145
144
|
# session. By default, this will not include "invisible" channels
|
146
145
|
# (such as those created by forwarding ports and such), but if you pass
|
@@ -150,14 +149,14 @@ module Net
|
|
150
149
|
# to be run.
|
151
150
|
#
|
152
151
|
# ssh.loop { ssh.busy? }
|
153
|
-
def busy?(include_invisible=false)
|
152
|
+
def busy?(include_invisible = false)
|
154
153
|
if include_invisible
|
155
154
|
channels.any?
|
156
155
|
else
|
157
156
|
channels.any? { |id, ch| !ch[:invisible] }
|
158
157
|
end
|
159
158
|
end
|
160
|
-
|
159
|
+
|
161
160
|
# The main event loop. Calls #process until #process returns false. If a
|
162
161
|
# block is given, it is passed to #process, otherwise a default proc is
|
163
162
|
# used that just returns true if there are any channels active (see #busy?).
|
@@ -175,7 +174,7 @@ module Net
|
|
175
174
|
# int_pressed = false
|
176
175
|
# trap("INT") { int_pressed = true }
|
177
176
|
# ssh.loop(0.1) { not int_pressed }
|
178
|
-
def loop(wait=nil, &block)
|
177
|
+
def loop(wait = nil, &block)
|
179
178
|
running = block || Proc.new { busy? }
|
180
179
|
loop_forever { break unless process(wait, &running) }
|
181
180
|
begin
|
@@ -188,7 +187,7 @@ module Net
|
|
188
187
|
end
|
189
188
|
end
|
190
189
|
end
|
191
|
-
|
190
|
+
|
192
191
|
# The core of the event loop. It processes a single iteration of the event
|
193
192
|
# loop. If a block is given, it should return false when the processing
|
194
193
|
# should abort, which causes #process to return false. Otherwise,
|
@@ -223,13 +222,13 @@ module Net
|
|
223
222
|
# connections.delete_if { |ssh| !ssh.process(0.1, &condition) }
|
224
223
|
# break if connections.empty?
|
225
224
|
# end
|
226
|
-
def process(wait=nil, &block)
|
225
|
+
def process(wait = nil, &block)
|
227
226
|
@event_loop.process(wait, &block)
|
228
227
|
rescue StandardError
|
229
228
|
force_channel_cleanup_on_close if closed?
|
230
229
|
raise
|
231
230
|
end
|
232
|
-
|
231
|
+
|
233
232
|
# This is called internally as part of #process. It dispatches any
|
234
233
|
# available incoming packets, and then runs Net::SSH::Connection::Channel#process
|
235
234
|
# for any active channels. If a block is given, it is invoked at the
|
@@ -237,31 +236,33 @@ module Net
|
|
237
236
|
# false, this method returns false. Otherwise, it returns true.
|
238
237
|
def preprocess(&block)
|
239
238
|
return false if block_given? && !yield(self)
|
239
|
+
|
240
240
|
ev_preprocess(&block)
|
241
241
|
return false if block_given? && !yield(self)
|
242
|
+
|
242
243
|
return true
|
243
244
|
end
|
244
|
-
|
245
|
+
|
245
246
|
# Called by event loop to process available data before going to
|
246
247
|
# event multiplexing
|
247
248
|
def ev_preprocess(&block)
|
248
249
|
dispatch_incoming_packets(raise_disconnect_errors: false)
|
249
250
|
each_channel { |id, channel| channel.process unless channel.local_closed? }
|
250
251
|
end
|
251
|
-
|
252
|
+
|
252
253
|
# Returns the file descriptors the event loop should wait for read/write events,
|
253
254
|
# we also return the max wait
|
254
255
|
def ev_do_calculate_rw_wait(wait)
|
255
256
|
r = listeners.keys
|
256
257
|
w = r.select { |w2| w2.respond_to?(:pending_write?) && w2.pending_write? }
|
257
|
-
[r,w,io_select_wait(wait)]
|
258
|
+
[r, w, io_select_wait(wait)]
|
258
259
|
end
|
259
|
-
|
260
|
+
|
260
261
|
# This is called internally as part of #process.
|
261
262
|
def postprocess(readers, writers)
|
262
263
|
ev_do_handle_events(readers, writers)
|
263
264
|
end
|
264
|
-
|
265
|
+
|
265
266
|
# It loops over the given arrays of reader IO's and writer IO's,
|
266
267
|
# processing them as needed, and
|
267
268
|
# then calls Net::SSH::Transport::Session#rekey_as_needed to allow the
|
@@ -277,12 +278,12 @@ module Net
|
|
277
278
|
end
|
278
279
|
end
|
279
280
|
end
|
280
|
-
|
281
|
+
|
281
282
|
Array(writers).each do |writer|
|
282
283
|
writer.send_pending
|
283
284
|
end
|
284
285
|
end
|
285
|
-
|
286
|
+
|
286
287
|
# calls Net::SSH::Transport::Session#rekey_as_needed to allow the
|
287
288
|
# transport layer to rekey
|
288
289
|
def ev_do_postprocess(was_events)
|
@@ -290,7 +291,7 @@ module Net
|
|
290
291
|
transport.rekey_as_needed
|
291
292
|
true
|
292
293
|
end
|
293
|
-
|
294
|
+
|
294
295
|
# Send a global request of the given type. The +extra+ parameters must
|
295
296
|
# be even in number, and conform to the same format as described for
|
296
297
|
# Net::SSH::Buffer.from. If a callback is not specified, the request will
|
@@ -314,7 +315,7 @@ module Net
|
|
314
315
|
pending_requests << callback if callback
|
315
316
|
self
|
316
317
|
end
|
317
|
-
|
318
|
+
|
318
319
|
# Requests that a new channel be opened. By default, the channel will be
|
319
320
|
# of type "session", but if you know what you're doing you can select any
|
320
321
|
# of the channel types supported by the SSH protocol. The +extra+ parameters
|
@@ -334,27 +335,27 @@ module Net
|
|
334
335
|
# end
|
335
336
|
#
|
336
337
|
# channel.wait
|
337
|
-
def open_channel(type="session", *extra, &on_confirm)
|
338
|
+
def open_channel(type = "session", *extra, &on_confirm)
|
338
339
|
local_id = get_next_channel_id
|
339
|
-
|
340
|
+
|
340
341
|
channel = Channel.new(self, type, local_id, @max_pkt_size, @max_win_size, &on_confirm)
|
341
342
|
msg = Buffer.from(:byte, CHANNEL_OPEN, :string, type, :long, local_id,
|
342
|
-
|
343
|
-
|
343
|
+
:long, channel.local_maximum_window_size,
|
344
|
+
:long, channel.local_maximum_packet_size, *extra)
|
344
345
|
send_message(msg)
|
345
|
-
|
346
|
+
|
346
347
|
channels[local_id] = channel
|
347
348
|
end
|
348
|
-
|
349
|
+
|
349
350
|
class StringWithExitstatus < String
|
350
351
|
def initialize(str, exitstatus)
|
351
352
|
super(str)
|
352
353
|
@exitstatus = exitstatus
|
353
354
|
end
|
354
|
-
|
355
|
+
|
355
356
|
attr_reader :exitstatus
|
356
357
|
end
|
357
|
-
|
358
|
+
|
358
359
|
# A convenience method for executing a command and interacting with it. If
|
359
360
|
# no block is given, all output is printed via $stdout and $stderr. Otherwise,
|
360
361
|
# the block is called for each data and extended data packet, with three
|
@@ -379,17 +380,17 @@ module Net
|
|
379
380
|
open_channel do |channel|
|
380
381
|
channel.exec(command) do |ch, success|
|
381
382
|
raise "could not execute command: #{command.inspect}" unless success
|
382
|
-
|
383
|
+
|
383
384
|
if status
|
384
|
-
channel.on_request("exit-status") do |ch2,data|
|
385
|
+
channel.on_request("exit-status") do |ch2, data|
|
385
386
|
status[:exit_code] = data.read_long
|
386
387
|
end
|
387
|
-
|
388
|
+
|
388
389
|
channel.on_request("exit-signal") do |ch2, data|
|
389
390
|
status[:exit_signal] = data.read_long
|
390
391
|
end
|
391
392
|
end
|
392
|
-
|
393
|
+
|
393
394
|
channel.on_data do |ch2, data|
|
394
395
|
if block
|
395
396
|
block.call(ch2, :stdout, data)
|
@@ -397,7 +398,7 @@ module Net
|
|
397
398
|
$stdout.print(data)
|
398
399
|
end
|
399
400
|
end
|
400
|
-
|
401
|
+
|
401
402
|
channel.on_extended_data do |ch2, type, data|
|
402
403
|
if block
|
403
404
|
block.call(ch2, :stderr, data)
|
@@ -408,7 +409,7 @@ module Net
|
|
408
409
|
end
|
409
410
|
end
|
410
411
|
end
|
411
|
-
|
412
|
+
|
412
413
|
# Same as #exec, except this will block until the command finishes. Also,
|
413
414
|
# if no block is given, this will return all output (stdout and stderr)
|
414
415
|
# as a single string.
|
@@ -418,20 +419,20 @@ module Net
|
|
418
419
|
# the returned string has an exitstatus method to query it's exit satus
|
419
420
|
def exec!(command, status: nil, &block)
|
420
421
|
block_or_concat = block || Proc.new do |ch, type, data|
|
421
|
-
ch[:result] ||=
|
422
|
+
ch[:result] ||= String.new
|
422
423
|
ch[:result] << data
|
423
424
|
end
|
424
|
-
|
425
|
+
|
425
426
|
status ||= {}
|
426
427
|
channel = exec(command, status: status, &block_or_concat)
|
427
428
|
channel.wait
|
428
|
-
|
429
|
-
channel[:result] ||=
|
429
|
+
|
430
|
+
channel[:result] ||= String.new unless block
|
430
431
|
channel[:result] &&= channel[:result].force_encoding("UTF-8") unless block
|
431
|
-
|
432
|
+
|
432
433
|
StringWithExitstatus.new(channel[:result], status[:exit_code]) if channel[:result]
|
433
434
|
end
|
434
|
-
|
435
|
+
|
435
436
|
# Enqueues a message to be sent to the server as soon as the socket is
|
436
437
|
# available for writing. Most programs will never need to call this, but
|
437
438
|
# if you are implementing an extension to the SSH protocol, or if you
|
@@ -442,7 +443,7 @@ module Net
|
|
442
443
|
def send_message(message)
|
443
444
|
transport.enqueue_message(message)
|
444
445
|
end
|
445
|
-
|
446
|
+
|
446
447
|
# Adds an IO object for the event loop to listen to. If a callback
|
447
448
|
# is given, it will be invoked when the io is ready to be read, otherwise,
|
448
449
|
# the io will merely have its #fill method invoked.
|
@@ -480,19 +481,19 @@ module Net
|
|
480
481
|
def listen_to(io, &callback)
|
481
482
|
listeners[io] = callback
|
482
483
|
end
|
483
|
-
|
484
|
+
|
484
485
|
# Removes the given io object from the listeners collection, so that the
|
485
486
|
# event loop will no longer monitor it.
|
486
487
|
def stop_listening_to(io)
|
487
488
|
listeners.delete(io)
|
488
489
|
end
|
489
|
-
|
490
|
+
|
490
491
|
# Returns a reference to the Net::SSH::Service::Forward service, which can
|
491
492
|
# be used for forwarding ports over SSH.
|
492
493
|
def forward
|
493
494
|
@forward ||= Service::Forward.new(self)
|
494
495
|
end
|
495
|
-
|
496
|
+
|
496
497
|
# Registers a handler to be invoked when the server wants to open a
|
497
498
|
# channel on the client. The callback receives the connection object,
|
498
499
|
# the new channel object, and the packet itself as arguments, and should
|
@@ -506,7 +507,7 @@ module Net
|
|
506
507
|
def on_open_channel(type, &block)
|
507
508
|
channel_open_handlers[type] = block
|
508
509
|
end
|
509
|
-
|
510
|
+
|
510
511
|
# Registers a handler to be invoked when the server sends a global request
|
511
512
|
# of the given type. The callback receives the request data as the first
|
512
513
|
# parameter, and true/false as the second (indicating whether a response
|
@@ -517,61 +518,61 @@ module Net
|
|
517
518
|
old, @on_global_request[type] = @on_global_request[type], block
|
518
519
|
old
|
519
520
|
end
|
520
|
-
|
521
|
+
|
521
522
|
def cleanup_channel(channel)
|
522
523
|
if channel.local_closed? and channel.remote_closed?
|
523
524
|
info { "#{host} delete channel #{channel.local_id} which closed locally and remotely" }
|
524
525
|
channels.delete(channel.local_id)
|
525
526
|
end
|
526
527
|
end
|
527
|
-
|
528
|
+
|
528
529
|
# If the #preprocess and #postprocess callbacks for this session need to run
|
529
530
|
# periodically, this method returns the maximum number of seconds which may
|
530
531
|
# pass between callbacks.
|
531
532
|
def max_select_wait_time
|
532
533
|
@keepalive.interval if @keepalive.enabled?
|
533
534
|
end
|
534
|
-
|
535
|
+
|
535
536
|
private
|
536
|
-
|
537
|
+
|
537
538
|
# iterate channels with the posibility of callbacks opening new channels during the iteration
|
538
539
|
def each_channel(&block)
|
539
540
|
channels.dup.each(&block)
|
540
541
|
end
|
541
|
-
|
542
|
+
|
542
543
|
# Read all pending packets from the connection and dispatch them as
|
543
544
|
# appropriate. Returns as soon as there are no more pending packets.
|
544
545
|
def dispatch_incoming_packets(raise_disconnect_errors: true)
|
545
546
|
while packet = transport.poll_message
|
546
547
|
raise Net::SSH::Exception, "unexpected response #{packet.type} (#{packet.inspect})" unless MAP.key?(packet.type)
|
547
|
-
|
548
|
+
|
548
549
|
send(MAP[packet.type], packet)
|
549
550
|
end
|
550
551
|
rescue StandardError
|
551
552
|
force_channel_cleanup_on_close if closed?
|
552
553
|
raise if raise_disconnect_errors || !$!.is_a?(Net::SSH::Disconnect)
|
553
554
|
end
|
554
|
-
|
555
|
+
|
555
556
|
# Returns the next available channel id to be assigned, and increments
|
556
557
|
# the counter.
|
557
558
|
def get_next_channel_id
|
558
559
|
@channel_id_counter += 1
|
559
560
|
end
|
560
|
-
|
561
|
+
|
561
562
|
def force_channel_cleanup_on_close
|
562
563
|
channels.each do |id, channel|
|
563
564
|
channel_closed(channel)
|
564
565
|
end
|
565
566
|
end
|
566
|
-
|
567
|
+
|
567
568
|
def channel_closed(channel)
|
568
569
|
channel.remote_closed!
|
569
570
|
channel.close
|
570
|
-
|
571
|
+
|
571
572
|
cleanup_channel(channel)
|
572
573
|
channel.do_close
|
573
574
|
end
|
574
|
-
|
575
|
+
|
575
576
|
# Invoked when a global request is received. The registered global
|
576
577
|
# request callback will be invoked, if one exists, and the necessary
|
577
578
|
# reply returned.
|
@@ -583,41 +584,41 @@ module Net
|
|
583
584
|
if result != :sent && result != true && result != false
|
584
585
|
raise "expected global request handler for `#{packet[:request_type]}' to return true, false, or :sent, but got #{result.inspect}"
|
585
586
|
end
|
586
|
-
|
587
|
+
|
587
588
|
if packet[:want_reply] && result != :sent
|
588
589
|
msg = Buffer.from(:byte, result ? REQUEST_SUCCESS : REQUEST_FAILURE)
|
589
590
|
send_message(msg)
|
590
591
|
end
|
591
592
|
end
|
592
|
-
|
593
|
+
|
593
594
|
# Invokes the next pending request callback with +true+.
|
594
595
|
def request_success(packet)
|
595
596
|
info { "global request success" }
|
596
597
|
callback = pending_requests.shift
|
597
598
|
callback.call(true, packet) if callback
|
598
599
|
end
|
599
|
-
|
600
|
+
|
600
601
|
# Invokes the next pending request callback with +false+.
|
601
602
|
def request_failure(packet)
|
602
603
|
info { "global request failure" }
|
603
604
|
callback = pending_requests.shift
|
604
605
|
callback.call(false, packet) if callback
|
605
606
|
end
|
606
|
-
|
607
|
+
|
607
608
|
# Called when the server wants to open a channel. If no registered
|
608
609
|
# channel handler exists for the given channel type, CHANNEL_OPEN_FAILURE
|
609
610
|
# is returned, otherwise the callback is invoked and everything proceeds
|
610
611
|
# accordingly.
|
611
612
|
def channel_open(packet)
|
612
613
|
info { "channel open #{packet[:channel_type]}" }
|
613
|
-
|
614
|
+
|
614
615
|
local_id = get_next_channel_id
|
615
|
-
|
616
|
+
|
616
617
|
channel = Channel.new(self, packet[:channel_type], local_id, @max_pkt_size, @max_win_size)
|
617
618
|
channel.do_open_confirmation(packet[:remote_id], packet[:window_size], packet[:packet_size])
|
618
|
-
|
619
|
+
|
619
620
|
callback = channel_open_handlers[packet[:channel_type]]
|
620
|
-
|
621
|
+
|
621
622
|
if callback
|
622
623
|
begin
|
623
624
|
callback[self, channel, packet]
|
@@ -632,80 +633,80 @@ module Net
|
|
632
633
|
else
|
633
634
|
failure = [3, "unknown channel type #{channel.type}"]
|
634
635
|
end
|
635
|
-
|
636
|
+
|
636
637
|
if failure
|
637
638
|
error { failure.inspect }
|
638
639
|
msg = Buffer.from(:byte, CHANNEL_OPEN_FAILURE, :long, channel.remote_id, :long, failure[0], :string, failure[1], :string, "")
|
639
640
|
end
|
640
|
-
|
641
|
+
|
641
642
|
send_message(msg)
|
642
643
|
end
|
643
|
-
|
644
|
+
|
644
645
|
def channel_open_confirmation(packet)
|
645
646
|
info { "channel_open_confirmation: #{packet[:local_id]} #{packet[:remote_id]} #{packet[:window_size]} #{packet[:packet_size]}" }
|
646
647
|
channel = channels[packet[:local_id]]
|
647
648
|
channel.do_open_confirmation(packet[:remote_id], packet[:window_size], packet[:packet_size])
|
648
649
|
end
|
649
|
-
|
650
|
+
|
650
651
|
def channel_open_failure(packet)
|
651
652
|
error { "channel_open_failed: #{packet[:local_id]} #{packet[:reason_code]} #{packet[:description]}" }
|
652
653
|
channel = channels.delete(packet[:local_id])
|
653
654
|
channel.do_open_failed(packet[:reason_code], packet[:description])
|
654
655
|
end
|
655
|
-
|
656
|
+
|
656
657
|
def channel_window_adjust(packet)
|
657
658
|
info { "channel_window_adjust: #{packet[:local_id]} +#{packet[:extra_bytes]}" }
|
658
659
|
channels[packet[:local_id]].do_window_adjust(packet[:extra_bytes])
|
659
660
|
end
|
660
|
-
|
661
|
+
|
661
662
|
def channel_request(packet)
|
662
663
|
info { "channel_request: #{packet[:local_id]} #{packet[:request]} #{packet[:want_reply]}" }
|
663
664
|
channels[packet[:local_id]].do_request(packet[:request], packet[:want_reply], packet[:request_data])
|
664
665
|
end
|
665
|
-
|
666
|
+
|
666
667
|
def channel_data(packet)
|
667
668
|
info { "channel_data: #{packet[:local_id]} #{packet[:data].length}b" }
|
668
669
|
channels[packet[:local_id]].do_data(packet[:data])
|
669
670
|
end
|
670
|
-
|
671
|
+
|
671
672
|
def channel_extended_data(packet)
|
672
673
|
info { "channel_extended_data: #{packet[:local_id]} #{packet[:data_type]} #{packet[:data].length}b" }
|
673
674
|
channels[packet[:local_id]].do_extended_data(packet[:data_type], packet[:data])
|
674
675
|
end
|
675
|
-
|
676
|
+
|
676
677
|
def channel_eof(packet)
|
677
678
|
info { "channel_eof: #{packet[:local_id]}" }
|
678
679
|
channels[packet[:local_id]].do_eof
|
679
680
|
end
|
680
|
-
|
681
|
+
|
681
682
|
def channel_close(packet)
|
682
683
|
info { "channel_close: #{packet[:local_id]}" }
|
683
|
-
|
684
|
+
|
684
685
|
channel = channels[packet[:local_id]]
|
685
686
|
channel_closed(channel)
|
686
687
|
end
|
687
|
-
|
688
|
+
|
688
689
|
def channel_success(packet)
|
689
690
|
info { "channel_success: #{packet[:local_id]}" }
|
690
691
|
channels[packet[:local_id]].do_success
|
691
692
|
end
|
692
|
-
|
693
|
+
|
693
694
|
def channel_failure(packet)
|
694
695
|
info { "channel_failure: #{packet[:local_id]}" }
|
695
696
|
channels[packet[:local_id]].do_failure
|
696
697
|
end
|
697
|
-
|
698
|
+
|
698
699
|
def io_select_wait(wait)
|
699
700
|
[wait, max_select_wait_time].compact.min
|
700
701
|
end
|
701
|
-
|
702
|
+
|
702
703
|
MAP = Constants.constants.each_with_object({}) do |name, memo|
|
703
704
|
value = const_get(name)
|
704
705
|
next unless Integer === value
|
706
|
+
|
705
707
|
memo[value] = name.downcase.to_sym
|
706
708
|
end
|
707
709
|
end
|
708
|
-
|
709
710
|
end
|
710
711
|
end
|
711
712
|
end
|