net-ssh 2.9.4 → 3.0.1

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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.travis.yml +12 -4
  4. data/CHANGES.txt +43 -5
  5. data/README.rdoc +8 -50
  6. data/Rakefile +13 -5
  7. data/lib/net/ssh/authentication/key_manager.rb +28 -19
  8. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +11 -4
  9. data/lib/net/ssh/authentication/methods/password.rb +3 -1
  10. data/lib/net/ssh/authentication/pageant.rb +42 -22
  11. data/lib/net/ssh/connection/channel.rb +7 -26
  12. data/lib/net/ssh/connection/session.rb +7 -14
  13. data/lib/net/ssh/known_hosts.rb +20 -1
  14. data/lib/net/ssh/proxy/command.rb +14 -3
  15. data/lib/net/ssh/proxy/http.rb +3 -2
  16. data/lib/net/ssh/proxy/socks4.rb +2 -1
  17. data/lib/net/ssh/proxy/socks5.rb +3 -2
  18. data/lib/net/ssh/ruby_compat.rb +0 -5
  19. data/lib/net/ssh/transport/algorithms.rb +3 -4
  20. data/lib/net/ssh/transport/cipher_factory.rb +0 -1
  21. data/lib/net/ssh/transport/server_version.rb +9 -3
  22. data/lib/net/ssh/transport/session.rb +11 -9
  23. data/lib/net/ssh/version.rb +4 -4
  24. data/lib/net/ssh.rb +17 -2
  25. data/net-ssh-public_cert.pem +15 -15
  26. data/net-ssh.gemspec +11 -4
  27. data/setup.rb +1 -1
  28. data/test/README.txt +6 -13
  29. data/test/authentication/methods/test_keyboard_interactive.rb +21 -0
  30. data/test/authentication/test_key_manager.rb +14 -1
  31. data/test/configs/proxy_remote_user +2 -0
  32. data/test/connection/test_channel.rb +0 -3
  33. data/test/connection/test_session.rb +15 -21
  34. data/test/integration/README.txt +19 -0
  35. data/test/integration/Vagrantfile +12 -0
  36. data/test/integration/common.rb +57 -0
  37. data/test/integration/playbook.yml +48 -0
  38. data/test/integration/test_id_rsa_keys.rb +84 -0
  39. data/test/known_hosts/github_hash +1 -0
  40. data/test/start/test_options.rb +15 -1
  41. data/test/test_all.rb +1 -0
  42. data/test/test_config.rb +6 -0
  43. data/test/test_known_hosts.rb +11 -3
  44. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +5 -1
  45. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +5 -1
  46. data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +2 -17
  47. data/test/transport/test_algorithms.rb +21 -17
  48. data/test/transport/test_packet_stream.rb +1 -0
  49. data/test/transport/test_session.rb +1 -1
  50. data.tar.gz.sig +0 -0
  51. metadata +25 -19
  52. metadata.gz.sig +0 -0
  53. data/Rudyfile +0 -96
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e7f14fbfcfd32cc75f187e18b727926b727993ec
4
- data.tar.gz: 5aba78f7570373c5cbb0b441b620ade113c356d5
3
+ metadata.gz: 95e458a7ed58017d4658db4ac2b3126a0f09d43f
4
+ data.tar.gz: 1840e7ee533d79e4a42bb2e42cd1c3d6e31da5f8
5
5
  SHA512:
6
- metadata.gz: 0f9f618270c98f579a6cb999fab3b4d1d58c38e200db68931c911cd8055a3e8cc51d1a84246de80889a5dabb1c138af5607d955766143d9a512ea5f2f6b95fc5
7
- data.tar.gz: 5628810355c03a737accd6fae7225287183397edcce63b14a090caec1f3722455f6c98b76d4b4d5b11f19515ebe0f6ef3a78b37b4ef4fcd2ce4c8a868c786574
6
+ metadata.gz: 1906d2443742af07de4b8dad4b7c3f85cde768f703f4e445f0cbd249311febd8e9e33d276b37d90af4119d702b6dc5d4a7b7cc3e9b581b63444dd87d7141732c
7
+ data.tar.gz: edefc22e10946ea02716b0665c83f1d99ebc2db02f26c36f42dcc2e310dbb33052f749eb20f2726cbd361be007954d831f6740c37da8c43280472112ccdb2e3e
checksums.yaml.gz.sig CHANGED
Binary file
data/.travis.yml CHANGED
@@ -1,10 +1,18 @@
1
1
  language: ruby
