net-ssh 2.9.1 → 2.9.2

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: aac9757449c9d417f334f8a5c44b9b69105c4c9b
4
+ data.tar.gz: eda8ba20fad936162084101482eb1f1c821b67a3
5
+ SHA512:
6
+ metadata.gz: fc1b60e3b51388d6b66fc38b7d4aaba56ead796820c2374a0aad1b48de094c2fb382db4159425d404f66612d8e61f0a1436b16dd90873235fcacca9b64bf3da5
7
+ data.tar.gz: f5dce76345e1774539a47d70c4f516e52164171d38da7569fd2f0489f34cc8599d0914115ad2108d89ca12cf667fa36a062a188508d3d136692075421d4e0307
checksums.yaml.gz.sig ADDED
Binary file
data/CHANGES.txt CHANGED
@@ -1,4 +1,21 @@
1
+ === 2.9.2-rc3
1
2
 
3
+ * Remove advertised algorithms that were not working (curve25519-sha256@libssh.org) [mfazekas]
4
+
5
+ === 2.9.2-rc2
6
+
7
+ * number_of_password_prompts is now accepted as ssh option, by setting it 0 net-ssh will not ask for password for password auth as with previous versions [mfazekas]
8
+
9
+ === 2.9.2-rc1
10
+
11
+ * Documentation fixes and refactoring to keepalive [detiber, mfazekas]
12
+
13
+ === 2.9.2-beta
14
+
15
+ * Remove advertised algorithms that were not working (ssh-rsa-cert-* *ed25519 acm*-gcm@openssh.com) [mfazekas]
16
+ * Unkown algorithms now ignored instead of failed [mfazekas]
17
+ * Asks for password with password auth (up to number_of_password_prompts) [mfazekas]
18
+ * Removed warnings [amatsuda]
2
19
 
3
20
  === 2.9.1 / 13 May 2014
4
21
 
data/README.rdoc CHANGED
@@ -171,6 +171,11 @@ Run a single test file like this:
171
171
 
172
172
  * JRuby 1.5: 99% tests pass (448 tests, 1846 assertions, 1 failures)
173
173
 
174
+ === BUILDING GEM
175
+
176
+ Since building the gem requires the private key if you want to build a .gem locally please use the NET_SSH_NOKEY=1 envirnoment variable:
177
+
178
+ rake build NET_SSH_NOKEY=1
174
179
 
175
180
  === PORT FORWARDING TESTS
176
181
 
data/Rakefile CHANGED
@@ -1,3 +1,10 @@
1
+ # coding: UTF-8
2
+ #
3
+ # Also in your terminal environment run:
4
+ # $ export LANG=en_US.UTF-8
5
+ # $ export LANGUAGE=en_US.UTF-8
6
+ # $ export LC_ALL=en_US.UTF-8
7
+
1
8
  require "rubygems"
2
9
  require "rake"
3
10
  require "rake/clean"
@@ -21,7 +28,7 @@ begin
21
28
  s.description = s.summary + " It allows you to write programs that invoke and interact with processes on remote servers, via SSH2."
22
29
  s.email = "net-ssh@solutious.com"
23
30
  s.homepage = "https://github.com/net-ssh/net-ssh"
24
- s.authors = ["Jamis Buck", "Delano Mandelbaum"]
31
+ s.authors = ["Jamis Buck", "Delano Mandelbaum", "Miklós Fazekas"]
25
32
 
26
33
  # Note: this is run at package time not install time so if you are
27
34
  # running on jruby, you need to install jruby-pageant manually.
@@ -34,10 +41,16 @@ begin
34
41
 
35
42
  s.license = "MIT"
36
43
 
37
- s.signing_key = File.join('/mnt/gem/', 'gem-private_key.pem')
38
- s.cert_chain = ['gem-public_cert.pem']
44
+ unless ENV['NET_SSH_NOKEY']
45
+ signing_key = File.join('/mnt/gem/', 'net-ssh-private_key.pem')
46
+ s.signing_key = File.join('/mnt/gem/', 'net-ssh-private_key.pem')
47
+ s.cert_chain = ['net-ssh-public_cert.pem']
48
+ unless (Rake.application.top_level_tasks & ['build','install']).empty?
49
+ 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)
50
+ end
51
+ end
39
52
  end
