packetfu 1.0.3.pre → 1.0.4.pre

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