packetfu 1.1.13 → 2.0.0

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 (56) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/ISSUE_TEMPLATE.md +29 -0
  4. data/.github/workflows/verify.yml +72 -0
  5. data/.travis.yml +10 -6
  6. data/LICENSE.txt +1 -1
  7. data/README.md +8 -8
  8. data/certs/todb.pem +25 -0
  9. data/examples/100kpackets.rb +2 -2
  10. data/examples/ackscan.rb +7 -6
  11. data/examples/pcap2pcapng.rb +2 -2
  12. data/examples/readpcap.rb +28 -0
  13. data/lib/packetfu/capture.rb +1 -1
  14. data/lib/packetfu/config.rb +2 -2
  15. data/lib/packetfu/inject.rb +1 -1
  16. data/lib/packetfu/packet.rb +6 -3
  17. data/lib/packetfu/pcap.rb +25 -25
  18. data/lib/packetfu/pcapng/file.rb +1 -1
  19. data/lib/packetfu/protos/arp.rb +1 -8
  20. data/lib/packetfu/protos/eth.rb +0 -7
  21. data/lib/packetfu/protos/hsrp.rb +0 -7
  22. data/lib/packetfu/protos/icmp/header.rb +7 -10
  23. data/lib/packetfu/protos/icmp.rb +0 -7
  24. data/lib/packetfu/protos/icmpv6.rb +4 -17
  25. data/lib/packetfu/protos/ip/header.rb +2 -2
  26. data/lib/packetfu/protos/ip/mixin.rb +9 -0
  27. data/lib/packetfu/protos/ip.rb +0 -8
  28. data/lib/packetfu/protos/ipv6/mixin.rb +12 -0
  29. data/lib/packetfu/protos/ipv6.rb +0 -7
  30. data/lib/packetfu/protos/lldp.rb +1 -8
  31. data/lib/packetfu/protos/tcp.rb +73 -30
  32. data/lib/packetfu/protos/udp/header.rb +4 -5
  33. data/lib/packetfu/protos/udp.rb +6 -18
  34. data/lib/packetfu/structfu.rb +1 -1
  35. data/lib/packetfu/version.rb +1 -1
  36. data/packetfu.gemspec +10 -18
  37. data/spec/arp_spec.rb +1 -1
  38. data/spec/capture_spec.rb +137 -0
  39. data/spec/eth_spec.rb +1 -1
  40. data/spec/icmp_spec.rb +1 -1
  41. data/spec/icmpv6_spec.rb +1 -1
  42. data/spec/inject_spec.rb +95 -0
  43. data/spec/ip_spec.rb +23 -1
  44. data/spec/packetfu_spec.rb +1 -1
  45. data/spec/pcap_spec.rb +3 -3
  46. data/spec/pcapng/file_spec.rb +1 -1
  47. data/spec/spec_helper.rb +4 -2
  48. data/spec/structfu_spec.rb +86 -82
  49. data/spec/tcp_spec.rb +155 -53
  50. data/test/sample-ipv6.pcap +0 -0
  51. data.tar.gz.sig +0 -0
  52. metadata +64 -37
  53. metadata.gz.sig +0 -0
  54. data/test/test_capture.rb +0 -58
  55. data/test/test_inject.rb +0 -31
  56. data/test/test_structfu.rb +0 -114
@@ -23,7 +23,7 @@ module PacketFu
23
23
  # icmpv6_pkt.ipv6_saddr="2000::1234"
24
24
  # icmpv6_pkt.ipv6_daddr="2000::5678"
25
25
  #
26
- # icmpv6_pkt.recalc
26
+ # icmpv6_pkt.recalc
27
27
  # icmpv6_pkt.to_f('/tmp/icmpv6.pcap')
28
28
  #
29
29
  # == Parameters
@@ -49,13 +49,6 @@ module PacketFu
49
49
  return true
50
50
  end
51
51
 
52
- def read(str=nil, args={})
53
- raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
54
- @eth_header.read(str)
55
- super(args)
56
- self
57
- end
58
-
59
52
  def initialize(args={})
60
53
  @eth_header = EthHeader.new(args).read(args[:eth])
