packetfu 1.0.3.pre → 1.0.4.pre

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.
data/.document CHANGED
@@ -1,4 +1,3 @@
1
1
  lib/packetfu.rb
2
2
  lib/packetfu/
3
- examples/packetfu-shell.rb
4
-
3
+ README
data/README CHANGED
@@ -1,11 +1,12 @@
1
1
  = PacketFu
2
2
 
3
3
  A library for reading a writing packets to an interface or to a libpcap-formatted file.
4
- It is maintained at http://code.google.com/p/packetfu
4
+
5
+ It is maintained at http://code.google.com/p/packetfu and https://github.com/todb/packetfu (which repository will win?)
5
6
 
6
7
  == Documentation
7
8
 
8
- PacketFu is rdoc-compatable. In the same directory as this file, run "rdoc" by itself, and then view doc/index.html with your favored browser. Once that's done, navigate at the top, and read up on how to create a Packet or Capture from an interface with show_live or whatever.
9
+ PacketFu is rdoc-compatible, which means it's sdoc compatible. In the same directory as this file, run "sdoc" by itself (gem install sdoc), and then view doc/index.html with your favored browser. Once that's done, navigate at the top, and read up on how to create a Packet or Capture from an interface with show_live or whatever.
9
10
 
10
11
  == Requirements
11
12
 
@@ -19,10 +20,41 @@ $ rvm gem install pcaprub
19
20
 
20
21
  Marshall Beddoe's PcapRub is required only for packet reading and writing from a network interfaces (which is a pretty big only). PcapRub itself relies on libpcap 0.9.8 or later for packet injection. PcapRub also requires root privilieges to access the interface directly.
21
22
 
23
+ === Platforms
24
+
25
+ I tend to test with the following (with bash):
26
+
27
+ date > /tmp/tests.txt; for i in default 1.8.6-p420 1.8.7-p334 1.9.1-p431 1.9.2-p180 1.9.3-head
28
+ do rvm use $i >> /tmp/tests.txt
29
+ echo Testing with $i
30
+ echo $i >> /tmp/tests.txt; echo +++++++++++++++++++++++ >> /tmp/tests.txt
31
+ rvmsudo ./all_tests.rb >> /tmp/tests.txt; rspec . >> /tmp/tests.txt
32
+ done
33
+
34
+ ==== Passing Platforms
35
+
36
+ * 1.9.1-p378 -- my favorite and my best
37
+ * 1.8.7-p334
38
+ * 1.9.2-p180
39
+ * 1.9.3-head
40
+
41
+ ==== Problem Platforms
42
+
43
+ * 1.8.6-p420 -- Has problems with pcaprub and capture/inject
44
+ * 1.9.1-p431 -- Has problems with loading gems in general, see http://redmine.ruby-lang.org/issues/2404
45
+
46
+ Technically, these are pcaprub problems and not PacketFu problems, but PacketFu should at least fail better at them.
47
+
48
+ Incidentally, I suspect these Ruby problems are the crux of the Mac OSX problems that people report. Try a different Ruby build and please let me know what works for you.
49
+
50
+
22
51
  == Examples
23
52
 
24
53
  PacketFu ships with dozens and dozens of tests, built on Test::Unit. These should give good pointers on how you're expected to use it. See the /tests directory. Furthermore, PacketFu also ships with packetfu-shell.rb, which should be run via IRB (as root, if you intend to use your interfaces).
25
54
 
26
55
  == Author
27
56
 
28
- PacketFu is maintained primarily by Tod Beardsley <todb@planb-security.net>
57
+ PacketFu is maintained primarily by Tod Beardsley todb@planb-security.net, with help from Open Source Land.
58
+
59
+ See LICENSE for licensing details.
60
+
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # new-simple-stats.rb demonstrates the performance difference
4
+ # between the old and busted way to parse pcap files and the
5
+ # new hotness of stream parsing. Spoiler alert: Against a pcap
6
+ # file of 1GB, the old way would eat all your memory and take
7
+ # forever. This still takes kinda forever, but at 5000 packets
8
+ # every 11 seconds (my own benchmark) for this script, at least
9
+ # it doesn't hog up all your memory.
10
+
11
+ require 'examples' # For path setting slight-of-hand
12
+ require 'packetfu'
13
+
14
+ def print_results(stats)
15
+ stats.each_pair { |k,v| puts "%-12s: %10d" % [k,v] }
16
+ end
17
+
18
+ # Takes a file name, parses the packets, and records the packet
19
+ # type based on its PacketFu class.
20
+ def count_packet_types(file)
21
+ stats = {}
22
+ count = 0
23
+ start_time = Time.now
24
+ PacketFu::PcapFile.read_packets(file) do |pkt|
25
+ kind = pkt.proto.last.to_sym
26
+ stats[kind] ? stats[kind] += 1 : stats[kind] = 1
27
+ count += 1
28
+ elapsed = (Time.now - start_time).to_i
29
+ if count % 5_000 == 0
30
+ puts "After #{count} packets (#{elapsed} seconds elapsed):"
31
+ print_results(stats)
32
+ end
33
+ end
34
+ puts "Final results for #{count} packets (#{elapsed} seconds elapsed):"
35
+ print_results(stats)
36
+ end
37
+
38
+ if File.readable?(infile = (ARGV[0] || 'in.pcap'))
39
+ title = "Packets by packet type in '#{infile}'"
40
+ puts "-" * title.size
41
+ puts title
42
+ puts "-" * title.size
43
+ count_packet_types(infile)
44
+ else
45
+ raise RuntimeError, "Need an infile, like so: #{$0} in.pcap"
46
+ end
47
+
48
+
49
+
50
+
51
+
@@ -3,6 +3,11 @@
3
3
  # Simple-stats.rb takes a pcap file, and gives some simple
