packetfu 1.1.5 → 1.1.6

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.
Files changed (52) hide show
  1. data/.document +5 -2
  2. data/.gitignore +1 -0
  3. data/LICENSE.txt +1 -1
  4. data/bench/after-2012-07-28.txt +25 -0
  5. data/bench/before-2012-07-28.txt +25 -0
  6. data/bench/benchit.rb +68 -0
  7. data/bench/calc_delta.rb +17 -0
  8. data/bench/octets.rb +22 -0
  9. data/bench/octets_after.txt +8 -0
  10. data/bench/octets_after_refactor.txt +8 -0
  11. data/bench/octets_before.txt +8 -0
  12. data/lib/packetfu.rb +8 -3
  13. data/lib/packetfu/packet.rb +2 -2
  14. data/lib/packetfu/pcap.rb +20 -4
  15. data/lib/packetfu/protos/arp.rb +7 -160
  16. data/lib/packetfu/protos/arp/header.rb +160 -0
  17. data/lib/packetfu/protos/arp/mixin.rb +38 -0
  18. data/lib/packetfu/protos/eth.rb +5 -247
  19. data/lib/packetfu/protos/eth/header.rb +247 -0
  20. data/lib/packetfu/protos/eth/mixin.rb +20 -0
  21. data/lib/packetfu/protos/hsrp.rb +13 -123
  22. data/lib/packetfu/protos/hsrp/header.rb +120 -0
  23. data/lib/packetfu/protos/hsrp/mixin.rb +31 -0
  24. data/lib/packetfu/protos/icmp.rb +10 -97
  25. data/lib/packetfu/protos/icmp/header.rb +93 -0
  26. data/lib/packetfu/protos/icmp/mixin.rb +17 -0
  27. data/lib/packetfu/protos/ip.rb +7 -295
  28. data/lib/packetfu/protos/ip/header.rb +335 -0
  29. data/lib/packetfu/protos/ip/mixin.rb +43 -0
  30. data/lib/packetfu/protos/ipv6.rb +7 -191
  31. data/lib/packetfu/protos/ipv6/header.rb +190 -0
  32. data/lib/packetfu/protos/ipv6/mixin.rb +31 -0
  33. data/lib/packetfu/protos/tcp.rb +13 -939
  34. data/lib/packetfu/protos/tcp/ecn.rb +42 -0
  35. data/lib/packetfu/protos/tcp/flags.rb +83 -0
  36. data/lib/packetfu/protos/tcp/header.rb +307 -0
  37. data/lib/packetfu/protos/tcp/hlen.rb +40 -0
  38. data/lib/packetfu/protos/tcp/mixin.rb +48 -0
  39. data/lib/packetfu/protos/tcp/option.rb +323 -0
  40. data/lib/packetfu/protos/tcp/options.rb +106 -0
  41. data/lib/packetfu/protos/tcp/reserved.rb +42 -0
  42. data/lib/packetfu/protos/udp.rb +12 -110
  43. data/lib/packetfu/protos/udp/header.rb +107 -0
  44. data/lib/packetfu/protos/udp/mixin.rb +23 -0
  45. data/lib/packetfu/utils.rb +24 -24
  46. data/lib/packetfu/version.rb +1 -1
  47. data/packetfu.gemspec +2 -2
  48. data/test/test_ip.rb +0 -19
  49. data/test/test_octets.rb +18 -21
  50. data/test/test_tcp.rb +10 -0
  51. data/test/test_udp.rb +17 -0
  52. metadata +79 -50
