net-ssh 2.0.20 → 2.0.21

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,18 @@
1
1
 
2
+
3
+ === 2.0.21 / 20 Mar 2010
4
+
5
+ * Fix for "IdentifyFile" in ~/.ssh/config does not work if no "Host" statement is given (http://net-ssh.lighthouseapp.com/projects/36253/tickets/9-identifyfile-in-sshconfig-does-not-work-if-no-host-statement-is-given#ticket-9-5) [xbaldauf, Delano Mandelbaum]
6
+
7
+ * Fix for client closes a forwarded connection, but the server is reading, net-ssh terminates with IOError socket closed (http://net-ssh.lighthouseapp.com/projects/36253/tickets/7) [Miklós Fazekas]
8
+
9
+ * Fix for client force closes (RST) a forwarded connection, but server is reading, net-ssh terminates with exception [Miklós Fazekas]
10
+
11
+ * Fix for server closes the sending side, the on_eof is not handled. [Miklós Fazekas]
12
+
13
+ * Removed Hanna dependency in Rakefile [Delano Mandelbaum]
14
+
15
+
2
16
  === 2.0.20 / 10 Feb 2010
3
17
 
4
18
  * Support "ProxyCommand none" directive [Andy Lo-A-Foe]
@@ -20,7 +34,9 @@
20
34
  === 2.0.16 / 28 Nov 2009
21
35
 
22
36
  * Fix for "multiple hosts are separated by whitespace" [Akinori MUSHA]
37
+
23
38
  * Add support for the ProxyCommand directive [Akinori MUSHA]
39
+
24
40
  * Switched from #recv(1) to #readpartial in lib/net/ssh/transport/server_version.rb, so that closed sockets are recognized [Alex Peuchert]
25
41
 
26
42
 
data/Rakefile CHANGED
@@ -1,10 +1,16 @@
1
1
  require 'rubygems'
2
2
  require 'rake/clean'
3
3
  require 'rake/gempackagetask'
4
- require 'hanna/rdoctask'
5
4
  require 'fileutils'
6
5
  include FileUtils
7
-
6
+
7
+ begin
8
+ require 'hanna/rdoctask'
9
+ rescue LoadError
10
+ require 'rake/rdoctask'
11
+ end
12
+
13
+
8
14
  task :default => :package
9
15
 
10
16
  # CONFIG =============================================================
@@ -147,4 +147,52 @@ module Net; module SSH
147
147
  end
148
148
  end
149
149
 
150
+
151
+
152
+ # Fixes for two issues by Miklós Fazekas:
153
+ #
154
+ # * if client closes a forwarded connection, but the server is
155
+ # reading, net-ssh terminates with IOError socket closed.
156
+ # * if client force closes (RST) a forwarded connection, but
157
+ # server is reading, net-ssh terminates with [an exception]
158
+ #
159
+ # See:
160
+ #
161
+ # http://net-ssh.lighthouseapp.com/projects/36253/tickets/7
162
+ # http://github.com/net-ssh/net-ssh/tree/portfwfix
163
+ #
164
+ module ForwardedBufferedIo
165
+ def fill(n=8192)
166
+ begin
167
+ super(n)
168
+ rescue Errno::ECONNRESET => e
169
+ debug { "connection was reset => shallowing exception:#{e}" }
170
+ return 0
171
+ rescue IOError => e
172
+ if e.message =~ /closed/ then
173
+ debug { "connection was reset => shallowing exception:#{e}" }
174
+ return 0
175
+ else
176
+ raise
177
+ end
178
+ end
179
+ end
180
+
181
+ def send_pending
182
+ begin
183
+ super
184
+ rescue Errno::ECONNRESET => e
185
+ debug { "connection was reset => shallowing exception:#{e}" }
186
+ return 0
187
+ rescue IOError => e
188
+ if e.message =~ /closed/ then
189
+ debug { "connection was reset => shallowing exception:#{e}" }
190
+ return 0
191
+ else
192
+ raise
193
+ end
194
+ end
195
+ end
196
+ end
197
+
150
198
  end; end
@@ -55,12 +55,14 @@ module Net; module SSH
55
55
  # ones. Returns a hash containing the OpenSSH options. (See
56
56
  # #translate for how to convert the OpenSSH options into Net::SSH
57
57
  # options.)
58
- def load(file, host, settings={})
59
- file = File.expand_path(file)
58
+ def load(path, host, settings={})
59
+ file = File.expand_path(path)
60
60
  return settings unless File.readable?(file)
61
61
 
62
+ globals = {}
62
63
  matched_host = nil
63
64
  multi_host = []
65
+ seen_host = false
64
66
  IO.foreach(file) do |line|
65
67
  next if line =~ /^\s*(?:#.*)?$/
66
68
 
@@ -75,29 +77,37 @@ module Net; module SSH
75
77
 
76
78
  key.downcase!
77
79
  value = $1 if value =~ /^"(.*)"$/
78
-
80
+
79
81
  value = case value.strip
80
82
  when /^\d+$/ then value.to_i
81
83
  when /^no$/i then false
82
84
  when /^yes$/i then true
83
85
  else value
84
86
  end
85
-
87
+
86
88
  if key == 'host'
87
89
  # Support "Host host1 host2 hostN".
88
90
  # See http://github.com/net-ssh/net-ssh/issues#issue/6
89
91
  multi_host = value.split(/\s+/)
90
92
  matched_host = multi_host.select { |h| host =~ pattern2regex(h) }.first
93
+ seen_host = true
94
+ elsif !seen_host
95
+ if key == 'identityfile'
96
+ (globals[key] ||= []) << value
97
+ else
98
+ globals[key] = value unless settings.key?(key)
99
+ end
91
100
  elsif !matched_host.nil?
92
101
  if key == 'identityfile'
93
- settings[key] ||= []
94
- settings[key] << value
102
+ (settings[key] ||= []) << value
95
103
  else
96
104
  settings[key] = value unless settings.key?(key)
97
105
  end
98
106
  end
99
107
  end
100
108
 
109
+ settings = globals.merge(settings) if globals
110
+
101
111
  return settings
102
112
  end
103
113
 
@@ -193,21 +193,42 @@ module Net; module SSH; module Service
193
193
  end
194
194
 
195
195
  private
196
-
196
+
197
197
  # Perform setup operations that are common to all forwarded channels.
198
198
  # +client+ is a socket, +channel+ is the channel that was just created,
199
199
  # and +type+ is an arbitrary string describing the type of the channel.
200
200
  def prepare_client(client, channel, type)
201
201
  client.extend(Net::SSH::BufferedIo)
202
+ client.extend(Net::SSH::ForwardedBufferedIo)
202
203
  client.logger = logger
203
204
 
204
205
  session.listen_to(client)
205
206
  channel[:socket] = client
206
207
 
207
- channel.on_data do |ch, data|
208
+ channel.on_data do |ch, data|
209
+ debug { "data:#{data.length} on #{type} forwarded channel" }
208
210
  ch[:socket].enqueue(data)
209
211
  end
210
-
212
+
213
+ # Handles server close on the sending side by Miklós Fazekas
214
+ channel.on_eof do |ch|
215
+ debug { "eof #{type} on #{type} forwarded channel" }
216
+ begin
217
+ ch[:socket].send_pending
218
+ ch[:socket].shutdown Socket::SHUT_WR
219
+ rescue IOError => e
220
+ if e.message =~ /closed/ then
221
+ debug { "epipe in on_eof => shallowing exception:#{e}" }
222
+ else
223
+ raise
224
+ end
225
+ rescue Errno::EPIPE => e
226
+ debug { "epipe in on_eof => shallowing exception:#{e}" }
227
+ rescue Errno::ENOTCONN => e
228
+ debug { "enotconn in on_eof => shallowing exception:#{e}" }
229
+ end
230
+ end
231
+
211
232
  channel.on_close do |ch|
212
233
  debug { "closing #{type} forwarded channel" }
213
234
  ch[:socket].close if !client.closed?
@@ -51,7 +51,7 @@ module Net; module SSH
51
51
  MINOR = 0
52
52
 
53
53
  # The tiny component of this version of the Net::SSH library
54
- TINY = 20
54
+ TINY = 21
55
55
 
56
56
  # The current version of the Net::SSH library as a Version instance
57
57
  CURRENT = new(MAJOR, MINOR, TINY)
@@ -1,7 +1,7 @@
1
1
  @spec = Gem::Specification.new do |s|
2
2
  s.name = "net-ssh"
3
3
  s.rubyforge_project = 'net-ssh'
4
- s.version = "2.0.20"
4
+ s.version = "2.0.21"
5
5
  s.summary = "Net::SSH: a pure-Ruby implementation of the SSH2 client protocol."
6
6
  s.description = s.summary
7
7
  s.authors = ["Jamis Buck", "Delano Mandelbaum"]
@@ -91,6 +91,7 @@
91
91
  setup.rb
92
92
  support/arcfour_check.rb
93
93
  support/ssh_tunnel_bug.rb
94
+ test/README.txt
94
95
  test/authentication/methods/common.rb
95
96
  test/authentication/methods/test_abstract.rb
96
97
  test/authentication/methods/test_hostbased.rb
@@ -105,9 +106,11 @@
105
106
  test/configs/exact_match
106
107
  test/configs/host_plus
107
108
  test/configs/multihost
109
+ test/configs/nohost
108
110
  test/configs/wild_cards
109
111
  test/connection/test_channel.rb
110
112
  test/connection/test_session.rb
113
+ test/manual/test_forward.rb
111
114
  test/test_all.rb
112
115
  test/test_buffer.rb
113
116
  test/test_buffered_io.rb
@@ -0,0 +1,42 @@
1
+ 2010-03-16
2
+
3
+ RUNNING TESTS
4
+
5
+ Run the test suite from the net-ssh directory with the following command:
6
+
7
+ ruby -Ilib -Itest -rrubygems test/test_all.rb
8
+
9
+ Run a single test file like this:
10
+
11
+ ruby -Ilib -Itest -rrubygems test/transport/test_server_version.rb
12
+
13
+
14
+ EXPECTED RESULTS
15
+
16
+ * Ruby 1.8: all tests pass
17
+
18
+ * Ruby 1.9: all tests pass
19
+
20
+ * JRuby 1.4: 96% tests pass (242 tests, 554 assertions, 0 failures, 8 errors)
21
+
22
+
23
+ PORT FORWARDING TESTS
24
+
25
+ ruby -Ilib -Itest -rrubygems test/manual/test_forward.rb
26
+
27
+ test_forward.rb must be run separately from the test suite because
28
+ it requires authorizing your public SSH keys on you localhost.
29
+
30
+ If you already have keys you can do this:
31
+
32
+ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
33
+
34
+ If you don't have keys see:
35
+
36
+ http://kimmo.suominen.com/docs/ssh/#ssh-keygen
37
+
38
+ You should now be able to login to your localhost with out
39
+ bring prompted for a password:
40
+
41
+ ssh localhost
42
+
@@ -0,0 +1,19 @@
1
+
2
+ IdentityFile ~/.ssh/id_dsa
3
+ IdentityFile ~/.ssh/id_rsa
4
+ Port 1980
5
+
6
+ Host test.host
7
+ Port 1985
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
@@ -0,0 +1,185 @@
1
+ # $ ruby -Ilib -Itest -rrubygems test/test_forward.rb
2
+
3
+ # Tests for the following patch:
4
+ #
5
+ # http://github.com/net-ssh/net-ssh/tree/portfwfix
6
+ #
7
+ # It fixes 3 issues, regarding closing forwarded ports:
8
+ #
9
+ # 1.) if client closes a forwarded connection, but the server is reading, net-ssh terminates with IOError socket closed.
10
+ # 2.) if client force closes (RST) a forwarded connection, but server is reading, net-ssh terminates with
11
+ # 3.) if server closes the sending side, the on_eof is not handled.
12
+ #
13
+ # More info:
14
+ #
15
+ # http://net-ssh.lighthouseapp.com/projects/36253/tickets/7
16
+
17
+ require 'common'
18
+ require 'net/ssh/buffer'
19
+ require 'net/ssh'
20
+ require 'timeout'
21
+
22
+ class TestForward < Test::Unit::TestCase
23
+
24
+ def localhost
25
+ 'localhost'
26
+ end
27
+
28
+ def ssh_start_params
29
+ [localhost ,ENV['USER']] #:verbose => :debug
30
+ end
31
+
32
+ def find_free_port
33
+ server = TCPServer.open(0)
34
+ server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR,true)
35
+ port = server.addr[1]
36
+ server.close
37
+ port
38
+ end
39
+
40
+ def start_server_sending_lot_of_data(exceptions)
41
+ server = TCPServer.open(0)
42
+ Thread.start do
43
+ loop do
44
+ Thread.start(server.accept) do |client|
45
+ begin
46
+ 10000.times do |i|
47
+ client.puts "item#{i}"
48
+ end
49
+ client.close
50
+ rescue
51
+ exceptions << $!
52
+ raise
53
+ end
54
+ end
55
+ end
56
+ end
57
+ return server
58
+ end
59
+
60
+ def start_server_closing_soon(exceptions=nil)
61
+ server = TCPServer.open(0)
62
+ Thread.start do
63
+ loop do
64
+ Thread.start(server.accept) do |client|
65
+ begin
66
+ client.recv(1024)
67
+ client.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, [1, 0].pack("ii"))
68
+ client.close
69
+ rescue
70
+ exceptions << $!
71
+ raise
72
+ end
73
+ end
74
+ end
75
+ end
76
+ return server
77
+ end
78
+
79
+ def test_loop_should_not_abort_when_local_side_of_forward_is_closed
80
+ session = Net::SSH.start(*ssh_start_params)
81
+ server_exc = Queue.new
82
+ server = start_server_sending_lot_of_data(server_exc)
83
+ remote_port = server.addr[1]
84
+ local_port = find_free_port
85
+ session.forward.local(local_port, localhost, remote_port)
86
+ client_done = Queue.new
87
+ Thread.start do
88
+ begin
89
+ client = TCPSocket.new(localhost, local_port)
90
+ client.recv(1024)
91
+ client.close
92
+ sleep(0.2)
93
+ ensure
94
+ client_done << true
95
+ end
96
+ end
97
+ session.loop(0.1) { client_done.empty? }
98
+ assert_equal "Broken pipe", "#{server_exc.pop}" unless server_exc.empty?
99
+ end
100
+
101
+ def test_loop_should_not_abort_when_local_side_of_forward_is_reset
102
+ session = Net::SSH.start(*ssh_start_params)
103
+ server_exc = Queue.new
104
+ server = start_server_sending_lot_of_data(server_exc)
105
+ remote_port = server.addr[1]
106
+ local_port = find_free_port
107
+ session.forward.local(local_port, localhost, remote_port)
108
+ client_done = Queue.new
109
+ Thread.start do
110
+ begin
111
+ client = TCPSocket.new(localhost, local_port)
112
+ client.recv(1024)
113
+ client.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, [1, 0].pack("ii"))
114
+ client.close
115
+ sleep(0.1)
116
+ ensure
117
+ client_done << true
118
+ end
119
+ end
120
+ session.loop(0.1) { client_done.empty? }
121
+ assert_equal "Broken pipe", "#{server_exc.pop}" unless server_exc.empty?
122
+ end
123
+
124
+ def test_loop_should_not_abort_when_server_side_of_forward_is_closed
125
+ session = Net::SSH.start(*ssh_start_params)
126
+ server = start_server_closing_soon
127
+ remote_port = server.addr[1]
128
+ local_port = find_free_port
129
+ session.forward.local(local_port, localhost, remote_port)
130
+ client_done = Queue.new
131
+ Thread.start do
132
+ begin
133
+ client = TCPSocket.new(localhost, local_port)
134
+ 1.times do |i|
135
+ client.puts "item#{i}"
136
+ end
137
+ client.close
138
+ sleep(0.1)
139
+ ensure
140
+ client_done << true
141
+ end
142
+ end
143
+ session.loop(0.1) { client_done.empty? }
144
+ end
145
+
146
+ def start_server
147
+ server = TCPServer.open(0)
148
+ Thread.start do
149
+ loop do
150
+ Thread.start(server.accept) do |client|
151
+ yield(client)
152
+ end
153
+ end
154
+ end
155
+ return server
156
+ end
157
+
158
+ def test_server_eof_should_be_handled
159
+ session = Net::SSH.start(*ssh_start_params)
160
+ server = start_server do |client|
161
+ client.write "This is a small message!"
162
+ client.close
163
+ end
164
+ client_done = Queue.new
165
+ client_exception = Queue.new
166
+ client_data = Queue.new
167
+ remote_port = server.addr[1]
168
+ local_port = find_free_port
169
+ session.forward.local(local_port, localhost, remote_port)
170
+ Thread.start do
171
+ begin
172
+ client = TCPSocket.new(localhost, local_port)
173
+ data = client.read(4096)
174
+ client.close
175
+ client_done << data
176
+ rescue
177
+ client_done << $!
178
+ end
179
+ end
180
+ timeout(5) do
181
+ session.loop(0.1) { client_done.empty? }
182
+ assert_equal "This is a small message!", client_done.pop
183
+ end
184
+ end
185
+ end
@@ -2,6 +2,7 @@
2
2
  # $ ruby -Ilib -Itest -rrubygems test/transport/test_server_version.rb
