net-ssh 2.6.8 → 2.7.0

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.
@@ -1,4 +1,13 @@
1
1
 
2
+
3
+ === 2.7.0 / 11 Sep 2013
4
+
5
+ * Fix for 'Could not parse PKey: no start line' error on private keys with passphrases (issue #101) [metametaclass]
6
+ * Automatically forward environment variables defined in OpenSSH config files [fnordfish]
7
+ * Guard against socket.gets being nil in Net::SSH::Proxy::HTTP [krishicks]
8
+ * Implemented experimental keepalive feature [noric]
9
+
10
+
2
11
  === 2.6.8 / 6 Jul 2013
3
12
 
4
13
  * Added support for host wildcard substitution [GabKlein]
@@ -62,12 +62,13 @@ 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, :kex, :keys, :key_data,
65
+ :encryption, :forward_agent, :hmac, :host_key,
66
+ :keepalive, :keepalive_interval, :kex, :keys, :key_data,
66
67
  :languages, :logger, :paranoid, :password, :port, :proxy,
67
68
  :rekey_blocks_limit,:rekey_limit, :rekey_packet_limit, :timeout, :verbose,
68
69
  :global_known_hosts_file, :user_known_hosts_file, :host_key_alias,
69
70
  :host_name, :user, :properties, :passphrase, :keys_only, :max_pkt_size,
70
- :max_win_size
71
+ :max_win_size, :send_env
71
72
  ]
72
73
 
73
74
  # The standard means of starting a new SSH connection. When used with a
@@ -124,6 +125,11 @@ module Net
124
125
  # specified in an SSH configuration file. It lets you specify an
125
126
  # "alias", similarly to adding an entry in /etc/hosts but without needing
126
127
  # to modify /etc/hosts.
128
+ # :keepalive => set to +true+ to send a keepalive packet to the SSH server
129
+ # when there's no traffic between the SSH server and Net::SSH client for
130
+ # the keepalive_interval seconds. Defaults to +false+.
131
+ # :keepalive_interval => the interval seconds for keepalive.
132
+ # Defaults to +300+ seconds.
127
133
  # * :kex => the key exchange algorithm (or algorithms) to use
128
134
  # * :keys => an array of file names of private keys to use for publickey
129
135
  # and hostbased authentication
@@ -151,6 +157,8 @@ module Net
151
157
  # * :rekey_blocks_limit => the max number of blocks to process before rekeying
152
158
  # * :rekey_limit => the max number of bytes to process before rekeying
153
159
  # * :rekey_packet_limit => the max number of packets to process before rekeying
160
+ # * :send_env => an array of local environment variable names to export to the
161
+ # remote environment. Names may be given as String or Regexp.
154
162
  # * :timeout => how long to wait for the initial connection to be made
155
163
  # * :user => the user name to log in as; this overrides the +user+
156
164
  # parameter, and is primarily only useful when provided via an SSH
@@ -229,6 +229,9 @@ module Net
229
229
  else
230
230
  identity
231
231
  end
232
+ rescue ArgumentError => e
233
+ process_identity_loading_error(identity, e)
234
+ nil
232
235
  rescue Exception => e
233
236
  process_identity_loading_error(identity, e)
234
237
  nil
@@ -176,6 +176,9 @@ module Net; module SSH
176
176
  hash[:user] = value
177
177
  when 'userknownhostsfile'
178
178
  hash[:user_known_hosts_file] = value
179
+ when 'sendenv'
180
+ multi_send_env = value.to_s.split(/\s+/)
181
+ hash[:send_env] = multi_send_env.map { |e| Regexp.new pattern2regex(e).source, false }
179
182
  end
180
183
  hash
181
184
  end
@@ -510,6 +510,7 @@ module Net; module SSH; module Connection
510
510
  @remote_window_size = @remote_maximum_window_size = max_window
511
511
  @remote_maximum_packet_size = max_packet
512
512
  connection.forward.agent(self) if connection.options[:forward_agent] && type == "session"
513
+ forward_local_env(connection.options[:send_env]) if connection.options[:send_env]
513
514
  @on_confirm_open.call(self) if @on_confirm_open
514
515
  end
515
516
 
@@ -625,6 +626,25 @@ module Net; module SSH; module Connection
625
626
  @local_maximum_window_size += 0x20000
