net-ssh 3.0.1 → 3.0.2.rc1

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.
@@ -0,0 +1,435 @@
1
+ # $ ruby -Ilib -Itest -rrubygems test/manual/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_relative './common'
18
+ require 'net/ssh/buffer'
19
+ require 'net/ssh'
20
+ require 'timeout'
21
+ require 'tempfile'
22
+
23
+ class TestForward < Test::Unit::TestCase
24
+ include IntegrationTestHelpers
25
+
26
+ def localhost
27
+ 'localhost'
28
+ end
29
+
30
+ def user
31
+ 'net_ssh_1'
32
+ end
33
+
34
+ def ssh_start_params
35
+ [localhost ,user , {:keys => @key_id_rsa, :verbose => :debug}]
36
+ end
37
+
38
+ def setup_ssh_env(&block)
39
+ tmpdir do |dir|
40
+ @key_id_rsa = "#{dir}/id_rsa"
41
+ sh "rm -rf #{@key_id_rsa} #{@key_id_rsa}.pub"
42
+ sh "ssh-keygen -f #{@key_id_rsa} -t rsa -N ''"
43
+ set_authorized_key(user,"#{@key_id_rsa}.pub")
44
+ yield
45
+ end
46
+ end
47
+
48
+ def start_server_sending_lot_of_data(exceptions)
49
+ server = TCPServer.open(0)
50
+ Thread.start do
51
+ loop do
52
+ Thread.start(server.accept) do |client|
53
+ begin
54
+ 10000.times do |i|
55
+ client.puts "item#{i}"
56
+ end
57
+ client.close
58
+ rescue
59
+ exceptions << $!
60
+ raise
61
+ end
62
+ end
63
+ end
64
+ end
65
+ return server
66
+ end
67
+
68
+ def start_server_closing_soon(exceptions=nil)
69
+ server = TCPServer.open(0)
70
+ Thread.start do
71
+ loop do
72
+ Thread.start(server.accept) do |client|
73
+ begin
74
+ client.recv(1024)
75
+ client.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, [1, 0].pack("ii"))
76
+ client.close
77
+ rescue
78
+ exceptions << $!
79
+ raise
80
+ end
81
+ end
82
+ end
83
+ end
84
+ return server
85
+ end
86
+
87
+ def test_in_file_no_password
88
+ setup_ssh_env do
89
+ ret = Net::SSH.start(*ssh_start_params) do |ssh|
90
+ #ret = Net::SSH.start("localhost", "net_ssh_1", {keys: @key_id_rsa}) do |ssh|
91
+ ssh.exec! 'echo "hello from:$USER"'
92
+ end
93
+ assert_equal "hello from:net_ssh_1\n", ret
94
+ end
95
+ end
96
+
97
+ def test_local_ephemeral_port_should_work_correctly
98
+ setup_ssh_env do
99
+ session = Net::SSH.start(*ssh_start_params)
100
+
101
+ assert_nothing_raised do
102
+ assigned_port = session.forward.local(0, localhost, 22)
103
+ assert_not_nil assigned_port
104
+ assert_operator assigned_port, :>, 0
105
+ end
106
+ end
107
+ end
108
+
109
+ def test_remote_ephemeral_port_should_work_correctly
110
+ setup_ssh_env do
111
+ session = Net::SSH.start(*ssh_start_params)
112
+
113
+ assert_nothing_raised do
114
+ session.forward.remote(22, localhost, 0, localhost)
115
+ session.loop { !(session.forward.active_remotes.length > 0) }
116
+ assigned_port = session.forward.active_remotes.first[0]
117
+ assert_not_nil assigned_port
118
+ assert_operator assigned_port, :>, 0
119
+ end
120
+ end
121
+ end
122
+
123
+ def test_remote_callback_should_fire
124
+ setup_ssh_env do
125
+ session = Net::SSH.start(*ssh_start_params)
126
+
127
+ assert_nothing_raised do
128
+ got_port = nil
129
+ session.forward.remote(22, localhost, 0, localhost) do |port|
130
+ got_port = port
131
+ end
132
+ session.loop { !(session.forward.active_remotes.length > 0) }
133
+ assert_operator session.forward.active_remote_destinations.length, :==, 1
134
+ assert_operator session.forward.active_remote_destinations.keys.first, :==, [ 22, localhost ]
135
+ assert_operator session.forward.active_remote_destinations.values.first, :==, [ got_port, localhost ]
136
+ assert_operator session.forward.active_remotes.first, :==, [ got_port, localhost ]
137
+ assigned_port = session.forward.active_remotes.first[0]
138
+ assert_operator got_port, :==, assigned_port
139
+ assert_not_nil assigned_port
140
+ assert_operator assigned_port, :>, 0
141
+ end
142
+ end
143
+ end
144
+
145
+ def test_remote_callback_should_fire_on_error_and_still_throw_exception
146
+ setup_ssh_env do
147
+ session = Net::SSH.start(*ssh_start_params)
148
+
149
+ assert_nothing_raised do
150
+ session.forward.remote(22, localhost, 22, localhost) do |port|
151
+ assert_operator port, :==, :error
152
+ end
153
+ end
154
+ assert_raises(Net::SSH::Exception) do
155
+ session.loop { true }
156
+ end
157
+ end
158
+ end
159
+
160
+ def test_remote_callback_should_fire_on_error_but_not_throw_exception_if_asked_not_to
161
+ setup_ssh_env do
162
+ session = Net::SSH.start(*ssh_start_params)
163
+
164
+ assert_nothing_raised do
165
+ got_port = nil
166
+ session.forward.remote(22, localhost, 22, localhost) do |port|
167
+ assert_operator port, :==, :error
168
+ got_port = port
169
+ :no_exception
170
+ end
171
+ session.loop { !got_port }
172
+ assert_operator got_port, :==, :error
173
+ assert_operator session.forward.active_remotes.length, :==, 0
174
+ end
175
+ end
176
+ end
177
+
178
+ def test_loop_should_not_abort_when_local_side_of_forward_is_closed
179
+ setup_ssh_env do
180
+ session = Net::SSH.start(*ssh_start_params)
181
+ server_exc = Queue.new
182
+ server = start_server_sending_lot_of_data(server_exc)
183
+ remote_port = server.addr[1]
184
+ local_port = 0 # request ephemeral port
185
+ session.forward.local(local_port, localhost, remote_port)
186
+ client_done = Queue.new
187
+ Thread.start do
188
+ begin
189
+ client = TCPSocket.new(localhost, local_port)
190
+ client.recv(1024)
191
+ client.close
192
+ sleep(0.2)
193
+ ensure
194
+ client_done << true
195
+ end
196
+ end
197
+ session.loop(0.1) { client_done.empty? }
198
+ assert_equal "Broken pipe", "#{server_exc.pop}" unless server_exc.empty?
199
+ end
200
+ end
201
+
202
+ def test_loop_should_not_abort_when_local_side_of_forward_is_reset
203
+ setup_ssh_env do
204
+ session = Net::SSH.start(*ssh_start_params)
205
+ server_exc = Queue.new
206
+ server = start_server_sending_lot_of_data(server_exc)
207
+ remote_port = server.addr[1]
208
+ local_port = 0 # request ephemeral port
209
+ session.forward.local(local_port, localhost, remote_port)
210
+ client_done = Queue.new
211
+ Thread.start do
212
+ begin
213
+ client = TCPSocket.new(localhost, local_port)
214
+ client.recv(1024)
215
+ client.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, [1, 0].pack("ii"))
216
+ client.close
217
+ sleep(0.1)
218
+ ensure
219
+ client_done << true
220
+ end
221
+ end
222
+ session.loop(0.1) { client_done.empty? }
223
+ assert_equal "Broken pipe", "#{server_exc.pop}" unless server_exc.empty?
224
+ end
225
+ end
226
+
227
+ def create_local_socket(&blk)
228
+ tempfile = Tempfile.new("net_ssh_forward_test")
229
+ path = tempfile.path
230
+ tempfile.delete
231
+ yield UNIXServer.open(path)
232
+ File.delete(path)
233
+ end if defined?(UNIXServer)
234
+
235
+ def test_forward_local_unix_socket_to_remote_port
236
+ setup_ssh_env do
237
+ session = Net::SSH.start(*ssh_start_params)
238
+ server_exc = Queue.new
239
+ server = start_server_sending_lot_of_data(server_exc)
240
+ remote_port = server.addr[1]
241
+ client_data = nil
242
+
243
+ create_local_socket do |local_socket|
244
+ session.forward.local(local_socket, localhost, remote_port)
245
+ client_done = Queue.new
246
+
247
+ Thread.start do
248
+ begin
249
+ client = UNIXSocket.new(local_socket.path)
250
+ client_data = client.recv(1024)
251
+ client.close
252
+ sleep(0.2)
253
+ ensure
254
+ client_done << true
255
+ end
256
+ end
257
+
258
+ session.loop(0.1) { client_done.empty? }
259
+ end
260
+
261
+ assert_not_nil(client_data, "client should have received data")
262
+ assert(client_data.match(/item\d/), 'client should have received the string item')
263
+ end
264
+ end if defined?(UNIXSocket)
265
+
266
+ def test_loop_should_not_abort_when_server_side_of_forward_is_closed
267
+ setup_ssh_env do
268
+ session = Net::SSH.start(*ssh_start_params)
269
+ server = start_server_closing_soon
270
+ remote_port = server.addr[1]
271
+ local_port = 0 # request ephemeral port
272
+ session.forward.local(local_port, localhost, remote_port)
273
+ client_done = Queue.new
274
+ Thread.start do
275
+ begin
276
+ client = TCPSocket.new(localhost, local_port)
277
+ 1.times do |i|
278
+ client.puts "item#{i}"
279
+ end
280
+ client.close
281
+ sleep(0.1)
282
+ ensure
283
+ client_done << true
284
+ end
285
+ end
286
+ session.loop(0.1) { client_done.empty? }
287
+ end
288
+ end
289
+
290
+ def start_server
291
+ server = TCPServer.open(0)
292
+ Thread.start do
293
+ loop do
294
+ Thread.start(server.accept) do |client|
295
+ yield(client)
296
+ end
297
+ end
298
+ end
299
+ return server
300
+ end
301
+
302
+ def test_client_close_should_be_handled_remote
303
+ setup_ssh_env do
304
+ message = "This is a small message!"*1000
305
+ session = Net::SSH.start(*ssh_start_params)
306
+ server_done = Queue.new
307
+ server = start_server do |client|
308
+ begin
309
+ data = client.read message.size
310
+ server_done << data
311
+ client.close
312
+ rescue
313
+ server_done << $!
314
+ end
315
+ end
316
+ client_done = Queue.new
317
+ got_remote_port = Queue.new
318
+ local_port = server.addr[1]
319
+ session.forward.remote(0, localhost, local_port) do |actual_remote_port|
320
+ got_remote_port << actual_remote_port
321
+ end
322
+ session.loop(0.1) { got_remote_port.empty? }
323
+ remote_port = got_remote_port.pop
324
+ Thread.start do
325
+ begin
326
+ client = TCPSocket.new(localhost, remote_port)
327
+ client.write(message)
328
+ client.close
329
+ client_done << true
330
+ rescue
331
+ client_done << $!
332
+ end
333
+ end
334
+ timeout(5) do
335
+ session.loop(0.1) { server_done.empty? }
336
+ assert_equal message, server_done.pop
337
+ end
338
+ end
339
+ end
340
+
341
+ def test_client_close_should_be_handled
342
+ setup_ssh_env do
343
+ message = "This is a small message!"*1000
344
+ session = Net::SSH.start(*ssh_start_params)
345
+ server_done = Queue.new
346
+ server = start_server do |client|
347
+ begin
348
+ data = client.read message.size
349
+ server_done << data
350
+ client.close
351
+ rescue
352
+ server_done << $!
353
+ end
354
+ end
355
+ client_done = Queue.new
356
+ remote_port = server.addr[1]
357
+ local_port = session.forward.local(0, localhost, remote_port)
358
+ Thread.start do
359
+ begin
360
+ client = TCPSocket.new(localhost, local_port)
361
+ client.write(message)
362
+ client.close
363
+ client_done << true
364
+ rescue
365
+ client_done << $!
366
+ end
367
+ end
368
+ timeout(5) do
369
+ session.loop(0.1) { server_done.empty? }
370
+ assert_equal message, server_done.pop
371
+ end
372
+ end
373
+ end
374
+
375
+ def test_server_eof_should_be_handled_remote
376
+ setup_ssh_env do
377
+ message = "This is a small message!"
378
+ session = Net::SSH.start(*ssh_start_params)
379
+ server = start_server do |client|
380
+ client.write message
381
+ client.close
382
+ end
383
+ client_done = Queue.new
384
+ got_remote_port = Queue.new
385
+ local_port = server.addr[1]
386
+ session.forward.remote(0, localhost, local_port) do |actual_remote_port|
387
+ got_remote_port << actual_remote_port
388
+ end
389
+ session.loop(0.1) { got_remote_port.empty? }
390
+ remote_port = got_remote_port.pop
391
+ Thread.start do
392
+ begin
393
+ client = TCPSocket.new(localhost, remote_port)
394
+ data = client.read(4096)
395
+ client.close
396
+ client_done << data
397
+ rescue
398
+ client_done << $!
399
+ end
400
+ end
401
+ timeout(5) do
402
+ session.loop(0.1) { client_done.empty? }
403
+ assert_equal message, client_done.pop
404
+ end
405
+ end
406
+ end
407
+
408
+ def test_server_eof_should_be_handled
409
+ setup_ssh_env do
410
+ message = "This is a small message!"
411
+ session = Net::SSH.start(*ssh_start_params)
412
+ server = start_server do |client|
413
+ client.write message
414
+ client.close
415
+ end
416
+ client_done = Queue.new
417
+ remote_port = server.addr[1]
418
+ local_port = session.forward.local(0, localhost, remote_port)
419
+ Thread.start do
420
+ begin
421
+ client = TCPSocket.new(localhost, local_port)
422
+ data = client.read(4096)
423
+ client.close
424
+ client_done << data
425
+ rescue
426
+ client_done << $!
427
+ end
428
+ end
429
+ timeout(5) do
430
+ session.loop(0.1) { client_done.empty? }
431
+ assert_equal message, client_done.pop
432
+ end
433
+ end
434
+ end
435
+ end
@@ -10,12 +10,6 @@ require 'net/ssh'
10
10
  class TestIDRSAPKeys < Test::Unit::TestCase