40
- Jeweler::GemcutterTasks.new
53
+ Jeweler::RubygemsDotOrgTasks.new
41
54
  rescue LoadError
42
55
  puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
43
56
  end
@@ -94,10 +94,16 @@ module Net; module SSH; module Authentication
94
94
 
95
95
  identities = []
96
96
  body.read_long.times do
97
- key = Buffer.new(body.read_string).read_key
98
- key.extend(Comment)
99
- key.comment = body.read_string
100
- identities.push key
97
+ key_str = body.read_string
98
+ comment_str = body.read_string
99
+ begin
100
+ key = Buffer.new(key_str).read_key
101
+ key.extend(Comment)
102
+ key.comment = comment_str
103
+ identities.push key
104
+ rescue NotImplementedError => e
105
+ error { "ignoring unimplemented key:#{e.message} #{comment_str}" }
106
+ end
101
107
  end
102
108
 
103
109
  return identities
@@ -1,4 +1,5 @@
1
1
  require 'net/ssh/errors'
2
+ require 'net/ssh/prompt'
2
3
  require 'net/ssh/authentication/methods/abstract'
3
4
 
4
5
  module Net
@@ -8,25 +9,36 @@ module Net
8
9
 
9
10
  # Implements the "password" SSH authentication method.
10
11
  class Password < Abstract
12
+ include Prompt
13
+
11
14
  # Attempt to authenticate the given user for the given service. If
12
- # the password parameter is nil, this will never do anything except
13
- # return false.
15
+ # the password parameter is nil, this will ask for password
14
16
  def authenticate(next_service, username, password=nil)
15
- return false unless password
17
+ retries = 0
18
+ max_retries = get_max_retries
19
+ return false if !password && max_retries == 0
16
20
 
17
- send_message(userauth_request(username, next_service, "password", false, password))
18
- message = session.next_message
21
+ begin
22
+ password_to_send = password || ask_password(username)
19
23
 
20
- case message.type
21
- when USERAUTH_SUCCESS
22
- debug { "password succeeded" }
23
- return true
24
- when USERAUTH_FAILURE
24
+ send_message(userauth_request(username, next_service, "password", false, password_to_send))
25
+ message = session.next_message
26
+ retries += 1
27
+
28
+ if message.type == USERAUTH_FAILURE
25
29
  debug { "password failed" }
26
30
 
27
31
  raise Net::SSH::Authentication::DisallowedMethod unless
28
32
  message[:authentications].split(/,/).include? 'password'
33
+ password = nil
34
+ end
35
+ end until (message.type != USERAUTH_FAILURE || retries >= max_retries)
29
36
 
37
+ case message.type
38
+ when USERAUTH_SUCCESS
39
+ debug { "password succeeded" }
40
+ return true
41
+ when USERAUTH_FAILURE
30
42
  return false
31
43
  when USERAUTH_PASSWD_CHANGEREQ
32
44
  debug { "password change request received, failing" }
@@ -35,6 +47,19 @@ module Net
35
47
  raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
36
48
  end
37
49
  end
50
+
51
+ private
52
+
53
+ NUMBER_OF_PASSWORD_PROMPTS = 3
54
+
55
+ def ask_password(username)
56
+ echo = false
57
+ prompt("#{username}@#{session.transport.host}'s password:", echo)
58
+ end
59
+
60
+ def get_max_retries
61
+ (session.transport.options||{})[:number_of_password_prompts] || NUMBER_OF_PASSWORD_PROMPTS
62
+ end
38
63
  end
39
64
 
40
65
  end
@@ -255,7 +255,7 @@ module Net; module SSH
255
255
  key.e = read_bignum
256
256
  key.n = read_bignum
257
257
 
258
- when /^ecdsa\-sha2\-(\w*)$/, /^ssh-ed25519(-cert-v01@openssh.com)?$/
258
+ when /^ecdsa\-sha2\-(\w*)$/
259
259
  unless defined?(OpenSSL::PKey::EC)