4
4
  # stastics on the protocols found. It's mainly used to
5
5
  # demonstrate a method to parse pcap files.
6
+ #
7
+ # XXX: DO NOT USE THIS METHOD TO READ PCAP FILES.
8
+ #
9
+ # See new-simple-stats.rb for an example of the streaming
10
+ # parsing method.
6
11
 
7
12
  require 'examples' # For path setting slight-of-hand
8
13
  require 'packetfu'
@@ -12,6 +17,7 @@ require 'packetfu'
12
17
  def count_packet_types(file)
13
18
  file = File.open(file) {|f| f.read}
14
19
  stats = {}
20
+ count = 0
15
21
  pcapfile = PacketFu::PcapPackets.new
16
22
  pcapfile.read(file)
17
23
  pcapfile.each do |p|
@@ -23,6 +29,8 @@ def count_packet_types(file)
23
29
  else
24
30
  stats[kind] = 0
25
31
  end
32
+ count += 1
33
+ break if count >= 1_000
26
34
  end
27
35
  stats.each_pair { |k,v| puts "%-12s: %4d" % [k,v] }
28
36
  end
@@ -173,7 +173,7 @@ module PacketFu
173
173
  # different ones off of (like a fuzzer might), you'll want
174
174
  # to use clone()
175
175
  def clone
176
- Marshal.load(Marshal.dump(self))
176
+ Packet.parse(self.to_s)
177
177
  end
178
178
 
179
179
  # If two packets are represented as the same binary string, and
@@ -209,7 +209,7 @@ module PacketFu
209
209
  # peek traverses the @header list in reverse to find a suitable
210
210
  # format.
211
211
  #
212
- # == Format
212
+ # === Format
213
213
  #
214
214
  # * A one or two character protocol initial. It should be unique
215
215
  # * The packet size
@@ -218,7 +218,7 @@ module PacketFu
218
218
  # Ideally, related peek_formats will all line up with each other
219
219
  # when printed to the screen.
220
220
  #
221
- # == Example
221
+ # === Example
222
222
  #
223
223
  # tcp_packet.peek
224
224
  # #=> "T 1054 10.10.10.105:55000 -> 192.168.145.105:80 [......] S:adc7155b|I:8dd0"
@@ -283,11 +283,10 @@ module PacketFu
283
283
 
284
284
  # Hexify provides a neatly-formatted dump of binary data, familar to hex readers.
285
285
  def hexify(str)
286
- if str.respond_to? :force_encoding
287
- str.force_encoding("ASCII-8BIT")
288
- end
286
+ str.force_encoding("ASCII-8BIT") if str.respond_to? :force_encoding
289
287
  hexascii_lines = str.to_s.unpack("H*")[0].scan(/.{1,32}/)
290
- chars = str.to_s.gsub(/[\x00-\x1f\x7f-\xff]/,'.')
288
+ regex = Regexp.new('[\x00-\x1f\x7f-\xff]', nil, 'n')
289
+ chars = str.to_s.gsub(regex,'.')
291
290
  chars_lines = chars.scan(/.{1,16}/)
292
291
  ret = []
293
292
  hexascii_lines.size.times {|i| ret << "%-48s %s" % [hexascii_lines[i].gsub(/(.{2})/,"\\1 "),chars_lines[i]]}
data/lib/packetfu/pcap.rb CHANGED
@@ -44,6 +44,10 @@ module PacketFu
44
44
  :thiszone, :sigfigs, :snaplen, :network)
45
45
  include StructFu
46
46
 
47
+ MAGIC_INT32 = 0xa1b2c3d4
48
+ MAGIC_LITTLE = [MAGIC_INT32].pack("V")
49
+ MAGIC_BIG = [MAGIC_INT32].pack("N")
50
+
47
51
  def initialize(args={})
48
52
  set_endianness(args[:endian] ||= :little)
49
53
  init_fields(args)
@@ -54,7 +58,7 @@ module PacketFu
54
58
 
55
59
  # Called by initialize to set the initial fields.
56
60
  def init_fields(args={})
57
- args[:magic] = @int32.new(args[:magic] || 0xa1b2c3d4)
61
+ args[:magic] = @int32.new(args[:magic] || PcapHeader::MAGIC_INT32)
58
62
  args[:ver_major] = @int16.new(args[:ver_major] || 2)
59
63
  args[:ver_minor] ||= @int16.new(args[:ver_minor] || 4)
60
64
  args[:thiszone] ||= @int32.new(args[:thiszone])
@@ -76,7 +80,7 @@ module PacketFu
76
80
  force_binary(str)
77
81
  return self if str.nil?
78
82
  str.force_encoding("binary") if str.respond_to? :force_encoding