@@ -0,0 +1,42 @@
1
+ module PacketFu
2
+ # Implements the Reserved bits for TCPHeader.
3
+ #
4
+ # ==== Header Definition
5
+ #
6
+ #
7
+ # Fixnum (1 bit) :r1
8
+ # Fixnum (1 bit) :r2
9
+ # Fixnum (1 bit) :r3
10
+ class TcpReserved < Struct.new(:r1, :r2, :r3)
11
+
12
+ include StructFu
13
+
14
+ def initialize(args={})
15
+ super(
16
+ args[:r1] || 0,
17
+ args[:r2] || 0,
18
+ args[:r3] || 0) if args.kind_of? Hash
19
+ end
20
+
21
+ # Returns the Reserved field as an integer.
22
+ def to_i
23
+ (r1.to_i << 2) + (r2.to_i << 1) + r3.to_i
24
+ end
25
+
26
+ # Reads a string to populate the object.
27
+ def read(str)
28
+ force_binary(str)
29
+ return self if str.nil? || str.size.zero?
30
+ if 1.respond_to? :ord
31
+ byte = str[0].ord
32
+ else
33
+ byte = str[0]
34
+ end
35
+ self[:r1] = byte & 0b00000100 == 0b00000100 ? 1 : 0
36
+ self[:r2] = byte & 0b00000010 == 0b00000010 ? 1 : 0
37
+ self[:r3] = byte & 0b00000001 == 0b00000001 ? 1 : 0
38
+ self
39
+ end
40
+
41
+ end
42
+ end
@@ -1,109 +1,13 @@
1
- module PacketFu
2
-
3
- # UDPHeader is a complete UDP struct, used in UDPPacket. Many Internet-critical protocols
4
- # rely on UDP, such as DNS and World of Warcraft.
5
- #
6
- # For more on UDP packets, see http://www.networksorcery.com/enp/protocol/udp.htm
7
- #
8
- # ==== Header Definition
9
- # Int16 :udp_src
10
- # Int16 :udp_dst
11
- # Int16 :udp_len Default: calculated
12
- # Int16 :udp_sum Default: 0. Often calculated.
13
- # String :body
14
- class UDPHeader < Struct.new(:udp_src, :udp_dst, :udp_len, :udp_sum, :body)
15
-
16
- include StructFu
17
-
18
- def initialize(args={})
19
- super(
20
- Int16.new(args[:udp_src]),
21
- Int16.new(args[:udp_dst]),
22
- Int16.new(args[:udp_len] || udp_calc_len),
23
- Int16.new(args[:udp_sum]),
24
- StructFu::String.new.read(args[:body])
25
- )
26
- end
27
-
28
- # Returns the object in string form.
29
- def to_s
30
- self.to_a.map {|x| x.to_s}.join
31
- end
32
-
33
- # Reads a string to populate the object.
34
- def read(str)
35
- force_binary(str)
36
- return self if str.nil?
37
- self[:udp_src].read(str[0,2])
38
- self[:udp_dst].read(str[2,2])
39
- self[:udp_len].read(str[4,2])
40
- self[:udp_sum].read(str[6,2])
41
- self[:body].read(str[8,str.size])
42
- self
43
- end
44
-
45
- # Setter for the UDP source port.
46
- def udp_src=(i); typecast i; end
47
- # Getter for the UDP source port.
48
- def udp_src; self[:udp_src].to_i; end
49
- # Setter for the UDP destination port.
50
- def udp_dst=(i); typecast i; end
51
- # Getter for the UDP destination port.
52
- def udp_dst; self[:udp_dst].to_i; end
53
- # Setter for the length field. Usually should be recalc()'ed instead.
54
- def udp_len=(i); typecast i; end
55
- # Getter for the length field.
56
- def udp_len; self[:udp_len].to_i; end
57
- # Setter for the checksum. Usually should be recalc()'ed instad.
58
- def udp_sum=(i); typecast i; end
59
- # Getter for the checksum.
60
- def udp_sum; self[:udp_sum].to_i; end
61
-
62
- # Returns the true length of the UDP packet.
63
- def udp_calc_len
64
- body.to_s.size + 8
65
- end
66
-
67
- # Recalculates calculated fields for UDP.
68
- def udp_recalc(args=:all)
69
- arg = arg.intern if arg.respond_to? :intern
70
- case args
71
- when :udp_len
72
- self.udp_len = udp_calc_len
73
- when :all
74
- self.udp_recalc(:udp_len)
75
- else
76
- raise ArgumentError, "No such field `#{arg}'"
77
- end
78
- end
1
+ require 'packetfu/protos/eth/header'
2
+ require 'packetfu/protos/eth/mixin'
79
3
 
80
- # Equivalent to udp_src.to_i
81
- def udp_sport
82
- self.udp_src
83
- end
4
+ require 'packetfu/protos/ip/header'
5
+ require 'packetfu/protos/ip/mixin'
84
6
 
85
- # Equivalent to udp_src=
86
- def udp_sport=(arg)
87
- self.udp_src=(arg)
88
- end
7
+ require 'packetfu/protos/udp/header'
8
+ require 'packetfu/protos/udp/mixin'
89
9
 
90
- # Equivalent to udp_dst
91
- def udp_dport
92
- self.udp_dst
93
- end
94
-
95
- # Equivalent to udp_dst=
96
- def udp_dport=(arg)
97
- self.udp_dst=(arg)
98
- end
99
-
100
- # Readability aliases
101
-
102
- def udp_sum_readable
103
- "0x%04x" % udp_sum
104
- end
105
-
106
- end
10
+ module PacketFu
107
11
 