2
+ sudo: false
2
3
  rvm:
3
- - "1.9.3"
4
- - "2.0.0"
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - 2.1.0
7
+ - 2.2.0
8
+ - jruby-head
9
+ - jruby-19mode
10
+ - rbx-2
5
11
 
6
-
7
- install: gem install jeweler test-unit mocha
12
+ install: gem install test-unit mocha
8
13
 
9
14
  script: rake test
10
15
 
16
+ matrix:
17
+ allow_failures:
18
+ - rvm: jruby-head
data/CHANGES.txt CHANGED
@@ -1,10 +1,48 @@
1
- === 2.9.4-rc1
1
+ === 3.0.1
2
+ === 3.0.1.rc1
2
3
 
3
- * Bugfix: CHANNEL_CLOSE was sent before draining ouput buffer #280 [Christopher F. Auston]
4
+ * Breaking change from 2.* series: exec! without block now returns empty string instread of nil if command has no output [https://github.com/net-ssh/net-ssh/pull/273]
5
+ * Support remote_user as %r in proxy commands [Dominic Scheirlinck]
6
+ * Raise Net::SSH::ConnectionTimeout from connection timeout [Carl Hoerberg]
4
7
 
5
- [Note: 2.9.3 release changes went to 3.0, 2.9.4 is backport of fix form 3.0]
8
+ === 3.0.0.rc1
9
+
10
+ * SemVer: Major version change because of dropping of ruby 1.9
11
+
12
+ === 2.10.1.rc2
13
+
14
+ * Win: Use fiddle on ruby 2.1 too [Charlie Savage]
15
+
16
+ === 2.10.1.rc1
17
+
18
+ * Added ruby 2.0 requirement to gemspec [Alex Schultz]
19
+
20
+ === 2.10.0
21
+
22
+ === 2.10.0-beta2
23
+
24
+ * Fix :passphrase option with :non_interactive [Jeremy Stanley]
25
+ * Use Socket.tcp with connect_timeout instead of Timeout::timeout [Carl Hörberg]
26
+ * Support for hostname hashes [Jef Mathiot]
27
+ * Ruby 1.9.3 is no longer supported but should moslty work expect for stuff like connect_timeout
28
+
29
+ === 2.10.0-beta1
30
+
31
+ * Fix could not parse PKey error. [Andrey Voronkov]
32
+ * Workaround for threading issue in MRI + singleton method declaration [Matt Brictson]
33
+ * Configuration change: we no longer append all supported algorithms, this is so you can exclude insecure algorithms. If you want to use the old behaviour specify append_all_supported_algorithms => true [voidus, mfazekas]
34
+ * New configuration option: :non_interactive => true in case you prefer an authmethod to fail rather than prompt. [mfazekas]
35
+ * Configuration change: password will now ask for password up to the :number_of_password_prompts times. If you want the
36
+ 2.9.1 behaviour of never asking password please set number_of_password_prompts to 0.
37
+
38
+ === 2.9.4-beta1
39
+
40
+ * Use sysread and syswrite on Windows instead of read_nonblock and write [marc-etienne]
41
+ * Windows/peagant: use fiddle on ruby 2.2+/windows [Charlie Savage]
42
+ * Check if ssh key is a file [kiela]
43
+
44
+ === 2.9.3
6
45
 
7
- === 2.9.2
8
46
  === 2.9.2-rc3
9
47
 
10
48
  * Remove advertised algorithms that were not working (curve25519-sha256@libssh.org) [mfazekas]
@@ -21,7 +59,7 @@
21
59
 
22
60
  * Remove advertised algorithms that were not working (ssh-rsa-cert-* *ed25519 acm*-gcm@openssh.com) [mfazekas]
23
61
  * Unkown algorithms now ignored instead of failed [mfazekas]
24
- * Asks for password with password auth (up to number_of_password_prompts) [mfazekas]
62
+ * Configuration change: Asks for password with password auth (up to number_of_password_prompts) [mfazekas]
25
63
  * Removed warnings [amatsuda]
26
64
 
27
65
  === 2.9.1 / 13 May 2014
data/README.rdoc CHANGED
@@ -32,6 +32,7 @@ In a nutshell:
32
32
  Net::SSH.start('host', 'user', :password => "password") do |ssh|
33
33
  # capture all stderr and stdout output from a remote process
34
34
  output = ssh.exec!("hostname")
35
+ puts output
35
36
 
36
37
  # capture only stdout matching a particular pattern
37
38
  stdout = ""
@@ -101,8 +102,8 @@ However, in order to be sure the code you're installing hasn't been tampered wit
101
102
 
102
103
  # Add the public key as a trusted certificate
103
104
  # (You only need to do this once)
104
- $ curl -O https://raw.github.com/net-ssh/net-ssh/master/gem-public_cert.pem
105
- $ gem cert --add gem-public_cert.pem
105
+ $ curl -O https://raw.githubusercontent.com/net-ssh/net-ssh/master/net-ssh-public_cert.pem
106
+ $ gem cert --add net-ssh-public_cert.pem
106
107
 
107
108
  Then, when install the gem, do so with high security:
108
109
 
@@ -110,47 +111,11 @@ Then, when install the gem, do so with high security:
110
111
 
111
112
  If you don't add the public key, you'll see an error like "Couldn't verify data signature". If you're still having trouble let me know and I'll give you a hand.
112
113
 
113
- == RUBY 1.8 SUPPORT
114
-
115
- net-ssh supports Ruby 1.8.x up until the 2.5.1 release. Later releases will work but the test suite is no longer guaranteed to pass all tests.
116
-
117
- == JRUBY 1.6
118
-
119
- There is an issue with jruby-openssl that produces the following error in jruby 1.6:
120
-
121
- <ArgumentError> wrong number of arguments (2 for 1)
122
- /home/offers/tracking/shared/bundle/jruby/1.8/gems/net-ssh-2.6.0/lib/net/ssh/key_factory.rb:77:in `load_data_private_key'
123
-
124
- You can downgrade jruby-openssl to version 0.7.4 (before they added the PKey.read method) to resolve it or upgrade jruby to 1.7. See issue #61 for more info: https://github.com/net-ssh/net-ssh/issues/61.
125
-
126
-
127
- == ARCFOUR SUPPORT:
128
-
129
- from Karl Varga:
130
-
131
- Ruby's OpenSSL bindings always return a key length of 16 for RC4 ciphers, which means that when we try to use ARCFOUR256 or higher, Net::SSH generates keys which are consistently too short - 16 bytes as opposed to 32 bytes - resulting in the following error:
132
-
133
- OpenSSL::CipherError: key length too short
134
-
135
- My patch simply instructs Net::SSH to build keys of the the proper length, regardless of the required key length reported by OpenSSL.
136
-
137
- You should also be aware that your OpenSSL C libraries may also contain this bug. I've updated to 0.9.8k, but according to this thread[https://bugzilla.mindrot.org/show_bug.cgi?id=1291], the bug existed as recently as 0.9.8e! I've manually taken a look at my header files and they look ok, which is what makes me think it's a bug in the Ruby implementation.
138
-
139
- To see your OpenSSL version:
140
-
141
- $ openssl version
142
- OpenSSL 0.9.8k 25 Mar 2009
143
-
144
- After installing this gem, verify that Net::SSH is generating keys of the correct length by running the script <tt>support/arcfour_check.rb</tt>:
145
-
146
- $ ruby arcfour_support.rb
147
-
148
- which should produce the following:
149
-
150
- arcfour128: [16, 8] OpenSSL::Cipher::Cipher
151
- arcfour256: [32, 8] OpenSSL::Cipher::Cipher
152
- arcfour512: [64, 8] OpenSSL::Cipher::Cipher
114
+ == RUBY 1.x SUPPORT
153
115
 
116
+ * Ruby 1.8.x is supported up until the net-ssh 2.5.1 release.
117
+ * Ruby 1.9.x is supported up until the net-ssh 2.9.x release.
118
+ * Current net-ssh releases require Ruby 2.0 or later.
154
119
 
155
120
  == RUNNING TESTS
156
121
 
@@ -162,14 +127,7 @@ Run a single test file like this:
162
127
 
163
128
  ruby -Ilib -Itest -rrubygems test/transport/test_server_version.rb
164
129
 
165
-
166
- === EXPECTED RESULTS
167
-
168
- * Ruby 1.8: all tests pass
169
-
170
- * Ruby 1.9: all tests pass
171
-
172
- * JRuby 1.5: 99% tests pass (448 tests, 1846 assertions, 1 failures)
130
+ To run integration tests see test/integration/README.txt
173
131
 
174
132
  === BUILDING GEM
175
133
 
data/Rakefile CHANGED
@@ -8,6 +8,7 @@
8
8
  require "rubygems"
9
9
  require "rake"
10
10
  require "rake/clean"
11
+ if RUBY_VERSION >= '1.9.0'
11
12
  require "rdoc/task"
12
13
 
13
14
  task :default => ["build"]
@@ -29,6 +30,7 @@ begin
29
30
  s.email = "net-ssh@solutious.com"
30
31
  s.homepage = "https://github.com/net-ssh/net-ssh"
31
32
  s.authors = ["Jamis Buck", "Delano Mandelbaum", "Miklós Fazekas"]
33
+ s.required_ruby_version = '>= 2.0'
32
34
 
33
35
  # Note: this is run at package time not install time so if you are
34
36
  # running on jruby, you need to install jruby-pageant manually.
@@ -55,11 +57,6 @@ rescue LoadError
55
57
  puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
56
58
  end
57
59
 
58
- require 'rake/testtask'
59
- Rake::TestTask.new do |t|
60
- t.libs = ["lib", "test"]
61
- end
62
-
63
60
  extra_files = %w[LICENSE.txt THANKS.txt CHANGES.txt ]
64
61
  RDoc::Task.new do |rdoc|
65
62
  rdoc.rdoc_dir = "rdoc"
@@ -73,3 +70,14 @@ RDoc::Task.new do |rdoc|
73
70
  rdoc.rdoc_files.include(file) if File.exists?(file)
74
71
  }
75
72
  end
73
+ end
74
+
75
+ require 'rake/testtask'
76
+ Rake::TestTask.new do |t|
77
+ t.libs = ["lib", "test"]
78
+ end
79
+
80
+ Rake::TestTask.new(:'integration-test') do |t|
81
+ t.libs = ["lib", "test/integration"]
82
+ t.pattern = 'test/integration/test_*.rb'
83
+ end
@@ -98,7 +98,7 @@ module Net
98
98
  def each_identity
99
99
  prepared_identities = prepare_identities_from_files + prepare_identities_from_data
100
100
 
101
- user_identities = load_identities(prepared_identities, false)
101
+ user_identities = load_identities(prepared_identities, false, true)
102
102
 
103
103
  if agent
104
104
  agent.identities.each do |key|
@@ -114,7 +114,7 @@ module Net
114
114
  end
115
115
  end
116
116
 
117
- user_identities = load_identities(user_identities, true)
117
+ user_identities = load_identities(user_identities, !options[:non_interactive], false)
118
118
 
119
119
  user_identities.each do |identity|
120
120
  key = identity.delete(:public_key)
@@ -139,7 +139,7 @@ module Net
139
139
 
140
140
  if info[:key].nil? && info[:from] == :file
141
141
  begin
142
- info[:key] = KeyFactory.load_private_key(info[:file], options[:passphrase], true)
142
+ info[:key] = KeyFactory.load_private_key(info[:file], options[:passphrase], !options[:non_interactive])
143
143
  rescue Exception, OpenSSL::OpenSSLError => e
144
144
  raise KeyManagerError, "the given identity is known, but the private key could not be loaded: #{e.class} (#{e.message})"
145
145
  end
@@ -187,15 +187,24 @@ module Net
187
187
  # Prepares identities from user key_files for loading, preserving their order and sources.
188
188
  def prepare_identities_from_files
189
189
  key_files.map do |file|
190
- public_key_file = file + ".pub"
191
- if File.readable?(public_key_file)
192
- { :load_from => :pubkey_file, :file => file }
193
- elsif File.readable?(file)
194
- { :load_from => :privkey_file, :file => file }
190
+ if readable_file?(file)
191
+ identity = {}
192
+ public_key_file = file + ".pub"
193
+ if readable_file?(public_key_file)
194
+ identity[:load_from] = :pubkey_file
195
+ identity[:pubkey_file] = public_key_file
196
+ else
197
+ identity[:load_from] = :privkey_file
198
+ end
199
+ identity.merge(privkey_file: file)
195
200
  end
196
201
  end.compact
197
202
  end
198
203
 
204
+ def readable_file?(path)
205
+ File.file?(path) && File.readable?(path)
206
+ end
207
+
199
208
  # Prepared identities from user key_data, preserving their order and sources.
200
209
  def prepare_identities_from_data
201
210
  key_data.map do |data|
@@ -203,18 +212,18 @@ module Net
203
212
  end
204
213
  end
205
214
 
206
- # Load prepared identities. Private key decryption errors ignored if passphrase was not prompted.
207
- def load_identities(identities, ask_passphrase)
215
+ # Load prepared identities. Private key decryption errors ignored if ignore_decryption_errors
216
+ def load_identities(identities, ask_passphrase, ignore_decryption_errors)
208
217
  identities.map do |identity|
209
218
  begin
210
219
  case identity[:load_from]
211
220
  when :pubkey_file
212
- key = KeyFactory.load_public_key(identity[:file] + ".pub")
213
- { :public_key => key, :from => :file, :file => identity[:file] }
221
+ key = KeyFactory.load_public_key(identity[:pubkey_file])
222
+ { :public_key => key, :from => :file, :file => identity[:privkey_file] }
214
223
  when :privkey_file
215
- private_key = KeyFactory.load_private_key(identity[:file], options[:passphrase], ask_passphrase)
224
+ private_key = KeyFactory.load_private_key(identity[:privkey_file], options[:passphrase], ask_passphrase)
216
225
  key = private_key.send(:public_key)
217
- { :public_key => key, :from => :file, :file => identity[:file], :key => private_key }
226
+ { :public_key => key, :from => :file, :file => identity[:privkey_file], :key => private_key }
218
227
  when :data
219
228
  private_key = KeyFactory.load_data_private_key(identity[:data], options[:passphrase], ask_passphrase)
220
229
  key = private_key.send(:public_key)
@@ -224,11 +233,11 @@ module Net
224
233
  end
225
234
 
226
235
  rescue OpenSSL::PKey::RSAError, OpenSSL::PKey::DSAError, OpenSSL::PKey::ECError => e
227
- if ask_passphrase
236
+ if ignore_decryption_errors
237
+ identity
238
+ else
228
239
  process_identity_loading_error(identity, e)
229
240
  nil
230
- else
231
- identity
232
241
  end
233
242
  rescue ArgumentError => e
234
243
  process_identity_loading_error(identity, e)
@@ -243,9 +252,9 @@ module Net
243
252
  def process_identity_loading_error(identity, e)
244
253
  case identity[:load_from]
245
254
  when :pubkey_file
246
- error { "could not load public key file `#{identity[:file]}': #{e.class} (#{e.message})" }
255
+ error { "could not load public key file `#{identity[:pubkey_file]}': #{e.class} (#{e.message})" }
247
256
  when :privkey_file
248
- error { "could not load private key file `#{identity[:file]}': #{e.class} (#{e.message})" }
257
+ error { "could not load private key file `#{identity[:privkey_file]}': #{e.class} (#{e.message})" }
249
258
  else
250
259
  raise e
251
260
  end
@@ -38,8 +38,10 @@ module Net
38
38
  debug { "keyboard-interactive info request" }
39
39
 
40
40
  unless password
41
- puts(name) unless name.empty?
42
- puts(instruction) unless instruction.empty?
41
+ if interactive?
42
+ puts(name) unless name.empty?
43
+ puts(instruction) unless instruction.empty?
44
+ end
43
45
  end
44
46
 
45
47
  _ = message.read_string # lang_tag
@@ -48,7 +50,8 @@ module Net
48
50
  message.read_long.times do
49
51
  text = message.read_string
50
52
  echo = message.read_bool
51
- responses << (password || prompt(text, echo))
53
+ password_to_send = password || (interactive? ? prompt(text, echo) : nil)
54
+ responses << password_to_send
52
55
  end
53
56
 
54
57
  # if the password failed the first time around, don't try
@@ -62,8 +65,12 @@ module Net
62
65
  end
63
66
  end
64
67
  end
65
- end
66
68
 
69
+ def interactive?
70
+ options = session.transport.options || {}
71
+ !options[:non_interactive]
72
+ end
73
+ end
67
74
  end
68
75
  end
69
76
  end
@@ -58,7 +58,9 @@ module Net
58
58
  end
59
59
 
60
60
  def get_max_retries
61
- (session.transport.options||{})[:number_of_password_prompts] || NUMBER_OF_PASSWORD_PROMPTS
61
+ options = session.transport.options || {}
62
+ result = options[:number_of_password_prompts] || NUMBER_OF_PASSWORD_PROMPTS
63
+ options[:non_interactive] ? 0 : result
62
64
  end
63
65
  end
64
66
 
@@ -1,10 +1,20 @@
1
- require 'dl/import'
2
-
3
1
  if RUBY_VERSION < "1.9"
2
+ require 'dl/import'
4
3
  require 'dl/struct'
5
- else
4
+ elsif RUBY_VERSION < "2.1"
5
+ require 'dl/import'
6
6
  require 'dl/types'
7
7
  require 'dl'
8
+ else
9
+ require 'fiddle'
10
+ require 'fiddle/types'
11
+ require 'fiddle/import'
12
+
13
+ # For now map DL to Fiddler versus updating all the code below
14
+ module DL
15
+ CPtr = Fiddle::Pointer
16
+ RUBY_FREE = Fiddle::RUBY_FREE
17
+ end
8
18
  end
9
19
 
10
20
  require 'net/ssh/errors'
@@ -36,12 +46,17 @@ module Net; module SSH; module Authentication
36
46
  dlload 'advapi32'
37
47
 
38
48
  SIZEOF_DWORD = DL.sizeof('L')
39
- else
49
+ elsif RUBY_VERSION < "2.1"
40
50
  extend DL::Importer
41
51
  dlload 'user32','kernel32', 'advapi32'
42
52
  include DL::Win32Types
43
53
 
44
54
  SIZEOF_DWORD = DL::SIZEOF_LONG
55
+ else
56
+ extend Fiddle::Importer
57
+ dlload 'user32','kernel32', 'advapi32'
58
+ include Fiddle::Win32Types
59
+ SIZEOF_DWORD = Fiddle::SIZEOF_LONG
45
60
  end
46
61
 
47
62
  typealias("LPCTSTR", "char *") # From winnt.h
@@ -73,7 +88,7 @@ module Net; module SSH; module Authentication
73
88
  extern 'HANDLE CreateFileMapping(HANDLE, void *, DWORD, ' +
74
89
  'DWORD, DWORD, LPCTSTR)'
75
90
 
76
- # args: hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
91
+ # args: hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
77
92
  # dwfileOffsetLow, dwNumberOfBytesToMap
78
93
  extern 'LPVOID MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, DWORD)'
79
94
 
@@ -86,7 +101,7 @@ module Net; module SSH; module Authentication
86
101
  # args: hWnd, Msg, wParam, lParam, fuFlags, uTimeout, lpdwResult
87
102
  extern 'LRESULT SendMessageTimeout(HWND, UINT, WPARAM, LPARAM, ' +
88
103
  'UINT, UINT, PDWORD_PTR)'
89
-
104
+
90
105
  # args: none
91
106
  extern 'DWORD GetLastError()'
92
107
 
@@ -112,8 +127,8 @@ module Net; module SSH; module Authentication
112
127
  extern 'BOOL IsValidSecurityDescriptor(LPVOID)'
113
128
 
114
129
  # Constants needed for security attribute retrieval.
115
- # Specifies the access mask corresponding to the desired access
116
- # rights.
130
+ # Specifies the access mask corresponding to the desired access
131
+ # rights.
117
132
  TOKEN_QUERY = 0x8
118
133
 
119
134
  # The value of TOKEN_USER from the TOKEN_INFORMATION_CLASS enum.
@@ -138,6 +153,9 @@ module Net; module SSH; module Authentication
138
153
  'LPVOID Group', 'LPVOID Sacl',
139
154
  'LPVOID Dacl']