260
260
  raise NotImplementedError, "unsupported key type `#{type}'"
261
261
  else
@@ -31,6 +31,7 @@ module Net; module SSH
31
31
  # * RekeyLimit => :rekey_limit
32
32
  # * User => :user
33
33
  # * UserKnownHostsFile => :user_known_hosts_file
34
+ # * NumberOfPasswordPrompts => :number_of_password_prompts
34
35
  #
35
36
  # Note that you will never need to use this class directly--you can control
36
37
  # whether the OpenSSH configuration files are read by passing the :config
@@ -59,7 +60,7 @@ module Net; module SSH
59
60
  # #default_files), translates the resulting hash into the options
60
61
  # recognized by Net::SSH, and returns them.
61
62
  def for(host, files=default_files)
62
- hash = translate(files.inject({}) { |settings, file|
63
+ translate(files.inject({}) { |settings, file|
63
64
  load(file, host, settings)
64
65
  })
65
66
  end
@@ -76,7 +77,6 @@ module Net; module SSH
76
77
 
77
78
  globals = {}
78
79
  matched_host = nil
79
- multi_host = []
80
80
  seen_host = false
81
81
  IO.foreach(file) do |line|
82
82
  next if line =~ /^\s*(?:#.*)?$/
@@ -220,6 +220,8 @@ module Net; module SSH
220
220
  when 'sendenv'
221
221
  multi_send_env = value.to_s.split(/\s+/)
222
222
  hash[:send_env] = multi_send_env.map { |e| Regexp.new pattern2regex(e).source, false }
223
+ when 'numberofpasswordprompts'
224
+ hash[:number_of_password_prompts] = value.to_i
223
225
  end
224
226
  hash
225
227
  end
@@ -523,7 +523,7 @@ module Net; module SSH; module Connection
523
523
  @on_open_failed.call(self, reason_code, description)
524
524
  else
525
525
  raise ChannelOpenFailed.new(reason_code, description)
526
- end
526
+ end
527
527
  end
528
528
 
529
529
  # Invoked when the server sends a CHANNEL_WINDOW_ADJUST packet, and
@@ -0,0 +1,55 @@
1
+ require 'net/ssh/loggable'
2
+ module Net; module SSH; module Connection
3
+
4
+ class Keepalive
5
+ include Loggable
6
+
7
+ def initialize(session)
8
+ @last_keepalive_sent_at = nil
9
+ @unresponded_keepalive_count = 0
10
+ @session = session
11
+ self.logger = session.logger
12
+ end
13
+
14
+ def options
15
+ @session.options
16
+ end
17
+
18
+ def enabled?
19
+ options[:keepalive]
20
+ end
21
+
22
+ def interval
23
+ options[:keepalive_interval] || Session::DEFAULT_IO_SELECT_TIMEOUT
24
+ end
25
+
26
+ def should_send?
27
+ return false unless enabled?
28
+ return true unless @last_keepalive_sent_at
29
+ Time.now - @last_keepalive_sent_at >= interval
30
+ end
31
+
32
+ def keepalive_maxcount
33
+ (options[:keepalive_maxcount] || 3).to_i
34
+ end
35
+
36
+ def send_as_needed(readers, writers)
37
+ return unless readers.nil? && writers.nil?
38
+ return unless should_send?
39
+ info { "sending keepalive #{@unresponded_keepalive_count}" }
40
+
41
+ @unresponded_keepalive_count += 1
42
+ @session.send_global_request("keepalive@openssh.com") { |success, response|
43
+ debug { "keepalive response successful. Missed #{@unresponded_keepalive_count-1} keepalives" }
44
+ @unresponded_keepalive_count = 0
45
+ }
46
+ @last_keepalive_sent_at = Time.now
47
+ if keepalive_maxcount > 0 && @unresponded_keepalive_count > keepalive_maxcount
48
+ error { "Timeout, server #{@session.host} not responding. Missed #{@unresponded_keepalive_count-1} timeouts." }
49
+ @unresponded_keepalive_count = 0
50
+ raise Net::SSH::Timeout, "Timeout, server #{@session.host} not responding."
51
+ end
52
+ end
53
+ end
54
+
55
+ end; end; end
@@ -3,6 +3,7 @@ require 'net/ssh/ruby_compat'
3
3
  require 'net/ssh/connection/channel'
4
4
  require 'net/ssh/connection/constants'
5
5
  require 'net/ssh/service/forward'
6
+ require 'net/ssh/connection/keepalive'
6
7
 
7
8
  module Net; module SSH; module Connection
8
9
 
@@ -79,7 +80,7 @@ module Net; module SSH; module Connection
79
80
  @max_pkt_size = (options.has_key?(:max_pkt_size) ? options[:max_pkt_size] : 0x8000)
80
81
  @max_win_size = (options.has_key?(:max_win_size) ? options[:max_win_size] : 0x20000)
81
82
 
82
- @last_keepalive_sent_at = nil
83
+ @keepalive = Keepalive.new(self)
83
84
  end
84
85
 
85
86
  # Retrieves a custom property from this instance. This can be used to
@@ -244,7 +245,7 @@ module Net; module SSH; module Connection
244
245
  writer.send_pending
245
246
  end
246
247
 
247
- send_keepalive_as_needed(readers, writers)
248
+ @keepalive.send_as_needed(readers, writers)
248
249
  transport.rekey_as_needed
249
250
 
250
251
  return true
@@ -598,27 +599,8 @@ module Net; module SSH; module Connection
598
599
 
599
600
  def io_select_wait(wait)
600
601
  return wait if wait
601
- return wait unless options[:keepalive]
602
- keepalive_interval
603
- end
604
-
605
- def keepalive_interval
606
- options[:keepalive_interval] || DEFAULT_IO_SELECT_TIMEOUT
607
- end
608
-
609
- def should_send_keepalive?
610
- return false unless options[:keepalive]
611
- return true unless @last_keepalive_sent_at
612
- Time.now - @last_keepalive_sent_at >= keepalive_interval
613
- end
614
-
615
- def send_keepalive_as_needed(readers, writers)
616
- return unless readers.nil? && writers.nil?
617
- return unless should_send_keepalive?
618
- info { "sending keepalive" }
619
- msg = Net::SSH::Buffer.from(:byte, Packet::IGNORE, :string, "keepalive")
620
- send_message(msg)
621
- @last_keepalive_sent_at = Time.now
602
+ return wait unless @keepalive.enabled?
603
+ @keepalive.interval
622
604
  end
623
605
 
624
606
  MAP = Constants.constants.inject({}) do |memo, name|
@@ -14,6 +14,10 @@ module Net; module SSH
14
14
  # unexpectedly.
15
15
  class Disconnect < Exception; end
16
16
 
17
+ # This exception is raised when the remote host has disconnected/
18
+ # timeouted unexpectedly.
19
+ class Timeout < Disconnect; end
20
+
17
21
  # This exception is primarily used internally, but if you have a channel
18
22
  # request handler (see Net::SSH::Connection::Channel#on_request) that you
19
23
  # want to fail in such a way that the server knows it failed, you can
@@ -15,17 +15,9 @@ module Net; module SSH
15
15
  SUPPORTED_TYPE = %w(ssh-rsa ssh-dss
16
16
  ecdsa-sha2-nistp256
17
17
  ecdsa-sha2-nistp384
18
- ecdsa-sha2-nistp521
19
- ssh-ed25519-cert-v01@openssh.com
20
- ssh-rsa-cert-v01@openssh.com
21
- ssh-rsa-cert-v00@openssh.com
22
- ssh-ed25519
23
- )
18
+ ecdsa-sha2-nistp521)
24
19
  else
25
- SUPPORTED_TYPE = %w(ssh-rsa ssh-dss
26
- ssh-rsa-cert-v01@openssh.com
27
- ssh-rsa-cert-v00@openssh.com
28
- )
20
+ SUPPORTED_TYPE = %w(ssh-rsa ssh-dss)
29
21
  end
30
22
 
31
23
 
@@ -88,13 +88,13 @@ module Net; module SSH; module Service
88
88
  end
89
89
 
90
90
  prepare_client(client, channel, :local)
91
-
91
+
92
92
  channel.on_open_failed do |ch, code, description|
93
93
  channel.error { "could not establish direct channel: #{description} (#{code})" }
94
94
  channel[:socket].close
95
95
  end
96
96
  end
97
-
97
+
98
98
  local_port
99
99
  end
100
100
 
@@ -131,22 +131,52 @@ module Net; module SSH; module Service
131
131
  # the port number. The assigned port will show up in the # #active_remotes
132
132
  # list.
133
133
  #
134
+ # remote_host is interpreted by the server per RFC 4254, which has these
135
+ # special values:
136
+ #
137
+ # - "" means that connections are to be accepted on all protocol
138
+ # families supported by the SSH implementation.
139
+ # - "0.0.0.0" means to listen on all IPv4 addresses.
140
+ # - "::" means to listen on all IPv6 addresses.
141
+ # - "localhost" means to listen on all protocol families supported by
142
+ # the SSH implementation on loopback addresses only ([RFC3330] and
143
+ # [RFC3513]).
144
+ # - "127.0.0.1" and "::1" indicate listening on the loopback
145
+ # interfaces for IPv4 and IPv6, respectively.
146
+ #
147
+ # You may pass a block that will be called when the the port forward
148
+ # request receives a response. This block will be passed the remote_port
149
+ # that was actually bound to, or nil if the binding failed. If the block
150
+ # returns :no_exception, the "failed binding" exception will not be thrown.
151
+ #
134
152
  # If you want to block until the port is active, you could do something
135
153
  # like this:
136
154
  #
137
- # old_active_remotes = ssh.forward.active_remotes
138
- # ssh.forward.remote(80, "www.google.com", 0, "0.0.0.0")
139
- # ssh.loop { !(ssh.forward.active_remotes.length > old_active_remotes.length) }
140
- # assigned_port = (ssh.forward.active_remotes - old_active_remotes).first[0]
155
+ # got_remote_port = nil
156
+ # remote(port, host, remote_port, remote_host) do |actual_remote_port|
157
+ # got_remote_port = actual_remote_port || :error
158
+ # :no_exception # will yield the exception on my own thread
159
+ # end
160
+ # session.loop { !got_remote_port }
161
+ # if got_remote_port == :error
162
+ # raise Net::SSH::Exception, "remote forwarding request failed"
163
+ # end
164
+ #
141
165
  def remote(port, host, remote_port, remote_host="127.0.0.1")
142
166
  session.send_global_request("tcpip-forward", :string, remote_host, :long, remote_port) do |success, response|
143
167
  if success
144
168
  remote_port = response.read_long if remote_port == 0
145
169
  debug { "remote forward from remote #{remote_host}:#{remote_port} to #{host}:#{port} established" }
146
170
  @remote_forwarded_ports[[remote_port, remote_host]] = Remote.new(host, port)
171
+ yield remote_port, remote_host if block_given?
147
172
  else
148
- error { "remote forwarding request failed" }
149
- raise Net::SSH::Exception, "remote forwarding request failed"
173
+ instruction = if block_given?
174
+ yield :error
175
+ end
176
+ unless instruction == :no_exception
177
+ error { "remote forwarding request failed" }
178
+ raise Net::SSH::Exception, "remote forwarding request failed"
179
+ end
150
180
  end
151
181
  end
152
182
  end
@@ -183,6 +213,16 @@ module Net; module SSH; module Service
183
213
  @remote_forwarded_ports.keys
184
214
  end
185
215
 
216
+ # Returns all active remote forwarded ports and where they forward to. The
217
+ # returned value is a hash from [<forwarding port on the local host>, <local forwarding address>]
218
+ # to [<port on the remote host>, <remote bind address>].
219
+ def active_remote_destinations
220
+ @remote_forwarded_ports.inject({}) do |result, (remote, local)|
221
+ result[[local.port, local.host]] = remote
222
+ result
223
+ end
224
+ end
225
+
186
226
  # Enables SSH agent forwarding on the given channel. The forwarded agent
187
227
  # will remain active even after the channel closes--the channel is only
188
228
  # used as the transport for enabling the forwarded connection. You should
@@ -216,7 +256,7 @@ module Net; module SSH; module Service
216
256
  end
217
257
 
218
258
  private
219
-
259
+
220
260
  # Perform setup operations that are common to all forwarded channels.
221
261
  # +client+ is a socket, +channel+ is the channel that was just created,
222
262
  # and +type+ is an arbitrary string describing the type of the channel.
@@ -228,11 +268,11 @@ module Net; module SSH; module Service
228
268
  session.listen_to(client)
229
269
  channel[:socket] = client
230
270
 
231
- channel.on_data do |ch, data|
271
+ channel.on_data do |ch, data|
232
272
  debug { "data:#{data.length} on #{type} forwarded channel" }
233
273
  ch[:socket].enqueue(data)
234
274
  end
235
-
275
+
236
276
  # Handles server close on the sending side by Miklós Fazekas
237
277
  channel.on_eof do |ch|
238
278
  debug { "eof #{type} on #{type} forwarded channel" }
@@ -251,7 +291,7 @@ module Net; module SSH; module Service
251
291
  debug { "enotconn in on_eof => shallowing exception:#{e}" }
252
292
  end
253
293
  end
254
-
294
+
255
295
  channel.on_close do |ch|
256
296
  debug { "closing #{type} forwarded channel" }
257
297
  ch[:socket].close if !client.closed?
@@ -42,14 +42,12 @@ module Net; module SSH; module Transport
42
42
  camellia192-ctr@openssh.org
43
43
  camellia256-ctr@openssh.org
44
44
  cast128-ctr blowfish-ctr 3des-ctr
45
- aes256-gcm@openssh.com aes128-gcm@openssh.com
46
45
  ),
46
+
47
47
  :hmac => %w(hmac-sha1 hmac-md5 hmac-sha1-96 hmac-md5-96
48
48
  hmac-ripemd160 hmac-ripemd160@openssh.com
49
49
  hmac-sha2-256 hmac-sha2-512 hmac-sha2-256-96
50
- hmac-sha2-512-96 none
51
- hmac-sha2-512-etm@openssh.com hmac-sha2-256-etm@openssh.com
52
- umac-128-etm@openssh.com),
50
+ hmac-sha2-512-96 none),
53
51
 