79
- if str[0,4] == self[:magic].to_s || true # TODO: raise if it's not magic.
83
+ if str[0,4] == self[:magic].to_s
80
84
  self[:magic].read str[0,4]
81
85
  self[:ver_major].read str[4,2]
82
86
  self[:ver_minor].read str[6,2]
@@ -84,6 +88,8 @@ module PacketFu
84
88
  self[:sigfigs].read str[12,4]
85
89
  self[:snaplen].read str[16,4]
86
90
  self[:network].read str[20,4]
91
+ else
92
+ raise "Incorrect magic for libpcap"
87
93
  end
88
94
  self
89
95
  end
@@ -163,6 +169,7 @@ module PacketFu
163
169
 
164
170
  # Reads a string to populate the object.
165
171
  def read(str)
172
+ return unless str
166
173
  force_binary(str)
167
174
  self[:timestamp].read str[0,8]
168
175
  self[:incl_len].read str[8,4]
@@ -194,10 +201,9 @@ module PacketFu
194
201
  def read(str)
195
202
  force_binary(str)
196
203
  return self if str.nil?
197
- magic = "\xa1\xb2\xc3\xd4"
198
- if str[0,4] == magic
204
+ if str[0,4] == PcapHeader::MAGIC_BIG
199
205
  @endian = :big
200
- elsif str[0,4] == magic.reverse
206
+ elsif str[0,4] == PcapHeader::MAGIC_LITTLE
201
207
  @endian = :little
202
208
  else
203
209
  raise ArgumentError, "Unknown file format for #{self.class}"
@@ -288,10 +294,6 @@ module PacketFu
288
294
 
289
295
  alias_method :f2a, :file_to_array
290
296
 
291
- def self.file_to_array(fname)
292
- PcapFile.new.file_to_array(:f => fname)
293
- end
294
-
295
297
  # Takes an array of packets (as generated by file_to_array), and writes them
296
298
  # to a file. Valid arguments are:
297
299
  #
@@ -397,6 +399,94 @@ module PacketFu
397
399
 
398
400
  end
399
401
 
402
+ # PcapFile also can behave as a singleton class, which is usually the better
403
+ # way to handle pcap files of really any size, since it doesn't require
404
+ # storing packets before handing them off to a given block. This is really
405
+ # the way to go.
406
+ class PcapFile
407
+ class << self
408
+
409
+ # Takes a given file and returns an array of the packet bytes. Here
410
+ # for backwards compatibilty.
411
+ def file_to_array(fname)
412
+ PcapFile.new.file_to_array(:f => fname)
413
+ end
414
+
415
+ # Takes a given file name, and reads out the packets. If given a block,
416
+ # it will yield back a PcapPacket object per packet found.
417
+ def read(fname,&block)
418
+ begin
419
+ file_handle = File.open(fname, "rb")
420
+ pcap_packets = PcapPackets.new unless block
421
+ file_header = PcapHeader.new
422
+ file_header.read file_handle.read(24)
423
+ packet_count = 0
424
+ pcap_packet = PcapPacket.new(:endian => file_header.endian)
425
+ while pcap_packet.read file_handle.read(16) do
426
+ len = pcap_packet.incl_len
427
+ pcap_packet.data = StructFu::String.new.read(file_handle.read(len.to_i))
428
+ packet_count += 1
429
+ if pcap_packet.data.size < len.to_i
430
+ warn "Packet ##{packet_count} is corrupted: expected #{len.to_i}, got #{pcap_packet.data.size}. Exiting."
431
+ break
432
+ end
433
+ if block
434
+ yield pcap_packet
435
+ else
436
+ pcap_packets << pcap_packet
437
+ end
438
+ end
439
+ unless block
440
+ return pcap_packets
441
+ end
442
+ ensure
443
+ file_handle.close
444
+ end
445
+ end
446
+
447
+ # Takes a filename, and an optional block. If a block is given,
448
+ # yield back the raw packet data from the given file. Otherwise,
449
+ # return an array of parsed packets.
450
+ def read_packet_bytes(fname,&block)
451
+ count = 0
452
+ packets = [] unless block
453
+ read(fname) do |packet|
454
+ if block
455
+ count += 1
456
+ yield packet.data.to_s
457
+ else
458
+ packets << packet.data.to_s
459
+ end
460
+ end
461
+ block ? count : packets
462
+ end
463
+
464
+ alias :file_to_array :read_packet_bytes
465
+
466
+ # Takes a filename, and an optional block. If a block is given,
467
+ # yield back parsed packets from the given file. Otherwise, return
468
+ # an array of parsed packets.
469
+ #
470
+ # This is a brazillian times faster than the old methods of extracting
471
+ # packets from files.
472
+ def read_packets(fname,&block)
473
+ count = 0
474
+ packets = [] unless block
475
+ read_packet_bytes(fname) do |packet|
476
+ if block
477
+ count += 1
478
+ yield Packet.parse(packet)
479
+ else
480
+ packets << Packet.parse(packet)
481
+ end
482
+ end
483
+ block ? count : packets
484
+ end
485
+
486
+ end
487
+
488
+ end
489
+
400
490
  end
401
491
 
402
492
  module PacketFu
