net-ssh 3.3.0.beta1 → 4.0.0.alpha1
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.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.
|