11
11
  include IntegrationTestHelpers
12
12
 
13
- def tmpdir(&block)
14
- Dir.mktmpdir do |dir|
15
- yield(dir)
16
- end
17
- end
18
-
19
13
  def test_in_file_no_password
20
14
  tmpdir do |dir|
21
15
  sh "rm -rf #{dir}/id_rsa #{dir}/id_rsa.pub"
@@ -81,4 +75,22 @@ class TestIDRSAPKeys < Test::Unit::TestCase
81
75
  assert_equal "hello from:net_ssh_1\n", ret
82
76
  end
83
77
  end
78
+
79
+ def test_asks_for_passwords_when_read_from_memory
80
+ tmpdir do |dir|
81
+ sh "rm -rf #{dir}/id_rsa #{dir}/id_rsa.pub"
82
+ sh "ssh-keygen -f #{dir}/id_rsa -t rsa -N 'pwd12'"
83
+ set_authorized_key('net_ssh_1',"#{dir}/id_rsa.pub")
84
+ private_key = File.read("#{dir}/id_rsa")
85
+
86
+ options = {keys: [], key_data: [private_key]}
87
+
88
+ key_manager = Net::SSH::Authentication::KeyManager.new(nil, options)
89
+
90
+ Net::SSH::KeyFactory.expects(:prompt).with('Enter passphrase for :', false).returns('pwd12')
91
+ Net::SSH.start("localhost", "net_ssh_1", options) do |ssh|
92
+ ssh.exec! 'whoami'
93
+ end
94
+ end
95
+ end
84
96
  end