@@ -411,7 +501,7 @@ module PacketFu
411
501
  # Reads the magic string of a pcap file, and determines
412
502
  # if it's :little or :big endian.
413
503
  def get_byte_order(pcap_file)
414
- byte_order = ((pcap_file[0,4] == "\xd4\xc3\xb2\xa1") ? :little : :big)
504
+ byte_order = ((pcap_file[0,4] == PcapHeader::MAGIC_LITTLE) ? :little : :big)
415
505
  return byte_order
416
506
  end
417
507
 
@@ -609,7 +609,7 @@ module PacketFu
609
609
  # Note that by using TcpOptions#encode, strings supplied as values which
610
610
  # can be converted to numbers will be converted first.
611
611
  #
612
- # == Example
612
+ # === Example
613
613
  #
614
614
  # t = TcpOptions.new
615
615
  # t.encode("MS:1460,WS:6")
@@ -774,10 +774,15 @@ module PacketFu
774
774
 
775
775
  # Getter for the TCP Header Length value.
776
776
  def tcp_hlen; self[:tcp_hlen].to_i; end
777
- # Setter for the TCP Header Length value.
777
+ # Setter for the TCP Header Length value. Can take
778
+ # either a string or an integer. Note that if it's
779
+ # a string, the top four bits are used.
778
780
  def tcp_hlen=(i)
779
- if i.kind_of? PacketFu::TcpHlen
780
- self[:tcp_hlen]=i
781
+ case i
782
+ when PacketFu::TcpHlen
783
+ self[:tcp_hlen] = i
784
+ when Numeric
785
+ self[:tcp_hlen] = TcpHlen.new(:hlen => i.to_i)
781
786
  else
782
787
  self[:tcp_hlen].read(i)
783
788
  end
@@ -787,8 +792,15 @@ module PacketFu
787
792
  def tcp_reserved; self[:tcp_reserved].to_i; end
788
793
  # Setter for the TCP Reserved field.
789
794
  def tcp_reserved=(i)
790
- if i.kind_of? PacketFu::TcpReserved
795
+ case i
796
+ when PacketFu::TcpReserved
791
797
  self[:tcp_reserved]=i
798
+ when Numeric
799
+ args = {}
800
+ args[:r1] = (i & 0b100) >> 2
801
+ args[:r2] = (i & 0b010) >> 1
802
+ args[:r3] = (i & 0b001)
803
+ self[:tcp_reserved] = TcpReserved.new(args)
792
804
  else
793
805
  self[:tcp_reserved].read(i)
794
806
  end
@@ -798,8 +810,15 @@ module PacketFu
798
810
  def tcp_ecn; self[:tcp_ecn].to_i; end
799
811
  # Setter for the ECN bits.
800
812
  def tcp_ecn=(i)
801
- if i.kind_of? PacketFu::TcpEcn
813
+ case i
814
+ when PacketFu::TcpEcn
802
815
  self[:tcp_ecn]=i
816
+ when Numeric
817
+ args = {}
818
+ args[:n] = (i & 0b100) >> 2
819
+ args[:c] = (i & 0b010) >> 1
820
+ args[:e] = (i & 0b001)
821
+ self[:tcp_ecn] = TcpEcn.new(args)
803
822
  else
804
823
  self[:tcp_ecn].read(i)
805
824
  end
@@ -809,7 +828,8 @@ module PacketFu
809
828
  def tcp_opts; self[:tcp_opts].to_s; end
810
829
  # Setter for TCP Options.
811
830
  def tcp_opts=(i)
812
- if i.kind_of? PacketFu::TcpOptions
831
+ case i
832
+ when PacketFu::TcpOptions
813
833
  self[:tcp_opts]=i
814
834
  else
815
835
  self[:tcp_opts].read(i)
@@ -846,7 +866,7 @@ module PacketFu
846
866
  def tcp_flags_dotmap
847
867
  dotmap = tcp_flags.members.map do |flag|
848
868
  status = self.tcp_flags.send flag
849
- status == 0 ? "." : flag.to_s.upcase[0]
869
+ status == 0 ? "." : flag.to_s.upcase[0].chr
850
870
  end
851
871
  dotmap.join
852
872
  end
@@ -118,12 +118,12 @@ module StructFu
118
118
 
119
119
  end
120
120
 
121
- # Int16be is a two byte value in big-endian format.
121
+ # Int16be is a two byte value in big-endian format. The endianness cannot be altered.
122
122
  class Int16be < Int16
123
123
  undef :endian=
124
124
  end
125
125
 
126
- # Int16le is a two byte value in little-endian format.
126
+ # Int16le is a two byte value in little-endian format. The endianness cannot be altered.
127
127
  class Int16le < Int16
128
128
  undef :endian=
129
129
  def initialize(v=nil, e=:little)
@@ -147,12 +147,12 @@ module StructFu
147
147
 
148
148
  end
149
149
 
150
- # Int32be is a four byte value in big-endian format.
150
+ # Int32be is a four byte value in big-endian format. The endianness cannot be altered.
151
151
  class Int32be < Int32
152
152
  undef :endian=
153
153
  end
154
154
 
155
- # Int32le is a four byte value in little-endian format.
155
+ # Int32le is a four byte value in little-endian format. The endianness cannot be altered.
156
156
  class Int32le < Int32
