librex 0.0.1 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/README +4 -0
  2. data/lib/rex/exploitation/cmdstager.rb +9 -133
  3. data/lib/rex/exploitation/cmdstager/base.rb +170 -0
  4. data/lib/rex/exploitation/cmdstager/debug_asm.rb +142 -0
  5. data/lib/rex/exploitation/cmdstager/debug_write.rb +136 -0
  6. data/lib/rex/exploitation/cmdstager/tftp.rb +63 -0
  7. data/lib/rex/exploitation/cmdstager/vbs.rb +128 -0
  8. data/lib/rex/io/stream.rb +2 -2
  9. data/lib/rex/io/stream_server.rb +1 -1
  10. data/lib/rex/job_container.rb +7 -6
  11. data/lib/rex/mime/header.rb +12 -10
  12. data/lib/rex/mime/message.rb +57 -26
  13. data/lib/rex/ole/directory.rb +5 -4
  14. data/lib/rex/ole/samples/create_ole.rb +0 -0
  15. data/lib/rex/ole/samples/dir.rb +0 -0
  16. data/lib/rex/ole/samples/dump_stream.rb +1 -1
  17. data/lib/rex/ole/samples/ole_info.rb +0 -0
  18. data/lib/rex/parser/nexpose_xml.rb +131 -0
  19. data/lib/rex/parser/nmap_xml.rb +1 -0
  20. data/lib/rex/peparsey/pe.rb +21 -3
  21. data/lib/rex/post/meterpreter/client.rb +6 -1
  22. data/lib/rex/post/meterpreter/client_core.rb +2 -2
  23. data/lib/rex/post/meterpreter/extensions/priv/priv.rb +19 -18
  24. data/lib/rex/post/meterpreter/packet.rb +68 -0
  25. data/lib/rex/post/meterpreter/packet_dispatcher.rb +2 -2
  26. data/lib/rex/post/meterpreter/packet_response_waiter.rb +5 -5
  27. data/lib/rex/post/meterpreter/ui/console.rb +2 -0
  28. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb +5 -2
  29. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/sys.rb +2 -2
  30. data/lib/rex/proto/dcerpc/client.rb.ut.rb +0 -0
  31. data/lib/rex/proto/http/client.rb +8 -3
  32. data/lib/rex/proto/http/packet.rb +11 -1
  33. data/lib/rex/proto/smb/client.rb +1 -1
  34. data/lib/rex/proto/smb/utils.rb +72 -24
  35. data/lib/rex/proto/tftp.rb +3 -0
  36. data/lib/rex/proto/tftp/constants.rb +37 -0
  37. data/lib/rex/proto/tftp/server.rb +249 -0
  38. data/lib/rex/proto/tftp/server.rb.ut.rb +28 -0
  39. data/lib/rex/script/meterpreter.rb +6 -0
  40. data/lib/rex/services/local_relay.rb +2 -2
  41. data/lib/rex/socket/ip.rb +9 -8
  42. data/lib/rex/socket/range_walker.rb +43 -5
  43. data/lib/rex/socket/udp.rb +11 -4
  44. data/lib/rex/text.rb +42 -19
  45. data/lib/rex/ui/interactive.rb +24 -22
  46. data/lib/rex/ui/text/irb_shell.rb +4 -2
  47. data/lib/rex/ui/text/output/file.rb +6 -0
  48. data/lib/rex/ui/text/shell.rb +14 -18
  49. data/lib/rex/zip/samples/comment.rb +0 -0
  50. data/lib/rex/zip/samples/mkwar.rb +0 -0
  51. data/lib/rex/zip/samples/mkzip.rb +0 -0
  52. data/lib/rex/zip/samples/recursive.rb +0 -0
  53. metadata +20 -5
@@ -24,6 +24,17 @@ TLV_META_TYPE_COMPRESSED = (1 << 29)
24
24
  TLV_META_TYPE_GROUP = (1 << 30)
25
25
  TLV_META_TYPE_COMPLEX = (1 << 31)
26
26
 