54
52
  :compression => %w(none zlib@openssh.com zlib),
55
53
  :language => %w()
@@ -57,13 +55,10 @@ module Net; module SSH; module Transport
57
55
  if defined?(OpenSSL::PKey::EC)
58
56
  ALGORITHMS[:host_key] += %w(ecdsa-sha2-nistp256
59
57
  ecdsa-sha2-nistp384
60
- ecdsa-sha2-nistp521
61
- ssh-ed25519-cert-v01@openssh.com
62
- ssh-ed25519)
58
+ ecdsa-sha2-nistp521)
63
59
  ALGORITHMS[:kex] += %w(ecdh-sha2-nistp256
64
60
  ecdh-sha2-nistp384
65
- ecdh-sha2-nistp521
66
- curve25519-sha256@libssh.org)
61
+ ecdh-sha2-nistp521)
67
62
  end
68
63
 
69
64
  # The underlying transport layer session that supports this object
@@ -221,8 +216,13 @@ module Net; module SSH; module Transport
221
216
  # apply the preferred algorithm order, if any
222
217
  if options[algorithm]
223
218
  algorithms[algorithm] = Array(options[algorithm]).compact.uniq
224
- invalid = algorithms[algorithm].detect { |name| !ALGORITHMS[algorithm].include?(name) }
225
- raise NotImplementedError, "unsupported #{algorithm} algorithm: `#{invalid}'" if invalid
219
+ unsupported = []
220
+ algorithms[algorithm].select! do |name|
221
+ supported = ALGORITHMS[algorithm].include?(name)
222
+ unsupported << name unless supported
223
+ supported
224
+ end
225
+ lwarn { "unsupported #{algorithm} algorithm: `#{unsupported}'" } unless unsupported.empty?
226
226
 