140
155
 
156
+ # The COPYDATASTRUCT is used to send WM_COPYDATA messages
157
+ COPYDATASTRUCT = struct ['uintptr_t dwData', 'DWORD cbData', 'LPVOID lpData']
158
+
141
159
  # Compatibility for security attribute retrieval.
142
160
  if RUBY_VERSION < "1.9"
143
161
  # Alias functions to > 1.9 capitalization
@@ -198,11 +216,10 @@ module Net; module SSH; module Authentication
198
216
  raise_error_if_zero(
199
217
  Win.IsValidSecurityDescriptor(psd_information))
200
218
 
201
- nLength = Win::SECURITY_ATTRIBUTES.size
202
- lpSecurityDescriptor = psd_information
203
- bInheritHandle = 1
204
- sa = [nLength, lpSecurityDescriptor.to_i,
205
- bInheritHandle].pack("LLC")
219
+ sa = Win::SECURITY_ATTRIBUTES.new(malloc_ptr(Win::SECURITY_ATTRIBUTES.size))
220
+ sa.nLength = Win::SECURITY_ATTRIBUTES.size
221
+ sa.lpSecurityDescriptor = psd_information.to_i
222
+ sa.bInheritHandle = 1
206
223
 
207
224
  return sa
208
225
  end
@@ -278,7 +295,7 @@ module Net; module SSH; module Authentication
278
295
  new