157
157
  undef :endian=
158
158
  def initialize(v=nil, e=:little)
@@ -208,8 +208,8 @@ module StructFu
208
208
  # is calculated upon assignment. If you'd prefer to have
209
209
  # an incorrect value, use the syntax, obj[:string]="value"
210
210
  # instead. Note, by using the alternate form, you must
211
- # #calc before you can trust the int's value. Think of the
212
- # = assignment as "set to equal," while the []= assignment
211
+ # #calc before you can trust the int's value. Think of the =
212
+ # assignment as "set to equal," while the []= assignment
213
213
  # as "boxing in" the value. Maybe.
214
214
  def string=(s)
215
215
  self[:string] = s
@@ -246,9 +246,13 @@ module StructFu
246
246
  # is used is dependant on which :mode is set (with self.mode).
247
247
  #
248
248
  # :parse : Read the length, and then read in that many bytes of the string.
249
- # The string may be truncated or padded out with nulls, as dictated by the value.
250
- # :fix : Skip the length, read the rest of the string, then set the length to what it ought to be.
251
- # else : If neither of these modes are set, just perfom a normal read(). This is the default.
249
+ # The string may be truncated or padded out with nulls, as dictated by the value.
250
+ #
251
+ # :fix : Skip the length, read the rest of the string, then set the length
252
+ # to what it ought to be.
253
+ #
254
+ # else : If neither of these modes are set, just perfom a normal read().
255
+ # This is the default.
252
256
  def parse(s)
253
257
  unless s[0,int.width].size == int.width
254
258
  raise StandardError, "String is too short for type #{int.class}"
@@ -1,13 +1,14 @@
1
1
  module PacketFu
2
2
 
3
3
  # Check the repo's for version release histories
4
- VERSION = "1.0.3"
4
+ VERSION = "1.0.4"
5
5
 
6
+ # Returns PacketFu::VERSION
6
7
  def self.version
7
8
  VERSION
8
9
  end
9
10
 
10
- # Returns the version in a binary format for easy comparisons.
11
+ # Returns a version string in a binary format for easy comparisons.
11
12
  def self.binarize_version(str)
12
13
  if(str.respond_to?(:split) && str =~ /^[0-9]+(\.([0-9]+)(\.[0-9]+)?)?\..+$/)
13
14
  bin_major,bin_minor,bin_teeny = str.split(/\x2e/).map {|x| x.to_i}
data/lib/packetfu.rb CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  # :title: PacketFu Documentation
3
+ # :main: ../README
3
4
  # :include: ../README
4
- # :include: ../INSTALL
5
5
  # :include: ../LICENSE
6
6
 
7
7
  cwd = File.expand_path(File.dirname(__FILE__))
@@ -14,6 +14,19 @@ require 'rubygems' if RUBY_VERSION =~ /^1\.[0-8]/
14
14
 
15
15
  module PacketFu
16
16
 
17
+ # Picks up all the protocols defined in the protos subdirectory
18
+ def self.require_protos(cwd)
19
+ protos_dir = File.join(cwd, "packetfu", "protos")
20
+ Dir.new(protos_dir).each do |fname|
21
+ next unless fname[/\.rb$/]
22
+ begin
23
+ require File.join(protos_dir,fname)
24
+ rescue
25
+ warn "Warning: Could not load `#{fname}'. Skipping."
26
+ end
27
+ end
28
+ end
29
+
17
30
  # Sets the expected byte order for a pcap file. See PacketFu::Read.set_byte_order
18
31
  @byte_order = :little
19
32
 
@@ -32,6 +45,7 @@ module PacketFu
32
45
  end
33
46
 
34
47
  pcaprub_platform_require
48
+
35
49
  if @pcaprub_loaded
36
50
  if Pcap.version !~ /[0-9]\.[7-9][0-9]?(-dev)?/ # Regex for 0.7-dev and beyond.
37
51
  @pcaprub_loaded = false # Don't bother with broken versions
@@ -41,6 +55,7 @@ module PacketFu
41
55
  require "packetfu/inject"
42
56
  end
43
57
 
58
+ # Returns the status of pcaprub
44
59
  def self.pcaprub_loaded?
45
60
  @pcaprub_loaded
46
61
  end
@@ -50,6 +65,7 @@ module PacketFu
50
65
  constants.map { |const| const_get(const) if const_get(const).kind_of? Class}.compact
51
66
  end
52
67
 
68
+ # Adds the class to PacketFu's list of packet classes -- used in packet parsing.
53
69
  def self.add_packet_class(klass)
54
70
  raise "Need a class" unless klass.kind_of? Class
55
71
  if klass.name !~ /[A-Za-z0-9]Packet/
@@ -60,6 +76,7 @@ module PacketFu
60
76
  @packet_classes.sort! {|x,y| x.name <=> y.name}
61
77
  end
62
78
 
79
+ # Presumably, there may be a time where you'd like to remove a packet class.
63
80
  def self.remove_packet_class(klass)
64
81
  raise "Need a class" unless klass.kind_of? Class
65
82
  @packet_classes ||= []
@@ -67,10 +84,12 @@ module PacketFu
67
84
  @packet_classes
68
85
  end
69
86
 
87
+ # Returns an array of packet classes
70
88
  def self.packet_classes