61
54
  @ipv6_header = IPv6Header.new(args).read(args[:ipv6])
@@ -72,12 +65,7 @@ module PacketFu
72
65
 
73
66
  # Calculates the checksum for the object.
74
67
  def icmpv6_calc_sum
75
- checksum = 0
76
-
77
- # Compute sum on pseudo-header
78
- [ipv6_src, ipv6_dst].each do |iaddr|
79
- 8.times { |i| checksum += (iaddr >> (i*16)) & 0xffff }
80
- end
68
+ checksum = ipv6_calc_sum_on_addr
81
69
  checksum += PacketFu::ICMPv6Header::PROTOCOL_NUMBER
82
70
  checksum += ipv6_len
83
71
  # Then compute it on ICMPv6 header + payload
@@ -96,9 +84,8 @@ module PacketFu
96
84
  end
97
85
 
98
86
  # Recalculates the calculatable fields for ICMPv6.
99
- def icmpv6_recalc(arg=:all)
100
- arg = arg.intern if arg.respond_to? :intern
101
- case arg
87
+ def icmpv6_recalc(arg = :all)
88
+ case arg.to_sym
102
89
  when :icmpv6_sum
103
90
  self.icmpv6_sum = icmpv6_calc_sum
104
91
  when :all
@@ -107,7 +107,7 @@ module PacketFu
107
107
  # Int16 :ip_len, Default: calculated
108
108
  # Int16 :ip_id, Default: calculated # IRL, hardly random.
109
109
  # Int16 :ip_frag, Default: 0 # TODO: Break out the bits
110
- # Int8 :ip_ttl, Default: 0xff # Changes per flavor
110
+ # Int8 :ip_ttl, Default: 64 # https://www.iana.org/assignments/ip-parameters/ip-parameters.xml
111
111
  # Int8 :ip_proto, Default: 0x01 # TCP: 0x06, UDP 0x11, ICMP 0x01
112
112
  # Int16 :ip_sum, Default: calculated
113
113
  # Octets :ip_src
@@ -131,7 +131,7 @@ module PacketFu
131
131
  Int16.new(args[:ip_len] || 20),
132
132
  Int16.new(args[:ip_id] || ip_calc_id),
133
133
  Int16.new(args[:ip_frag]),
134
- Int8.new(args[:ip_ttl] || 32),
134
+ Int8.new(args[:ip_ttl] || 64),
135
135
  Int8.new(args[:ip_proto]),
136
136
  Int16.new(args[:ip_sum] || ip_calc_sum),
137
137
  Octets.new.read(args[:ip_src] || "\x00\x00\x00\x00"),
@@ -40,5 +40,14 @@ module PacketFu
40
40
  def ip_ttl=(v); self.ip_header.ip_ttl= v; end
41
41
  def ip_v; self.ip_header.ip_v ; end
42
42
  def ip_v=(v); self.ip_header.ip_v= v; end
43
+
44
+ # Add method to each packet class on IP to ease checksum computation
45
+ def ip_calc_sum_on_addr(cksum=0)
46
+ checksum = cksum
47
+ checksum += (ip_src.to_i >> 16)
48
+ checksum += (ip_src.to_i & 0xffff)
49
+ checksum += (ip_dst.to_i >> 16)
50
+ checksum += (ip_dst.to_i & 0xffff)
51
+ end
43
52
  end
44
53
  end
@@ -16,7 +16,6 @@ module PacketFu
16
16
  # ip_pkt.ip_saddr="10.20.30.40"
17
17
  # ip_pkt.ip_daddr="192.168.1.1"
18
18
  # ip_pkt.ip_proto=1
19
- # ip_pkt.ip_ttl=64
20
19
  # ip_pkt.ip_payload="\x00\x00\x12\x34\x00\x01\x00\x01"+
21
20
  # "Lovingly hand-crafted echo responses delivered directly to your door."
22
21
  # ip_pkt.recalc
@@ -54,13 +53,6 @@ module PacketFu
54
53
  end
55
54
  end
56
55
 
57
- def read(str=nil, args={})
58
- raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
59
- @eth_header.read(str)
60
- super(args)
61
- self
62
- end
63
-
64
56
  # Creates a new IPPacket object.