279
296
  end
280
297
 
281
- # Create a new instance that communicates with the running pageant
298
+ # Create a new instance that communicates with the running pageant
282
299
  # instance. If no such instance is running, this will cause an error.
283
300
  def initialize
284
301
  @win = Win.FindWindow("Pageant", "Pageant")
@@ -296,20 +313,20 @@ module Net; module SSH; module Authentication
296
313
  # the first.
297
314
  def send(data, *args)
298
315
  @input_buffer.append(data)
299
-
316
+
300
317
  ret = data.length
301
-
318
+
302
319
  while true
303
320
  return ret if @input_buffer.length < 4
304
321
  msg_length = @input_buffer.read_long + 4
305
322
  @input_buffer.reset!
306
-
323
+
307
324
  return ret if @input_buffer.length < msg_length
308
325
  msg = @input_buffer.read!(msg_length)
309
326
  @output_buffer.append(send_query(msg))
310
327
  end
311
328
  end
312
-
329
+
313
330
  # Reads +n+ bytes from the cached result of the last query. If +n+
314
331
  # is +nil+, returns all remaining data from the last query.
315
332
  def read(n = nil)
@@ -341,7 +358,7 @@ module Net; module SSH; module Authentication
341
358
  "Creation of file mapping failed with error: #{Win.GetLastError}"