27
+ # Exclude compressed from the mask since other meta types (e.g. RAW) can also
28
+ # be compressed
29
+ TLV_META_MASK = (
30
+ TLV_META_TYPE_STRING |
31
+ TLV_META_TYPE_UINT |
32
+ TLV_META_TYPE_RAW |
33
+ TLV_META_TYPE_BOOL |
34
+ TLV_META_TYPE_GROUP |
35
+ TLV_META_TYPE_COMPLEX
36
+ )
37
+
27
38
  #
28
39
  # TLV base starting points
29
40
  #
@@ -114,6 +125,63 @@ class Tlv
114
125
  end
115
126
  end
116
127
 
128
+ def inspect
129
+ utype = type ^ TLV_META_TYPE_COMPRESSED
130
+ meta = case (utype & TLV_META_MASK)
131
+ when TLV_META_TYPE_STRING; "STRING"
132
+ when TLV_META_TYPE_UINT; "INT"
133
+ when TLV_META_TYPE_RAW; "RAW"
134
+ when TLV_META_TYPE_BOOL; "BOOL"
135
+ when TLV_META_TYPE_GROUP; "GROUP"
136
+ when TLV_META_TYPE_COMPLEX; "COMPLEX"
137
+ else; 'unknown-meta-type'
138
+ end
139
+ stype = case type
140
+ when TLV_TYPE_REQUEST_ID; "REQUEST-ID"
141
+ when TLV_TYPE_METHOD; "METHOD"
142
+ when TLV_TYPE_RESULT; "RESULT"
143
+ when TLV_TYPE_EXCEPTION; "EXCEPTION"
144
+ when TLV_TYPE_STRING; "STRING"
145
+ when TLV_TYPE_UINT; "UINT"
146
+ when TLV_TYPE_BOOL; "BOOL"
147
+
148
+ when TLV_TYPE_LENGTH; "LENGTH"
149
+ when TLV_TYPE_DATA; "DATA"
150
+ when TLV_TYPE_FLAGS; "FLAGS"
151
+
152
+ when TLV_TYPE_CHANNEL_ID; "CHANNEL-ID"
153
+ when TLV_TYPE_CHANNEL_TYPE; "CHANNEL-TYPE"
154
+ when TLV_TYPE_CHANNEL_DATA; "CHANNEL-DATA"
155
+ when TLV_TYPE_CHANNEL_DATA_GROUP; "CHANNEL-DATA-GROUP"
156
+ when TLV_TYPE_CHANNEL_CLASS; "CHANNEL-CLASS"
157
+ when TLV_TYPE_CHANNEL_PARENTID; "CHANNEL-PARENTID"
158
+
159
+ when TLV_TYPE_SEEK_WHENCE; "SEEK-WHENCE"
160
+ when TLV_TYPE_SEEK_OFFSET; "SEEK-OFFSET"
161
+ when TLV_TYPE_SEEK_POS; "SEEK-POS"
162
+
163
+ when TLV_TYPE_EXCEPTION_CODE; "EXCEPTION-CODE"
164
+ when TLV_TYPE_EXCEPTION_STRING; "EXCEPTION-STRING"
165
+
166
+ when TLV_TYPE_LIBRARY_PATH; "LIBRARY-PATH"
167
+ when TLV_TYPE_TARGET_PATH; "TARGET-PATH"
168
+ when TLV_TYPE_MIGRATE_PID; "MIGRATE-PID"
169
+ when TLV_TYPE_MIGRATE_LEN; "MIGRATE-LEN"
170
+ when TLV_TYPE_MIGRATE_PAYLOAD; "MIGRATE-PAYLOAD"
171
+ when TLV_TYPE_MIGRATE_ARCH; "MIGRATE-ARCH"
172
+
173
+ # Extension classes don't exist yet, so can't use their constants
174
+ # here.
175
+ #when Extensions::Stdapi::TLV_TYPE_IP; 'ip-address'
176
+ else; "unknown-#{type}"
177
+ end
178
+ val = value.inspect
179
+ if val.length > 50
180
+ val = val[0,50] + ' ..."'
181
+ end
182
+ "#<#{self.class} type=#{stype} meta-type=#{meta} #{self.class.to_s =~ /Packet/ ? "tlvs=#{@tlvs.inspect}" : "value=#{val}"} >"
183
+ end
184
+
117
185
  ##