65
57
  def initialize(args={})
66
58
  @eth_header = EthHeader.new(args).read(args[:eth])
@@ -28,5 +28,17 @@ module PacketFu
28
28
  def ipv6_daddr=(v); self.ipv6_header.ipv6_daddr= v; end
29
29
  def ipv6_src_readable; self.ipv6_header.ipv6_src_readable; end
30
30
  def ipv6_dst_readable; self.ipv6_header.ipv6_dst_readable; end
31
+ def ipv6?; not self.ipv6_header.nil?; end
32
+
33
+ # Add method to each packet class on IPv6 to ease checksum computation
34
+ def ipv6_calc_sum_on_addr(cksum=0)
35
+ checksum = cksum
36
+ [ipv6_src, ipv6_dst].each do |iaddr|
37
+ 8.times do |i|
38
+ checksum += (iaddr >> (i * 16)) & 0xffff
39
+ end
40
+ end
41
+ checksum
42
+ end
31
43
  end
32
44
  end
@@ -34,13 +34,6 @@ module PacketFu
34
34
  true
35
35
  end
36
36
 
37
- def read(str=nil,args={})
38
- raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
39
- @eth_header.read(str)
40
- super(args)
41
- self
42
- end
43
-
44
37
  def initialize(args={})
45
38
  @eth_header = (args[:eth] || EthHeader.new)
46
39
  @ipv6_header = (args[:ipv6] || IPv6Header.new)
@@ -8,7 +8,7 @@ require 'packetfu/protos/lldp/mixin'
8
8
  module PacketFu
9
9
 
10
10
  class LLDPPacket < Packet
11
- MAGIC = Regexp.new("^\x01\x80\xc2\x00\x00[\x0e\x03\x00]", nil, "n")
11
+ MAGIC = Regexp.new("^\x01\x80\xc2\x00\x00[\x0e\x03\x00]".force_encoding('ASCII-8BIT'), Regexp::NOENCODING)
12
12
  include ::PacketFu::EthHeaderMixin
13
13
  include ::PacketFu::LLDPHeaderMixin
14
14
 
@@ -22,13 +22,6 @@ module PacketFu
22
22
  true
23
23
  end
24
24
 
25
- def read(str=nil,args={})
26
- raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
27
- @eth_header.read(str)
28
- super(args)
29
- self
30
- end
31
-
32
25
  def initialize(args={})
33
26
  @eth_header = EthHeader.new(args).read(args[:eth])
34
27
  @lldp_header = LLDPHeader.new(args).read(args[:lldp])
@@ -8,6 +8,9 @@ require 'packetfu/protos/tcp/mixin'
8
8
  require 'packetfu/protos/ip/header'
9
9
  require 'packetfu/protos/ip/mixin'
10
10
 
11
+ require 'packetfu/protos/ipv6/header'
12
+ require 'packetfu/protos/ipv6/mixin'
13
+
11
14
  module PacketFu
12
15
  # TCPPacket is used to construct TCP packets. They contain an EthHeader, an IPHeader, and a TCPHeader.
13
16
  #
@@ -25,6 +28,16 @@ module PacketFu
25
28
  # tcp_pkt.recalc
26
29
  # tcp_pkt.to_f('/tmp/tcp.pcap')
27
30
  #
31
+ # tcp6_pkt = PacketFu::TCPPacket.new(:on_ipv6 => true)
32
+ # tcp6_pkt.tcp_flags.syn=1
33
+ # tcp6_pkt.tcp_dst=80
34
+ # tcp6_pkt.tcp_win=5840
35
+ # tcp6_pkt.tcp_options="mss:1460,sack.ok,ts:#{rand(0xffffffff)};0,nop,ws:7"
36
+ # tcp6_pkt.ipv6_saddr="4::1"
37
+ # tcp6_pkt.ipv6_daddr="12:3::4567"
38
+ # tcp6_pkt.recalc
39
+ # tcp6_pkt.to_f('/tmp/udp.pcap')
40
+ #
28
41
  # == Parameters
29
42
  # :eth