342
359
  end
343
360
 
344
- ptr = Win.MapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0,
361
+ ptr = Win.MapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0,
345
362
  0)
346
363
 
347
364
  if ptr.nil? || ptr.null?
@@ -350,10 +367,13 @@ module Net; module SSH; module Authentication
350
367
 
351
368
  Win.set_ptr_data(ptr, query)
352
369
 
353
- cds = Win.get_ptr [AGENT_COPYDATA_ID, mapname.size + 1,
354
- Win.get_cstr(mapname)].pack("LLp")
370
+ # using struct to achieve proper alignment and field size on 64-bit platform
371
+ cds = Win::COPYDATASTRUCT.new(Win.malloc_ptr(Win::COPYDATASTRUCT.size))
372
+ cds.dwData = AGENT_COPYDATA_ID
373
+ cds.cbData = mapname.size + 1
374
+ cds.lpData = Win.get_cstr(mapname)
355
375
  succ = Win.SendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL,
356
- cds, Win::SMTO_NORMAL, 5000, id)
376
+ cds.to_ptr, Win::SMTO_NORMAL, 5000, id)
357
377
 
358
378
  if succ > 0
359
379
  retlen = 4 + ptr.to_s(4).unpack("N")[0]
@@ -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 = @local_closed = @remote_closed = false
129
+ @closing = @eof = @sent_eof = false
130
130
  end
