7
7
require 'rex/post/meterpreter/extensions/stdapi/fs/io'
8
8
require 'rex/post/meterpreter/extensions/stdapi/fs/file_stat'
9
9
require 'fileutils'
10
+ require 'filesize'
10
11
11
12
module Rex
12
13
module Post
@@ -25,6 +26,8 @@ class File < Rex::Post::Meterpreter::Extensions::Stdapi::Fs::IO
25
26
26
27
include Rex ::Post ::File
27
28
29
+ MIN_BLOCK_SIZE = 1024
30
+
28
31
class << self
29
32
attr_accessor :client
30
33
end
@@ -312,7 +315,7 @@ def File.download(dest, src_files, opts = nil, &stat)
312
315
dest += timestamp
313
316
end
314
317
315
- stat . call ( 'downloading ' , src , dest ) if ( stat )
318
+ stat . call ( 'Downloading ' , src , dest ) if ( stat )
316
319
result = download_file ( dest , src , opts , &stat )
317
320
stat . call ( result , src , dest ) if ( stat )
318
321
}
@@ -325,8 +328,11 @@ def File.download_file(dest_file, src_file, opts = nil, &stat)
325
328
continue = false
326
329
tries = false
327
330
tries_no = 0
331
+ stat ||= lambda { }
332
+
328
333
if opts
329
334
continue = true if opts [ "continue" ]
335
+ adaptive = true if opts [ 'adaptive' ]
330
336
tries = true if opts [ "tries" ]
331
337
tries_no = opts [ "tries_no" ]
332
338
end
@@ -346,17 +352,19 @@ def File.download_file(dest_file, src_file, opts = nil, &stat)
346
352
dir = ::File . dirname ( dest_file )
347
353
::FileUtils . mkdir_p ( dir ) if dir and not ::File . directory? ( dir )
348
354
355
+ src_size = Filesize . new ( src_stat . size ) . pretty
356
+
349
357
if continue
350
358
# continue downloading the file - skip downloaded part in the source
351
359
dst_fd = ::File . new ( dest_file , "ab" )
352
360
begin
353
361
dst_fd . seek ( 0 , ::IO ::SEEK_END )
354
362
in_pos = dst_fd . pos
355
363
src_fd . seek ( in_pos )
356
- stat . call ( 'continuing from ' , in_pos , src_file ) if ( stat )
364
+ stat . call ( "Continuing from #{ Filesize . new ( in_pos ) . pretty } of #{ src_size } " , src_file , dest_file )
357
365
rescue
358
366
# if we can't seek, download again
359
- stat . call ( 'error continuing - downloading from scratch' , src_file , dest_file ) if ( stat )
367
+ stat . call ( 'Error continuing - downloading from scratch' , src_file , dest_file )
360
368
dst_fd . close
361
369
dst_fd = ::File . new ( dest_file , "wb" )
362
370
end
@@ -365,41 +373,59 @@ def File.download_file(dest_file, src_file, opts = nil, &stat)
365
373
end
366
374
367
375
# Keep transferring until EOF is reached...
376
+ block_size = opts [ 'block_size' ] || 1024 * 1024
368
377
begin
369
378
if tries
370
379
# resume when timeouts encountered
371
380
seek_back = false
381
+ adjust_block = false
372
382
tries_cnt = 0
373
383
begin # while
374
384
begin # exception
375
385
if seek_back
376
386
in_pos = dst_fd . pos
377
387
src_fd . seek ( in_pos )
378
388
seek_back = false
379
- stat . call ( 'resuming at ' , in_pos , src_file ) if ( stat )
389
+ stat . call ( "Resuming at #{ Filesize . new ( in_pos ) . pretty } of #{ src_size } " , src_file , dest_file )
380
390
else
381
391
# succesfully read and wrote - reset the counter
382
392
tries_cnt = 0
383
393
end
384
- data = src_fd . read
394
+ adjust_block = true
395
+ data = src_fd . read ( block_size )
396
+ adjust_block = false
385
397
rescue Rex ::TimeoutError
386
398
# timeout encountered - either seek back and retry or quit
387
399
if ( tries && ( tries_no == 0 || tries_cnt < tries_no ) )
388
400
tries_cnt += 1
389
401
seek_back = true
390
- stat . call ( 'error downloading - retry #' , tries_cnt , src_file ) if ( stat )
402
+ # try a smaller block size for the next round
403
+ if adaptive && adjust_block
404
+ block_size = [ block_size >> 1 , MIN_BLOCK_SIZE ] . max
405
+ adjust_block = false
406
+ msg = "Error downloading, block size set to #{ block_size } - retry # #{ tries_cnt } "
407
+ stat . call ( msg , src_file , dest_file )
408
+ else
409
+ stat . call ( "Error downloading - retry # #{ tries_cnt } " , src_file , dest_file )
410
+ end
391
411
retry
392
412
else
393
- stat . call ( 'error downloading - giving up' , src_file , dest_file ) if ( stat )
413
+ stat . call ( 'Error downloading - giving up' , src_file , dest_file )
394
414
raise
395
415
end
396
416
end
397
417
dst_fd . write ( data ) if ( data != nil )
418
+ percent = dst_fd . pos . to_f / src_stat . size . to_f * 100.0
419
+ msg = "Downloaded #{ Filesize . new ( dst_fd . pos ) . pretty } of #{ src_size } (#{ percent . round ( 2 ) } %)"
420
+ stat . call ( msg , src_file , dest_file )
398
421
end while ( data != nil )
399
422
else
400
423
# do the simple copying quiting on the first error
401
- while ( ( data = src_fd . read ) != nil )
424
+ while ( ( data = src_fd . read ( block_size ) ) != nil )
402
425
dst_fd . write ( data )
426
+ percent = dst_fd . pos . to_f / src_stat . size . to_f * 100.0
427
+ msg = "Downloaded #{ Filesize . new ( dst_fd . pos ) . pretty } of #{ src_size } (#{ percent . round ( 2 ) } %)"
428
+ stat . call ( msg , src_file , dest_file )
403
429
end
404
430
end
405
431
rescue EOFError
0 commit comments