30
43
  # A pre-generated EthHeader object.
@@ -41,42 +54,58 @@ module PacketFu
41
54
  class TCPPacket < Packet
42
55
  include ::PacketFu::EthHeaderMixin
43
56
  include ::PacketFu::IPHeaderMixin
57
+ include ::PacketFu::IPv6HeaderMixin
44
58
  include ::PacketFu::TCPHeaderMixin
45
59
 
46
- attr_accessor :eth_header, :ip_header, :tcp_header
60
+ attr_accessor :eth_header, :ip_header, :ipv6_header, :tcp_header
47
61
 
48
62
  def self.can_parse?(str)
49
63
  return false unless str.size >= 54
50
64
  return false unless EthPacket.can_parse? str
51
- return false unless IPPacket.can_parse? str
52
- return false unless str[23,1] == "\x06"
53
- return true
65
+ if IPPacket.can_parse? str
66
+ return true if str[23,1] == "\x06"
67
+ elsif IPv6Packet.can_parse? str
68
+ return true if str[20,1] == "\x06"
69
+ end
70
+ return false
54
71
  end
55
72
 
56
73
  def read(str=nil, args={})
57
- raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
58
- @eth_header.read(str)
59
-
74
+ super
60
75
  # Strip off any extra data, if we are asked to do so.
61
76
  if args[:strip]
62
77
  tcp_body_len = self.ip_len - self.ip_hlen - (self.tcp_hlen * 4)
63
78
  @tcp_header.body.read(@tcp_header.body.to_s[0,tcp_body_len])
79
+ tcp_calc_sum
80
+ @ip_header.ip_recalc
64
81
  end
65
- super(args)
66
82
  self
67
83
  end
68
84
 
69
85
  def initialize(args={})
70
- @eth_header = (args[:eth] || EthHeader.new)
71
- @ip_header = (args[:ip] || IPHeader.new)
72
- @tcp_header = (args[:tcp] || TCPHeader.new)
73
- @tcp_header.flavor = args[:flavor].to_s.downcase
86
+ if args[:on_ipv6] or args[:ipv6]
87
+ @eth_header = EthHeader.new(args.merge(:eth_proto => 0x86dd)).read(args[:eth])
88
+ @ipv6_header = IPv6Header.new(args).read(args[:ipv6])
89
+ @tcp_header = TCPHeader.new(args).read(args[:tcp])
90
+
91
+ @ipv6_header.body = @tcp_header
92
+ @eth_header.body = @ipv6_header
93
+ @headers = [@eth_header, @ipv6_header, @tcp_header]
94
+
95
+ @ipv6_header.ipv6_next = 0x06
96
+ else
97
+ @eth_header = EthHeader.new(args.merge(:eth_proto => 0x0800)).read(args[:eth])
98
+ @ip_header = IPHeader.new(args).read(args[:ip])
99
+ @tcp_header = TCPHeader.new(args).read(args[:tcp])
74
100
 
75
- @ip_header.body = @tcp_header
76
- @eth_header.body = @ip_header
77
- @headers = [@eth_header, @ip_header, @tcp_header]
101
+ @ip_header.body = @tcp_header
102
+ @eth_header.body = @ip_header
103
+ @headers = [@eth_header, @ip_header, @tcp_header]
104
+
105
+ @ip_header.ip_proto = 0x06
106
+ end
107
+ @tcp_header.flavor = args[:flavor].to_s.downcase
78
108
 
79
- @ip_header.ip_proto=0x06
80
109
  super
81
110
  if args[:flavor]
82
111
  tcp_calc_flavor(@tcp_header.flavor)
@@ -118,12 +147,16 @@ module PacketFu
118
147
  # from the IP header, too.
119
148
  #++
120
149
  def tcp_calc_sum
121
- checksum = (ip_src.to_i >> 16)
122
- checksum += (ip_src.to_i & 0xffff)
123
- checksum += (ip_dst.to_i >> 16)
124
- checksum += (ip_dst.to_i & 0xffff)
150
+ if @ipv6_header
151
+ checksum = ipv6_calc_sum_on_addr
152
+ tcp_len = ipv6_len
153
+ else
154
+ checksum = ip_calc_sum_on_addr
155
+ tcp_len = ip_len.to_i - ((ip_hl.to_i) * 4)
156
+ end
157
+
125
158
  checksum += 0x06 # TCP Protocol.