3
3
  Dir.chdir(File.dirname(__FILE__)) do
4
4
  test_files = Dir['**/test_*.rb']
5
+ test_files = test_files.reject { |f| f =~ /^manual/ }
5
6
  test_files = test_files.select { |f| f =~ Regexp.new(ENV['ONLY']) } if ENV['ONLY']
6
7
  test_files = test_files.reject { |f| f =~ Regexp.new(ENV['EXCEPT']) } if ENV['EXCEPT']
7
8
  test_files.each { |file| require(file) }
@@ -38,6 +38,12 @@ class TestConfig < Test::Unit::TestCase
38
38
  assert !config.key?(:rekey_limit)
39
39
  end
40
40
 
41
+ def test_load_with_no_host
42
+ config = Net::SSH::Config.load(config(:nohost), "test.host")
43
+ assert_equal %w(~/.ssh/id_dsa ~/.ssh/id_rsa), config['identityfile']
44
+ assert_equal 1985, config['port']
45
+ end
46
+
41
47
  def test_load_with_multiple_hosts
42
48
  config = Net::SSH::Config.load(config(:multihost), "test.host")
43
49
  assert config['compression']
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.0.20
4
+ version: 2.0.21
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jamis Buck
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2010-02-10 00:00:00 -05:00
13
+ date: 2010-03-20 00:00:00 -04:00
14
14
  default_executable:
15
15
  dependencies: []
16
16
 
@@ -99,6 +99,7 @@ files:
99
99
  - setup.rb
100
100
  - support/arcfour_check.rb
101
101
  - support/ssh_tunnel_bug.rb
102
+ - test/README.txt
102
103
  - test/authentication/methods/common.rb
103
104
  - test/authentication/methods/test_abstract.rb
104
105
  - test/authentication/methods/test_hostbased.rb
@@ -113,9 +114,11 @@ files:
113
114
  - test/configs/exact_match
114
115
  - test/configs/host_plus
115
116
  - test/configs/multihost
117
+ - test/configs/nohost
116
118
  - test/configs/wild_cards
117
119
  - test/connection/test_channel.rb
118
120
  - test/connection/test_session.rb
121
+ - test/manual/test_forward.rb
119
122
  - test/test_all.rb
120
123
  - test/test_buffer.rb
121
124
  - test/test_buffered_io.rb