net-ssh 2.9.2 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
|