108
12
  # UDPPacket is used to construct UDP Packets. They contain an EthHeader, an IPHeader, and a UDPHeader.
109
13
  #
@@ -131,6 +35,9 @@ module PacketFu
131
35
  # :config
132
36
  # A hash of return address details, often the output of Utils.whoami?
133
37
  class UDPPacket < Packet
38
+ include ::PacketFu::EthHeaderMixin
39
+ include ::PacketFu::IPHeaderMixin
40
+ include ::PacketFu::UDPHeaderMixin
134
41
 
135
42
  attr_accessor :eth_header, :ip_header, :udp_header
136
43
 
@@ -145,15 +52,10 @@ module PacketFu
145
52
  def read(str=nil, args={})
146
53
  raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
147
54
  @eth_header.read(str)
148
- @ip_header.read(str[14,str.size])
149
- @eth_header.body = @ip_header
150
55
  if args[:strip]
151
- udp_len = str[16,2].unpack("n")[0] - 20
152
- @udp_header.read(str[14+(@ip_header.ip_hlen),udp_len])
153
- else
154
- @udp_header.read(str[14+(@ip_header.ip_hlen),str.size])
56
+ udp_body_len = self.ip_len - self.ip_hlen - 8
57
+ @udp_header.body.read(@udp_header.body.to_s[0,udp_body_len])
155
58
  end
156
- @ip_header.body = @udp_header
157
59
  super(args)
158
60
  self
159
61
  end
@@ -0,0 +1,107 @@
1
+ module PacketFu
2
+
3
+ # UDPHeader is a complete UDP struct, used in UDPPacket. Many Internet-critical protocols
4
+ # rely on UDP, such as DNS and World of Warcraft.
5
+ #
6
+ # For more on UDP packets, see http://www.networksorcery.com/enp/protocol/udp.htm
7
+ #
8
+ # ==== Header Definition
9
+ # Int16 :udp_src
10
+ # Int16 :udp_dst
11
+ # Int16 :udp_len Default: calculated
12
+ # Int16 :udp_sum Default: 0. Often calculated.
13
+ # String :body
14
+ class UDPHeader < Struct.new(:udp_src, :udp_dst, :udp_len, :udp_sum, :body)
15
+
16
+ include StructFu
17
+
18
+ def initialize(args={})
19
+ super(
20
+ Int16.new(args[:udp_src]),
21
+ Int16.new(args[:udp_dst]),
22
+ Int16.new(args[:udp_len] || udp_calc_len),
23
+ Int16.new(args[:udp_sum]),
24
+ StructFu::String.new.read(args[:body])
25
+ )
26
+ end
27
+
28
+ # Returns the object in string form.
29
+ def to_s
30
+ self.to_a.map {|x| x.to_s}.join
31
+ end
32
+
33
+ # Reads a string to populate the object.
34
+ def read(str)
35
+ force_binary(str)
36
+ return self if str.nil?
37
+ self[:udp_src].read(str[0,2])
38
+ self[:udp_dst].read(str[2,2])
39
+ self[:udp_len].read(str[4,2])
40
+ self[:udp_sum].read(str[6,2])
41
+ self[:body].read(str[8,str.size])
42
+ self
43
+ end
44
+
45
+ # Setter for the UDP source port.
46
+ def udp_src=(i); typecast i; end
47
+ # Getter for the UDP source port.
48
+ def udp_src; self[:udp_src].to_i; end
49
+ # Setter for the UDP destination port.
50
+ def udp_dst=(i); typecast i; end
51
+ # Getter for the UDP destination port.
52
+ def udp_dst; self[:udp_dst].to_i; end
53
+ # Setter for the length field. Usually should be recalc()'ed instead.
54
+ def udp_len=(i); typecast i; end
55
+ # Getter for the length field.
56
+ def udp_len; self[:udp_len].to_i; end
57
+ # Setter for the checksum. Usually should be recalc()'ed instad.
58
+ def udp_sum=(i); typecast i; end
59
+ # Getter for the checksum.
60
+ def udp_sum; self[:udp_sum].to_i; end
61
+
62
+ # Returns the true length of the UDP packet.
63
+ def udp_calc_len
64
+ body.to_s.size + 8
65
+ end
66
+
67
+ # Recalculates calculated fields for UDP.
68
+ def udp_recalc(args=:all)
69
+ arg = arg.intern if arg.respond_to? :intern
70
+ case args
71
+ when :udp_len
72
+ self.udp_len = udp_calc_len
73
+ when :all
74
+ self.udp_recalc(:udp_len)
75
+ else
76
+ raise ArgumentError, "No such field `#{arg}'"
77
+ end
78
+ end
79
+
80
+ # Equivalent to udp_src.to_i
81
+ def udp_sport
82
+ self.udp_src
83
+ end
84
+
85
+ # Equivalent to udp_src=
86
+ def udp_sport=(arg)
87
+ self.udp_src=(arg)
88
+ end
89
+
90
+ # Equivalent to udp_dst
91
+ def udp_dport
92
+ self.udp_dst
93
+ end
94
+
95
+ # Equivalent to udp_dst=
96
+ def udp_dport=(arg)
97
+ self.udp_dst=(arg)
98
+ end
99
+
100
+ # Readability aliases
101
+
102
+ def udp_sum_readable
103
+ "0x%04x" % udp_sum
104
+ end
105
+
106
+ end
107
+ end
@@ -0,0 +1,23 @@
1
+ module PacketFu
2
+ # This Mixin simplifies access to the UDPHeaders. Mix this in with your
3
+ # packet interface, and it will add methods that essentially delegate to
4
+ # the 'udp_header' method (assuming that it is a UDPHeader object)
5
+ module UDPHeaderMixin
6
+ def udp_src=(v); self.udp_header.udp_src= v; end
7
+ def udp_src; self.udp_header.udp_src; end
8
+ def udp_dst=(v); self.udp_header.udp_dst= v; end
9
+ def udp_dst; self.udp_header.udp_dst; end
10
+ def udp_len=(v); self.udp_header.udp_len= v; end
11
+ def udp_len; self.udp_header.udp_len; end
12
+ def udp_sum=(v); self.udp_header.udp_sum= v; end
13
+ def udp_sum; self.udp_header.udp_sum; end
14
+ def udp_calc_len; self.udp_header.udp_calc_len; end
15
+ def udp_recalc(*v); self.udp_header.udp_recalc(*v); end
16
+ def udp_sport; self.udp_header.udp_sport; end
17
+ def udp_sport=(v); self.udp_header.udp_sport= v; end
18
+ def udp_dport; self.udp_header.udp_dport; end
19
+ def udp_dport=(v); self.udp_header.udp_dport= v; end
20
+ def udp_sum_readable; self.udp_header.udp_sum_readable; end
21
+ end
22
+ end
23
+
@@ -183,30 +183,30 @@ module PacketFu
183
183
  ret[:ip6_obj] = IPAddr.new($1)