71
89
  @packet_classes || []
72
90
  end
73
91
 
92
+ # Returns an array of packet types by packet prefix.
74
93
  def self.packet_prefixes
75
94
  return [] unless @packet_classes
76
95
  @packet_classes.map {|p| p.to_s.split("::").last.to_s.downcase.gsub(/packet$/,"")}
@@ -78,22 +97,10 @@ module PacketFu
78
97
 
79
98
  end
80
99
 
81
- def require_protos(cwd)
82
- protos_dir = File.join(cwd, "packetfu", "protos")
83
- Dir.new(protos_dir).each do |fname|
84
- next unless fname[/\.rb$/]
85
- begin
86
- require File.join(protos_dir,fname)
87
- rescue
88
- warn "Warning: Could not load `#{fname}'. Skipping."
89
- end
90
- end
91
- end
92
-
93
100
  require File.join(cwd,"packetfu","version")
94
101
  require File.join(cwd,"packetfu","pcap")
95
102
  require File.join(cwd,"packetfu","packet")
96
- require_protos(cwd)
103
+ PacketFu.require_protos(cwd)
97
104
  require File.join(cwd,"packetfu","utils")
98
105
  require File.join(cwd,"packetfu","config")
99
106
 
data/test/icmp_test.pcap CHANGED
Binary file
data/test/ip_test.pcap CHANGED
Binary file
@@ -1,8 +1,13 @@
1
1
  require File.join("..","lib","packetfu")
2
2
 