131
131
 
132
132
  # A shortcut for accessing properties of the channel (see #properties).
@@ -269,28 +269,14 @@ module Net; module SSH; module Connection
269
269
  connection.loop { active? }
270
270
  end
271
271
 
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.
272
+ # Returns true if the channel is currently closing, but not actually
273
+ # closed. A channel is closing when, for instance, #close has been
274
+ # invoked, but the server has not yet responded with a CHANNEL_CLOSE
275
+ # packet of its own.
277
276
  def closing?
278
277
  @closing
279
278
  end
280
279
 
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
280
  # Requests that the channel be closed. If the channel is already closing,
295
281
  # this does nothing, nor does it do anything if the channel has not yet
296
282
  # been confirmed open (see #do_open_confirmation). Otherwise, it sends a
@@ -299,6 +285,7 @@ module Net; module SSH; module Connection
299
285
  return if @closing
300
286
  if remote_id
301
287
  @closing = true
288
+ connection.send_message(Buffer.from(:byte, CHANNEL_CLOSE, :long, remote_id))
302
289
  end
303
290
  end
304
291
 
@@ -324,16 +311,10 @@ module Net; module SSH; module Connection
324
311
  @on_process.call(self) if @on_process
325
312
  enqueue_pending_output
326
313
 
327
- if @eof and not @sent_eof and output.empty? and remote_id and not @local_closed
314
+ if @eof and not @sent_eof and output.empty? and remote_id
328
315
  connection.send_message(Buffer.from(:byte, CHANNEL_EOF, :long, remote_id))