118
186
  #
119
187
  # Conditionals
@@ -71,7 +71,7 @@ module PacketDispatcher
71
71
  response = send_packet_wait_response(packet, t)
72
72
 
73
73
  if (response == nil)
74
- raise TimeoutError
74
+ raise TimeoutError.new("Send timed out")
75
75
  elsif (response.result != 0)
76
76
  e = RequestError.new(packet.method, response.result)
77
77
 
@@ -186,7 +186,7 @@ module PacketDispatcher
186
186
  # thread above.
187
187
  while(not @finish)
188
188
  if(@pqueue.empty?)
189
- select(nil, nil, nil, 0.10)
189
+ ::IO.select(nil, nil, nil, 0.10)
190
190
  next
191
191
  end
192
192
 
@@ -59,19 +59,19 @@ class PacketResponseWaiter
59
59
  def wait(interval)
60
60
  if( interval and interval == -1 )
61
61
  while(not self.done)
62
- select(nil, nil, nil, 0.1)
63
- end
64
- else
62
+ ::IO.select(nil, nil, nil, 0.1)
63
+ end
64
+ else
65
65
  begin
66
66
  Timeout.timeout(interval) {
67
67
  while(not self.done)
68
- select(nil, nil, nil, 0.1)
68
+ ::IO.select(nil, nil, nil, 0.1)
69
69
  end
70
70
  }
71
71
  rescue Timeout::Error
72
72
  self.response = nil
73
73
  end
74
- end
74
+ end
75
75
  return self.response
76
76
  end
77
77
 
@@ -53,6 +53,8 @@ class Console
53
53
  # assumed that init_ui has been called prior.
54
54
  #
55
55
  def interact(&block)
56
+ init_tab_complete
57
+
56
58
  # Run queued commands