3
+ unless %x{#{$0} --version} =~ /^2\.6/
4
+ puts "PacketFu needs rspec 2.6 or so."
5
+ exit 1
6
+ end
7
+
3
8
  describe PacketFu, "version information" do
4
9
  it "reports a version number" do
5
- PacketFu::VERSION.should == "1.0.2"
10
+ PacketFu::VERSION.should match /^1\.[0-9]\.[0-9]$/
6
11
  end
7
12
  its(:version) {should eq PacketFu::VERSION}
8
13
 
data/test/tcp_spec.rb ADDED
@@ -0,0 +1,100 @@
1
+ require File.join("..","lib","packetfu")
2
+
3
+ include PacketFu
4
+
5
+ def unusual_numeric_handling_headers(header,i)
6
+ camelized_header = header.to_s.split("_").map {|x| x.capitalize}.join
7
+ header_class = PacketFu.const_get camelized_header
8
+ specify { subject.send(header).should == i }
9
+ specify { subject.send(header).should be_kind_of Integer }
10
+ specify { subject.headers.last[header].should be_kind_of header_class }
11
+ end
12
+
13
+ def tcp_hlen_numeric(i)
14
+ unusual_numeric_handling_headers(:tcp_hlen,i)
15
+ end
16
+
17
+ def tcp_reserved_numeric(i)
18
+ unusual_numeric_handling_headers(:tcp_reserved,i)
19
+ end
20
+
21
+ def tcp_ecn_numeric(i)
22
+ unusual_numeric_handling_headers(:tcp_ecn,i)
23
+ end
24
+
25
+
26
+ describe TCPPacket do
27
+
28
+ subject do
29
+ bytes = PcapFile.file_to_array("sample2.pcap")[2]
30
+ packet = Packet.parse(bytes)
31
+ end
32
+
33
+ context "TcpHlen reading and setting" do
34
+ context "TcpHlen set via #read" do
35
+ tcp_hlen_numeric(8)
36
+ end
37
+ context "TcpHlen set via an Integer for the setter" do
38
+ (0..15).each do |i|
39
+ context "i is #{i}" do
40
+ before { subject.tcp_hlen = i }
41
+ tcp_hlen_numeric(i)
42
+ end
43
+ end
44
+ end
45
+ context "TcpHlen set via a String for the setter" do
46
+ before { subject.tcp_hlen = "\x60" }
47
+ tcp_hlen_numeric(6)
48
+ end
49
+ context "TcpHlen set via a TcpHlen for the setter" do
50
+ before { subject.tcp_hlen = TcpHlen.new(:hlen => 7) }
51
+ tcp_hlen_numeric(7)
52
+ end
53
+ end
54
+
55
+ context "TcpReserved reading and setting" do
56
+ context "TcpReserved set via #read" do
57
+ tcp_reserved_numeric(0)
58
+ end
59
+ context "TcpReserved set via an Integer for the setter" do
60
+ (0..7).each do |i|
61
+ context "i is #{i}" do
62
+ before { subject.tcp_reserved = i }
63
+ tcp_reserved_numeric(i)
64
+ end
65
+ end
66
+ end
67
+ context "TcpReserved set via a String for the setter" do
68
+ before { subject.tcp_reserved = "\x03" }
69
+ tcp_reserved_numeric(3)
70
+ end
71
+ context "TcpReserved set via a TcpReserved for the setter" do
72
+ before { subject.tcp_reserved = TcpReserved.new(:r1 => 1, :r2 => 0, :r3 => 1) }
73
+ tcp_reserved_numeric(5)
74
+ end
75
+ end
76
+
77
+ context "TcpEcn reading and setting" do
78
+ context "TcpEcn set via #read" do
79
+ tcp_ecn_numeric(0)
80
+ end
81
+ context "TcpEcn set via an Integer for the setter" do
82
+ (0..7).each do |i|
83
+ context "i is #{i}" do
84
+ before { subject.tcp_ecn = i }
85
+ tcp_ecn_numeric(i)
86
+ end
87
+ end
88
+ end
89
+ context "TcpEcn set via a String for the setter" do
90
+ before { subject.tcp_ecn = "\x00\xc0" }
91
+ tcp_ecn_numeric(3)
92
+ end
93
+ context "TcpEcn set via a TcpEcn for the setter" do
94
+ before { subject.tcp_ecn = TcpEcn.new(:n => 1, :c => 0, :e => 1) }
95
+ tcp_ecn_numeric(5)
96
+ end
97
+ end
98
+
99
+ end
100
+
data/test/tcp_test.pcap CHANGED
Binary file
data/test/test_arp.rb CHANGED
@@ -107,7 +107,7 @@ class ArpTest < Test::Unit::TestCase
107
107
  puts "ARP Peek format: "
108
108
  puts a.peek
109
109
  puts "\n"
110
- assert_equal(66,a.peek.size)
110
+ assert(a.peek.size <= 80)
111
111
  end
112
112
 
113
113
  def test_arp_pcap
data/test/test_hsrp.rb CHANGED
@@ -15,57 +15,6 @@ class HSRPTest < Test::Unit::TestCase
15
15
  # pkt.to_f('udp_test.pcap','a')
16
16
  end
17
17
 
18
- =begin
19
- # The rest of these tests are snarfed from UDP. TODO: need to update
20
- # these for hsrp, shouldn't be long.
21
- def test_hsrp_pcap
22
- u = UDPPacket.new
23
- assert_kind_of UDPPacket, u
24
- u.recalc
25
- u.to_f('udp_test.pcap','a')
26
- u.ip_saddr = "10.20.30.40"
27
- u.ip_daddr = "50.60.70.80"
28
- u.payload = "+some fakey-fake udp packet"
29
- u.udp_src = 1205
30
- u.udp_dst = 13013
31
- u.recalc
32
- u.to_f('udp_test.pcap','a')
33
- end
34
-
35
- def test_udp_peek
36
- u = UDPPacket.new
37
- u.ip_saddr = "10.20.30.40"
38
- u.ip_daddr = "50.60.70.80"
39
- u.udp_src = 53
40
- u.udp_dport = 1305
41
- u.payload = "abcdefghijklmnopqrstuvwxyz"
42
- u.recalc
43
- puts "\n"
44
- puts "UDP Peek format: "
45
- puts u.peek
46
- assert_equal 78,u.peek.size
47
- end
48
-
49
- def test_udp_checksum
50
- sample_packet = PcapFile.new.file_to_array(:f => 'sample.pcap')[0]
51
- pkt = Packet.parse(sample_packet)
52
- assert_kind_of UDPPacket, pkt
53
- pkt.recalc
54
- assert_equal(0x8bf8, pkt.udp_sum.to_i)
55
- pkt.to_f('udp_test.pcap','a')
56
- end
57
-
58
- def test_udp_alter
59
- sample_packet = PcapFile.new.file_to_array(:f => 'sample.pcap')[0]
60
- pkt = Packet.parse(sample_packet)
61
- assert_kind_of UDPPacket, pkt
62
- pkt.payload = pkt.payload.gsub(/metasploit/,"MeatPistol")
63
- pkt.recalc
64
- assert_equal(0x8341, pkt.udp_sum)
65
- pkt.to_f('udp_test.pcap','a')
66
- end
67
- =end
68
-
69
18
  end
70
19
 
71
20
  # vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
data/test/test_icmp.rb CHANGED
@@ -24,7 +24,7 @@ class ICMPTest < Test::Unit::TestCase
24
24
  puts "\n"
25
25
  puts "ICMP Peek format: "
26
26
  puts i.peek
27
- assert_equal 78,i.peek.size
27
+ assert (i.peek.size <= 80)
28
28
  end
29
29
 
30
30
  def test_icmp_pcap
data/test/test_ip.rb CHANGED
@@ -48,7 +48,7 @@ class IPTest < Test::Unit::TestCase
48
48
  puts "\n"
49
49
  puts "IP Peek format: "
50
50
  puts i.peek
51
- assert_equal 78,i.peek.size
51
+ assert (i.peek.size <= 80)
52
52
  end
53
53
 
54
54
  def test_ip_pcap
data/test/test_ip6.rb CHANGED
@@ -47,7 +47,7 @@ class IPv6Test < Test::Unit::TestCase
47
47
  puts "\n"
48
48
  puts "IPv6 Peek format: "
49
49
  puts i.peek
50
- assert_equal 78, i.peek.size
50
+ assert (i.peek.size <= 80)
51
51
  end
52
52
 
53
53
  =begin
data/test/test_tcp.rb CHANGED
@@ -262,7 +262,7 @@ class TCPPacketTest < Test::Unit::TestCase
262
262
  puts "\n"
263
263
  puts "TCP Peek format: "
264
264
  puts t.peek
265
- assert_equal 78,t.peek.size
265
+ assert (t.peek.size <= 80)
266
266
  end
267
267
 
268
268
  def test_tcp_pcap
data/test/test_udp.rb CHANGED
@@ -24,7 +24,7 @@ class UDPTest < Test::Unit::TestCase
24
24
  puts "\n"
25
25
  puts "UDP Peek format: "
26
26
  puts u.peek
27
- assert_equal 78,u.peek.size
27
+ assert (u.peek.size <= 80)
28
28
  end
29
29
 
30
30
  def test_udp_pcap
data/test/udp_test.pcap CHANGED
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: packetfu
3
3
  version: !ruby/object:Gem::Version
4
- hash: -1237194367
4
+ hash: 199653271
5
5
  prerelease: 6
6
6
  segments:
7
7
  - 1
8
8
  - 0
9
- - 3
9
+ - 4
10
10
  - pre
11
- version: 1.0.3.pre
11
+ version: 1.0.4.pre
12
12
  platform: ruby
13
13
  authors:
14
14
  - Tod Beardsley
@@ -78,7 +78,6 @@ files:
78
78
  - lib/packetfu/protos/hsrp.rb
79
79
  - lib/packetfu/utils.rb
80
80
  - lib/packetfu/packet.rb
81
- - CHANGES
82
81
  - INSTALL
83
82
  - LICENSE
84
83
  - README
@@ -88,7 +87,6 @@ files:
88
87
  - test/udp_test.pcap
89
88
  - test/sample2.pcap
90
89
  - test/sample.pcap
91
- - test/dissect_thinger.rb
92
90
  - test/test_ip6.rb
93
91
  - test/all_tests.rb
94
92
  - test/test_invalid.rb
@@ -98,6 +96,7 @@ files:
98
96
  - test/icmp_test.pcap
99
97
  - test/test_udp.rb
100
98
  - test/sample_hsrp_pcapr.cap
99
+ - test/tcp_spec.rb
101
100
  - test/test_tcp.rb
102
101
  - test/tcp_test.pcap
103
102
  - test/test_arp.rb
@@ -127,6 +126,7 @@ files:
127
126
  - examples/idsv2.rb
128
127
  - examples/ackscan.rb
129
128
  - examples/ids.rb
129
+ - examples/new-simple-stats.rb
130
130
  has_rdoc: true
131
131
  homepage: http://code.google.com/p/packetfu/
132
132
  licenses: []
data/CHANGES DELETED
@@ -1,36 +0,0 @@
1
- = Changelog
2
-
3
- == Version 1.0.0 # to be released July 29, 2010
4
- Missed a bunch of updates in the Changelog. Mea culpa.
5
- Squashed all Ruby version bugs -- works now on 1.8.6, 1.8.7, 1.9.1, and 1.9.2-head.
6
- Added ENV['IFACE'] parsing for interface selection. Sadly, rvmsudo doesn't preserve ENV by default, but system ruby should work fine.
7
- Removed my own distros of pcaprub. You're on your own, now, but shadowbq's or Metasploit's versions should do just fine.
8
-
9
- == Version 0.1.1 # February 18, 2009
10
- Added .is_proto? functions, self.proto function, Packet.headers attr_accessor across the board.
11
- Removed dependency on pcaprub to read pcap-formatted files. (r54)
12
- Added ability to preserve timestamps when reading files using :keep_ts => true argument on Read#f2a and Write honors both saved timestamp or invented timestamps. (r55)
13
- Various minor bugs. (r56)
14
- Relaxed the requirement for PcapRub to 0.7-dev now that I handle packet reading on my own. Windows users and threading will be broke without 0.8-dev, though. (r57)
15
- Endianness of pcap files (for both reading and writing) is now supported (r58).
16
- Handle non-version-4 IP packets correctlier (r59) (thanks tmanning!).
17
- Merge of Metasploit-local patches, including Write.append (byte-order safe). (r60)
18
-
19
- == Version 0.1.0 # September 13, 2008
20
- Various minor bugs fixed.
21
- Added a Windows compatability mode via a compiled pcaprub.
22
- Note: Works fine on XP, works okay on Vista. Users are encouraged to compile their own pcaprub installations.
23
-
24
- == Verison 0.0.3 # September 3, 2008 # r25
25
- First tagged version. Naturally, bugs were found that moment.
26
-
27
- == Version 0.3.0 # January 11, 2010 # r129
28
- Rewrote pretty much everything using Struct instead of BinData. Huge success.
29
- Start to get back in the habit of documentation changes.
30
-
31
- == Version 0.3.1 # FUTURE
32
- r130: Add convenience methods for checking the version.
33
- r131: Fix TCP option setting.
34
- r132: pcaprub to 0.9-dev.
35
-
36
-
@@ -1,15 +0,0 @@
1
- # This just allows you to eyeball the dissection stuff to make sure it's all right.
2
-
3
- require File.join("..","lib","packetfu")
4
- puts "Loaded: PacketFu v#{PacketFu.version}"
5
- # $: << File.join(File.expand_path(File.dirname(__FILE__)),"..","lib")
6
-
7
- include PacketFu
8
-
9
- packets = PcapFile.file_to_array "test/sample2.pcap"
10
- packets.each do |packet|
11
- puts packet.inspect
12
- pkt = Packet.parse(packet)
13
- puts pkt.dissect
14
- sleep 1
15
- end