126
- checksum += (ip_len.to_i - ((ip_hl.to_i) * 4))
159
+ checksum += tcp_len
127
160
  checksum += tcp_src
128
161
  checksum += tcp_dst
129
162
  checksum += (tcp_seq.to_i >> 16)
@@ -140,8 +173,8 @@ module PacketFu
140
173
 
141
174
  chk_tcp_opts = (tcp_opts.to_s.size % 2 == 0 ? tcp_opts.to_s : tcp_opts.to_s + "\x00")
142
175
  chk_tcp_opts.unpack("n*").each {|x| checksum = checksum + x }
143
- if (ip_len - ((ip_hl + tcp_hlen) * 4)) >= 0
144
- real_tcp_payload = payload[0,( ip_len - ((ip_hl + tcp_hlen) * 4) )] # Can't forget those pesky FCSes!
176
+ if (tcp_len - (tcp_hlen * 4)) >= 0
177
+ real_tcp_payload = payload[0, (tcp_len - (tcp_hlen * 4))] # Can't forget those pesky FCSes!
145
178
  else
146
179
  real_tcp_payload = payload # Something's amiss here so don't bother figuring out where the real payload is.
147
180
  end
@@ -181,19 +214,29 @@ module PacketFu
181
214
  # source and dest information, packet flags, sequence
182
215
  # number, and IPID.
183
216
  def peek_format
184
- peek_data = ["T "]
185
- peek_data << "%-5d" % self.to_s.size
186
- peek_data << "%-21s" % "#{self.ip_saddr}:#{self.tcp_src}"
187
- peek_data << "->"
188
- peek_data << "%21s" % "#{self.ip_daddr}:#{self.tcp_dst}"
217
+ if ipv6?
218
+ peek_data = ["6T "]
219
+ peek_data << "%-5d" % self.to_s.size
220
+ peek_data << "%-31s" % "#{self.ipv6_saddr}:#{self.tcp_src}"
221
+ peek_data << "->"
222
+ peek_data << "%31s" % "#{self.ipv6_daddr}:#{self.tcp_dst}"
223
+ else
224
+ peek_data = ["T "]
225
+ peek_data << "%-5d" % self.to_s.size
226
+ peek_data << "%-21s" % "#{self.ip_saddr}:#{self.tcp_src}"
227
+ peek_data << "->"
228
+ peek_data << "%21s" % "#{self.ip_daddr}:#{self.tcp_dst}"
229
+ end
189
230
  flags = ' ['
190
231
  flags << self.tcp_flags_dotmap
191
232
  flags << '] '
192
233
  peek_data << flags
193
234
  peek_data << "S:"
194
235
  peek_data << "%08x" % self.tcp_seq
195
- peek_data << "|I:"
196
- peek_data << "%04x" % self.ip_id
236
+ unless ipv6?
237
+ peek_data << "|I:"
238
+ peek_data << "%04x" % self.ip_id
239
+ end
197
240
  peek_data.join
198
241
  end
199
242
 
@@ -10,7 +10,7 @@ module PacketFu
10
10
  # Int16 :udp_src
11
11
  # Int16 :udp_dst
12
12
  # Int16 :udp_len Default: calculated
13
- # Int16 :udp_sum Default: 0. Often calculated.
13
+ # Int16 :udp_sum Default: 0. Often calculated.
14
14
  # String :body
15
15
  class UDPHeader < Struct.new(:udp_src, :udp_dst, :udp_len, :udp_sum, :body)
16
16
 
@@ -66,9 +66,8 @@ module PacketFu
66
66
  end
67
67
 
68
68
  # Recalculates calculated fields for UDP.
69
- def udp_recalc(args=:all)
70
- arg = arg.intern if arg.respond_to? :intern
71
- case args
69
+ def udp_recalc(arg = :all)
70
+ case arg.to_sym
72
71
  when :udp_len
