net-ssh 3.0.1 → 3.0.2.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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