329
316
  @sent_eof = true
330
317
  end
331
-
332
- if @closing and not @local_closed and output.empty? and remote_id
333
- connection.send_message(Buffer.from(:byte, CHANNEL_CLOSE, :long, remote_id))
334
- @local_closed = true
335
- connection.cleanup_channel(self)
336
- end
337
318
  end
338
319
 
339
320
  # Registers a callback to be invoked when data packets are received by the
@@ -220,7 +220,7 @@ module Net; module SSH; module Connection
220
220
  def preprocess
221
221
  return false if block_given? && !yield(self)
222
222
  dispatch_incoming_packets
223
- channels.each { |id, channel| channel.process unless channel.local_closed? }
223
+ channels.each { |id, channel| channel.process unless channel.closing? }
224
224
  return false if block_given? && !yield(self)
225
225
  return true
226
226
  end
@@ -351,19 +351,21 @@ module Net; module SSH; module Connection
351
351
  end
352
352
 
353
353
  # Same as #exec, except this will block until the command finishes. Also,
354
- # if a block is not given, this will return all output (stdout and stderr)
354
+ # if no block is given, this will return all output (stdout and stderr)
355
355
  # as a single string.
356
356
  #
357
357
  # matches = ssh.exec!("grep something /some/files")
358
358
  def exec!(command, &block)