626
627
  end
627
628
  end
629
+
630
+ # Gets an +Array+ of local environment variables in the remote process'
631
+ # environment.
632
+ # A variable name can either be described by a +Regexp+ or +String+.
633
+ #
634
+ # channel.forward_local_env [/^GIT_.*$/, "LANG"]
635
+ def forward_local_env(env_variable_patterns)
636
+ Array(env_variable_patterns).each do |env_variable_pattern|
637
+ matched_variables = ENV.find_all do |env_name, _|
638
+ case env_variable_pattern
639
+ when Regexp then env_name =~ env_variable_pattern
640
+ when String then env_name == env_variable_pattern
641
+ end
642
+ end
643
+ matched_variables.each do |env_name, env_value|
644
+ self.env(env_name, env_value)
645
+ end
646
+ end
647
+ end
628
648
  end
629
649
 
630
650
  end; end; end
@@ -25,6 +25,9 @@ module Net; module SSH; module Connection
25
25
  class Session
26
26
  include Constants, Loggable
27
27
 
28
+ # Default IO.select timeout threshold
29
+ DEFAULT_IO_SELECT_TIMEOUT = 300
30
+
28
31
  # The underlying transport layer abstraction (see Net::SSH::Transport::Session).
29
32
  attr_reader :transport
30
33
 
@@ -75,6 +78,8 @@ module Net; module SSH; module Connection
75
78
 
76
79
  @max_pkt_size = (options.has_key?(:max_pkt_size) ? options[:max_pkt_size] : 0x8000)
77
80
  @max_win_size = (options.has_key?(:max_win_size) ? options[:max_win_size] : 0x20000)
81
+
82
+ @last_keepalive_sent_at = nil
78
83
  end
79
84
 
80
85
  # Retrieves a custom property from this instance. This can be used to
@@ -201,7 +206,7 @@ module Net; module SSH; module Connection
201
206
 
202
207
  r = listeners.keys
203
208
  w = r.select { |w2| w2.respond_to?(:pending_write?) && w2.pending_write? }
204
- readers, writers, = Net::SSH::Compat.io_select(r, w, nil, wait)
209
+ readers, writers, = Net::SSH::Compat.io_select(r, w, nil, io_select_wait(wait))
205
210
 
206
211
  postprocess(readers, writers)
207
212
  end
@@ -239,6 +244,7 @@ module Net; module SSH; module Connection
239
244
  writer.send_pending
240
245
  end
241
246
 
247
+ send_keepalive_as_needed(readers, writers)
242
248
  transport.rekey_as_needed
243
249
 
244
250
  return true
@@ -590,6 +596,31 @@ module Net; module SSH; module Connection
590
596
  channels[packet[:local_id]].do_failure
591
597
  end
592
598
 
599
+ def io_select_wait(wait)
600
+ 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
622
+ end
623
+
593
624
  MAP = Constants.constants.inject({}) do |memo, name|
594
625
  value = const_get(name)
595
626
  next unless Integer === value
@@ -73,7 +73,7 @@ module Net; module SSH; module Proxy
73
73
  version, code, reason = socket.gets.chomp.split(/ /, 3)
74
74
  headers = {}
75
75
 
76
- while (line = socket.gets.chomp) != ""
76
+ while (line = socket.gets) && (line.chomp! != "")
77
77
  name, value = line.split(/:/, 2)
78
78
  headers[name.strip] = value.strip
79
79
  end
@@ -48,10 +48,10 @@ module Net; module SSH
48
48
  MAJOR = 2
49
49
 
50
50
  # The minor component of this version of the Net::SSH library
51
- MINOR = 6
51
+ MINOR = 7
52
52
 
53
53
  # The tiny component of this version of the Net::SSH library
54
- TINY = 8
54
+ TINY = 0
55
55
 
56
56
  # The current version of the Net::SSH library as a Version instance
57
57
  CURRENT = new(MAJOR, MINOR, TINY)
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "net-ssh"
8
- s.version = "2.6.8"
8
+ s.version = "2.7.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Jamis Buck", "Delano Mandelbaum"]
12
- s.date = "2013-07-06"
12
+ s.date = "2013-09-11"
13
13
  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."