57
59
  commands.delete_if { |ent|
58
60
  run_single(ent)
@@ -86,8 +86,11 @@ class Console::CommandDispatcher::Stdapi::Fs
86
86
  print_line("Usage: cd directory")
87
87
  return true
88
88
  end
89
-
90
- client.fs.dir.chdir(args[0])
89
+ if args[0] =~ /\%(\w*)\%/
90
+ client.fs.dir.chdir(client.fs.file.expand_path(args[0].upcase))
91
+ else
92
+ client.fs.dir.chdir(args[0])
93
+ end
91
94
 
92
95
  return true
93
96
  end
@@ -460,8 +460,8 @@ class Console::CommandDispatcher::Stdapi::Sys
460
460
 
461
461
  print_line("Computer: " + info['Computer'])
462
462
  print_line("OS : " + info['OS'])
463
- print_line("Arch : " + info['Architecture'])
464
- print_line("Language: " + info['System Language'])
463
+ print_line("Arch : " + info['Architecture']) if info['Architecture']
464
+ print_line("Language: " + info['System Language']) if info['System Language']
465
465
 
466
466
  return true
467
467
  end
File without changes
@@ -301,15 +301,18 @@ class Client
301
301
  end
302
302
 
303
303
  #
304
- # Transmit a HTTP request and receive the response
304
+ # Transmit an HTTP request and receive the response
305
+ # If persist is set, then the request will attempt
306
+ # to reuse an existing connection.
305
307
  #
306
- def send_recv(req, t = -1)
308
+ def send_recv(req, t = -1, persist=false)
309
+ @pipeline = persist
307
310
  send_request(req)
308
311
  read_response(t)
309
312
  end
310
313
 
311
314
  #
312
- # Send a HTTP request to the server
315
+ # Send an HTTP request to the server
313
316
  #
314
317
  def send_request(req)
315
318
  connect
@@ -362,10 +365,12 @@ class Client
362
365
  rblob = rbody.to_s + rbufq.to_s
363
366
  tries = 0
364
367
  begin
368
+ # XXX This doesn't deal with chunked encoding or "Content-type: text/html; charset=..."
365
369
  while tries < 20 and resp.headers["Content-Type"]== "text/html" and rblob !~ /<\/html>/i
366
370
  buff = conn.get_once(-1, 0.05)
367
371
  break if not buff
368
372
  rblob += buff
373
+ tries += 1
369
374
  end
370
375
  rescue ::Errno::EPIPE, ::EOFError, ::IOError
371
376
  end
@@ -92,6 +92,7 @@ class Packet
92
92
  end
93
93
  end
94
94
  rescue
95
+ # This rescue might be a problem because it will swallow TimeoutError
95
96
  self.error = $!
96
97
  return ParseCode::Error
97
98
  end
@@ -331,8 +332,17 @@ protected
331
332
  # Remove any leading newlines or spaces
332
333
  self.bufq.lstrip!
333
334
 
335
+ # If we didn't get a newline, then this might not be the full
336
+ # length, go back and get more.
337
+ # e.g.
338
+ # first packet: "200"
339
+ # second packet: "0\r\n\r\n<html>..."
340
+ if not bufq.index("\n")
341
+ return
342
+ end
343
+
334
344
  # Extract the actual hexadecimal length value
335
- clen = self.bufq.slice!(/^[a-zA-Z0-9]*\r?\n/)
345
+ clen = self.bufq.slice!(/^[a-fA-F0-9]+\r?\n/)
336
346
 
337
347
  clen.rstrip! if (clen)
338
348
 
@@ -96,7 +96,7 @@ EVADE = Rex::Proto::SMB::Evasions
96
96
  while ( (chunk = data.slice!(0, size)).length > 0 )
97
97
  ret = self.socket.put(chunk)
98
98
  if (wait > 0)
99
- select(nil, nil, nil, wait)
99
+ ::IO.select(nil, nil, nil, wait)
100
100
  end
101
101
  end
102
102
  return ret
@@ -347,26 +347,74 @@ CONST = Rex::Proto::SMB::Constants
347
347
  #
348
348
  # Process Type 3 NTLM Message (in Base64)
349
349
  #
350
+ # from http://www.innovation.ch/personal/ronald/ntlm.html
351
+ #
352
+ # struct {
353
+ # byte protocol[8]; // 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0'
354
+ # byte type; // 0x03
355
+ # byte zero[3];
356
+ #
357
+ # short lm_resp_len; // LanManager response length (always 0x18)
358
+ # short lm_resp_len; // LanManager response length (always 0x18)
359
+ # short lm_resp_off; // LanManager response offset
360
+ # byte zero[2];
361
+ #
362
+ # short nt_resp_len; // NT response length (always 0x18)
363
+ # short nt_resp_len; // NT response length (always 0x18)
364
+ # short nt_resp_off; // NT response offset
365
+ # byte zero[2];
366
+ #
367
+ # short dom_len; // domain string length
368
+ # short dom_len; // domain string length
369
+ # short dom_off; // domain string offset (always 0x40)
370
+ # byte zero[2];
371
+ #
372
+ # short user_len; // username string length
373
+ # short user_len; // username string length
374
+ # short user_off; // username string offset
375
+ # byte zero[2];
376
+ #
377
+ # short host_len; // host string length
378
+ # short host_len; // host string length
379
+ # short host_off; // host string offset
380
+ # byte zero[6];
381
+ #
382
+ # short msg_len; // message length
383
+ # byte zero[2];
384
+ #
385
+ # short flags; // 0x8201
386
+ # byte zero[2];
387
+ #
388
+ # byte dom[*]; // domain string (unicode UTF-16LE)
389
+ # byte user[*]; // username string (unicode UTF-16LE)
390
+ # byte host[*]; // host string (unicode UTF-16LE)
391
+ # byte lm_resp[*]; // LanManager response
392
+ # byte nt_resp[*]; // NT response
393
+ # } type_3_message
394
+ #
350
395
  def self.process_type3_message(message)
351
396
  decode = Rex::Text.decode_base64(message.strip)
352
- type = decode[8]
397
+ type = decode[8,1].unpack("C").first
353
398
  if (type == 3)
354
- domoff = decode[32] # domain offset
355
- domlen = decode[28] # domain length
356
- useroff = decode[40] # username offset
357
- userlen = decode[36] # username length
358
- hostoff = decode[48] # hostname offset
359
- hostlen = decode[44] # hostname length
360
- lmoff = decode[16] # LM hash offset
361
- lmlen = decode[12] # LM hash length
362
- ntoff = decode[24] # NT hash offset
363
- ntlen = decode[20] # NT hash length
364
-
365
- domain = decode[domoff..domoff+domlen-1]
366
- user = decode[useroff..useroff+userlen-1]
367
- host = decode[hostoff..hostoff+hostlen-1]
368
- lm = decode[lmoff..lmoff+lmlen-1].unpack("H*")
369
- nt = decode[ntoff..ntoff+ntlen-1].unpack("H*")
399
+ lm_len = decode[12,2].unpack("v").first
400
+ lm_offset = decode[16,2].unpack("v").first
401
+ lm = decode[lm_offset, lm_len].unpack("H*").first
402
+
403
+ nt_len = decode[20,2].unpack("v").first
404
+ nt_offset = decode[24,2].unpack("v").first
405
+ nt = decode[nt_offset, nt_len].unpack("H*").first
406
+
407
+ dom_len = decode[28,2].unpack("v").first
408
+ dom_offset = decode[32,2].unpack("v").first
409
+ domain = decode[dom_offset, dom_len]
410
+
411
+ user_len = decode[36,2].unpack("v").first
412
+ user_offset = decode[40,2].unpack("v").first
413
+ user = decode[user_offset, user_len]
414
+
415
+ host_len = decode[44,2].unpack("v").first
416
+ host_offset = decode[48,2].unpack("v").first
417
+ host = decode[host_offset, host_len]
370
418
 
371
419
  return domain, user, host, lm, nt
372
420
  else
@@ -386,13 +434,13 @@ CONST = Rex::Proto::SMB::Constants
386
434
  win_name = Rex::Text.to_unicode(win_name)
387
435
  decode = Rex::Text.decode_base64(message.strip)
388
436
 
389
- type = decode[8]
437
+ type = decode[8,1].unpack("C").first
390
438
 
391
439
  if (type == 1)
392
440
  # A type 1 message has been received, lets build a type 2 message response
393
441
 
394
- reqflags = decode[12..15]
395
- reqflags = Integer("0x" + reqflags.unpack("h8").to_s.reverse)
442
+ reqflags = decode[12,4]
443
+ reqflags = reqflags.unpack("V").first
396
444
 
397
445
  if (reqflags & CONST::REQUEST_TARGET) == CONST::REQUEST_TARGET
398
446
 
@@ -470,12 +518,12 @@ CONST = Rex::Proto::SMB::Constants
470
518
  def self.downgrade_type_message(message)
471
519
  decode = Rex::Text.decode_base64(message.strip)
472
520
 
473
- type = decode[8]
521
+ type = decode[8,1].unpack("C").first
474
522
 
475
523
  if (type > 0 and type < 4)
476
524
  reqflags = decode[12..15] if (type == 1 or type == 3)
477
525
  reqflags = decode[20..23] if (type == 2)
478
- reqflags = Integer("0x" + reqflags.unpack("h8").to_s.reverse)
526
+ reqflags = reqflags.unpack("V")
479
527
 
480
528
  # Remove NEGOTIATE_NTLMV2_KEY and NEGOTIATE_ALWAYS_SIGN, this lowers the negotiation
481
529
  # down to LMv1/NTLMv1.
@@ -497,9 +545,9 @@ CONST = Rex::Proto::SMB::Constants
497
545
  idx = 0
498
546
  0.upto(3) do |cnt|
499
547
  if (type == 2)
500
- decode[23-cnt] = Integer("0x" + flags[idx .. idx + 1])
548
+ decode[23-cnt] = [flags[idx,1]].pack("C")
501
549
  else
502
- decode[15-cnt] = Integer("0x" + flags[idx .. idx + 1])
550
+ decode[15-cnt] = [flags[idx,1]].pack("C")
503
551
  end
504
552
  idx += 2
505
553
  end
@@ -0,0 +1,3 @@
1
+ # $Id: tftp.rb 9333 2010-05-21 00:03:04Z jduck $
2
+ require 'rex/proto/tftp/constants'
3
+ require 'rex/proto/tftp/server'
@@ -0,0 +1,37 @@
1
+ # $Id: constants.rb 9334 2010-05-21 00:15:10Z jduck $
2
+ require 'rex/proto/tftp'
3
+
4
+ module Rex
5
+ module Proto
6
+ module TFTP
7
+
8
+ OPCODES = %w{ Unknown RRQ WRQ DATA ACK ERROR }
9
+ OpRead = 1
10
+ OpWrite = 2
11
+ OpData = 3
12
+ OpAck = 4
13
+ OpError = 5
14
+
15
+
16
+ ERRCODES = [
17
+ "Undefined",
18
+ "File not found",
19
+ "Access violation",
20
+ "Disk full or allocation exceeded",
21
+ "Illegal TFTP operation",
22
+ "Unknown transfer ID",
23
+ "File already exists",
24
+ "No such user"
25
+ ]
26
+
27
+ ErrFileNotFound = 1
28
+ ErrAccessViolation = 2
29
+ ErrDiskFull = 3
30
+ ErrIllegalOperation = 4
31
+ ErrUnknownTransferId = 5
32
+ ErrFileExists = 6
33
+ ErrNoSuchUser = 7
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,249 @@
1
+ # $Id: server.rb 9375 2010-05-26 22:39:56Z jduck $
2
+ require 'rex/socket'
3
+ require 'rex/proto/tftp'
4
+
5
+ module Rex
6
+ module Proto
7
+ module TFTP
8
+
9
+ #
10
+ # Little util function
11
+ #
12
+ def self.get_string(data)
13
+ ret = data.slice!(0,data.index("\x00"))
14
+ # Slice off the nul byte.
15
+ data.slice!(0,1)
16
+ ret
17
+ end
18
+
19
+
20
+ ##
21
+ #
22
+ # TFTP Server class
23
+ #
24
+ ##
25
+ class Server
26
+
27
+ def initialize(port = 69, listen_host = '0.0.0.0', context = {})
28
+ self.listen_host = listen_host
29
+ self.listen_port = port
30
+ self.context = context
31
+ self.sock = nil
32
+ @shutting_down = false
33
+
34
+ self.files = []
35
+ self.transfers = []
36
+ end
37
+
38
+
39
+ #
40
+ # Start the TFTP server
41
+ #
42
+ def start
43
+ self.sock = Rex::Socket::Udp.create(
44
+ 'LocalHost' => listen_host,
45
+ 'LocalPort' => listen_port,
46
+ 'Context' => context
47
+ )
48
+
49
+ self.thread = Thread.new {
50
+ monitor_socket
51
+ }
52
+ end
53
+
54
+
55
+ #
56
+ # Stop the TFTP server
57
+ #
58
+ def stop
59
+ @shutting_down = true
60
+
61
+ # Wait a maximum of 30 seconds for all transfers to finish.
62
+ start = Time.now
63
+ while (self.transfers.length > 0)
64
+ ::IO.select(nil, nil, nil, 0.5)
65
+ dur = Time.now - start
66
+ break if (dur > 30)
67
+ end
68
+
69
+ self.files.clear
70
+ self.thread.kill
71
+ self.sock.close
72
+ end
73
+
74
+
75
+ #
76
+ # Register a filename and content for a client to request
77
+ #
78
+ def register_file(fn, content)
79
+ self.files << {
80
+ :name => fn,
81
+ :data => content
82
+ }
83
+ end
84
+
85
+
86
+ #
87
+ # Send an error packet w/the specified code and string
88
+ #
89
+ def send_error(from, num)
90
+ if (num < 1 or num >= ERRCODES.length)
91
+ # ignore..
92
+ return
93
+ end
94
+ pkt = [OpError, num].pack('nn')
95
+ pkt << ERRCODES[num]
96
+ pkt << "\x00"
97
+ send_packet(from, pkt)
98
+ end
99
+
100
+
101
+ #
102
+ # Send a single packet to the specified host
103
+ #
104
+ def send_packet(from, pkt)
105
+ self.sock.sendto(pkt, from[0], from[1])
106
+ end
107
+
108
+
109
+ #
110
+ # Find the hash entry for a file that may be offered
111
+ #
112
+ def find_file(fname)
113
+ self.files.each do |f|
114
+ if (fname == f[:name])
115
+ return f
116
+ end
117
+ end
118
+ nil
119
+ end
120
+
121
+
122
+ attr_accessor :listen_host, :listen_port, :context
123
+ attr_accessor :sock, :files, :transfers
124
+ attr_accessor :thread
125
+
126
+
127
+ protected
128
+
129
+ #
130
+ # See if there is anything to do.. If so, dispatch it.
131
+ #
132
+ def monitor_socket
133
+ while true
134
+ rds = [@sock]
135
+ wds = []
136
+ self.transfers.each do |tr|
137
+ if (not tr[:last_sent])
138
+ wds << @sock
139
+ break
140
+ end
141
+ end
142
+ eds = [@sock]
143
+
144
+ r,w,e = ::IO.select(rds,wds,eds,1)
145
+
146
+ if (r != nil and r[0] == self.sock)
147
+ buf,host,port = self.sock.recvfrom(65535)
148
+ # Lame compatabilitiy :-/
149
+ from = [host, port]
150
+ dispatch_request(from, buf)
151
+ end
152
+
153
+ #
154
+ # Check to see if transfers need maintenance
155
+ #
156
+ self.transfers.each do |tr|
157
+ # Are we awaiting an ack?
158
+ if (tr[:last_sent])
159
+ elapsed = Time.now - tr[:last_sent]
160
+ if (elapsed >= 3)
161
+ # max retries reached?
162
+ if (tr[:retries] < 3)
163
+ #puts "[-] ack timed out, resending block"
164
+ tr[:last_sent] = nil
165
+ tr[:retries] += 1
166
+ else
167
+ #puts "[-] maximum tries reached, terminating transfer"
168
+ self.transfers.delete(tr)
169
+ end
170
+ end
171
+ elsif (w != nil and w[0] == self.sock)
172
+ # No ack waiting, send next block..
173
+ chunk = tr[:file][:data].slice(tr[:offset], 512)
174
+ if (chunk and chunk.length >= 0)
175
+ pkt = [OpData, tr[:block]].pack('nn')
176
+ pkt << chunk
177
+ send_packet(tr[:from], pkt)
178
+ tr[:last_sent] = Time.now
179
+ else
180
+ # no more chunks.. transfer is most likely done.
181
+ self.transfers.delete(tr)
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
187
+
188
+
189
+ #
190
+ # Dispatch a packet that we received
191
+ #
192
+ def dispatch_request(from, buf)
193
+
194
+ op = buf.unpack('n')[0]
195
+ buf.slice!(0,2)
196
+
197
+ #start = "[*] TFTP - %s:%u - %s" % [from[0], from[1], OPCODES[op]]
198
+
199
+ case op
200
+ when OpRead
201
+ # Process RRQ packets
202
+ fn = TFTP::get_string(buf)
203
+ mode = TFTP::get_string(buf).downcase
204
+
205
+ #puts "%s %s %s" % [start, fn, mode]
206
+
207
+ if (not @shutting_down) and (file = self.find_file(fn))
208
+ self.transfers << {
209
+ :from => from,
210
+ :file => file,
211
+ :block => 1,
212
+ :offset => 0,
213
+ :last_sent => nil,
214
+ :retries => 0
215
+ }
216
+ else
217
+ #puts "[-] file not found!"
218
+ send_error(from, ErrFileNotFound)
219
+ end
220
+
221
+ when OpAck
222
+ # Process ACK packets
223
+ block = buf.unpack('n')[0]
224
+ #puts "%s %d" % [start, block]
225
+
226
+ self.transfers.each do |tr|
227
+ if (from == tr[:from] and block == tr[:block])
228
+ # acked! send the next block
229
+ tr[:block] += 1
230
+ tr[:offset] += 512
231
+ tr[:last_sent] = nil
232
+ tr[:retries] = 0
233
+ end
234
+ end
235
+
236
+ else
237
+ # Other packets are unsupported
238
+ #puts start
239
+ send_error(from, ErrAccessViolation)
240
+
241
+ end
242
+ end
243
+
244
+ end
245
+
246
+ end
247
+ end
248
+ end
249
+