net-ssh 3.3.0.beta1 → 4.0.0.alpha1
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.tar.gz.sig +0 -0
- data/.travis.yml +2 -1
- data/CHANGES.txt +3 -16
- data/Gemfile +17 -0
- data/README.rdoc +1 -1
- data/Rakefile +16 -9
- data/lib/net/ssh.rb +1 -5
- data/lib/net/ssh/authentication/agent/java_pageant.rb +1 -1
- data/lib/net/ssh/authentication/agent/socket.rb +5 -5
- data/lib/net/ssh/authentication/ed25519.rb +140 -0
- data/lib/net/ssh/authentication/key_manager.rb +2 -2
- data/lib/net/ssh/authentication/pageant.rb +1 -1
- data/lib/net/ssh/buffer.rb +5 -23
- data/lib/net/ssh/connection/session.rb +3 -20
- data/lib/net/ssh/key_factory.rb +14 -4
- data/lib/net/ssh/proxy/http.rb +2 -2
- data/lib/net/ssh/service/forward.rb +1 -1
- data/lib/net/ssh/test/socket.rb +1 -1
- data/lib/net/ssh/transport/algorithms.rb +2 -16
- data/lib/net/ssh/transport/cipher_factory.rb +16 -22
- 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/session.rb +0 -1
- data/lib/net/ssh/version.rb +3 -3
- data/net-ssh.gemspec +28 -9
- data/test/authentication/test_agent.rb +1 -9
- data/test/authentication/test_ed25519.rb +77 -0
- data/test/common.rb +0 -16
- data/test/connection/test_channel.rb +3 -3
- data/test/connection/test_session.rb +0 -1
- data/test/integration/{README.txt → README.md} +2 -1
- data/test/integration/common.rb +8 -6
- data/test/integration/playbook.yml +8 -7
- data/test/integration/test_ed25519_pkeys.rb +70 -0
- data/test/integration/test_forward.rb +15 -120
- data/test/integration/test_id_rsa_keys.rb +11 -11
- data/test/integration/test_proxy.rb +2 -2
- data/test/test_buffer.rb +1 -29
- data/test/transport/kex/test_ecdh_sha2_nistp384.rb +1 -1
- data/test/transport/test_algorithms.rb +6 -6
- data/test/transport/test_cipher_factory.rb +0 -119
- data/test/transport/test_packet_stream.rb +0 -576
- data/test/transport/test_session.rb +1 -1
- metadata +79 -6
- metadata.gz.sig +0 -0
- data/test/integration/test_encoding.rb +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0844171c8c635cf6a3507e82ea80755ba3f62907
|
4
|
+
data.tar.gz: 695c5873e12985df1085af7de53c9953415dfd16
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d12f8d882dc07599c08f5ca9824ecf631faee841a6bc53f6e4bd16141061208b560cc128a844652dbdd9beaf3720f986b05a5aa4eeed31ac3c75990c440225ab
|
7
|
+
data.tar.gz: c3307431b05c1107f98046b7347bd915d468da7ffa5c18f68b471153cb416e0003709b8ed4da418a4c2ce73e22124db1d3208848e25b1c05722adc2b63ed6b56
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/.travis.yml
CHANGED
data/CHANGES.txt
CHANGED
@@ -1,20 +1,7 @@
|
|
1
|
-
===
|
1
|
+
=== 4.0.0.alpha1
|
2
2
|
|
3
|
-
*
|
4
|
-
|
5
|
-
=== 3.2.1 beta 1
|
6
|
-
|
7
|
-
* Fix pageant/windows [Elconas, #385]
|
8
|
-
|
9
|
-
=== 3.2.0
|
10
|
-
|
11
|
-
* Added agent_socket_factory option [Alon Goldboim]
|
12
|
-
* Send KEXINIT asap don't wait for server [Miklos Fazekas]
|
13
|
-
* Close channels in case server closed connection [Miklos Fazekas]
|
14
|
-
|
15
|
-
=== 3.1.1
|
16
|
-
|
17
|
-
* added missing etc require
|
3
|
+
* ed25519 key support [Miklos Fazekas]
|
4
|
+
* removed camellia [Miklos Fazekas]
|
18
5
|
|
19
6
|
=== 3.1.0
|
20
7
|
=== 3.1.0.rc1
|
data/Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Note: this is run at package time not install time so if you are
|
4
|
+
# running on jruby, you need to install jruby-pageant manually.
|
5
|
+
gem 'jruby-pageant', ">=1.1.1" if RUBY_PLATFORM == "java"
|
6
|
+
|
7
|
+
gem 'rbnacl-libsodium', ">=1.0.2"
|
8
|
+
gem 'rbnacl', ">=3.1.2"
|
9
|
+
gem 'bcrypt_pbkdf', '1.0.0.alpha1' unless RUBY_PLATFORM == "java"
|
10
|
+
|
11
|
+
|
12
|
+
group :development do
|
13
|
+
gem 'rake'
|
14
|
+
gem 'test-unit', ">= 0.8.5"
|
15
|
+
gem 'mocha'
|
16
|
+
gem 'jeweler'
|
17
|
+
end
|
data/README.rdoc
CHANGED
data/Rakefile
CHANGED
@@ -32,15 +32,7 @@ begin
|
|
32
32
|
s.authors = ["Jamis Buck", "Delano Mandelbaum", "Miklós Fazekas"]
|
33
33
|
s.required_ruby_version = '>= 2.0'
|
34
34
|
|
35
|
-
#
|
36
|
-
# running on jruby, you need to install jruby-pageant manually.
|
37
|
-
if RUBY_PLATFORM == "java"
|
38
|
-
s.add_dependency 'jruby-pageant', ">=1.1.1"
|
39
|
-
end
|
40
|
-
|
41
|
-
s.add_development_dependency 'test-unit'
|
42
|
-
s.add_development_dependency 'mocha'
|
43
|
-
|
35
|
+
# dependencies defined in Gemfile
|
44
36
|
s.license = "MIT"
|
45
37
|
|
46
38
|
unless ENV['NET_SSH_NOKEY']
|
@@ -51,6 +43,21 @@ begin
|
|
51
43
|
raise "No key found at #{signing_key} for signing, use rake <taskname> NET_SSH_NOKEY=1 to build without key" unless File.exist?(signing_key)
|
52
44
|
end
|
53
45
|
end
|
46
|
+
def s.to_ruby
|
47
|
+
# see https://github.com/technicalpickles/jeweler/issues/170
|
48
|
+
result = super
|
49
|
+
result = result.chomp("\n").split("\n").map do |line|
|
50
|
+
if line =~ /%q<bcrypt_pbkdf>/
|
51
|
+
line += ' unless RUBY_PLATFORM == "java"'
|
52
|
+
else
|
53
|
+
line
|
54
|
+
end
|
55
|
+
end.join("\n") << "\n"
|
56
|
+
fail "Unexpected gemspec:#{result.inspect}" unless result.chomp!("\nend\n")
|
57
|
+
result << "\n s.add_dependency('jruby-pageant', ['>= 1.1.1']) if RUBY_PLATFORM == 'jruby'"
|
58
|
+
result << "\nend\n"
|
59
|
+
result
|
60
|
+
end
|
54
61
|
end
|
55
62
|
Jeweler::RubygemsDotOrgTasks.new
|
56
63
|
rescue LoadError
|
data/lib/net/ssh.rb
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
ENV['HOME'] ||= ENV['HOMEPATH'] ? "#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}" : Dir.pwd
|
4
4
|
|
5
5
|
require 'logger'
|
6
|
-
require 'etc'
|
7
6
|
|
8
7
|
require 'net/ssh/config'
|
9
8
|
require 'net/ssh/errors'
|
@@ -70,7 +69,7 @@ module Net
|
|
70
69
|
:known_hosts, :global_known_hosts_file, :user_known_hosts_file, :host_key_alias,
|
71
70
|
:host_name, :user, :properties, :passphrase, :keys_only, :max_pkt_size,
|
72
71
|
:max_win_size, :send_env, :use_agent, :number_of_password_prompts,
|
73
|
-
:append_supported_algorithms, :non_interactive
|
72
|
+
:append_supported_algorithms, :non_interactive
|
74
73
|
]
|
75
74
|
|
76
75
|
# The standard means of starting a new SSH connection. When used with a
|
@@ -192,9 +191,6 @@ module Net
|
|
192
191
|
# password auth method
|
193
192
|
# * :non_interactive => non interactive applications should set it to true
|
194
193
|
# to prefer failing a password/etc auth methods vs asking for password
|
195
|
-
# * :agent_socket_factory => enables the user to pass a lambda/block that will serve as the socket factory
|
196
|
-
# Net::SSH::start(user,host,agent_socket_factory: ->{ UNIXSocket.open('/foo/bar') })
|
197
|
-
# example: ->{ UNIXSocket.open('/foo/bar')}
|
198
194
|
#
|
199
195
|
# If +user+ parameter is nil it defaults to USER from ssh_config, or
|
200
196
|
# local username
|
@@ -19,7 +19,7 @@ module Net; module SSH; module Authentication
|
|
19
19
|
|
20
20
|
# Instantiates a new agent object, connects to a running SSH agent,
|
21
21
|
# negotiates the agent protocol version, and returns the agent object.
|
22
|
-
def self.connect(logger=nil
|
22
|
+
def self.connect(logger=nil)
|
23
23
|
agent = new(logger)
|
24
24
|
agent.connect!
|
25
25
|
agent
|
@@ -42,9 +42,9 @@ module Net; module SSH; module Authentication
|
|
42
42
|
|
43
43
|
# Instantiates a new agent object, connects to a running SSH agent,
|
44
44
|
# negotiates the agent protocol version, and returns the agent object.
|
45
|
-
def self.connect(logger=nil
|
45
|
+
def self.connect(logger=nil)
|
46
46
|
agent = new(logger)
|
47
|
-
agent.connect!
|
47
|
+
agent.connect!
|
48
48
|
agent.negotiate!
|
49
49
|
agent
|
50
50
|
end
|
@@ -59,10 +59,10 @@ module Net; module SSH; module Authentication
|
|
59
59
|
# given by the attribute writers. If the agent on the other end of the
|
60
60
|
# socket reports that it is an SSH2-compatible agent, this will fail
|
61
61
|
# (it only supports the ssh-agent distributed by OpenSSH).
|
62
|
-
def connect!
|
62
|
+
def connect!
|
63
63
|
begin
|
64
64
|
debug { "connecting to ssh-agent" }
|
65
|
-
@socket = agent_socket_factory.
|
65
|
+
@socket = agent_socket_factory.open(ENV['SSH_AUTH_SOCK'])
|
66
66
|
rescue
|
67
67
|
error { "could not connect to ssh-agent" }
|
68
68
|
raise AgentNotAvailable, $!.message
|
@@ -132,7 +132,7 @@ module Net; module SSH; module Authentication
|
|
132
132
|
private
|
133
133
|
|
134
134
|
# Returns the agent socket factory to use.
|
135
|
-
def
|
135
|
+
def agent_socket_factory
|
136
136
|
if Net::SSH::Authentication::PLATFORM == :win32
|
137
137
|
Pageant::Socket
|
138
138
|
else
|
@@ -0,0 +1,140 @@
|
|
1
|
+
gem 'rbnacl-libsodium'
|
2
|
+
gem 'rbnacl'
|
3
|
+
|
4
|
+
require 'rbnacl/libsodium'
|
5
|
+
require 'rbnacl'
|
6
|
+
require 'rbnacl/signatures/ed25519/verify_key'
|
7
|
+
require 'rbnacl/signatures/ed25519/signing_key'
|
8
|
+
|
9
|
+
require 'rbnacl/hash'
|
10
|
+
|
11
|
+
require 'base64'
|
12
|
+
|
13
|
+
require 'net/ssh/transport/cipher_factory'
|
14
|
+
require 'bcrypt_pbkdf' unless RUBY_PLATFORM == "java"
|
15
|
+
|
16
|
+
module RbNaCl
|
17
|
+
module Signatures
|
18
|
+
module Ed25519
|
19
|
+
class SigningKeyFromFile < SigningKey
|
20
|
+
def initialize(pk,sk)
|
21
|
+
@signing_key = sk
|
22
|
+
@verify_key = VerifyKey.new(pk)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module ED25519
|
30
|
+
class PubKey
|
31
|
+
def initialize(data)
|
32
|
+
@verify_key = RbNaCl::Signatures::Ed25519::VerifyKey.new(data)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.read_keyblob(buffer)
|
36
|
+
PubKey.new(buffer.read_string)
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_blob
|
40
|
+
Net::SSH::Buffer.from(:string,"ssh-ed25519",:string,@verify_key.to_bytes).to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
def ssh_type
|
44
|
+
"ssh-ed25519"
|
45
|
+
end
|
46
|
+
|
47
|
+
def ssh_do_verify(sig,data)
|
48
|
+
@verify_key.verify(sig,data)
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_pem
|
52
|
+
# TODO this is not pem
|
53
|
+
ssh_type + Base64.encode64(@verify_key.to_bytes)
|
54
|
+
end
|
55
|
+
|
56
|
+
def fingerprint
|
57
|
+
@fingerprint ||= OpenSSL::Digest::MD5.hexdigest(to_blob).scan(/../).join(":")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class PrivKey
|
62
|
+
CipherFactory = Net::SSH::Transport::CipherFactory
|
63
|
+
|
64
|
+
MBEGIN = "-----BEGIN OPENSSH PRIVATE KEY-----\n"
|
65
|
+
MEND = "-----END OPENSSH PRIVATE KEY-----\n"
|
66
|
+
MAGIC = "openssh-key-v1"
|
67
|
+
|
68
|
+
def initialize(datafull,password)
|
69
|
+
raise ArgumentError.new("Expected #{MBEGIN} at start of private key") unless datafull.start_with?(MBEGIN)
|
70
|
+
raise ArgumentError.new("Expected #{MEND} at end of private key") unless datafull.end_with?(MEND)
|
71
|
+
datab64 = datafull[MBEGIN.size ... -MEND.size]
|
72
|
+
data = Base64.decode64(datab64)
|
73
|
+
raise ArgumentError.new("Expected #{MAGIC} at start of decoded private key") unless data.start_with?(MAGIC)
|
74
|
+
buffer = Net::SSH::Buffer.new(data[MAGIC.size+1 .. -1])
|
75
|
+
|
76
|
+
ciphername = buffer.read_string
|
77
|
+
raise ArgumentError.new("#{ciphername} in private key is not supported") unless
|
78
|
+
CipherFactory.supported?(ciphername)
|
79
|
+
|
80
|
+
kdfname = buffer.read_string
|
81
|
+
raise ArgumentError.new("Expected #{kdfname} to be or none or bcrypt") unless %w(none bcrypt).include?(kdfname)
|
82
|
+
|
83
|
+
kdfopts = Net::SSH::Buffer.new(buffer.read_string)
|
84
|
+
num_keys = buffer.read_long
|
85
|
+
raise ArgumentError.new("Only 1 key is supported in ssh keys #{num_keys} was in private key") unless num_keys == 1
|
86
|
+
_pubkey = buffer.read_string
|
87
|
+
|
88
|
+
len = buffer.read_long
|
89
|
+
|
90
|
+
keylen, blocksize, ivlen = CipherFactory.get_lengths(ciphername, iv_len: true)
|
91
|
+
raise ArgumentError.new("Private key len:#{len} is not a multiple of #{blocksize}") if
|
92
|
+
((len < blocksize) || ((blocksize > 0) && (len % blocksize) != 0))
|
93
|
+
|
94
|
+
if kdfname == 'bcrypt'
|
95
|
+
salt = kdfopts.read_string
|
96
|
+
rounds = kdfopts.read_long
|
97
|
+
|
98
|
+
raise "BCryptPbkdf is not implemented for jruby" if RUBY_PLATFORM == "java"
|
99
|
+
key = BCryptPbkdf::key(password, salt, keylen + ivlen, rounds)
|
100
|
+
else
|
101
|
+
key = '\x00' * (keylen + ivlen)
|
102
|
+
end
|
103
|
+
|
104
|
+
cipher = CipherFactory.get(ciphername, key: key[0...keylen], iv:key[keylen...keylen+ivlen], decrypt: true)
|
105
|
+
|
106
|
+
decoded = cipher.update(buffer.remainder_as_buffer.to_s)
|
107
|
+
decoded << cipher.final
|
108
|
+
|
109
|
+
decoded = Net::SSH::Buffer.new(decoded)
|
110
|
+
check1 = decoded.read_long
|
111
|
+
check2 = decoded.read_long
|
112
|
+
|
113
|
+
raise ArgumentError, "Decrypt failed on private key" if (check1 != check2)
|
114
|
+
|
115
|
+
_type_name = decoded.read_string
|
116
|
+
pk = decoded.read_string
|
117
|
+
sk = decoded.read_string
|
118
|
+
_comment = decoded.read_string
|
119
|
+
|
120
|
+
@pk = pk
|
121
|
+
@sign_key = RbNaCl::Signatures::Ed25519::SigningKeyFromFile.new(pk,sk)
|
122
|
+
end
|
123
|
+
|
124
|
+
def public_key
|
125
|
+
PubKey.new(@pk)
|
126
|
+
end
|
127
|
+
|
128
|
+
def ssh_do_sign(data)
|
129
|
+
@sign_key.sign(data)
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.read(data,password)
|
133
|
+
self.new(data,password)
|
134
|
+
end
|
135
|
+
|
136
|
+
def self.read_keyblob(buffer)
|
137
|
+
ED25519::PubKey.read_keyblob(buffer)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -147,7 +147,7 @@ module Net
|
|
147
147
|
|
148
148
|
if info[:key]
|
149
149
|
return Net::SSH::Buffer.from(:string, identity.ssh_type,
|
150
|
-
:
|
150
|
+
:string, info[:key].ssh_do_sign(data.to_s)).to_s
|
151
151
|
end
|
152
152
|
|
153
153
|
if info[:from] == :agent
|
@@ -176,7 +176,7 @@ module Net
|
|
176
176
|
# or if the agent is otherwise not available.
|
177
177
|
def agent
|
178
178
|
return unless use_agent?
|
179
|
-
@agent ||= Agent.connect(logger
|
179
|
+
@agent ||= Agent.connect(logger)
|
180
180
|
rescue AgentNotAvailable
|
181
181
|
@use_agent = false
|
182
182
|
nil
|
@@ -254,7 +254,7 @@ module Net; module SSH; module Authentication
|
|
254
254
|
Win.GetTokenInformation(token_handle,
|
255
255
|
token_information_class,
|
256
256
|
Win::NULL, 0, preturn_length)
|
257
|
-
ptoken_information = malloc_ptr(preturn_length.
|
257
|
+
ptoken_information = malloc_ptr(preturn_length.ptr.to_i)
|
258
258
|
|
259
259
|
# This call is going to write the requested information to
|
260
260
|
# the memory location referenced by token_information.
|
data/lib/net/ssh/buffer.rb
CHANGED
@@ -34,7 +34,6 @@ module Net; module SSH
|
|
34
34
|
# * :long => write a 4-byte integer (#write_long)
|
35
35
|
# * :byte => write a single byte (#write_byte)
|
36
36
|
# * :string => write a 4-byte length followed by character data (#write_string)
|
37
|
-
# * :mstring => same as string, but caller cannot resuse the string, avoids potential duplication (#write_moved)
|
38
37
|
# * :bool => write a single byte, interpreted as a boolean (#write_bool)
|
39
38
|
# * :bignum => write an SSH-encoded bignum (#write_bignum)
|
40
39
|
# * :key => write an SSH-encoded key value (#write_key)
|
@@ -183,7 +182,7 @@ module Net; module SSH
|
|
183
182
|
consume!
|
184
183
|
data
|
185
184
|
end
|
186
|
-
|
185
|
+
|
187
186
|
# Return the next 8 bytes as a 64-bit integer (in network byte order).
|
188
187
|
# Returns nil if there are less than 8 bytes remaining to be read in the
|
189
188
|
# buffer.
|
@@ -256,6 +255,9 @@ module Net; module SSH
|
|
256
255
|
key.e = read_bignum
|
257
256
|
key.n = read_bignum
|
258
257
|
|
258
|
+
when /^ssh-ed25519$/
|
259
|
+
key = ED25519::PubKey.read_keyblob(self)
|
260
|
+
|
259
261
|
when /^ecdsa\-sha2\-(\w*)$/
|
260
262
|
unless defined?(OpenSSL::PKey::EC)
|
261
263
|
raise NotImplementedError, "unsupported key type `#{type}'"
|
@@ -282,14 +284,7 @@ module Net; module SSH
|
|
282
284
|
# Writes the given data literally into the string. Does not alter the
|
283
285
|
# read position. Returns the buffer object.
|
284
286
|
def write(*data)
|
285
|
-
data.each { |datum| @content << datum
|
286
|
-
self
|
287
|
-
end
|
288
|
-
|
289
|
-
# Optimized version of write where the caller gives up ownership of string
|
290
|
-
# to the method. This way we can mutate the string.
|
291
|
-
def write_moved(string)
|
292
|
-
@content << string.force_encoding('BINARY')
|
287
|
+
data.each { |datum| @content << datum }
|
293
288
|
self
|
294
289
|
end
|
295
290
|
|
@@ -332,19 +327,6 @@ module Net; module SSH
|
|
332
327
|
self
|
333
328
|
end
|
334
329
|
|
335
|
-
# Writes each argument to the buffer as an SSH2-encoded string. Each
|
336
|
-
# string is prefixed by its length, encoded as a 4-byte long integer.
|
337
|
-
# Does not alter the read position. Returns the buffer object.
|
338
|
-
# Might alter arguments see write_moved
|
339
|
-
def write_mstring(*text)
|
340
|
-
text.each do |string|
|
341
|
-
s = string.to_s
|
342
|
-
write_long(s.bytesize)
|
343
|
-
write_moved(s)
|
344
|
-
end
|
345
|
-
self
|
346
|
-
end
|
347
|
-
|
348
330
|
# Writes each argument to the buffer as a (C-style) boolean, with 1
|
349
331
|
# meaning true, and 0 meaning false. Does not alter the read position.
|
350
332
|
# Returns the buffer object.
|
@@ -210,9 +210,6 @@ module Net; module SSH; module Connection
|
|
210
210
|
readers, writers, = Net::SSH::Compat.io_select(r, w, nil, io_select_wait(wait))
|
211
211
|
|
212
212
|
postprocess(readers, writers)
|
213
|
-
rescue
|
214
|
-
force_channel_cleanup_on_close if closed?
|
215
|
-
raise
|
216
213
|
end
|
217
214
|
|
218
215
|
# This is called internally as part of #process. It dispatches any
|
@@ -368,7 +365,6 @@ module Net; module SSH; module Connection
|
|
368
365
|
channel.wait
|
369
366
|
|
370
367
|
channel[:result] ||= "" unless block
|
371
|
-
channel[:result] &&= channel[:result].force_encoding("UTF-8") unless block
|
372
368
|
|
373
369
|
return channel[:result]
|
374
370
|
end
|
@@ -460,9 +456,9 @@ module Net; module SSH; module Connection
|
|
460
456
|
end
|
461
457
|
|
462
458
|
def cleanup_channel(channel)
|
463
|
-
|
464
|
-
|
465
|
-
|
459
|
+
if channel.local_closed? and channel.remote_closed?
|
460
|
+
info { "#{host} delete channel #{channel.local_id} which closed locally and remotely" }
|
461
|
+
channels.delete(channel.local_id)
|
466
462
|
end
|
467
463
|
end
|
468
464
|
|
@@ -479,9 +475,6 @@ module Net; module SSH; module Connection
|
|
479
475
|
|
480
476
|
send(MAP[packet.type], packet)
|
481
477
|
end
|
482
|
-
rescue
|
483
|
-
force_channel_cleanup_on_close if closed?
|
484
|
-
raise
|
485
478
|
end
|
486
479
|
|
487
480
|
# Returns the next available channel id to be assigned, and increments
|
@@ -490,16 +483,6 @@ module Net; module SSH; module Connection
|
|
490
483
|
@channel_id_counter += 1
|
491
484
|
end
|
492
485
|
|
493
|
-
def force_channel_cleanup_on_close
|
494
|
-
channels.each do |id, channel|
|
495
|
-
channel.remote_closed!
|
496
|
-
channel.close
|
497
|
-
|
498
|
-
cleanup_channel(channel)
|
499
|
-
channel.do_close
|
500
|
-
end
|
501
|
-
end
|
502
|
-
|
503
486
|
# Invoked when a global request is received. The registered global
|
504
487
|
# request callback will be invoked, if one exists, and the necessary
|
505
488
|
# reply returned.
|