14
14
  s.email = "net-ssh@solutious.com"
15
15
  s.extra_rdoc_files = [
@@ -126,12 +126,14 @@ Gem::Specification.new do |s|
126
126
  "test/configs/multihost",
127
127
  "test/configs/nohost",
128
128
  "test/configs/numeric_host",
129
+ "test/configs/send_env",
129
130
  "test/configs/substitutes",
130
131
  "test/configs/wild_cards",
131
132
  "test/connection/test_channel.rb",
132
133
  "test/connection/test_session.rb",
133
134
  "test/known_hosts/github",
134
135
  "test/manual/test_forward.rb",
136
+ "test/start/test_options.rb",
135
137
  "test/start/test_transport.rb",
136
138
  "test/test_all.rb",
137
139
  "test/test_buffer.rb",
@@ -0,0 +1,2 @@
1
+ Host 1234
2
+ SendEnv GIT_* LANG LC_*
@@ -363,6 +363,44 @@ module Connection
363
363
  session.process
364
364
  end
365
365
 
366
+ def test_process_should_call_enqueue_message_if_io_select_timed_out
367
+ timeout = Net::SSH::Connection::Session::DEFAULT_IO_SELECT_TIMEOUT
368
+ options = { :keepalive => true }
369
+ expected_packet = P(:byte, Net::SSH::Packet::IGNORE, :string, "keepalive")
370
+ IO.stubs(:select).with([socket],[],nil,timeout).returns(nil)
371
+ transport.expects(:enqueue_message).with{ |msg| msg.content == expected_packet.content }
372
+ session(options).process
373
+ end
374
+
375
+ def test_process_should_not_call_enqueue_message_unless_io_select_timed_out
376
+ timeout = Net::SSH::Connection::Session::DEFAULT_IO_SELECT_TIMEOUT
377
+ options = { :keepalive => true }
378
+ IO.stubs(:select).with([socket],[],nil,timeout).returns([[],[],[]])
379
+ transport.expects(:enqueue_message).never
380
+ session(options).process
381
+ end
382
+
383
+ def test_process_should_not_call_enqueue_message_unless_keepalive_interval_not_go_on
384
+ timeout = 10
385
+ options = { :keepalive => true, :keepalive_interval => timeout }
386
+ Time.stubs(:now).returns(Time.at(0), Time.at(9), Time.at(timeout))
387
+ IO.stubs(:select).with([socket],[],nil,timeout).returns(nil)
388
+ transport.expects(:enqueue_message).times(2)
389
+ 3.times { session(options).process }
390
+ end
391
+
392
+ def test_process_should_call_io_select_with_nil_as_last_arg_if_keepalive_disabled
393
+ IO.expects(:select).with([socket],[],nil,nil).returns([[],[],[]])
394
+ session.process
395
+ end
396
+
397
+ def test_process_should_call_io_select_with_interval_as_last_arg_if_keepalive_interval_passed
398
+ timeout = 10
399
+ options = { :keepalive => true, :keepalive_interval => timeout }
400
+ IO.expects(:select).with([socket],[],nil,timeout).returns([[],[],[]])
401
+ session(options).process
402
+ end
403
+
366
404
  def test_loop_should_call_process_until_process_returns_false
367
405
  IO.stubs(:select).with([socket],[],nil,nil).returns([[],[],[]])
368
406
  session.expects(:process).with(nil).times(4).returns(true,true,true,false).yields
@@ -0,0 +1,36 @@
1
+ require 'common'
2
+ require 'net/ssh'
3
+
4
+ module NetSSH
5
+ class TestStartOptions < Test::Unit::TestCase
6
+ def setup
7
+ authentication_session = mock('authentication_session')
8
+ authentication_session.stubs(:authenticate).returns(true)
9
+ Net::SSH::Authentication::Session.stubs(:new).returns(authentication_session)
10
+ Net::SSH::Transport::Session.stubs(:new).returns(mock('transport_session'))
11
+ Net::SSH::Connection::Session.stubs(:new).returns(mock('connection_session'))
12
+ end
13
+
14
+ def test_start_should_accept_keepalive_option
15
+ assert_nothing_raised do
16
+ options = { :keepalive => true }
17
+ Net::SSH.start('localhost', 'testuser', options)
18
+ end
19
+ end
20
+
21
+ def test_start_should_accept_keepalive_interval_option
22
+ assert_nothing_raised do
23
+ options = { :keepalive_interval => 10 }
24
+ Net::SSH.start('localhost', 'testuser', options)
25
+ end
26
+ end
27
+
28
+ def test_start_should_accept_send_env_option
29
+ assert_nothing_raised do
30
+ options = { :send_env => [ /^LC_.*$/, "LANG" ] }
31
+ Net::SSH.start('localhost', 'testuser', options)
32
+ end
33
+ end
34
+ end
35
+ end
36
+
@@ -86,7 +86,8 @@ class TestConfig < Test::Unit::TestCase
86
86
  'passwordauthentication' => true,
87
87
  'port' => 1234,
88
88
  'pubkeyauthentication' => true,
89
- 'rekeylimit' => 1024
89
+ 'rekeylimit' => 1024,
90
+ 'sendenv' => "LC_*"
90
91
  }