73
72
  self.udp_len = udp_calc_len
74
73
  when :all
@@ -92,7 +91,7 @@ module PacketFu
92
91
  def udp_dport
93
92
  self.udp_dst
94
93
  end
95
-
94
+
96
95
  # Equivalent to udp_dst=
97
96
  def udp_dport=(arg)
98
97
  self.udp_dst=(arg)
@@ -64,13 +64,13 @@ module PacketFu
64
64
  end
65
65
 
66
66
  def read(str=nil, args={})
67
- raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
68
- @eth_header.read(str)
67
+ super
69
68
  if args[:strip]
70
69
  udp_body_len = self.ip_len - self.ip_hlen - 8
71
70
  @udp_header.body.read(@udp_header.body.to_s[0,udp_body_len])
71
+ udp_calc_sum
72
+ @ip_header.ip_recalc unless ipv6?
72
73
  end
73
- super(args)
74
74
  self
75
75
  end
76
76
 
@@ -103,19 +103,12 @@ module PacketFu
103
103
  def udp_calc_sum
104
104
  # This is /not/ delegated down to @udp_header since we need info
105
105
  # from the IP header, too.
106
- checksum = 0
107
106
  if @ipv6_header
108
- [ipv6_src, ipv6_dst].each do |iaddr|
109
- 8.times do |i|
110
- checksum += (iaddr >> (i * 16)) & 0xffff
111
- end
112
- end
107
+ checksum = ipv6_calc_sum_on_addr
113
108
  else
114
- checksum += (ip_src.to_i >> 16)
115
- checksum += (ip_src.to_i & 0xffff)
116
- checksum += (ip_dst.to_i >> 16)
117
- checksum += (ip_dst.to_i & 0xffff)
109
+ checksum = ip_calc_sum_on_addr
118
110
  end
111
+
119
112
  checksum += 0x11
120
113
  checksum += udp_len.to_i
121
114
  checksum += udp_src.to_i
@@ -179,11 +172,6 @@ module PacketFu
179
172
  end
180
173
  end
181
174
 
182
- # Is that packet an UDP on IPv6 packet ?
183
- def ipv6?
184
- not @ipv6_header.nil?
185
- end
186
-
187
175
  end
188
176
 
189
177
  end
@@ -18,7 +18,7 @@ module StructFu
18
18
  # expected type for that element.
19
19
  def typecast(i)