359
- block ||= Proc.new do |ch, type, data|
359
+ block_or_concat = block || Proc.new do |ch, type, data|
360
360
  ch[:result] ||= ""
361
361
  ch[:result] << data
362
362
  end
363
363
 
364
- channel = exec(command, &block)
364
+ channel = exec(command, &block_or_concat)
365
365
  channel.wait
366
366
 
367
+ channel[:result] ||= "" unless block
368
+
367
369
  return channel[:result]
368
370
  end
369
371
 
@@ -453,14 +455,6 @@ module Net; module SSH; module Connection
453
455
  old
454
456
  end
455
457
 
456
- def cleanup_channel(channel)
457
- if channel.local_closed? and channel.remote_closed?
458
- info { "#{host} delete channel #{channel.local_id} which closed locally and remotely" }
459
- channels.delete(channel.local_id)
460
- end
461
- end
462
-
463
-
464
458
  private
465
459
 
466
460
  # Read all pending packets from the connection and dispatch them as
@@ -589,10 +583,9 @@ module Net; module SSH; module Connection
589
583
  info { "channel_close: #{packet[:local_id]}" }
590
584
 
591
585
  channel = channels[packet[:local_id]]
592
- channel.remote_closed!
593
586
  channel.close
594
587
 
595
- cleanup_channel(channel)
588
+ channels.delete(packet[:local_id])
596
589
  channel.do_close
597
590
  end
598
591