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
@@ -1,4 +1,6 @@
1
1
  require 'strscan'
2
+ require 'openssl'
3
+ require 'base64'
2
4
  require 'net/ssh/buffer'
3
5
 
4
6
  module Net; module SSH
@@ -111,7 +113,9 @@ module Net; module SSH
111
113
  next if scanner.match?(/$|#/)
112
114
 
113
115
  hostlist = scanner.scan(/\S+/).split(/,/)
114
- next unless entries.all? { |entry| hostlist.include?(entry) }
116
+ found = entries.all? { |entry| hostlist.include?(entry) } ||
117
+ known_host_hash?(hostlist, entries, scanner)
118
+ next unless found
115
119
 
116
120
  scanner.skip(/\s*/)
117
121
  type = scanner.scan(/\S+/)
@@ -127,6 +131,21 @@ module Net; module SSH
127
131
  keys
128
132
  end
129
133
 
134
+ # Indicates whether one of the entries matches an hostname that has been
135
+ # stored as a HMAC-SHA1 hash in the known hosts.
136
+ def known_host_hash?(hostlist, entries, scanner)
137
+ if hostlist.size == 1 && hostlist.first =~ /\A\|1(\|.+){2}\z/
138
+ chunks = hostlist.first.split(/\|/)
139
+ salt = Base64.decode64(chunks[2])
140
+ digest = OpenSSL::Digest.new('sha1')
141
+ entries.each do |entry|
142
+ hmac = OpenSSL::HMAC.digest(digest, salt, entry)
143
+ return true if Base64.encode64(hmac).chomp == chunks[3]
144
+ end
145
+ end
146
+ false
147
+ end
148
+
130
149
  # Tries to append an entry to the current source file for the given host
131
150
  # and key. If it is unable to (because the file is not writable, for
132
151
  # instance), an exception will be raised.
@@ -1,4 +1,5 @@
1
1
  require 'socket'
2
+ require 'rubygems'
2
3
  require 'net/ssh/proxy/errors'
3
4
  require 'net/ssh/ruby_compat'
4
5
 
@@ -66,12 +67,22 @@ module Net; module SSH; module Proxy
66
67
  raise ConnectError, "#{e}: #{command_line}"
67
68
  end
68
69
  @command_line = command_line
69
- class << io
70
- def send(data, flag)
70
+ if Gem.win_platform?
71
+ # read_nonblock and write_nonblock are not available on Windows
72
+ # pipe. Use sysread and syswrite as a replacement works.
73
+ def io.send(data, flag)
74
+ syswrite(data)
75
+ end
76
+
77
+ def io.recv(size)
78
+ sysread(size)
79
+ end
80
+ else
81
+ def io.send(data, flag)
71
82
  write_nonblock(data)
72
83
  end
73
84
 
74
- def recv(size)
85
+ def io.recv(size)
75
86
  read_nonblock(size)
76
87
  end
77
88
  end
@@ -48,8 +48,9 @@ module Net; module SSH; module Proxy
48
48
 
49
49
  # Return a new socket connected to the given host and port via the
50
50
  # proxy that was requested when the socket factory was instantiated.
51
- def open(host, port, connection_options = nil)
52
- socket = TCPSocket.new(proxy_host, proxy_port)
51
+ def open(host, port, connection_options)
52
+ socket = Socket.tcp(proxy_host, proxy_port, nil, nil,
53
+ connect_timeout: connection_options[:timeout])
53
54
  socket.write "CONNECT #{host}:#{port} HTTP/1.0\r\n"
54
55
 
55
56
  if options[:user]
@@ -48,7 +48,8 @@ module Net
48
48
  # Return a new socket connected to the given host and port via the
49
49
  # proxy that was requested when the socket factory was instantiated.
50
50
  def open(host, port, connection_options)
51
- socket = TCPSocket.new(proxy_host, proxy_port)
51
+ socket = Socket.tcp(proxy_host, proxy_port, nil, nil,
52
+ connect_timeout: connection_options[:timeout])
52
53
  ip_addr = IPAddr.new(Resolv.getaddress(host))
53
54
 
54
55
  packet = [VERSION, CONNECT, port.to_i, ip_addr.to_i, options[:user]].pack("CCnNZ*")
@@ -62,8 +62,9 @@ module Net
62
62
 
63
63
  # Return a new socket connected to the given host and port via the
64
64
  # proxy that was requested when the socket factory was instantiated.
65
- def open(host, port, connection_options = nil)
66
- socket = TCPSocket.new(proxy_host, proxy_port)
65
+ def open(host, port, connection_options)
66
+ socket = Socket.tcp(proxy_host, proxy_port, nil, nil,
67
+ connect_timeout: connection_options[:timeout])
67
68
 
68
69
  methods = [METHOD_NO_AUTH]
69
70
  methods << METHOD_PASSWD if options[:user]
@@ -9,11 +9,6 @@ class String
9
9
  self[index] = c
10
10
  end
11
11
  end
12
- if RUBY_VERSION < "1.8.7"
13
- def bytesize
14
- self.size
15
- end
16
- end
17
12
  end
18
13
 
19
14
  module Net; module SSH
@@ -224,10 +224,9 @@ module Net; module SSH; module Transport
224
224
  end
225
225
  lwarn { "unsupported #{algorithm} algorithm: `#{unsupported}'" } unless unsupported.empty?
226
226
 
227
- # make sure all of our supported algorithms are tacked onto the
228
- # end, so that if the user tries to give a list of which none are
229
- # supported, we can still proceed.
230
- list.each { |name| algorithms[algorithm] << name unless algorithms[algorithm].include?(name) }
227
+ if options[:append_all_supported_algorithms]
228
+ list.each { |name| algorithms[algorithm] << name unless algorithms[algorithm].include?(name) }
229
+ end
231
230
  end
232
231
  end
233
232
 
@@ -76,7 +76,6 @@ module Net; module SSH; module Transport
76
76
  cipher.padding = 0
77
77
 
78
78
  cipher.extend(Net::SSH::Transport::CTR) if (name =~ /-ctr(@openssh.org)?$/)
79
-
80
79
  cipher.iv = Net::SSH::Transport::KeyExpander.expand_key(cipher.iv_len, options[:iv], options) if ossl_name != "rc4"
81
80
 
82
81
  key_len = KEY_LEN_OVERRIDE[name] || cipher.key_len
@@ -25,11 +25,11 @@ module Net; module SSH; module Transport
25
25
 
26
26
  # Instantiates a new ServerVersion and immediately (and synchronously)
27
27
  # negotiates the SSH protocol in effect, using the given socket.
28
- def initialize(socket, logger)
28
+ def initialize(socket, logger, timeout = nil)
29
29
  @header = ""
30
30
  @version = nil
31
31
  @logger = logger
32
- negotiate!(socket)
32
+ negotiate!(socket, timeout)
33
33
  end
34
34
 
35
35
  private
@@ -37,9 +37,12 @@ module Net; module SSH; module Transport
37
37
  # Negotiates the SSH protocol to use, via the given socket. If the server
38
38
  # reports an incompatible SSH version (e.g., SSH1), this will raise an
39
39
  # exception.
40
- def negotiate!(socket)
40
+ def negotiate!(socket, timeout)
41
41
  info { "negotiating protocol version" }
42
42
 
43
+ if timeout && !IO.select([socket], nil, nil, timeout)
44
+ raise Net::SSH::ConnectionTimeout, "timeout during server version negotiating"
45
+ end
43
46
  loop do
44
47
  @version = ""
45
48
  loop do
@@ -63,6 +66,9 @@ module Net; module SSH; module Transport
63
66
  raise Net::SSH::Exception, "incompatible SSH version `#{@version}'"
64
67
  end
65
68
 
69
+ if timeout && !IO.select(nil, [socket], nil, timeout)
70
+ raise Net::SSH::ConnectionTimeout, "timeout during client version negotiating"
71
+ end
66
72
  debug { "local is `#{PROTO_VERSION}'" }
67
73
  socket.write "#{PROTO_VERSION}\r\n"
68
74
  socket.flush
@@ -1,5 +1,4 @@
1
1
  require 'socket'
2
- require 'timeout'
3
2
 
4
3
  require 'net/ssh/errors'
5
4
  require 'net/ssh/loggable'
@@ -63,14 +62,15 @@ module Net; module SSH; module Transport
63
62
  @options = options
64
63
 
65
64
  debug { "establishing connection to #{@host}:#{@port}" }
66
- factory = options[:proxy] || TCPSocket
67
- @socket = timeout(options[:timeout] || 0) {
68
- case
69
- when options[:proxy] then factory.open(@host, @port, options)
70
- when @bind_address.nil? then factory.open(@host, @port)
71
- else factory.open(@host, @port, @bind_address)
65
+
66
+ @socket =
67
+ if (factory = options[:proxy])
68
+ factory.open(@host, @port, options)
69
+ else
70
+ Socket.tcp(@host, @port, @bind_address, nil,
71
+ connect_timeout: options[:timeout])
72
72
  end
73
- }
73
+
74
74
  @socket.extend(PacketStream)
75
75
  @socket.logger = @logger
76
76
 
@@ -81,10 +81,12 @@ module Net; module SSH; module Transport
81
81
  @host_key_verifier = select_host_key_verifier(options[:paranoid])
82
82
 
83
83
 
84
- @server_version = timeout(options[:timeout] || 0) { ServerVersion.new(socket, logger) }
84
+ @server_version = ServerVersion.new(socket, logger, options[:timeout])
85
85
 
86
86
  @algorithms = Algorithms.new(self, options)
87
87
  wait { algorithms.initialized? }
88
+ rescue Errno::ETIMEDOUT
89
+ raise Net::SSH::ConnectionTimeout
88
90
  end
89
91
 
90
92
  # Returns the host (and possibly IP address) in a format compatible with
@@ -45,15 +45,15 @@ module Net; module SSH
45
45
  end
46
46
 
47
47
  # The major component of this version of the Net::SSH library
48
- MAJOR = 2
48
+ MAJOR = 3
49
49
 
50
50
  # The minor component of this version of the Net::SSH library
51
- MINOR = 9
51
+ MINOR = 0
52
52
 
53
53
  # The tiny component of this version of the Net::SSH library
54
- TINY = 4
54
+ TINY = 1
55
55
 
56
- # The prerelease component of this version of the Net::SSH library
56
+ # The prerelease component of this version of the Net::SSH library
57
57
  # nil allowed
58
58
  PRE = nil
59
59
 
data/lib/net/ssh.rb CHANGED
@@ -62,13 +62,14 @@ module Net
62
62
  # Net::SSH.start for a description of each option.
63
63
  VALID_OPTIONS = [
64
64
  :auth_methods, :bind_address, :compression, :compression_level, :config,
65
- :encryption, :forward_agent, :hmac, :host_key,
65
+ :encryption, :forward_agent, :hmac, :host_key, :remote_user,
66
66
  :keepalive, :keepalive_interval, :keepalive_maxcount, :kex, :keys, :key_data,
67
67
  :languages, :logger, :paranoid, :password, :port, :proxy,
68
68
  :rekey_blocks_limit,:rekey_limit, :rekey_packet_limit, :timeout, :verbose,
69
69
  :global_known_hosts_file, :user_known_hosts_file, :host_key_alias,
70
70
  :host_name, :user, :properties, :passphrase, :keys_only, :max_pkt_size,
71
- :max_win_size, :send_env, :use_agent, :number_of_password_prompts
71
+ :max_win_size, :send_env, :use_agent, :number_of_password_prompts,
72
+ :append_supported_algorithms, :non_interactive
72
73
  ]
73
74
 
74
75
  # The standard means of starting a new SSH connection. When used with a
@@ -168,15 +169,25 @@ module Net
168
169
  # * :user => the user name to log in as; this overrides the +user+
169
170
  # parameter, and is primarily only useful when provided via an SSH
170
171
  # configuration file.
172
+ # * :remote_user => used for substitution into the '%r' part of a ProxyCommand
171
173
  # * :user_known_hosts_file => the location of the user known hosts file.
172
174
  # Set to an array to specify multiple user known hosts files.
173
175
  # Defaults to %w(~/.ssh/known_hosts ~/.ssh/known_hosts2).
174
176
  # * :use_agent => Set false to disable the use of ssh-agent. Defaults to
175
177
  # true
178
+ # * :non_interactive => set to true if your app is non interactive and prefers
179
+ # authentication failure vs password prompt
176
180
  # * :verbose => how verbose to be (Logger verbosity constants, Logger::DEBUG
177
181
  # is very verbose, Logger::FATAL is all but silent). Logger::FATAL is the
178
182
  # default. The symbols :debug, :info, :warn, :error, and :fatal are also
179
183
  # supported and are translated to the corresponding Logger constant.
184
+ # * :append_all_supported_algorithms => set to +true+ to append all supported
185
+ # algorithms by net-ssh. Was the default behaviour until 2.10
186
+ # * :number_of_password_prompts => Number of prompts for the password
187
+ # authentication method defaults to 3 set to 0 to disable prompt for
188
+ # password auth method
189
+ # * :non_interactive => non interactive applications should set it to true
190
+ # to prefer failing a password/etc auth methods vs asking for password
180
191
  def self.start(host, user, options={}, &block)
181
192
  invalid_options = options.keys - VALID_OPTIONS
182
193
  if invalid_options.any?
@@ -192,6 +203,10 @@ module Net
192
203
  options[:logger].level = Logger::FATAL
193
204
  end
194
205
 
206
+ if options[:non_interactive]
207
+ options[:number_of_password_prompts] = 0
208
+ end
209
+
195
210
  if options[:verbose]
196
211
  options[:logger].level = case options[:verbose]
197
212
  when Fixnum then options[:verbose]
@@ -1,20 +1,20 @@
1
1
  -----BEGIN CERTIFICATE-----
2
2
  MIIDODCCAiCgAwIBAgIBADANBgkqhkiG9w0BAQUFADBCMRAwDgYDVQQDDAduZXQt
3
3
  c3NoMRkwFwYKCZImiZPyLGQBGRYJc29sdXRpb3VzMRMwEQYKCZImiZPyLGQBGRYD
4
- Y29tMB4XDTE1MTIwNjIxMDYyNFoXDTE2MTIwNTIxMDYyNFowQjEQMA4GA1UEAwwH
4
+ Y29tMB4XDTE0MTIwMjE3MzkyMFoXDTE1MTIwMjE3MzkyMFowQjEQMA4GA1UEAwwH
5
5
  bmV0LXNzaDEZMBcGCgmSJomT8ixkARkWCXNvbHV0aW91czETMBEGCgmSJomT8ixk
6
- ARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMYnhNtn0f6p
7
- nTylB8mE8lMdoMLJC8KwpMWsvk73Pe2WVDsH/OSwwwz6oUGk1i70cJyDjIEBNpwT
8
- 88GpVXJSumvqVsf9fCg3mWNeb5t0J+aeNm9MIvYVMTqj5tydoXQiwnILRDYHV9tZ
9
- 1c3o59/VlahSTpZ7YEgzVufpAkvEGkbJiG849exiipK7MN/ZIkMOxYVnyRXk43Xc
10
- 6GYlsHOfSgPwcXwW5g57DCwLQLWrjDsTka28dxDmO7B5Lv5EqzINxVxWsu43OgZG
11
- 21Io/jIyf5PNpeKPKNGDuAQJ8mvdMYBJoDhtCwgsUYbl0BZzA7g4ytl51HtIeP+j
12
- Qp/eAvs/RrECAwEAAaM5MDcwCQYDVR0TBAIwADAdBgNVHQ4EFgQUBfKiwO2eM4NE
13
- iRrVG793qEPLYyMwCwYDVR0PBAQDAgSwMA0GCSqGSIb3DQEBBQUAA4IBAQCfZFdb
14
- p4jzkfIzGDbiOxd0R8sdqJoC4nMLEgnQ7dLulawwA3IXe3sHAKgA5kmH3prsKc5H
15
- zVmM5NlH2P1nRbegIkQTYiIod1hZQCNxdmVG/fprMqPq0ybpUOjjrP5pj0OtszE1
16
- F2dQia1hOEstMR+n0nAtWII9HJAEyeZjVV0s2Cl7Pt85XJ3hxFcCKwzqsK5xRI7a
17
- B3vwh3/JJYrFonIohQ//Lg9qTZASEkoKLlq1/hFeICoCGGIGLq45ZB7CzXLooCKi
18
- s/ZUKye79ELwFYKJOhjW5g725OL3hy+llhEleytwKRwgXFQBPTC4f5UkdxZVVWGH
19
- e2C9M1m/2odPZo8h
6
+ ARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ0qnw4JV5JN
7
+ MWelqu7pnW2z6GZJ7+zLFYJQNETJyF0U5zo7aCRK08OeUxnpu/TCCXK8iQVkNLfz
8
+ 9pVIhF+X8pMEIruAkYGwBt1aWfuSNeyodyMk0vpZdxBHbOTJ4qBRUc6qOtNOeOzv
9
+ 8ObYUX52P/EMMaeXTRU+e7MGkB9pb6FvPPNx5akxwIaoRvtcMsc/hJnQuP5r96w6
10
+ t06MgKbXhWAX6gev0RVlrQqzxXst6iuvsrgZGjFqzob5wbTiX9M0+bFAB0EI7tJC
11
+ sv5keEbtNRaU7p3ZbMm4wTHHJLOtD+BpUCSzwv4ToNj9mZtJBMYw2Eeo7z1DklEG
12
+ mr95zbe+zNMCAwEAAaM5MDcwCQYDVR0TBAIwADAdBgNVHQ4EFgQU1bTfpzmitXwv
13
+ LmTXi0IO5vd8NGYwCwYDVR0PBAQDAgSwMA0GCSqGSIb3DQEBBQUAA4IBAQA0Aps8
14
+ UPINGa8XUUtrZtzrgX0/iyXNkKY1ld85g1N3WKEAVLfQI7TlGr0Qv2Ekx6RqlxbR
15
+ Vyq08pytSnghW2otR3bIGMGQzqxAeRLb25cjEwH7YIJ32n7ZC1fpMnBZOBDmueWA
16
+ B9EonmoO3ne7AJSgIvBbZzBPhzM4HrQGRW8LsPFsuj+dcJI43HOQwkmv2TRz0+t6
17
+ mGZldmqLcK0abv4JepLfB9XTue3kuyA29NGBibqyvRwlKckLpvKfHZX6Jxad8xxm
18
+ MbvRpzgROzyfw1qYi4dnIyMwTtXFFcZ0a2jpxHPkcTYFK6TzvFgDLAP0Y/u9jqUQ
19
+ eZ7/3CdSi/isZHEw
20
20
  -----END CERTIFICATE-----
data/net-ssh.gemspec CHANGED
@@ -2,17 +2,17 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: net-ssh 2.9.4 ruby lib
5
+ # stub: net-ssh 3.0.1 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "net-ssh"
9
- s.version = "2.9.4"
9
+ s.version = "3.0.1"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
13
  s.authors = ["Jamis Buck", "Delano Mandelbaum", "Mikl\u{f3}s Fazekas"]
14
14
  s.cert_chain = ["net-ssh-public_cert.pem"]
15
- s.date = "2016-01-30"
15
+ s.date = "2015-09-25"
16
16
  s.description = "Net::SSH: a pure-Ruby implementation of the SSH2 client protocol. It allows you to write programs that invoke and interact with processes on remote servers, via SSH2."
17
17
  s.email = "net-ssh@solutious.com"
18
18
  s.extra_rdoc_files = [
@@ -26,7 +26,6 @@ Gem::Specification.new do |s|
26
26
  "Manifest",
27
27
  "README.rdoc",
28
28
  "Rakefile",
29
- "Rudyfile",
30
29
  "THANKS.txt",
31
30
  "lib/net/ssh.rb",
32
31
  "lib/net/ssh/authentication/agent.rb",
@@ -135,12 +134,19 @@ Gem::Specification.new do |s|
135
134
  "test/configs/negative_match",
136
135
  "test/configs/nohost",
137
136
  "test/configs/numeric_host",
137
+ "test/configs/proxy_remote_user",
138
138
  "test/configs/send_env",
139
139
  "test/configs/substitutes",
140
140
  "test/configs/wild_cards",
141
141
  "test/connection/test_channel.rb",
142
142
  "test/connection/test_session.rb",
143
+ "test/integration/README.txt",
144
+ "test/integration/Vagrantfile",
145
+ "test/integration/common.rb",
146
+ "test/integration/playbook.yml",
147
+ "test/integration/test_id_rsa_keys.rb",
143
148
  "test/known_hosts/github",
149
+ "test/known_hosts/github_hash",
144
150
  "test/manual/test_forward.rb",
145
151
  "test/manual/test_pageant.rb",
146
152
  "test/start/test_connection.rb",
@@ -180,6 +186,7 @@ Gem::Specification.new do |s|
180
186
  ]
181
187
  s.homepage = "https://github.com/net-ssh/net-ssh"
182
188
  s.licenses = ["MIT"]
189
+ s.required_ruby_version = Gem::Requirement.new(">= 2.0")
183
190
  s.rubyforge_project = "net-ssh"
184
191
  s.rubygems_version = "2.4.6"
185
192
  s.signing_key = "/mnt/gem/net-ssh-private_key.pem"
data/setup.rb CHANGED
@@ -1460,7 +1460,7 @@ class Installer
1460
1460
  begin
1461
1461
  require 'test/unit'
1462
1462
  rescue LoadError
1463
- setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.'
1463
+ setup_rb_error 'test/unit cannot loaded. You need Ruby 1.9 or later to invoke this task.'
1464
1464
  end
1465
1465
  runner = Test::Unit::AutoRunner.new(true)
1466
1466
  runner.to_run << TESTDIR
data/test/README.txt CHANGED
@@ -1,28 +1,21 @@
1
- 2011-01-19
2
-
3
1
  RUNNING TESTS
4
2
 
5
3
  Run the test suite from the net-ssh directory with the following command:
6
4
 
7
- ruby -Ilib -Itest -rrubygems test/test_all.rb
5
+ ruby -Ilib -Itest test/test_all.rb
8
6
 
9
7
  Run a single test file like this:
10
8
 
11
- ruby -Ilib -Itest -rrubygems test/transport/test_server_version.rb
12
-
9
+ ruby -Ilib -Itest test/transport/test_server_version.rb
13
10
 
14
11
  EXPECTED RESULTS
15
12
 
16
- * Ruby 1.9: all tests pass
17
-
18
- * Ruby 1.8: all tests pass (up until version 2.5)
19
-
20
- * JRuby 1.7: 98% test pass (510 tests, 1914 assertions, 2 failures, 9 errors)
21
-
22
- * JRuby 1.6: 98% test pass (510 tests, 1914 assertions, 4 failures, 5 errors)
13
+ https://travis-ci.org/net-ssh/net-ssh/
23
14
 
24
- * JRuby 1.5: 98% tests pass (510 tests, 1914 assertions, 5 failures, 5 errors)
15
+ INTEGRATION TESTS
25
16
 
17
+ brew install ansible ; ansible-galaxy install rvm_io.rvm1-ruby ; vagrant up ; vagrant ssh
18
+ cd /net-ssh ; rake integration-test
26
19
 
27
20
  PORT FORWARDING TESTS
28
21
 
@@ -71,6 +71,27 @@ module Authentication; module Methods
71
71
  assert subject.authenticate("ssh-connection", "jamis", "the-password")
72
72
  end
73
73
 
74
+ def test_authenticate_should_not_prompt_for_input_when_in_non_interactive_mode
75
+
76
+ def transport.options
77
+ {non_interactive: true}
78
+ end
79
+ transport.expect do |t,packet|
80
+ assert_equal USERAUTH_REQUEST, packet.type
81
+ t.return(USERAUTH_INFO_REQUEST, :string, "", :string, "", :string, "", :long, 2, :string, "Name:", :bool, true, :string, "Password:", :bool, false)
82
+ t.expect do |t2,packet2|
83
+ assert_equal USERAUTH_INFO_RESPONSE, packet2.type
84
+ assert_equal 2, packet2.read_long
85
+ assert_equal "", packet2.read_string
86
+ assert_equal "", packet2.read_string
87
+ t2.return(USERAUTH_SUCCESS)
88
+ end
89
+ end
90
+
91
+ assert subject.authenticate("ssh-connection", "jamis", nil)
92
+ end
93
+
94
+
74
95
  def test_authenticate_should_prompt_for_input_when_password_is_not_given
75
96
  subject.expects(:prompt).with("Name:", true).returns("name")
76
97
  subject.expects(:prompt).with("Password:", false).returns("password")
@@ -56,6 +56,15 @@ module Authentication
56
56
  assert_equal({:from => :file, :file => second, :key => dsa}, manager.known_identities[dsa])
57
57
  end
58
58
 
59
+ def test_each_identity_should_not_prompt_for_passphrase_in_non_interactive_mode
60
+ manager(:non_interactive => true).stubs(:agent).returns(nil)
61
+ first = File.expand_path("/first")
62
+ stub_file_private_key first, rsa, :passphrase => :should_not_be_asked
63
+ identities = []
64
+ manager.each_identity { |identity| identities << identity }
65
+ assert_equal(identities, [])
66
+ end
67
+
59
68
  def test_identities_should_load_from_agent
60
69
  manager.stubs(:agent).returns(agent)
61
70
 
@@ -156,7 +165,9 @@ module Authentication
156
165
 
157
166
  def stub_file_private_key(name, key, options = {})
158
167
  manager.add(name)
168
+ File.stubs(:file?).with(name).returns(true)
159
169
  File.stubs(:readable?).with(name).returns(true)
170
+ File.stubs(:file?).with(name + ".pub").returns(true)
160
171
  File.stubs(:readable?).with(name + ".pub").returns(false)
161
172
 
162
173
  case options.fetch(:passphrase, :indifferently)
@@ -179,7 +190,9 @@ module Authentication
179
190
 
180
191
  def stub_file_public_key(name, key)
181
192
  manager.add(name)
182
- File.stubs(:readable?).with(name).returns(false)
193
+ File.stubs(:file?).with(name).returns(true)
194
+ File.stubs(:readable?).with(name).returns(true)
195
+ File.stubs(:file?).with(name + ".pub").returns(true)
183
196
  File.stubs(:readable?).with(name + ".pub").returns(true)
184
197
 
185
198
  Net::SSH::KeyFactory.expects(:load_public_key).with(name + ".pub").returns(key).at_least_once
@@ -0,0 +1,2 @@
1
+ Host behind-proxy
2
+ ProxyCommand ssh %r@proxy -W %h:%p
@@ -74,11 +74,8 @@ module Connection
74
74
  assert !channel.closing?
75
75
 
76
76
  connection.expect { |t,packet| assert_equal CHANNEL_CLOSE, packet.type }
77
- connection.expects(:cleanup_channel).with(channel)
78
77
  channel.close
79
78
 
80
- channel.process
81
-
82
79
  assert channel.closing?
83
80
  end
84
81
 
@@ -117,14 +117,14 @@ module Connection
117
117
  end
118
118
 
119
119
  def test_process_should_exit_after_processing_if_block_is_true_then_false
120
- session.channels[0] = stub("channel", :local_closed? => false)
120
+ session.channels[0] = stub("channel", :closing? => false)
121
121
  session.channels[0].expects(:process)
122
122
  IO.expects(:select).never
123
123
  process_times(2)
124
124
  end
125
125
 
126
126
  def test_process_should_not_process_channels_that_are_closing
127
- session.channels[0] = stub("channel", :local_closed? => true)
127
+ session.channels[0] = stub("channel", :closing? => true)
128
128
  session.channels[0].expects(:process).never
129
129
  IO.expects(:select).never
130
130
  process_times(2)
@@ -299,17 +299,8 @@ module Connection
299
299
  end
300
300
 
301
301
  def test_channel_close_packet_should_be_routed_to_corresponding_channel_and_channel_should_be_closed_and_removed
302
- session.channels[14] = stub("channel") do
303
- # this simulates the case where we closed the channel first, sent
304
- # CHANNEL_CLOSE to server and are waiting for server's response.
305
- expects(:local_closed?).returns(true)
306
- expects(:do_close)
307
- expects(:close).with()
308
- expects(:remote_closed!).with()
309
- expects(:remote_closed?).with().returns(true)
310
- expects(:local_id).returns(14)
311
- end
312
-
302
+ channel_at(14).expects(:do_close).with()
303
+ session.channels[14].expects(:close).with()
313
304
  transport.return(CHANNEL_CLOSE, :long, 14)
314
305
  process_times(2)
315
306
  assert session.channels.empty?
@@ -386,13 +377,13 @@ module Connection
386
377
  options = { :keepalive => true, :keepalive_interval => 300, :keepalive_maxcount => 3 }
387
378
  expected_packet = P(:byte, Net::SSH::Packet::GLOBAL_REQUEST, :string, "keepalive@openssh.com", :bool, true)
388
379
  [1,2,3].each do |i|
389
- Time.stubs(:now).returns(i*300)
380
+ Time.stubs(:now).returns(Time.at(i*300))
390
381
  IO.stubs(:select).with([socket],[],nil,timeout).returns(nil)
391
- transport.expects(:enqueue_message).with{ |msg| msg.content == expected_packet.content }
382
+ transport.expects(:enqueue_message).with{ |msg| msg.content == expected_packet.content }
392
383
  session(options).process
393
384
  end
394
385
 
395
- Time.stubs(:now).returns(4*300)
386
+ Time.stubs(:now).returns(Time.at(4*300))
396
387
  IO.stubs(:select).with([socket],[],nil,timeout).returns(nil)
397
388
  transport.expects(:enqueue_message).with{ |msg| msg.content == expected_packet.content }
398
389
  assert_raises(Net::SSH::Timeout) { session(options).process }
@@ -482,6 +473,11 @@ module Connection
482
473
  assert_equal "some data", session.exec!("ls")
483
474
  end
484
475
 
476
+ def test_exec_bang_without_block_should_return_empty_string_for_empty_command_output
477
+ prep_exec('ls', :stdout, '')
478
+ assert_equal "", session.exec!('ls')
479
+ end
480
+
485
481
  private
486
482
 
487
483
  def prep_exec(command, *data)
@@ -497,10 +493,8 @@ module Connection
497
493
 
498
494
  t2.return(CHANNEL_SUCCESS, :long, p[:remote_id])
499
495
 
500
- 0.step(data.length-1, 2) do |index|
501
- type = data[index]
502
- datum = data[index+1]
503
-
496
+ data.each_slice(2) do |type, datum|
497
+ next if datum.empty?
504
498
  if type == :stdout
505
499
  t2.return(CHANNEL_DATA, :long, p[:remote_id], :string, datum)
506
500
  else
@@ -532,7 +526,7 @@ module Connection
532
526
  end
533
527
 
534
528
  def channel_at(local_id)
535
- session.channels[local_id] = stub("channel", :process => true, :local_closed? => false)
529
+ session.channels[local_id] = stub("channel", :process => true, :closing? => false)
536
530
  end
537
531
 
538
532
  def transport(options={})
@@ -0,0 +1,19 @@
1
+ # Integration tests with vagrant
2
+
3
+ Requirements:
4
+
5
+ * Vagrant (https://www.vagrantup.com/)
6
+ * Ansible (http://docs.ansible.com/intro_installation.html)
7
+
8
+ Setup:
9
+
10
+ ansible-galaxy install rvm_io.rvm1-ruby
11
+ vagrant up
12
+ vagrant ssh
13
+ cd /net-ssh
14
+ rake integration-test
15
+
16
+ # TODO
17
+
18
+ * get it running on ci (probalby needs docker)
19
+ * could not get gem install jeweler to work
@@ -0,0 +1,12 @@
1
+ VAGRANTFILE_API_VERSION = "2"
2
+
3
+ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
4
+ config.vm.box = "ubuntu/trusty64"
5
+ config.vm.provision "ansible" do |ansible|
6
+ ansible.playbook = "./playbook.yml"
7
+ ansible.sudo = true
8
+ ansible.verbose ='vvvv'
9
+ end
10
+
11
+ config.vm.synced_folder "../..", "/net-ssh"
12
+ end