net-ssh 2.9.2 → 4.0.0
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/.gitignore +6 -0
- data/.rubocop.yml +5 -0
- data/.rubocop_todo.yml +1129 -0
- data/.travis.yml +41 -5
- data/CHANGES.txt +133 -1
- data/Gemfile +13 -0
- data/Gemfile.norbnacl +10 -0
- data/Gemfile.norbnacl.lock +41 -0
- data/ISSUE_TEMPLATE.md +30 -0
- data/README.rdoc +26 -81
- data/Rakefile +63 -45
- data/appveyor.yml +51 -0
- data/lib/net/ssh/authentication/agent.rb +174 -14
- data/lib/net/ssh/authentication/ed25519.rb +137 -0
- data/lib/net/ssh/authentication/ed25519_loader.rb +21 -0
- data/lib/net/ssh/authentication/key_manager.rb +36 -30
- data/lib/net/ssh/authentication/methods/abstract.rb +4 -0
- data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +16 -9
- data/lib/net/ssh/authentication/methods/password.rb +17 -4
- data/lib/net/ssh/authentication/pageant.rb +166 -45
- data/lib/net/ssh/authentication/session.rb +3 -2
- data/lib/net/ssh/buffer.rb +49 -10
- data/lib/net/ssh/buffered_io.rb +17 -12
- data/lib/net/ssh/config.rb +39 -8
- data/lib/net/ssh/connection/channel.rb +42 -20
- data/lib/net/ssh/connection/event_loop.rb +114 -0
- data/lib/net/ssh/connection/keepalive.rb +2 -2
- data/lib/net/ssh/connection/session.rb +120 -34
- data/lib/net/ssh/errors.rb +6 -6
- data/lib/net/ssh/key_factory.rb +49 -43
- data/lib/net/ssh/known_hosts.rb +49 -3
- data/lib/net/ssh/prompt.rb +47 -78
- data/lib/net/ssh/proxy/command.rb +31 -5
- data/lib/net/ssh/proxy/http.rb +15 -11
- data/lib/net/ssh/proxy/https.rb +49 -0
- data/lib/net/ssh/proxy/socks4.rb +2 -1
- data/lib/net/ssh/proxy/socks5.rb +3 -2
- data/lib/net/ssh/ruby_compat.rb +2 -29
- data/lib/net/ssh/service/forward.rb +2 -2
- data/lib/net/ssh/test/channel.rb +7 -0
- data/lib/net/ssh/test/extensions.rb +17 -0
- data/lib/net/ssh/test/kex.rb +4 -4
- data/lib/net/ssh/test/packet.rb +18 -2
- data/lib/net/ssh/test/script.rb +16 -2
- data/lib/net/ssh/test/socket.rb +1 -1
- data/lib/net/ssh/test.rb +5 -5
- data/lib/net/ssh/transport/algorithms.rb +92 -75
- data/lib/net/ssh/transport/cipher_factory.rb +19 -26
- data/lib/net/ssh/transport/ctr.rb +7 -9
- data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +20 -9
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +5 -3
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +1 -1
- data/lib/net/ssh/transport/key_expander.rb +1 -0
- data/lib/net/ssh/transport/openssl.rb +1 -1
- data/lib/net/ssh/transport/packet_stream.rb +11 -3
- data/lib/net/ssh/transport/server_version.rb +13 -6
- data/lib/net/ssh/transport/session.rb +20 -10
- data/lib/net/ssh/transport/state.rb +1 -1
- data/lib/net/ssh/verifiers/secure.rb +8 -10
- data/lib/net/ssh/version.rb +4 -4
- data/lib/net/ssh.rb +62 -14
- data/net-ssh-public_cert.pem +19 -18
- data/net-ssh.gemspec +34 -194
- data/support/arcfour_check.rb +1 -1
- data/support/ssh_tunnel_bug.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +125 -109
- metadata.gz.sig +0 -0
- data/Rudyfile +0 -96
- data/lib/net/ssh/authentication/agent/java_pageant.rb +0 -85
- data/lib/net/ssh/authentication/agent/socket.rb +0 -178
- data/setup.rb +0 -1585
- data/test/README.txt +0 -47
- data/test/authentication/methods/common.rb +0 -28
- data/test/authentication/methods/test_abstract.rb +0 -51
- data/test/authentication/methods/test_hostbased.rb +0 -114
- data/test/authentication/methods/test_keyboard_interactive.rb +0 -100
- data/test/authentication/methods/test_none.rb +0 -41
- data/test/authentication/methods/test_password.rb +0 -95
- data/test/authentication/methods/test_publickey.rb +0 -148
- data/test/authentication/test_agent.rb +0 -224
- data/test/authentication/test_key_manager.rb +0 -227
- data/test/authentication/test_session.rb +0 -107
- data/test/common.rb +0 -108
- data/test/configs/auth_off +0 -5
- data/test/configs/auth_on +0 -4
- data/test/configs/empty +0 -0
- data/test/configs/eqsign +0 -3
- data/test/configs/exact_match +0 -8
- data/test/configs/host_plus +0 -10
- data/test/configs/multihost +0 -4
- data/test/configs/negative_match +0 -6
- data/test/configs/nohost +0 -19
- data/test/configs/numeric_host +0 -4
- data/test/configs/send_env +0 -2
- data/test/configs/substitutes +0 -8
- data/test/configs/wild_cards +0 -14
- data/test/connection/test_channel.rb +0 -467
- data/test/connection/test_session.rb +0 -543
- data/test/known_hosts/github +0 -1
- data/test/manual/test_forward.rb +0 -285
- data/test/manual/test_pageant.rb +0 -37
- data/test/start/test_connection.rb +0 -53
- data/test/start/test_options.rb +0 -43
- data/test/start/test_transport.rb +0 -28
- data/test/test_all.rb +0 -11
- data/test/test_buffer.rb +0 -433
- data/test/test_buffered_io.rb +0 -63
- data/test/test_config.rb +0 -221
- data/test/test_key_factory.rb +0 -191
- data/test/test_known_hosts.rb +0 -13
- data/test/transport/hmac/test_md5.rb +0 -41
- data/test/transport/hmac/test_md5_96.rb +0 -27
- data/test/transport/hmac/test_none.rb +0 -34
- data/test/transport/hmac/test_ripemd160.rb +0 -36
- data/test/transport/hmac/test_sha1.rb +0 -36
- data/test/transport/hmac/test_sha1_96.rb +0 -27
- data/test/transport/hmac/test_sha2_256.rb +0 -37
- data/test/transport/hmac/test_sha2_256_96.rb +0 -27
- data/test/transport/hmac/test_sha2_512.rb +0 -37
- data/test/transport/hmac/test_sha2_512_96.rb +0 -27
- data/test/transport/kex/test_diffie_hellman_group14_sha1.rb +0 -13
- data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +0 -146
- data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +0 -92
- data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +0 -34
- data/test/transport/kex/test_ecdh_sha2_nistp256.rb +0 -161
- data/test/transport/kex/test_ecdh_sha2_nistp384.rb +0 -38
- data/test/transport/kex/test_ecdh_sha2_nistp521.rb +0 -38
- data/test/transport/test_algorithms.rb +0 -324
- data/test/transport/test_cipher_factory.rb +0 -443
- data/test/transport/test_hmac.rb +0 -34
- data/test/transport/test_identity_cipher.rb +0 -40
- data/test/transport/test_packet_stream.rb +0 -1761
- data/test/transport/test_server_version.rb +0 -78
- data/test/transport/test_session.rb +0 -331
- data/test/transport/test_state.rb +0 -181
@@ -126,7 +126,7 @@ module Net; module SSH; module Connection
|
|
126
126
|
@pending_requests = []
|
127
127
|
@on_open_failed = @on_data = @on_extended_data = @on_process = @on_close = @on_eof = nil
|
128
128
|
@on_request = {}
|
129
|
-
@closing = @eof = @sent_eof = false
|
129
|
+
@closing = @eof = @sent_eof = @local_closed = @remote_closed = false
|
130
130
|
end
|
131
131
|
|
132
132
|
# A shortcut for accessing properties of the channel (see #properties).
|
@@ -189,12 +189,12 @@ module Net; module SSH; module Connection
|
|
189
189
|
end
|
190
190
|
|
191
191
|
# A hash of the valid PTY options (see #request_pty).
|
192
|
-
VALID_PTY_OPTIONS = { :
|
193
|
-
:
|
194
|
-
:
|
195
|
-
:
|
196
|
-
:
|
197
|
-
:
|
192
|
+
VALID_PTY_OPTIONS = { term: "xterm",
|
193
|
+
chars_wide: 80,
|
194
|
+
chars_high: 24,
|
195
|
+
pixels_wide: 640,
|
196
|
+
pixels_high: 480,
|
197
|
+
modes: {} }
|
198
198
|
|
199
199
|
# Requests that a pseudo-tty (or "pty") be made available for this channel.
|
200
200
|
# This is useful when you want to invoke and interact with some kind of
|
@@ -269,24 +269,33 @@ module Net; module SSH; module Connection
|
|
269
269
|
connection.loop { active? }
|
270
270
|
end
|
271
271
|
|
272
|
-
#
|
273
|
-
#
|
274
|
-
#
|
275
|
-
#
|
272
|
+
# True if close() has been called; NOTE: if the channel has data waiting to
|
273
|
+
# be sent then the channel will close after all the data is sent. See
|
274
|
+
# closed?() to determine if we have actually sent CHANNEL_CLOSE to server.
|
275
|
+
# This may be true for awhile before closed? returns true if we are still
|
276
|
+
# sending buffered output to server.
|
276
277
|
def closing?
|
277
278
|
@closing
|
278
279
|
end
|
279
280
|
|
280
|
-
#
|
281
|
-
|
282
|
-
|
283
|
-
|
281
|
+
# True if we have sent CHANNEL_CLOSE to the remote server.
|
282
|
+
def local_closed?
|
283
|
+
@local_closed
|
284
|
+
end
|
285
|
+
|
286
|
+
def remote_closed?
|
287
|
+
@remote_closed
|
288
|
+
end
|
289
|
+
|
290
|
+
def remote_closed!
|
291
|
+
@remote_closed = true
|
292
|
+
end
|
293
|
+
|
294
|
+
# Requests that the channel be closed. It only marks the channel to be closed
|
295
|
+
# the CHANNEL_CLOSE message will be sent from event loop
|
284
296
|
def close
|
285
297
|
return if @closing
|
286
|
-
|
287
|
-
@closing = true
|
288
|
-
connection.send_message(Buffer.from(:byte, CHANNEL_CLOSE, :long, remote_id))
|
289
|
-
end
|
298
|
+
@closing = true
|
290
299
|
end
|
291
300
|
|
292
301
|
# Returns true if the local end of the channel has declared that no more
|
@@ -311,10 +320,16 @@ module Net; module SSH; module Connection
|
|
311
320
|
@on_process.call(self) if @on_process
|
312
321
|
enqueue_pending_output
|
313
322
|
|
314
|
-
if @eof and not @sent_eof and output.empty? and remote_id
|
323
|
+
if @eof and not @sent_eof and output.empty? and remote_id and not @local_closed
|
315
324
|
connection.send_message(Buffer.from(:byte, CHANNEL_EOF, :long, remote_id))
|
316
325
|
@sent_eof = true
|
317
326
|
end
|
327
|
+
|
328
|
+
if @closing and not @local_closed and output.empty? and remote_id
|
329
|
+
connection.send_message(Buffer.from(:byte, CHANNEL_CLOSE, :long, remote_id))
|
330
|
+
@local_closed = true
|
331
|
+
connection.cleanup_channel(self)
|
332
|
+
end
|
318
333
|
end
|
319
334
|
|
320
335
|
# Registers a callback to be invoked when data packets are received by the
|
@@ -467,6 +482,7 @@ module Net; module SSH; module Connection
|
|
467
482
|
# convenient helper methods (see #exec and #subsystem).
|
468
483
|
def send_channel_request(request_name, *data, &callback)
|
469
484
|
info { "sending channel request #{request_name.inspect}" }
|
485
|
+
fail "Channel open not yet confirmed, please call send_channel_request(or exec) from block of open_channel" unless remote_id
|
470
486
|
msg = Buffer.from(:byte, CHANNEL_REQUEST,
|
471
487
|
:long, remote_id, :string, request_name,
|
472
488
|
:bool, !callback.nil?, *data)
|
@@ -613,6 +629,12 @@ module Net; module SSH; module Connection
|
|
613
629
|
|
614
630
|
private
|
615
631
|
|
632
|
+
# Runs the SSH event loop until the remote confirmed channel open
|
633
|
+
# experimental api
|
634
|
+
def wait_until_open_confirmed
|
635
|
+
connection.loop { !remote_id }
|
636
|
+
end
|
637
|
+
|
616
638
|
# Updates the local window size by the given amount. If the window
|
617
639
|
# size drops to less than half of the local maximum (an arbitrary
|
618
640
|
# threshold), a CHANNEL_WINDOW_ADJUST message will be sent to the
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'net/ssh/loggable'
|
2
|
+
require 'net/ssh/ruby_compat'
|
3
|
+
|
4
|
+
module Net; module SSH; module Connection
|
5
|
+
# EventLoop can be shared across multiple sessions
|
6
|
+
#
|
7
|
+
# one issue is with blocks passed to loop, etc.
|
8
|
+
# they should get current session as parameter, but in
|
9
|
+
# case you're using multiple sessions in an event loop it doesnt makes sense
|
10
|
+
# and we don't pass session.
|
11
|
+
class EventLoop
|
12
|
+
include Loggable
|
13
|
+
|
14
|
+
def initialize(logger=nil)
|
15
|
+
self.logger = logger
|
16
|
+
@sessions = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def register(session)
|
20
|
+
@sessions << session
|
21
|
+
end
|
22
|
+
|
23
|
+
# process until timeout
|
24
|
+
# if a block is given a session will be removed from loop
|
25
|
+
# if block returns false for that session
|
26
|
+
def process(wait = nil, &block)
|
27
|
+
return false unless ev_preprocess(&block)
|
28
|
+
|
29
|
+
ev_select_and_postprocess(wait)
|
30
|
+
end
|
31
|
+
|
32
|
+
# process the event loop but only for the sepcified session
|
33
|
+
def process_only(session, wait = nil)
|
34
|
+
orig_sessions = @sessions
|
35
|
+
begin
|
36
|
+
@sessions = [session]
|
37
|
+
return false unless ev_preprocess
|
38
|
+
ev_select_and_postprocess(wait)
|
39
|
+
ensure
|
40
|
+
@sessions = orig_sessions
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Call preprocess on each session. If block given and that
|
45
|
+
# block retuns false then we exit the processing
|
46
|
+
def ev_preprocess(&block)
|
47
|
+
return false if block_given? && !yield(self)
|
48
|
+
@sessions.each(&:ev_preprocess)
|
49
|
+
return false if block_given? && !yield(self)
|
50
|
+
return true
|
51
|
+
end
|
52
|
+
|
53
|
+
def ev_select_and_postprocess(wait)
|
54
|
+
owners = {}
|
55
|
+
r = []
|
56
|
+
w = []
|
57
|
+
minwait = nil
|
58
|
+
@sessions.each do |session|
|
59
|
+
sr,sw,actwait = session.ev_do_calculate_rw_wait(wait)
|
60
|
+
minwait = actwait if actwait && (minwait.nil? || actwait < minwait)
|
61
|
+
r.push(*sr)
|
62
|
+
w.push(*sw)
|
63
|
+
sr.each { |ri| owners[ri] = session }
|
64
|
+
sw.each { |wi| owners[wi] = session }
|
65
|
+
end
|
66
|
+
|
67
|
+
readers, writers, = Net::SSH::Compat.io_select(r, w, nil, minwait)
|
68
|
+
|
69
|
+
fired_sessions = {}
|
70
|
+
|
71
|
+
if readers
|
72
|
+
readers.each do |reader|
|
73
|
+
session = owners[reader]
|
74
|
+
(fired_sessions[session] ||= {r: [],w: []})[:r] << reader
|
75
|
+
end
|
76
|
+
end
|
77
|
+
if writers
|
78
|
+
writers.each do |writer|
|
79
|
+
session = owners[writer]
|
80
|
+
(fired_sessions[session] ||= {r: [],w: []})[:w] << writer
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
fired_sessions.each do |s,rw|
|
85
|
+
s.ev_do_handle_events(rw[:r],rw[:w])
|
86
|
+
end
|
87
|
+
|
88
|
+
@sessions.each { |s| s.ev_do_postprocess(fired_sessions.key?(s)) }
|
89
|
+
true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# optimized version for a single session
|
94
|
+
class SingleSessionEventLoop < EventLoop
|
95
|
+
# Compatibility for original single session event loops:
|
96
|
+
# we call block with session as argument
|
97
|
+
def ev_preprocess(&block)
|
98
|
+
return false if block_given? && !yield(@sessions.first)
|
99
|
+
@sessions.each(&:ev_preprocess)
|
100
|
+
return false if block_given? && !yield(@sessions.first)
|
101
|
+
return true
|
102
|
+
end
|
103
|
+
|
104
|
+
def ev_select_and_postprocess(wait)
|
105
|
+
raise "Only one session expected" unless @sessions.count == 1
|
106
|
+
session = @sessions.first
|
107
|
+
sr,sw,actwait = session.ev_do_calculate_rw_wait(wait)
|
108
|
+
readers, writers, = Net::SSH::Compat.io_select(sr, sw, nil, actwait)
|
109
|
+
|
110
|
+
session.ev_do_handle_events(readers,writers)
|
111
|
+
session.ev_do_postprocess(!((readers.nil? || readers.empty?) && (writers.nil? || writers.empty?)))
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end; end; end
|
@@ -33,8 +33,8 @@ class Keepalive
|
|
33
33
|
(options[:keepalive_maxcount] || 3).to_i
|
34
34
|
end
|
35
35
|
|
36
|
-
def send_as_needed(
|
37
|
-
return
|
36
|
+
def send_as_needed(was_events)
|
37
|
+
return if was_events
|
38
38
|
return unless should_send?
|
39
39
|
info { "sending keepalive #{@unresponded_keepalive_count}" }
|
40
40
|
|
@@ -4,6 +4,7 @@ require 'net/ssh/connection/channel'
|
|
4
4
|
require 'net/ssh/connection/constants'
|
5
5
|
require 'net/ssh/service/forward'
|
6
6
|
require 'net/ssh/connection/keepalive'
|
7
|
+
require 'net/ssh/connection/event_loop'
|
7
8
|
|
8
9
|
module Net; module SSH; module Connection
|
9
10
|
|
@@ -77,10 +78,13 @@ module Net; module SSH; module Connection
|
|
77
78
|
@on_global_request = {}
|
78
79
|
@properties = (options[:properties] || {}).dup
|
79
80
|
|
80
|
-
@max_pkt_size = (options.
|
81
|
-
@max_win_size = (options.
|
81
|
+
@max_pkt_size = (options.key?(:max_pkt_size) ? options[:max_pkt_size] : 0x8000)
|
82
|
+
@max_win_size = (options.key?(:max_win_size) ? options[:max_win_size] : 0x20000)
|
82
83
|
|
83
84
|
@keepalive = Keepalive.new(self)
|
85
|
+
|
86
|
+
@event_loop = options[:event_loop] || SingleSessionEventLoop.new
|
87
|
+
@event_loop.register(self)
|
84
88
|
end
|
85
89
|
|
86
90
|
# Retrieves a custom property from this instance. This can be used to
|
@@ -116,7 +120,11 @@ module Net; module SSH; module Connection
|
|
116
120
|
def close
|
117
121
|
info { "closing remaining channels (#{channels.length} open)" }
|
118
122
|
channels.each { |id, channel| channel.close }
|
119
|
-
|
123
|
+
begin
|
124
|
+
loop(0.1) { channels.any? }
|
125
|
+
rescue Net::SSH::Disconnect
|
126
|
+
raise unless channels.empty?
|
127
|
+
end
|
120
128
|
transport.close
|
121
129
|
end
|
122
130
|
|
@@ -186,6 +194,8 @@ module Net; module SSH; module Connection
|
|
186
194
|
# This will also cause all active channels to be processed once each (see
|
187
195
|
# Net::SSH::Connection::Channel#on_process).
|
188
196
|
#
|
197
|
+
# TODO revise example
|
198
|
+
#
|
189
199
|
# # process multiple Net::SSH connections in parallel
|
190
200
|
# connections = [
|
191
201
|
# Net::SSH.start("host1", ...),
|
@@ -203,13 +213,10 @@ module Net; module SSH; module Connection
|
|
203
213
|
# break if connections.empty?
|
204
214
|
# end
|
205
215
|
def process(wait=nil, &block)
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
readers, writers, = Net::SSH::Compat.io_select(r, w, nil, io_select_wait(wait))
|
211
|
-
|
212
|
-
postprocess(readers, writers)
|
216
|
+
@event_loop.process(wait, &block)
|
217
|
+
rescue
|
218
|
+
force_channel_cleanup_on_close if closed?
|
219
|
+
raise
|
213
220
|
end
|
214
221
|
|
215
222
|
# This is called internally as part of #process. It dispatches any
|
@@ -217,19 +224,38 @@ module Net; module SSH; module Connection
|
|
217
224
|
# for any active channels. If a block is given, it is invoked at the
|
218
225
|
# start of the method and again at the end, and if the block ever returns
|
219
226
|
# false, this method returns false. Otherwise, it returns true.
|
220
|
-
def preprocess
|
227
|
+
def preprocess(&block)
|
221
228
|
return false if block_given? && !yield(self)
|
222
|
-
|
223
|
-
channels.each { |id, channel| channel.process unless channel.closing? }
|
229
|
+
ev_preprocess(&block)
|
224
230
|
return false if block_given? && !yield(self)
|
225
231
|
return true
|
226
232
|
end
|
227
233
|
|
228
|
-
#
|
229
|
-
#
|
234
|
+
# Called by event loop to process available data before going to
|
235
|
+
# event multiplexing
|
236
|
+
def ev_preprocess(&block)
|
237
|
+
dispatch_incoming_packets(raise_disconnect_errors: false)
|
238
|
+
each_channel { |id, channel| channel.process unless channel.local_closed? }
|
239
|
+
end
|
240
|
+
|
241
|
+
# Returns the file descriptors the event loop should wait for read/write events,
|
242
|
+
# we also return the max wait
|
243
|
+
def ev_do_calculate_rw_wait(wait)
|
244
|
+
r = listeners.keys
|
245
|
+
w = r.select { |w2| w2.respond_to?(:pending_write?) && w2.pending_write? }
|
246
|
+
[r,w,io_select_wait(wait)]
|
247
|
+
end
|
248
|
+
|
249
|
+
# This is called internally as part of #process.
|
250
|
+
def postprocess(readers, writers)
|
251
|
+
ev_do_handle_events(readers, writers)
|
252
|
+
end
|
253
|
+
|
254
|
+
# It loops over the given arrays of reader IO's and writer IO's,
|
255
|
+
# processing them as needed, and
|
230
256
|
# then calls Net::SSH::Transport::Session#rekey_as_needed to allow the
|
231
257
|
# transport layer to rekey. Then returns true.
|
232
|
-
def
|
258
|
+
def ev_do_handle_events(readers, writers)
|
233
259
|
Array(readers).each do |reader|
|
234
260
|
if listeners[reader]
|
235
261
|
listeners[reader].call(reader)
|
@@ -244,11 +270,14 @@ module Net; module SSH; module Connection
|
|
244
270
|
Array(writers).each do |writer|
|
245
271
|
writer.send_pending
|
246
272
|
end
|
273
|
+
end
|
247
274
|
|
248
|
-
|
275
|
+
# calls Net::SSH::Transport::Session#rekey_as_needed to allow the
|
276
|
+
# transport layer to rekey
|
277
|
+
def ev_do_postprocess(was_events)
|
278
|
+
@keepalive.send_as_needed(was_events)
|
249
279
|
transport.rekey_as_needed
|
250
|
-
|
251
|
-
return true
|
280
|
+
true
|
252
281
|
end
|
253
282
|
|
254
283
|
# Send a global request of the given type. The +extra+ parameters must
|
@@ -306,6 +335,15 @@ module Net; module SSH; module Connection
|
|
306
335
|
channels[local_id] = channel
|
307
336
|
end
|
308
337
|
|
338
|
+
class StringWithExitstatus < String
|
339
|
+
def initialize(str, exitstatus)
|
340
|
+
super(str)
|
341
|
+
@exitstatus = exitstatus
|
342
|
+
end
|
343
|
+
|
344
|
+
attr_reader :exitstatus
|
345
|
+
end
|
346
|
+
|
309
347
|
# A convenience method for executing a command and interacting with it. If
|
310
348
|
# no block is given, all output is printed via $stdout and $stderr. Otherwise,
|
311
349
|
# the block is called for each data and extended data packet, with three
|
@@ -326,11 +364,21 @@ module Net; module SSH; module Connection
|
|
326
364
|
# puts data
|
327
365
|
# end
|
328
366
|
# end
|
329
|
-
def exec(command, &block)
|
367
|
+
def exec(command, status: nil, &block)
|
330
368
|
open_channel do |channel|
|
331
369
|
channel.exec(command) do |ch, success|
|
332
370
|
raise "could not execute command: #{command.inspect}" unless success
|
333
|
-
|
371
|
+
|
372
|
+
if status
|
373
|
+
channel.on_request("exit-status") do |ch2,data|
|
374
|
+
status[:exit_code] = data.read_long
|
375
|
+
end
|
376
|
+
|
377
|
+
channel.on_request("exit-signal") do |ch2, data|
|
378
|
+
status[:exit_signal] = data.read_long
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
334
382
|
channel.on_data do |ch2, data|
|
335
383
|
if block
|
336
384
|
block.call(ch2, :stdout, data)
|
@@ -351,20 +399,26 @@ module Net; module SSH; module Connection
|
|
351
399
|
end
|
352
400
|
|
353
401
|
# Same as #exec, except this will block until the command finishes. Also,
|
354
|
-
# if
|
402
|
+
# if no block is given, this will return all output (stdout and stderr)
|
355
403
|
# as a single string.
|
356
404
|
#
|
357
405
|
# matches = ssh.exec!("grep something /some/files")
|
358
|
-
|
359
|
-
|
406
|
+
#
|
407
|
+
# the returned string has an exitstatus method to query it's exit satus
|
408
|
+
def exec!(command, status: nil, &block)
|
409
|
+
block_or_concat = block || Proc.new do |ch, type, data|
|
360
410
|
ch[:result] ||= ""
|
361
411
|
ch[:result] << data
|
362
412
|
end
|
363
413
|
|
364
|
-
|
414
|
+
status ||= {}
|
415
|
+
channel = exec(command, status: status, &block_or_concat)
|
365
416
|
channel.wait
|
366
417
|
|
367
|
-
|
418
|
+
channel[:result] ||= "" unless block
|
419
|
+
channel[:result] &&= channel[:result].force_encoding("UTF-8") unless block
|
420
|
+
|
421
|
+
StringWithExitstatus.new(channel[:result], status[:exit_code]) if channel[:result]
|
368
422
|
end
|
369
423
|
|
370
424
|
# Enqueues a message to be sent to the server as soon as the socket is
|
@@ -453,11 +507,31 @@ module Net; module SSH; module Connection
|
|
453
507
|
old
|
454
508
|
end
|
455
509
|
|
510
|
+
def cleanup_channel(channel)
|
511
|
+
if channel.local_closed? and channel.remote_closed?
|
512
|
+
info { "#{host} delete channel #{channel.local_id} which closed locally and remotely" }
|
513
|
+
channels.delete(channel.local_id)
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
# If the #preprocess and #postprocess callbacks for this session need to run
|
518
|
+
# periodically, this method returns the maximum number of seconds which may
|
519
|
+
# pass between callbacks.
|
520
|
+
def max_select_wait_time
|
521
|
+
@keepalive.interval if @keepalive.enabled?
|
522
|
+
end
|
523
|
+
|
524
|
+
|
456
525
|
private
|
457
526
|
|
527
|
+
# iterate channels with the posibility of callbacks opening new channels during the iteration
|
528
|
+
def each_channel(&block)
|
529
|
+
channels.dup.each(&block)
|
530
|
+
end
|
531
|
+
|
458
532
|
# Read all pending packets from the connection and dispatch them as
|
459
533
|
# appropriate. Returns as soon as there are no more pending packets.
|
460
|
-
def dispatch_incoming_packets
|
534
|
+
def dispatch_incoming_packets(raise_disconnect_errors: true)
|
461
535
|
while packet = transport.poll_message
|
462
536
|
unless MAP.key?(packet.type)
|
463
537
|
raise Net::SSH::Exception, "unexpected response #{packet.type} (#{packet.inspect})"
|
@@ -465,6 +539,9 @@ module Net; module SSH; module Connection
|
|
465
539
|
|
466
540
|
send(MAP[packet.type], packet)
|
467
541
|
end
|
542
|
+
rescue
|
543
|
+
force_channel_cleanup_on_close if closed?
|
544
|
+
raise if raise_disconnect_errors || !$!.is_a?(Net::SSH::Disconnect)
|
468
545
|
end
|
469
546
|
|
470
547
|
# Returns the next available channel id to be assigned, and increments
|
@@ -473,6 +550,20 @@ module Net; module SSH; module Connection
|
|
473
550
|
@channel_id_counter += 1
|
474
551
|
end
|
475
552
|
|
553
|
+
def force_channel_cleanup_on_close
|
554
|
+
channels.each do |id, channel|
|
555
|
+
channel_closed(channel)
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
def channel_closed(channel)
|
560
|
+
channel.remote_closed!
|
561
|
+
channel.close
|
562
|
+
|
563
|
+
cleanup_channel(channel)
|
564
|
+
channel.do_close
|
565
|
+
end
|
566
|
+
|
476
567
|
# Invoked when a global request is received. The registered global
|
477
568
|
# request callback will be invoked, if one exists, and the necessary
|
478
569
|
# reply returned.
|
@@ -581,10 +672,7 @@ module Net; module SSH; module Connection
|
|
581
672
|
info { "channel_close: #{packet[:local_id]}" }
|
582
673
|
|
583
674
|
channel = channels[packet[:local_id]]
|
584
|
-
channel
|
585
|
-
|
586
|
-
channels.delete(packet[:local_id])
|
587
|
-
channel.do_close
|
675
|
+
channel_closed(channel)
|
588
676
|
end
|
589
677
|
|
590
678
|
def channel_success(packet)
|
@@ -598,9 +686,7 @@ module Net; module SSH; module Connection
|
|
598
686
|
end
|
599
687
|
|
600
688
|
def io_select_wait(wait)
|
601
|
-
|
602
|
-
return wait unless @keepalive.enabled?
|
603
|
-
@keepalive.interval
|
689
|
+
[wait, max_select_wait_time].compact.min
|
604
690
|
end
|
605
691
|
|
606
692
|
MAP = Constants.constants.inject({}) do |memo, name|
|
data/lib/net/ssh/errors.rb
CHANGED
@@ -5,14 +5,14 @@ module Net; module SSH
|
|
5
5
|
|
6
6
|
# This exception is raised when authentication fails (whether it be
|
7
7
|
# public key authentication, password authentication, or whatever).
|
8
|
-
class AuthenticationFailed < Exception; end
|
8
|
+
class AuthenticationFailed < Net::SSH::Exception; end
|
9
9
|
|
10
10
|
# This exception is raised when a connection attempt times out.
|
11
|
-
class ConnectionTimeout < Exception; end
|
11
|
+
class ConnectionTimeout < Net::SSH::Exception; end
|
12
12
|
|
13
13
|
# This exception is raised when the remote host has disconnected
|
14
14
|
# unexpectedly.
|
15
|
-
class Disconnect < Exception; end
|
15
|
+
class Disconnect < Net::SSH::Exception; end
|
16
16
|
|
17
17
|
# This exception is raised when the remote host has disconnected/
|
18
18
|
# timeouted unexpectedly.
|
@@ -23,14 +23,14 @@ module Net; module SSH
|
|
23
23
|
# want to fail in such a way that the server knows it failed, you can
|
24
24
|
# raise this exception in the handler and Net::SSH will translate that into
|
25
25
|
# a "channel failure" message.
|
26
|
-
class ChannelRequestFailed < Exception; end
|
26
|
+
class ChannelRequestFailed < Net::SSH::Exception; end
|
27
27
|
|
28
28
|
# This is exception is primarily used internally, but if you have a channel
|
29
29
|
# open handler (see Net::SSH::Connection::Session#on_open_channel) and you
|
30
30
|
# want to fail in such a way that the server knows it failed, you can
|
31
31
|
# raise this exception in the handler and Net::SSH will translate that into
|
32
32
|
# a "channel open failed" message.
|
33
|
-
class ChannelOpenFailed < Exception
|
33
|
+
class ChannelOpenFailed < Net::SSH::Exception
|
34
34
|
attr_reader :code, :reason
|
35
35
|
|
36
36
|
def initialize(code, reason)
|
@@ -42,7 +42,7 @@ module Net; module SSH
|
|
42
42
|
# Base class for host key exceptions. When rescuing this exception, you can
|
43
43
|
# inspect the key fingerprint and, if you want to proceed anyway, simply call
|
44
44
|
# the remember_host! method on the exception, and then retry.
|
45
|
-
class HostKeyError < Exception
|
45
|
+
class HostKeyError < Net::SSH::Exception
|
46
46
|
# the callback to use when #remember_host! is called
|
47
47
|
attr_writer :callback #:nodoc:
|
48
48
|
|