227
227
  # make sure all of our supported algorithms are tacked onto the
228
228
  # end, so that if the user tries to give a list of which none are
@@ -41,9 +41,6 @@ module Net; module SSH; module Transport
41
41
  "camellia192-ctr@openssh.org" => "camellia-192-ecb",
42
42
  "camellia256-ctr@openssh.org" => "camellia-256-ecb",
43
43
 
44
- "aes256-gcm@openssh.com" => "aes-256-gcm",
45
- "aes128-gcm@openssh.com" => "aes-128-gcm",
46
-
47
44
  "none" => "none",
48
45
  }
49
46
 
@@ -14,6 +14,8 @@ module Net; module SSH; module Transport
14
14
  # per the SSH2 protocol. It also adds an abstraction for polling packets,
15
15
  # to allow for both blocking and non-blocking reads.
16
16
  module PacketStream
17
+ PROXY_COMMAND_HOST_IP = '<no hostip for proxy command>'.freeze
18
+
17
19
  include BufferedIo
18
20
 
19
21
  def self.extended(object)
@@ -64,7 +66,7 @@ module Net; module SSH; module Transport
64
66
  addr = getpeername
65
67
  Socket.getnameinfo(addr, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV).first
66
68
  else
67
- "<no hostip for proxy command>"
69
+ PROXY_COMMAND_HOST_IP
68
70
  end