184
184
  end
185
185
  end # linux
186
- when /darwin/i
187
- ifconfig_data = %x[ifconfig #{iface}]
188
- if ifconfig_data =~ /#{iface}/i
189
- ifconfig_data = ifconfig_data.split(/[\s]*\n[\s]*/)
190
- else
191
- raise ArgumentError, "Cannot ifconfig #{iface}"
192
- end
193
- real_iface = ifconfig_data.first
194
- ret[:iface] = real_iface.split(':')[0]
195
- ifconfig_data.each do |s|
196
- case s
197
- when /ether[\s]([0-9a-fA-F:]{17})/i
198
- ret[:eth_saddr] = $1
199
- ret[:eth_src] = EthHeader.mac2str(ret[:eth_saddr])
200
- when /inet[\s]*([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(.*Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+))?/i
201
- ret[:ip_saddr] = $1
202
- ret[:ip_src] = [IPAddr.new($1).to_i].pack("N")
203
- ret[:ip4_obj] = IPAddr.new($1)
204
- ret[:ip4_obj] = ret[:ip4_obj].mask($3) if $3
205
- when /inet6[\s]*([0-9a-fA-F:\x2f]+)/
206
- ret[:ip6_saddr] = $1
207
- ret[:ip6_obj] = IPAddr.new($1)
208
- end
209
- end # darwin
186
+ when /darwin/i
187
+ ifconfig_data = %x[ifconfig #{iface}]
188
+ if ifconfig_data =~ /#{iface}/i
189
+ ifconfig_data = ifconfig_data.split(/[\s]*\n[\s]*/)
190
+ else
191
+ raise ArgumentError, "Cannot ifconfig #{iface}"
192
+ end
193
+ real_iface = ifconfig_data.first
194
+ ret[:iface] = real_iface.split(':')[0]
195
+ ifconfig_data.each do |s|
196
+ case s
197
+ when /ether[\s]([0-9a-fA-F:]{17})/i
198
+ ret[:eth_saddr] = $1
199
+ ret[:eth_src] = EthHeader.mac2str(ret[:eth_saddr])
200
+ when /inet[\s]*([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(.*Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+))?/i
201
+ ret[:ip_saddr] = $1
202
+ ret[:ip_src] = [IPAddr.new($1).to_i].pack("N")
203
+ ret[:ip4_obj] = IPAddr.new($1)
204
+ ret[:ip4_obj] = ret[:ip4_obj].mask($3) if $3
205
+ when /inet6[\s]*([0-9a-fA-F:\x2f]+)/
206
+ ret[:ip6_saddr] = $1
207
+ ret[:ip6_obj] = IPAddr.new($1)
208
+ end
209
+ end # darwin
210
210
  end # RUBY_PLATFORM
211
211
  ret
212
212
  end
@@ -1,7 +1,7 @@
1
1
  module PacketFu
2
2
 
3
3
  # Check the repo's for version release histories
4
- VERSION = "1.1.5" # Unscrewing the 1.1.4 gem
4
+ VERSION = "1.1.6"
5
5
 
6
6
  # Returns PacketFu::VERSION
7
7
  def self.version
data/packetfu.gemspec CHANGED
@@ -2,9 +2,9 @@ require 'rake'
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'packetfu'
5
- s.version = '1.1.5'
5
+ s.version = '1.1.6'
6
6
  s.authors = ['Tod Beardsley']
7
- s.email = 'todb@planb-security.net'
7
+ s.email = 'todb@packetfu.com'
8
8
  s.summary = 'PacketFu is a mid-level packet manipulation library.'
9
9
  s.homepage = 'https://github.com/todb/packetfu'
10
10
  s.description = %q{PacketFu is a mid-level packet manipulation library for Ruby. With it, users can read, parse, and write network packets with the level of ease and fun they expect from Ruby. Note that this gem does not automatically require pcaprub, since users may install pcaprub through non-gem means.}
data/test/test_ip.rb CHANGED
@@ -3,25 +3,6 @@ require 'test/unit'
3
3
  $:.unshift File.join(File.expand_path(File.dirname(__FILE__)), "..", "lib")
4
4
  require 'packetfu'
5
5
 
6
- class OctetsTest < Test::Unit::TestCase
7
- include PacketFu
8
-
9
- def test_octets_read
10
- o = Octets.new
11
- o.read("\x04\x03\x02\x01")
12
- assert_equal("4.3.2.1", o.to_x)
13
- end
14
-
15
- def test_octets_read_quad
16
- o = Octets.new
17
- o.read_quad("1.2.3.4")
18
- assert_equal("1.2.3.4", o.to_x)
19
- assert_equal("\x01\x02\x03\x04", o.to_s)
20
- assert_equal(0x01020304, o.to_i)
21
- end
22
-
23
- end
24
-
25
6
  class IPTest < Test::Unit::TestCase
26
7
  include PacketFu
27
8
 
data/test/test_octets.rb CHANGED
@@ -3,33 +3,30 @@ require 'test/unit'
3
3
  $:.unshift File.join(File.expand_path(File.dirname(__FILE__)), "..", "lib")
4
4
  require 'packetfu'
5
5
 
6
- class OctetTest < Test::Unit::TestCase
6
+ class OctetsTest < Test::Unit::TestCase
7
7
  include PacketFu
8
8
 
9
- def setup
10
- @o = Octets.new
9
+ def test_octets_read
10
+ o = Octets.new
11
+ o.read("\x04\x03\x02\x01")
12
+ assert_equal("4.3.2.1", o.to_x)
11
13
  end
12
14
 
13
- def test_create_octets
14
- assert_kind_of Octets, @o
15
+ def test_octets_read_quad
16
+ o = Octets.new
17
+ o.read_quad("1.2.3.4")
18
+ assert_equal("1.2.3.4", o.to_x)
19
+ assert_equal("\x01\x02\x03\x04", o.to_s)
20
+ assert_equal(0x01020304, o.to_i)
15
21
  end
16
22
 
17
- def test_read
18
- s = "\x0a\x0a\x0a\x0b"
19
- @o.read s
20
- assert_equal(s, @o.to_s)
21
- end
22
-
23
- def test_dotted
24
- s = "\x0a\x0a\x0a\x01"
25
- @o.read s
26
- assert_equal("10.10.10.1", @o.to_x)
27
- end
28
-
29
- def test_numerical
30
- s = "\x00\x00\x00\x80"
31
- @o.read s
32
- assert_equal(128, @o.to_i)
23
+ def test_octets_single_octet
24
+ o = Octets.new
25
+ o.read("ABCD")
26
+ assert_equal(o.o1, 0x41)
27
+ assert_equal(o.o2, 0x42)
28
+ assert_equal(o.o3, 0x43)
29
+ assert_equal(o.o4, 0x44)
33
30
  end
34
31
 
35
32
  end