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