69
71
  end
70
72
 
@@ -93,11 +93,16 @@ module Net; module SSH; module Transport
93
93
  @host_as_string ||= begin
94
94
  string = "#{host}"
95
95
  string = "[#{string}]:#{port}" if port != DEFAULT_PORT
96
- if socket.peer_ip != host
97
- string2 = socket.peer_ip
96
+
97
+ peer_ip = socket.peer_ip
98
+
99
+ if peer_ip != Net::SSH::Transport::PacketStream::PROXY_COMMAND_HOST_IP &&
100
+ peer_ip != host
101
+ string2 = peer_ip
98
102
  string2 = "[#{string2}]:#{port}" if port != DEFAULT_PORT
99
103
  string << "," << string2
100
104
  end
105
+
101
106
  string
102
107
  end
103
108
  end
@@ -16,15 +16,15 @@ module Net; module SSH
16
16
 
17
17
  # A convenience method for instantiating a new Version instance with the
18
18
  # given +major+, +minor+, and +tiny+ components.
19
- def self.[](major, minor, tiny)
20
- new(major, minor, tiny)
19
+ def self.[](major, minor, tiny, pre = nil)
20
+ new(major, minor, tiny, pre)
21
21
  end
22
22
 
23
23
  attr_reader :major, :minor, :tiny