20
20
  c = caller[0].match(/.*`([^']+)='/)[1]
21
- self[c.intern].read i
21
+ self[c.to_sym].read i
22
22
  end
23
23
 
24
24
  # Used like typecast(), but specifically for casting Strings to StructFu::Strings.
@@ -2,7 +2,7 @@
2
2
  module PacketFu
3
3
 
4
4
  # Check the repo's for version release histories
5
- VERSION = "1.1.13"
5
+ VERSION = "2.0.0"
6
6
 
7
7
  # Returns PacketFu::VERSION
8
8
  def self.version
data/packetfu.gemspec CHANGED
@@ -1,4 +1,3 @@
1
- require 'rake'
2
1
  require './lib/packetfu/version'
3
2
 
4
3
  Gem::Specification.new do |s|
@@ -14,26 +13,19 @@ Gem::Specification.new do |s|
14
13
  ease and fun they expect from Ruby.
15
14
  }
16
15
  s.files = `git ls-files`.split($/)
17
- s.license = 'BSD'
18
- s.required_ruby_version = '>= 2.1.0'
19
- s.add_dependency('pcaprub')
16
+ s.license = 'BSD-3-Clause'
17
+ s.required_ruby_version = '>= 2.7.0'
18
+ s.add_dependency('pcaprub', '~> 0.13.1')
20
19
  s.add_development_dependency('rake')
21
- s.add_development_dependency('rspec')
22
- s.add_development_dependency('rspec-its')
23
- s.add_development_dependency('sdoc')
24
- s.add_development_dependency('pry')
25
- s.add_development_dependency('coveralls')
26
-
20
+ s.add_development_dependency('rspec', '~> 3.0')
21
+ s.add_development_dependency('rspec-its', '~> 1.2')
22
+ s.add_development_dependency('sdoc', '~> 0.4')
23
+ s.add_development_dependency('pry-byebug')
24
+ s.add_development_dependency('coveralls', '~> 0.8')
27
25
 
28
26
  s.extra_rdoc_files = %w[.document README.md]
29
27
  s.test_files = (s.files & (Dir['spec/**/*_spec.rb'] + Dir['test/test_*.rb']) )
30
- s.rubyforge_project = 'packetfu'
31
-
32
- cert = File.expand_path("~/.ssh/gem-private_key_todb.pem")
33
-
34
- if File.exist?(cert) and File.readable?(cert)
35
- s.signing_key = cert
36
- s.cert_chain = ['gem-public_cert.pem']
37
- end
38
28
 
29
+ s.cert_chain = ['certs/todb.pem']
30
+ s.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $0 =~ /gem\z/
39
31
  end
data/spec/arp_spec.rb CHANGED
@@ -190,7 +190,7 @@ describe ARPPacket do
190
190
  expect(@temp_file.read).to eql("")
191
191
 
192
192
  @arp_packet.to_f(@temp_file.path, 'a')
193
- expect(File.exists?(@temp_file.path)).to be(true)
193
+ expect(File.exist?(@temp_file.path)).to be(true)
194
194
  expect(@temp_file.read.size).to be >= 76
195
195
  end
196
196
  end
@@ -0,0 +1,137 @@
1
+ # -*- coding: binary -*-
2
+ require 'spec_helper'
3
+ require 'packetfu/protos/eth'
4
+ require 'packetfu/protos/ip'
5
+ require 'packetfu/protos/ipv6'
6
+ require 'packetfu/protos/tcp'
7
+ require 'packetfu/protos/icmp'
8
+ require 'packetfu/config'
9
+ require 'packetfu/pcap'
10
+ require 'packetfu/utils'
11
+ require 'tempfile'
12
+
13
+ include PacketFu
14
+
15
+ describe Capture do
16
+
17
+ if Process.uid != 0
18
+ warn "Not running as root, PacketFu::Capture capabilities that require root will be skipped"
19
+ end
20
+
21
+ context "when creating an object from scratch" do
22
+ before :each do
23
+ @capture = PacketFu::Capture.new
24
+ end
25
+
26
+ it "should have sane defaults" do
27
+ expect(@capture.array).to be_kind_of(Array)
28
+ expect(@capture.stream).to be_kind_of(Array)
29
+ expect(@capture.iface).to be_kind_of(String)
30
+ expect(@capture.snaplen).to eql(65535)
31
+ expect(@capture.promisc).to eql(false)
32
+ expect(@capture.timeout).to eql(1)
33
+
34
+ # Requires root/sudo to get this...
35
+ if Process.uid == 0
36
+ expect(@capture.filter).to eql(nil)
37
+ else
38
+ expect{@capture.filter}.to raise_error(RuntimeError)
39
+ end
40
+ end
41
+
42
+ it "should allow creating a capture object with non-std attributes" do
43
+ # Can only run this if we're root
44
+ if Process.uid == 0
45
+ options = {
46
+ :iface => PacketFu::Utils::default_int,
47
+ :snaplen => 0xfffe,
48
+ :promisc => true,
49
+ :timeout => 5,
50
+ :filter => "not port 22",
51
+ }
52
+ @capture = PacketFu::Capture.new(options)
53
+
54
+ expect(@capture.array).to be_kind_of(Array)
55
+ expect(@capture.stream).to be_kind_of(PCAPRUB::Pcap)
56
+ expect(@capture.iface).to eql(options[:iface])
57
+ expect(@capture.snaplen).to eql(options[:snaplen])
58
+ expect(@capture.promisc).to eql(options[:promisc])
59
+ expect(@capture.timeout).to eql(options[:timeout])
60
+ expect(@capture.filter).to eql(options[:filter])
61
+ expect(@capture.bpf).to eql(options[:filter])
62
+ end
63
+ end
64
+ end
65
+
66
+ context "when capturing traffic on the wire" do
67
+ # Can only run this if we're root
68
+ if Process.uid == 0
69
+ it "should capture an ICMP echo request from the wire" do
70
+ daddr = PacketFu::Utils.rand_routable_daddr.to_s
71
+
72
+ def do_capture_test(daddr)
73
+ begin
74
+ Timeout::timeout(3) {
75
+ cap = PacketFu::Capture.new(:iface => PacketFu::Utils::default_int, :start => true)
76
+ cap.stream.each do |p|
77
+ pkt = PacketFu::Packet.parse p
78
+ next unless pkt.is_icmp?
79
+
80
+ if pkt.ip_daddr == daddr and pkt.icmp_type == 8
81
+ return true
82
+ end
83
+ end
84
+ }
85
+ rescue Timeout::Error
86
+ return false
87
+ end
88
+ end
89
+
90
+ capture_thread = Thread.new { expect(do_capture_test(daddr)).to eql(true) }
91
+ %x{ping -c 1 #{daddr}}
92
+ capture_thread.join
93
+ end
94
+
95
+ it "should capture only capture ICMP echo requests we ask for from the wire" do
96
+ daddr = PacketFu::Utils.rand_routable_daddr.to_s
97
+ daddr2 = PacketFu::Utils.rand_routable_daddr.to_s
98
+
99
+ def do_bpf_capture_test(daddr, daddr2)
100
+ count = 0
101
+ valid_icmp = false
102
+ invalid_icmp = false
103
+
104
+ begin
105
+ Timeout::timeout(3) {
106
+ cap = PacketFu::Capture.new(:iface => PacketFu::Utils::default_int, :start => true, :filter => "icmp and dst host #{daddr}")
107
+ cap.stream.each do |p|
108
+ pkt = PacketFu::Packet.parse p
109
+ next unless pkt.is_icmp?
110
+ count += 1
111
+
112
+ if pkt.ip_daddr == daddr and pkt.icmp_type == 8
113
+ valid_icmp = true
114
+ elsif pkt.ip_daddr == daddr2 and pkt.icmp_type == 8
115
+ invalid_icmp = true
116
+ end
117
+ end
118
+ }
119
+ rescue Timeout::Error
120
+ ### do nothing, we need to wait for the timeout anyways
121
+ end
122
+
123
+ if count == 1 && valid_icmp == true && invalid_icmp == false
124
+ return true
125
+ else
126
+ return false
127
+ end
128
+ end
129
+
130
+ capture_thread = Thread.new { expect(do_bpf_capture_test(daddr,daddr2)).to eql(true) }
131
+ %x{ping -c 1 #{daddr}}
132
+ %x{ping -c 1 #{daddr2}}
133
+ capture_thread.join
134
+ end
135
+ end
136
+ end
137
+ end
data/spec/eth_spec.rb CHANGED
@@ -130,7 +130,7 @@ describe EthPacket do
130
130
  expect(@temp_file.read).to eql("")
131
131
 
132
132
  @eth_packet.to_f(@temp_file.path, 'a')
133
- expect(File.exists?(@temp_file.path))
133
+ expect(File.exist?(@temp_file.path))
134
134
  expect(@temp_file.read.size).to be >= 30
135
135
  end
136
136
 
data/spec/icmp_spec.rb CHANGED
@@ -87,7 +87,7 @@ describe ICMPPacket, "when read from a pcap file" do
87
87
  expect(@temp_file.read).to eql("")
88
88
 
89
89
  @icmp_packet.to_f(@temp_file.path, 'a')
90
- expect(File.exists?(@temp_file.path))
90
+ expect(File.exist?(@temp_file.path))
91
91
  expect(@temp_file.read.size).to be >= 79
92
92
  end
93
93
 
data/spec/icmpv6_spec.rb CHANGED
@@ -80,7 +80,7 @@ describe ICMPv6Packet, "when read from a pcap file" do
80
80
  expect(@temp_file.read).to eql("")
81
81
 
82
82
  @icmpv6_packet.to_f(@temp_file.path, 'a')
83
- expect(File.exists?(@temp_file.path))
83
+ expect(File.exist?(@temp_file.path))
84
84
  expect(@temp_file.read.size).to be >= 79
85
85
  end
86
86