91
92
 
92
93
  net_ssh = Net::SSH::Config.translate(open_ssh)
@@ -103,6 +104,7 @@ class TestConfig < Test::Unit::TestCase
103
104
  assert_equal 1234, net_ssh[:port]
104
105
  assert_equal 1024, net_ssh[:rekey_limit]
105
106
  assert_equal "127.0.0.1", net_ssh[:bind_address]
107
+ assert_equal [/^LC_.*$/], net_ssh[:send_env]
106
108
  end
107
109
 
108
110
  def test_load_with_plus_sign_hosts
@@ -134,7 +136,13 @@ class TestConfig < Test::Unit::TestCase
134
136
  net_ssh = Net::SSH::Config.translate(config)
135
137
  assert_equal 'prefix.1234.sufix', net_ssh[:host_name]
136
138
  end
137
-
139
+
140
+ def test_load_with_send_env
141
+ config = Net::SSH::Config.load(config(:send_env), "1234")
142
+ net_ssh = Net::SSH::Config.translate(config)
143
+ assert_equal [/^GIT_.*$/, /^LANG$/, /^LC_.*$/], net_ssh[:send_env]
144
+ end
145
+
138
146
  private
139
147
 
140
148
  def config(name)
@@ -1,5 +1,6 @@
1
1
  require 'common'
2
2
  require 'net/ssh/transport/kex/diffie_hellman_group_exchange_sha1'
3
+ require 'transport/kex/test_diffie_hellman_group_exchange_sha1'
3
4
 
4
5
  module Transport; module Kex
5
6
 
@@ -3,6 +3,7 @@ require 'openssl'
3
3
  unless defined?(OpenSSL::PKey::EC)
4
4
  puts "Skipping tests for ecdh-sha2-nistp384 key exchange"
5
5
  else
6
+ require 'transport/kex/test_ecdh_sha2_nistp256'
6
7
  module Transport; module Kex
7
8
  class TestEcdhSHA2NistP384 < TestEcdhSHA2NistP256
8
9
 
@@ -3,6 +3,7 @@ require 'openssl'
3
3
  unless defined?(OpenSSL::PKey::EC)
4
4
  puts "Skipping tests for ecdh-sha2-nistp521 key exchange"
5
5
  else
6
+ require 'transport/kex/test_ecdh_sha2_nistp256'
6
7
  module Transport; module Kex
7
8
  class TestEcdhSHA2NistP521 < TestEcdhSHA2NistP256
8
9
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: net-ssh
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.8
4
+ version: 2.7.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-07-06 00:00:00.000000000 Z
13
+ date: 2013-09-11 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: test-unit
@@ -163,12 +163,14 @@ files:
163
163
  - test/configs/multihost
164
164
  - test/configs/nohost
165
165
  - test/configs/numeric_host
166
+ - test/configs/send_env
166
167
  - test/configs/substitutes
167
168
  - test/configs/wild_cards
168
169
  - test/connection/test_channel.rb
169
170
  - test/connection/test_session.rb
170
171
  - test/known_hosts/github
171
172
  - test/manual/test_forward.rb
173
+ - test/start/test_options.rb
172
174
  - test/start/test_transport.rb
173
175
  - test/test_all.rb
174
176
  - test/test_buffer.rb