24
24
 
25
25
  # Create a new Version object with the given components.
26
- def initialize(major, minor, tiny)
27
- @major, @minor, @tiny = major, minor, tiny
26
+ def initialize(major, minor, tiny, pre = nil)
27
+ @major, @minor, @tiny, @pre = major, minor, tiny, pre
28
28
  end
29
29
 
30
30
  # Compare this version to the given +version+ object.
@@ -35,7 +35,7 @@ module Net; module SSH
35
35
  # Converts this version object to a string, where each of the three
36
36
  # version components are joined by the '.' character. E.g., 2.0.0.
37
37
  def to_s
38
- @to_s ||= [@major, @minor, @tiny].join(".")
38
+ @to_s ||= [@major, @minor, @tiny, @pre].compact.join(".")
39
39
  end
40
40
 
41
41
  # Converts this version to a canonical integer that may be compared
@@ -51,10 +51,14 @@ module Net; module SSH
51
51
  MINOR = 9
52
52
 
53
53
  # The tiny component of this version of the Net::SSH library
54
- TINY = 1
54
+ TINY = 2
55
+
56
+ # The prerelease component of this version of the Net::SSH library
57
+ # nil allowed
58
+ PRE = nil
55
59
 
56
60
  # The current version of the Net::SSH library as a Version instance
57
- CURRENT = new(MAJOR, MINOR, TINY)
61
+ CURRENT = new(*[MAJOR, MINOR, TINY, PRE].compact)
58
62
 
59
63
  # The current version of the Net::SSH library as a String
60
64
  STRING = CURRENT.to_s