packetfu 1.1.11 → 1.1.12.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (193) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.rspec +2 -0
  4. data/.travis.yml +2 -3
  5. data/README.md +127 -0
  6. data/examples/100kpackets.rb +11 -10
  7. data/examples/ackscan.rb +4 -1
  8. data/examples/arp.rb +4 -5
  9. data/examples/arphood.rb +5 -4
  10. data/examples/dissect_thinger.rb +10 -7
  11. data/examples/ethernet.rb +8 -3
  12. data/examples/ids.rb +22 -4
  13. data/examples/idsv2.rb +25 -6
  14. data/examples/ifconfig.rb +6 -3
  15. data/examples/new-simple-stats.rb +5 -6
  16. data/examples/packetfu-shell.rb +11 -48
  17. data/examples/pcap2pcapng.rb +32 -0
  18. data/examples/simple-sniffer.rb +9 -4
  19. data/examples/simple-stats.rb +7 -8
  20. data/examples/slammer.rb +2 -2
  21. data/examples/uniqpcap.rb +17 -7
  22. data/lib/packetfu.rb +10 -175
  23. data/lib/packetfu/capture.rb +2 -2
  24. data/lib/packetfu/common.rb +142 -0
  25. data/lib/packetfu/config.rb +8 -8
  26. data/lib/packetfu/inject.rb +3 -3
  27. data/lib/packetfu/packet.rb +22 -18
  28. data/lib/packetfu/pcap.rb +2 -1
  29. data/lib/packetfu/pcapng.rb +37 -0
  30. data/lib/packetfu/pcapng/block.rb +25 -0
  31. data/lib/packetfu/pcapng/epb.rb +112 -0
  32. data/lib/packetfu/pcapng/file.rb +316 -0
  33. data/lib/packetfu/pcapng/idb.rb +125 -0
  34. data/lib/packetfu/pcapng/shb.rb +146 -0
  35. data/lib/packetfu/pcapng/spb.rb +83 -0
  36. data/lib/packetfu/pcapng/unknown_block.rb +60 -0
  37. data/lib/packetfu/protos.rb +3 -0
  38. data/lib/packetfu/protos/arp.rb +10 -10
  39. data/lib/packetfu/protos/icmpv6.rb +131 -0
  40. data/lib/packetfu/protos/icmpv6/header.rb +69 -0
  41. data/lib/packetfu/protos/icmpv6/mixin.rb +14 -0
  42. data/lib/packetfu/protos/ip.rb +4 -5
  43. data/lib/packetfu/protos/ipv6/header.rb +2 -0
  44. data/lib/packetfu/protos/udp.rb +24 -12
  45. data/lib/packetfu/structfu.rb +27 -0
  46. data/lib/packetfu/utils.rb +55 -9
  47. data/lib/packetfu/version.rb +1 -1
  48. data/packetfu.gemspec +13 -7
  49. data/spec/arp_spec.rb +11 -5
  50. data/spec/eth_spec.rb +20 -11
  51. data/spec/fake_packets.rb +28 -0
  52. data/spec/hsrp_spec.rb +15 -0
  53. data/spec/icmp_spec.rb +12 -5
  54. data/spec/icmpv6_spec.rb +98 -0
  55. data/spec/invalid_spec.rb +28 -0
  56. data/spec/ip_spec.rb +10 -5
  57. data/spec/ipv4_icmp.pcap +0 -0
  58. data/spec/ipv4_udp.pcap +0 -0
  59. data/spec/ipv6_icmp.pcap +0 -0
  60. data/spec/ipv6_spec.rb +4 -0
  61. data/spec/ipv6_udp.pcap +0 -0
  62. data/spec/lldp_spec.rb +36 -0
  63. data/spec/octets_spec.rb +43 -0
  64. data/spec/packet_spec.rb +24 -0
  65. data/spec/packetfu_spec.rb +6 -1
  66. data/spec/pcap_spec.rb +286 -0
  67. data/spec/pcapng/epb_spec.rb +81 -0
  68. data/spec/pcapng/file_spec.rb +295 -0
  69. data/spec/pcapng/file_spec_helper.rb +45 -0
  70. data/spec/pcapng/idb_spec.rb +53 -0
  71. data/spec/pcapng/shb_spec.rb +42 -0
  72. data/spec/pcapng/spb_spec.rb +43 -0
  73. data/spec/pcapng/unknown_block_spec.rb +36 -0
  74. data/spec/spec_helper.rb +3 -31
  75. data/spec/tcp_spec.rb +4 -1
  76. data/spec/udp_spec.rb +149 -1
  77. data/spec/utils_spec.rb +98 -15
  78. data/test/pcapng-test/output_be/advanced/test100.pcapng +0 -0
  79. data/test/pcapng-test/output_be/advanced/test100.txt +11 -0
  80. data/test/pcapng-test/output_be/advanced/test101.pcapng +0 -0
  81. data/test/pcapng-test/output_be/advanced/test101.txt +11 -0
  82. data/test/pcapng-test/output_be/advanced/test102.pcapng +0 -0
  83. data/test/pcapng-test/output_be/advanced/test102.txt +14 -0
  84. data/test/pcapng-test/output_be/basic/test001.pcapng +0 -0
  85. data/test/pcapng-test/output_be/basic/test001.txt +9 -0
  86. data/test/pcapng-test/output_be/basic/test002.pcapng +0 -0
  87. data/test/pcapng-test/output_be/basic/test002.txt +7 -0
  88. data/test/pcapng-test/output_be/basic/test003.pcapng +0 -0
  89. data/test/pcapng-test/output_be/basic/test003.txt +8 -0
  90. data/test/pcapng-test/output_be/basic/test004.pcapng +0 -0
  91. data/test/pcapng-test/output_be/basic/test004.txt +9 -0
  92. data/test/pcapng-test/output_be/basic/test005.pcapng +0 -0
  93. data/test/pcapng-test/output_be/basic/test005.txt +9 -0
  94. data/test/pcapng-test/output_be/basic/test006.pcapng +0 -0
  95. data/test/pcapng-test/output_be/basic/test006.txt +9 -0
  96. data/test/pcapng-test/output_be/basic/test007.pcapng +0 -0
  97. data/test/pcapng-test/output_be/basic/test007.txt +9 -0
  98. data/test/pcapng-test/output_be/basic/test008.pcapng +0 -0
  99. data/test/pcapng-test/output_be/basic/test008.txt +9 -0
  100. data/test/pcapng-test/output_be/basic/test009.pcapng +0 -0
  101. data/test/pcapng-test/output_be/basic/test009.txt +9 -0
  102. data/test/pcapng-test/output_be/basic/test010.pcapng +0 -0
  103. data/test/pcapng-test/output_be/basic/test010.txt +9 -0
  104. data/test/pcapng-test/output_be/basic/test011.pcapng +0 -0
  105. data/test/pcapng-test/output_be/basic/test011.txt +10 -0
  106. data/test/pcapng-test/output_be/basic/test012.pcapng +0 -0
  107. data/test/pcapng-test/output_be/basic/test012.txt +10 -0
  108. data/test/pcapng-test/output_be/basic/test013.pcapng +0 -0
  109. data/test/pcapng-test/output_be/basic/test013.txt +9 -0
  110. data/test/pcapng-test/output_be/basic/test014.pcapng +0 -0
  111. data/test/pcapng-test/output_be/basic/test014.txt +9 -0
  112. data/test/pcapng-test/output_be/basic/test015.pcapng +0 -0
  113. data/test/pcapng-test/output_be/basic/test015.txt +9 -0
  114. data/test/pcapng-test/output_be/basic/test016.pcapng +0 -0
  115. data/test/pcapng-test/output_be/basic/test016.txt +11 -0
  116. data/test/pcapng-test/output_be/basic/test017.pcapng +0 -0
  117. data/test/pcapng-test/output_be/basic/test017.txt +9 -0
  118. data/test/pcapng-test/output_be/basic/test018.pcapng +0 -0
  119. data/test/pcapng-test/output_be/basic/test018.txt +12 -0
  120. data/test/pcapng-test/output_be/difficult/test200.pcapng +0 -0
  121. data/test/pcapng-test/output_be/difficult/test200.txt +8 -0
  122. data/test/pcapng-test/output_be/difficult/test201.pcapng +0 -0
  123. data/test/pcapng-test/output_be/difficult/test201.txt +11 -0
  124. data/test/pcapng-test/output_be/difficult/test202.pcapng +0 -0
  125. data/test/pcapng-test/output_be/difficult/test202.txt +14 -0
  126. data/test/pcapng-test/output_le/advanced/test100.pcapng +0 -0
  127. data/test/pcapng-test/output_le/advanced/test100.txt +11 -0
  128. data/test/pcapng-test/output_le/advanced/test101.pcapng +0 -0
  129. data/test/pcapng-test/output_le/advanced/test101.txt +11 -0
  130. data/test/pcapng-test/output_le/advanced/test102.pcapng +0 -0
  131. data/test/pcapng-test/output_le/advanced/test102.txt +14 -0
  132. data/test/pcapng-test/output_le/basic/test001.pcapng +0 -0
  133. data/test/pcapng-test/output_le/basic/test001.txt +9 -0
  134. data/test/pcapng-test/output_le/basic/test002.pcapng +0 -0
  135. data/test/pcapng-test/output_le/basic/test002.txt +7 -0
  136. data/test/pcapng-test/output_le/basic/test003.pcapng +0 -0
  137. data/test/pcapng-test/output_le/basic/test003.txt +8 -0
  138. data/test/pcapng-test/output_le/basic/test004.pcapng +0 -0
  139. data/test/pcapng-test/output_le/basic/test004.txt +9 -0
  140. data/test/pcapng-test/output_le/basic/test005.pcapng +0 -0
  141. data/test/pcapng-test/output_le/basic/test005.txt +9 -0
  142. data/test/pcapng-test/output_le/basic/test006.pcapng +0 -0
  143. data/test/pcapng-test/output_le/basic/test006.txt +9 -0
  144. data/test/pcapng-test/output_le/basic/test007.pcapng +0 -0
  145. data/test/pcapng-test/output_le/basic/test007.txt +9 -0
  146. data/test/pcapng-test/output_le/basic/test008.pcapng +0 -0
  147. data/test/pcapng-test/output_le/basic/test008.txt +9 -0
  148. data/test/pcapng-test/output_le/basic/test009.pcapng +0 -0
  149. data/test/pcapng-test/output_le/basic/test009.txt +9 -0
  150. data/test/pcapng-test/output_le/basic/test010.pcapng +0 -0
  151. data/test/pcapng-test/output_le/basic/test010.txt +9 -0
  152. data/test/pcapng-test/output_le/basic/test011.pcapng +0 -0
  153. data/test/pcapng-test/output_le/basic/test011.txt +10 -0
  154. data/test/pcapng-test/output_le/basic/test012.pcapng +0 -0
  155. data/test/pcapng-test/output_le/basic/test012.txt +10 -0
  156. data/test/pcapng-test/output_le/basic/test013.pcapng +0 -0
  157. data/test/pcapng-test/output_le/basic/test013.txt +9 -0
  158. data/test/pcapng-test/output_le/basic/test014.pcapng +0 -0
  159. data/test/pcapng-test/output_le/basic/test014.txt +9 -0
  160. data/test/pcapng-test/output_le/basic/test015.pcapng +0 -0
  161. data/test/pcapng-test/output_le/basic/test015.txt +9 -0
  162. data/test/pcapng-test/output_le/basic/test016.pcapng +0 -0
  163. data/test/pcapng-test/output_le/basic/test016.txt +11 -0
  164. data/test/pcapng-test/output_le/basic/test017.pcapng +0 -0
  165. data/test/pcapng-test/output_le/basic/test017.txt +9 -0
  166. data/test/pcapng-test/output_le/basic/test018.pcapng +0 -0
  167. data/test/pcapng-test/output_le/basic/test018.txt +12 -0
  168. data/test/pcapng-test/output_le/difficult/test200.pcapng +0 -0
  169. data/test/pcapng-test/output_le/difficult/test200.txt +8 -0
  170. data/test/pcapng-test/output_le/difficult/test201.pcapng +0 -0
  171. data/test/pcapng-test/output_le/difficult/test201.txt +11 -0
  172. data/test/pcapng-test/output_le/difficult/test202.pcapng +0 -0
  173. data/test/pcapng-test/output_le/difficult/test202.txt +14 -0
  174. data/test/sample-ipv6.pcapng +0 -0
  175. data/test/sample-spb.pcapng +0 -0
  176. data/test/sample.pcapng +0 -0
  177. data/test/sample2.pcapng +0 -0
  178. metadata +190 -68
  179. checksums.yaml.gz.sig +0 -0
  180. data.tar.gz.sig +0 -2
  181. data/INSTALL.rdoc +0 -40
  182. data/README.rdoc +0 -64
  183. data/examples/examples.rb +0 -4
  184. data/setup.rb +0 -1586
  185. data/test/func_lldp.rb +0 -25
  186. data/test/ptest.rb +0 -16
  187. data/test/test_eth.rb +0 -93
  188. data/test/test_hsrp.rb +0 -20
  189. data/test/test_invalid.rb +0 -28
  190. data/test/test_octets.rb +0 -36
  191. data/test/test_pcap.rb +0 -211
  192. data/test/test_udp.rb +0 -100
  193. metadata.gz.sig +0 -2
@@ -1,10 +1,10 @@
1
1
  # -*- coding: binary -*-
2
2
  module PacketFu
3
3
 
4
- # The Config class holds various bits of useful default information
5
- # for packet creation. If initialized without arguments, @iface will be
4
+ # The Config class holds various bits of useful default information
5
+ # for packet creation. If initialized without arguments, @iface will be
6
6
  # set to ENV['IFACE'] or Pcap.lookupdev (or lo), and the @pcapfile will
7
- # be set to "/tmp/out.pcap" # (yes, it's Linux-biased, sorry, fixing
7
+ # be set to "/tmp/out.pcap" # (yes, it's Linux-biased, sorry, fixing
8
8
  # this is a TODO.)
9
9
  #
10
10
  # Any number of instance variables can be passed in to the intialize function (as a
@@ -23,7 +23,7 @@ module PacketFu
23
23
  # obj.config(:baz => "bat")
24
24
  # obj.config #=> {:iface=>"eth0", :baz=>"bat", :pcapfile=>"/tmp/out.pcap", :foo=>"bar"}
25
25
  class Config
26
- attr_accessor :eth_saddr, # The discovered eth_saddr
26
+ attr_accessor :eth_saddr, # The discovered eth_saddr
27
27
  :eth_daddr, # The discovered eth_daddr (ie, the gateway)
28
28
  :eth_src, # The discovered eth_src in binary form.
29
29
  :eth_dst, # The discovered eth_dst (gateway) in binary form.
@@ -31,11 +31,11 @@ module PacketFu
31
31
  :ip_src, # The discovered ip_src in binary form.
32
32
  :iface, # The declared interface.
33
33
  :pcapfile # A declared default file to write to.
34
-
34
+
35
35
  def initialize(args={})
36
36
  if Process.euid.zero?
37
37
  @iface = args[:iface] || ENV['IFACE'] || Pcap.lookupdev || "lo"
38
- end
38
+ end
39
39
  @pcapfile = "/tmp/out.pcap"
40
40
  args.each_pair { |k,v| self.instance_variable_set(("@#{k}"),v) }
41
41
  end
@@ -46,9 +46,9 @@ module PacketFu
46
46
  arg.each_pair {|k,v| self.instance_variable_set(("@" + k.to_s).intern, v)}
47
47
  else
48
48
  config_hash = {}
49
- self.instance_variables.each do |v|
49
+ self.instance_variables.each do |v|
50
50
  key = v.to_s.gsub(/^@/,"").to_sym
51
- config_hash[key] = self.instance_variable_get(v)
51
+ config_hash[key] = self.instance_variable_get(v)
52
52
  end
53
53
  config_hash
54
54
  end
@@ -4,7 +4,7 @@ module PacketFu
4
4
  # The Inject class handles injecting arrays of binary data on the wire.
5
5
  #
6
6
  # To inject single packets, use PacketFu::Packet.to_w() instead.
7
- class Inject
7
+ class Inject
8
8
  attr_accessor :array, :stream, :show_live # Leave these public and open.
9
9
  attr_reader :iface, :snaplen, :promisc, :timeout # Cant change after the init.
10
10
 
@@ -19,7 +19,7 @@ module PacketFu
19
19
  end
20
20
 
21
21
  # Takes an array, and injects them onto an interface. Note that
22
- # complete packets (Ethernet headers on down) are expected.
22
+ # complete packets (Ethernet headers on down) are expected.
23
23
  #
24
24
  # === Parameters
25
25
  #
@@ -48,7 +48,7 @@ module PacketFu
48
48
  pkt_count +=1
49
49
  puts "Sent Packet \##{pkt_count} (#{pkt.size})" if show_live
50
50
  end
51
- # Return # of packets sent, array size, and array total size
51
+ # Return # of packets sent, array size, and array total size
52
52
  [pkt_count, pkt_array.size, pkt_array.join.size]
53
53
  end
54
54
 
@@ -1,9 +1,10 @@
1
1
  # -*- coding: binary -*-
2
+
2
3
  module PacketFu
3
4
 
4
5
  # Packet is the parent class of EthPacket, IPPacket, UDPPacket, TCPPacket, and all
5
6
  # other packets. It acts as both a singleton class, so things like
6
- # Packet.parse can happen, and as an abstract class to provide
7
+ # Packet.parse can happen, and as an abstract class to provide
7
8
  # subclasses some structure.
8
9
  class Packet
9
10
 
@@ -24,7 +25,7 @@ module PacketFu
24
25
  end
25
26
 
26
27
  # Parse() creates the correct packet type based on the data, and returns the apporpiate
27
- # Packet subclass object.
28
+ # Packet subclass object.
28
29
  #
29
30
  # There is an assumption here that all incoming packets are either EthPacket
30
31
  # or InvalidPacket types. This will be addressed pretty soon.
@@ -41,7 +42,10 @@ module PacketFu
41
42
  else
42
43
  classes = PacketFu.packet_classes_by_layer_without_application
43
44
  end
44
- p = classes.detect { |pclass| pclass.can_parse?(packet) }.new
45
+
46
+ new_args = {}
47
+ new_args[:on_ipv6] = true if IPv6Packet.can_parse?(packet)
48
+ p = classes.detect { |pclass| pclass.can_parse?(packet) }.new(new_args)
45
49
  parsed_packet = p.read(packet,args)
46
50
  end
47
51
 
@@ -108,7 +112,7 @@ module PacketFu
108
112
  inj.array = [@headers[0].to_s]
109
113
  inj.inject
110
114
  end
111
-
115
+
112
116
  # Recalculates all the calcuated fields for all headers in the packet.
113
117
  # This is important since read() wipes out all the calculated fields
114
118
  # such as length and checksum and what all.
@@ -142,7 +146,7 @@ module PacketFu
142
146
  # A note on the :strip => true argument: If :strip is set, defined lengths of data will
143
147
  # be believed, and any trailers (such as frame check sequences) will be chopped off. This
144
148
  # helps to ensure well-formed packets, at the cost of losing perhaps important FCS data.
145
- #
149
+ #
146
150
  # If :strip is false, header lengths are /not/ believed, and all data will be piped in.
147
151
  # When capturing from the wire, this is usually fine, but recalculating the length before
148
152
  # saving or re-transmitting will absolutely change the data payload; FCS data will become
@@ -169,7 +173,7 @@ module PacketFu
169
173
 
170
174
  # Packets are bundles of lots of objects, so copying them
171
175
  # is a little complicated -- a dup of a packet is actually
172
- # full of pass-by-reference stuff in the @headers, so
176
+ # full of pass-by-reference stuff in the @headers, so
173
177
  # if you change one, you're changing all this copies, too.
174
178
  #
175
179
  # Normally, this doesn't seem to be a big deal, and it's
@@ -215,7 +219,7 @@ module PacketFu
215
219
  # format.
216
220
  #
217
221
  # === Format
218
- #
222
+ #
219
223
  # * A one or two character protocol initial. It should be unique
220
224
  # * The packet size
221
225
  # * Useful data in a human-usable form.
@@ -229,7 +233,7 @@ module PacketFu
229
233
  # #=> "T 1054 10.10.10.105:55000 -> 192.168.145.105:80 [......] S:adc7155b|I:8dd0"
230
234
  # tcp_packet.peek.size
231
235
  # #=> 79
232
- #
236
+ #
233
237
  def peek_format
234
238
  peek_data = ["? "]
235
239
  peek_data << "%-5d" % self.to_s.size
@@ -237,9 +241,9 @@ module PacketFu
237
241
  peek_data.join
238
242
  end
239
243
 
240
- # Defines the layer this packet type lives at, based on the number of headers it
244
+ # Defines the layer this packet type lives at, based on the number of headers it
241
245
  # requires. Note that this has little to do with the OSI model, since TCP/IP
242
- # doesn't really have Session and Presentation layers.
246
+ # doesn't really have Session and Presentation layers.
243
247
  #
244
248
  # Ethernet and the like are layer 1, IP, IPv6, and ARP are layer 2,
245
249
  # TCP, UDP, and other transport protocols are layer 3, and application
@@ -299,7 +303,7 @@ module PacketFu
299
303
  end
300
304
 
301
305
  # If @inspect_style is :default (or :ugly), the inspect output is the usual
302
- # inspect.
306
+ # inspect.
303
307
  #
304
308
  # If @inspect_style is :hex (or :pretty), the inspect output is
305
309
  # a much more compact hexdump-style, with a shortened set of packet header
@@ -335,7 +339,7 @@ module PacketFu
335
339
  table << [proto,[]]
336
340
  header.class.members.each do |elem|
337
341
  elem_sym = elem.to_sym # to_sym needed for 1.8
338
- next if elem_sym == :body
342
+ next if elem_sym == :body
339
343
  elem_type_value = []
340
344
  elem_type_value[0] = elem
341
345
  readable_element = "#{elem}_readable"
@@ -344,7 +348,7 @@ module PacketFu
344
348
  else
345
349
  elem_type_value[1] = header.send(elem)
346
350
  end
347
- elem_type_value[2] = header[elem.to_sym].class.name
351
+ elem_type_value[2] = header[elem.to_sym].class.name
348
352
  table[table_idx][1] << elem_type_value
349
353
  end
350
354
  end
@@ -364,7 +368,7 @@ module PacketFu
364
368
  dtable = self.dissection_table
365
369
  hex_body = nil
366
370
  if dtable.last.kind_of?(Array) and dtable.last.first == :body
367
- body = dtable.pop
371
+ body = dtable.pop
368
372
  hex_body = hexify(body[1])
369
373
  end
370
374
  elem_widths = [0,0,0]
@@ -376,11 +380,11 @@ module PacketFu
376
380
  end
377
381
  end
378
382
  end
379
- total_width = elem_widths.inject(0) {|sum,x| sum+x}
383
+ total_width = elem_widths.inject(0) {|sum,x| sum+x}
380
384
  table = ""
381
385
  dtable.each do |proto|
382
386
  table << "--"
383
- table << proto[0]
387
+ table << proto[0]
384
388
  if total_width > proto[0].size
385
389
  table << ("-" * (total_width - proto[0].size + 2))
386
390
  else
@@ -464,7 +468,7 @@ module PacketFu
464
468
  alias_method :protocol, :proto
465
469
  alias_method :length, :size
466
470
 
467
- # the Packet class should not be instantiated directly, since it's an
471
+ # the Packet class should not be instantiated directly, since it's an
468
472
  # abstract class that real packet types inherit from. Sadly, this
469
473
  # makes the Packet class more difficult to test directly.
470
474
  def initialize(args={})
@@ -493,7 +497,7 @@ module PacketFu
493
497
 
494
498
  #method_missing() delegates protocol-specific field actions to the apporpraite
495
499
  #class variable (which contains the associated packet type)
496
- #This register-of-protocols style switch will work for the
500
+ #This register-of-protocols style switch will work for the
497
501
  #forseeable future (there aren't /that/ many packet types), and it's a handy
498
502
  #way to know at a glance what packet types are supported.
499
503
  def method_missing(sym, *args, &block)
@@ -8,6 +8,7 @@ module StructFu
8
8
  unless [:little, :big].include? e
9
9
  raise ArgumentError, "Unknown endianness for #{self.class}"
10
10
  end
11
+ @int64 = e == :little ? Int64le : Int64be
11
12
  @int32 = e == :little ? Int32le : Int32be
12
13
  @int16 = e == :little ? Int16le : Int16be
13
14
  return e
@@ -422,7 +423,7 @@ module PacketFu
422
423
  end
423
424
  arr.each_with_index do |p,i|
424
425
  if p.kind_of? Hash # Binary timestamps are included
425
- this_ts = p.keys.first
426
+ this_ts = p.keys.first.dup
426
427
  this_incl_len = p.values.first.size
427
428
  this_orig_len = this_incl_len
428
429
  this_data = p.values.first
@@ -0,0 +1,37 @@
1
+ # -*- coding: binary -*-
2
+
3
+ module PacketFu
4
+
5
+ # Module to handle PCAP-NG file format.
6
+ # See http://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://raw.githubusercontent.com/pcapng/pcapng/master/draft-tuexen-opsawg-pcapng.xml&modeAsFormat=html/ascii&type=ascii#format_idb
7
+ module PcapNG
8
+
9
+ # Section Header Block type number
10
+ SHB_TYPE = StructFu::Int32.new(0x0A0D0D0A, :little)
11
+ # Interface Description Block type number
12
+ IDB_TYPE = StructFu::Int32.new(1, :little)
13
+ # Simple Packet Block type number
14
+ SPB_TYPE = StructFu::Int32.new(3, :little)
15
+ # Enhanced Packet Block type number
16
+ EPB_TYPE = StructFu::Int32.new(6, :little)
17
+
18
+ # Various LINKTYPE values from http://www.tcpdump.org/linktypes.html
19
+ # FIXME: only ETHERNET type is defined as this is the only link layer
20
+ # type supported by PacketFu
21
+ LINKTYPE_ETHERNET = 1
22
+
23
+ class Error < StandardError; end
24
+ class InvalidFileError < Error; end
25
+
26
+ end
27
+
28
+ end
29
+
30
+
31
+ require_relative 'pcapng/block.rb'
32
+ require_relative 'pcapng/unknown_block.rb'
33
+ require_relative 'pcapng/shb.rb'
34
+ require_relative 'pcapng/idb.rb'
35
+ require_relative 'pcapng/epb.rb'
36
+ require_relative 'pcapng/spb.rb'
37
+ require_relative 'pcapng/file.rb'
@@ -0,0 +1,25 @@
1
+ # -*- coding: binary -*-
2
+ module PacketFu
3
+ module PcapNG
4
+
5
+ module Block
6
+
7
+ # Calculate block length and update :block_len and block_len2 fields
8
+ def recalc_block_len
9
+ len = to_a.map(&:to_s).join.size
10
+ self[:block_len].value = self[:block_len2].value = len
11
+ end
12
+
13
+ # Pad given field to 32 bit boundary, if needed
14
+ def pad_field(*fields)
15
+ fields.each do |field|
16
+ unless self[field].size % 4 == 0
17
+ self[field] << "\x00" * (4 - (self[field].size % 4))
18
+ end
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,112 @@
1
+ require 'stringio'
2
+
3
+ module PacketFu
4
+ module PcapNG
5
+
6
+ # Pcapng::EPB represents a Extended Packet Block (EPB) of a pcapng file.
7
+ #
8
+ # == Pcapng::EPB Definition
9
+ # Int32 :type Default: 0x00000006
10
+ # Int32 :block_len
11
+ # Int32 :interface_id
12
+ # Int32 :tsh (timestamp high)
13
+ # Int32 :tsl (timestamp low)
14
+ # Int32 :cap_len
15
+ # Int32 :orig_len
16
+ # String :data
17
+ # String :options
18
+ # Int32 :block_len2
19
+ class EPB < Struct.new(:type, :block_len, :interface_id, :tsh, :tsl,
20
+ :cap_len, :orig_len, :data, :options, :block_len2)
21
+ include StructFu
22
+ include Block
23
+ attr_accessor :endian
24
+ attr_accessor :interface
25
+
26
+ MIN_SIZE = 8*4
27
+
28
+ def initialize(args={})
29
+ @endian = set_endianness(args[:endian] || :little)
30
+ init_fields(args)
31
+ super(args[:type], args[:block_len], args[:interface_id], args[:tsh],
32
+ args[:tsl], args[:cap_len], args[:orig_len], args[:data],
33
+ args[:options], args[:block_len2])
34
+ end
35
+
36
+ # Used by #initialize to set the initial fields
37
+ def init_fields(args={})
38
+ args[:type] = @int32.new(args[:type] || PcapNG::EPB_TYPE.to_i)
39
+ args[:block_len] = @int32.new(args[:block_len] || MIN_SIZE)
40
+ args[:interface_id] = @int32.new(args[:interface_id] || 0)
41
+ args[:tsh] = @int32.new(args[:tsh] || 0)
42
+ args[:tsl] = @int32.new(args[:tsl] || 0)
43
+ args[:cap_len] = @int32.new(args[:cap_len] || 0)
44
+ args[:orig_len] = @int32.new(args[:orig_len] || 0)
45
+ args[:data] = StructFu::String.new(args[:data] || '')
46
+ args[:options] = StructFu::String.new(args[:options] || '')
47
+ args[:block_len2] = @int32.new(args[:block_len2] || MIN_SIZE)
48
+ args
49
+ end
50
+
51
+ def has_options?
52
+ self[:options].size > 0
53
+ end
54
+
55
+ # Reads a String or a IO to populate the object
56
+ def read(str_or_io)
57
+ if str_or_io.respond_to? :read
58
+ io = str_or_io
59
+ else
60
+ io = StringIO.new(force_binary(str_or_io.to_s))
61
+ end
62
+ return self if io.eof?
63
+
64
+ self[:type].read io.read(4)
65
+ self[:block_len].read io.read(4)
66
+ self[:interface_id].read io.read(4)
67
+ self[:tsh].read io.read(4)
68
+ self[:tsl].read io.read(4)
69
+ self[:cap_len].read io.read(4)
70
+ self[:orig_len].read io.read(4)
71
+ self[:data].read io.read(self[:cap_len].to_i)
72
+ data_pad_len = (4 - (self[:cap_len].to_i % 4)) % 4
73
+ io.read data_pad_len
74
+ options_len = self[:block_len].to_i - self[:cap_len].to_i - data_pad_len
75
+ options_len -= MIN_SIZE
76
+ self[:options].read io.read(options_len)
77
+ self[:block_len2].read io.read(4)
78
+
79
+ unless self[:block_len].to_i == self[:block_len2].to_i
80
+ raise InvalidFileError, 'Incoherency in Extended Packet Block'
81
+ end
82
+
83
+ self
84
+ end
85
+
86
+ # Return timestamp as a Time object
87
+ def timestamp
88
+ Time.at((self[:tsh].to_i << 32 | self[:tsl].to_i) * ts_resol)
89
+ end
90
+
91
+ # Return the object as a String
92
+ def to_s
93
+ pad_field :data, :options
94
+ recalc_block_len
95
+ to_a.map(&:to_s).join
96
+ end
97
+
98
+
99
+ private
100
+
101
+ def ts_resol
102
+ if @interface.nil?
103
+ 1E-6
104
+ else
105
+ @interface.ts_resol
106
+ end
107
+ end
108
+
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,316 @@
1
+ require_relative 'shb'
2
+
3
+ module PacketFu
4
+ module PcapNG
5
+
6
+ # PcapNG::File is a complete Pcap-NG file handler.
7
+ class File
8
+ attr_accessor :sections
9
+
10
+ def initialize
11
+ @sections = []
12
+ end
13
+
14
+ # Read a string to populate the object. Note that this appends new blocks to
15
+ # the Pcapng::File object.
16
+ def read(str)
17
+ PacketFu.force_binary(str)
18
+ io = StringIO.new(str)
19
+ parse_section(io)
20
+ self
21
+ end
22
+
23
+ # Clear the contents of the Pcapng::File prior to reading in a new string.
24
+ # This string should contain a Section Header Block and an Interface Description
25
+ # Block to create a conform pcapng file.
26
+ def read!(str)
27
+ clear
28
+ PacketFu.force_binary(str)
29
+ read(str)
30
+ end
31
+
32
+ # Read a given file and analyze it.
33
+ # If given a block, it will yield PcapNG::EPB or PcapNG::SPB objects.
34
+ # This is the only way to get packet timestamps.
35
+ def readfile(fname, &blk)
36
+ unless ::File.readable?(fname)
37
+ raise ArgumentError, "cannot read file #{fname}"
38
+ end
39
+
40
+ ::File.open(fname, 'rb') do |f|
41
+ while !f.eof? do
42
+ parse_section(f)
43
+ end
44
+ end
45
+
46
+ if blk
47
+ count = 0
48
+ @sections.each do |section|
49
+ section.interfaces.each do |intf|
50
+ intf.packets.each { |pkt| count += 1; yield pkt }
51
+ end
52
+ end
53
+ count
54
+ end
55
+ end
56
+
57
+ # Give an array of parsed packets (raw data from packets).
58
+ # If a block is given, yield raw packet data from the given file.
59
+ def read_packet_bytes(fname, &blk)
60
+ count = 0
61
+ packets = [] unless blk
62
+
63
+ readfile(fname) do |packet|
64
+ if blk
65
+ count += 1
66
+ yield packet.data.to_s
67
+ else
68
+ packets << packet.data.to_s
69
+ end
70
+ end
71
+
72
+ blk ? count : packets
73
+ end
74
+
75
+ # Return an array of parsed packets.
76
+ # If a block is given, yield parsed packets from the given file.
77
+ def read_packets(fname, &blk)
78
+ count = 0
79
+ packets = [] unless blk
80
+
81
+ read_packet_bytes(fname) do |packet|
82
+ if blk
83
+ count += 1
84
+ yield Packet.parse(packet)
85
+ else
86
+ packets << Packet.parse(packet)
87
+ end
88
+ end
89
+
90
+ blk ? count : packets
91
+ end
92
+
93
+ # Return the object as a String
94
+ def to_s
95
+ @sections.map { |section| section.to_s }.join
96
+ end
97
+
98
+ # Clear the contents of the Pcapng::File.
99
+ def clear
100
+ @sections.clear
101
+ end
102
+
103
+ # #file_to_array translates a Pcap-NG file into an array of packets.
104
+ # Note that this strips out timestamps -- if you'd like to retain
105
+ # timestamps and other pcapng file information, you will want to
106
+ # use #read instead.
107
+ #
108
+ # Valid arguments are:
109
+ # * :filename If given, object is cleared and filename is analyzed
110
+ # before generating array. Else, array is generated
111
+ # from self.
112
+ # * :keep_timestamps If true, generates an array of hashes, each one with
113
+ # timestamp as key and packet as value. There is one hash
114
+ # per packet.
115
+ def file_to_array(args={})
116
+ filename = args[:filename] || args[:file]
117
+ if filename
118
+ clear
119
+ readfile filename
120
+ end
121
+
122
+ ary = []
123
+ @sections.each do |section|
124
+ section.interfaces.each do |itf|
125
+ if args[:keep_timestamps] || args[:keep_ts]
126
+ ary.concat itf.packets.map { |pkt| { pkt.timestamp => pkt.data.to_s } }
127
+ else
128
+ ary.concat itf.packets.map { |pkt| pkt.data.to_s}
129
+ end
130
+ end
131
+ end
132
+ ary
133
+ end
134
+
135
+ # Writes the Pcapng::File to a file. Takes the following arguments:
136
+ # :filename # The file to write to.
137
+ # :append # If set to true, the packets are appended to the file, rather
138
+ # # than overwriting.
139
+ def to_file(args={})
140
+ filename = args[:filename] || args[:file]
141
+ unless (!filename.nil? || filename.kind_of?(String))
142
+ raise ArgumentError, "Need a :filename for #{self.class}"
143
+ end
144
+
145
+ append = args[:append]
146
+ mode = ''
147
+ if append and ::File.exists? filename
148
+ mode = 'ab'
149
+ else
150
+ mode = 'wb'
151
+ end
152
+ ::File.open(filename,mode) {|f| f.write(self.to_s)}
153
+ [filename, self.to_s.size]
154
+ end
155
+
156
+ alias_method :to_f, :to_file
157
+
158
+ # Shorthand method for writing to a file. Can take either :file => 'name.pcapng'
159
+ # or simply 'name.pcapng'
160
+ def write(filename='out.pcapng')
161
+ if filename.kind_of?(Hash)
162
+ f = filename[:filename] || filename[:file] || 'out.pcapng'
163
+ else
164
+ f = filename.to_s
165
+ end
166
+ self.to_file(:filename => f.to_s, :append => false)
167
+ end
168
+
169
+ # Shorthand method for appendong to a file. Can take either
170
+ # :file => 'name.pcapng' or simply 'name.pcapng'
171
+ def append(filename='out.pcapng')
172
+ if filename.kind_of?(Hash)
173
+ f = filename[:filename] || filename[:file] || 'out.pcapng'
174
+ else
175
+ f = filename.to_s
176
+ end
177
+ self.to_file(:filename => f.to_s, :append => true)
178
+ end
179
+
180
+ # Takes an array of packets or a Hash.
181
+ #
182
+ # Array: as generated by file_to_array or Array of Packet objects.
183
+ # update Pcapng::File object without writing file on disk
184
+ # Hash: take packets from args and write them to a file. Valid arguments are:
185
+ # :filename # do not write file on disk if not given
186
+ # :array # Can either be an array of packet data, or a hash-value pair
187
+ # # of timestamp => data.
188
+ # :timestamp # Sets an initial timestamp (Time object)
189
+ # :ts_inc # Sets the increment between timestamps. Defaults to 1 second.
190
+ # :append # If true, then the packets are appended to the end of a file.
191
+ def array_to_file(args={})
192
+ case args
193
+ when Hash
194
+ filename = args[:filename] || args[:file]
195
+ ary = args[:array] || args[:arr]
196
+ unless ary.kind_of? Array
197
+ raise ArgumentError, ':array parameter needs to be an array'
198
+ end
199
+ ts = args[:timestamp] || args[:ts] || Time.now
200
+ ts_inc = args[:ts_inc] || 1
201
+ append = !!args[:append]
202
+ when Array
203
+ ary = args
204
+ ts = Time.now
205
+ ts_inc = 1
206
+ filename = nil
207
+ append = false
208
+ else
209
+ raise ArgumentError, 'unknown argument. Need either a Hash or Array'
210
+ end
211
+
212
+ section = SHB.new
213
+ @sections << section
214
+ itf = IDB.new(:endian => section.endian)
215
+ classify_block section, itf
216
+
217
+ ary.each_with_index do |pkt, i|
218
+ case pkt
219
+ when Hash
220
+ this_ts = pkt.keys.first.to_i
221
+ this_cap_len = pkt.values.first.to_s.size
222
+ this_data = pkt.values.first.to_s
223
+ else
224
+ this_ts = (ts + ts_inc * i).to_i
225
+ this_cap_len = pkt.to_s.size
226
+ this_data = pkt.to_s
227
+ end
228
+ this_ts = (this_ts / itf.ts_resol).to_i
229
+ this_tsh = this_ts >> 32
230
+ this_tsl = this_ts & 0xffffffff
231
+ this_pkt = EPB.new(:endian => section.endian,
232
+ :interface_id => 0,
233
+ :tsh => this_tsh,
234
+ :tsl => this_tsl,
235
+ :cap_len => this_cap_len,
236
+ :orig_len => this_cap_len,
237
+ :data => this_data)
238
+ classify_block section, this_pkt
239
+ end
240
+
241
+ if filename
242
+ self.to_f(:filename => filename, :append => append)
243
+ else
244
+ self
245
+ end
246
+ end
247
+
248
+
249
+ private
250
+
251
+ def parse_section(io)
252
+ shb = SHB.new
253
+ type = StructFu::Int32.new(0, shb.endian).read(io.read(4))
254
+ io.seek(-4, IO::SEEK_CUR)
255
+ shb = parse(type, io, shb)
256
+ raise InvalidFileError, 'no Section header found' unless shb.is_a?(SHB)
257
+
258
+ if shb.section_len.to_i != 0xffffffffffffffff
259
+ # Section length is defined
260
+ section = StringIO.new(io.read(shb.section_len.to_i))
261
+ while !section.eof? do
262
+ shb = @sections.last
263
+ type = StructFu::Int32.new(0, shb.endian).read(section.read(4))
264
+ section.seek(-4, IO::SEEK_CUR)
265
+ block = parse(type, section, shb)
266
+ end
267
+ else
268
+ # section length is undefined
269
+ while !io.eof?
270
+ shb = @sections.last
271
+ type = StructFu::Int32.new(0, shb.endian).read(io.read(4))
272
+ io.seek(-4, IO::SEEK_CUR)
273
+ block = parse(type, io, shb)
274
+ end
275
+ end
276
+ end
277
+
278
+ def parse(type, io, shb)
279
+ types = PcapNG.constants(false).select { |c| c.to_s =~ /_TYPE/ }.
280
+ map { |c| [PcapNG.const_get(c).to_i, c] }
281
+ types = Hash[types]
282
+
283
+ if types.has_key?(type.to_i)
284
+ klass = PcapNG.const_get(types[type.to_i].to_s.gsub(/_TYPE/, '').to_sym)
285
+ block = klass.new(endian: shb.endian)
286
+ else
287
+ block = UnknownBlock.new(endian: shb.endian)
288
+ end
289
+
290
+ classify_block shb, block
291
+ block.read(io)
292
+ end
293
+
294
+ def classify_block(shb, block)
295
+ case block
296
+ when SHB
297
+ @sections << block
298
+ when IDB
299
+ shb << block
300
+ block.section = shb
301
+ when EPB
302
+ shb.interfaces[block.interface_id.to_i] << block
303
+ block.interface = shb.interfaces[block.interface_id.to_i]
304
+ when SPB
305
+ shb.interfaces[0] << block
306
+ block.interface = shb.interfaces[0]
307
+ else
308
+ shb.unknown_blocks << block
309
+ block.section = shb
310
+ end
311
+ end
312
+
313
+ end
314
+
315
+ end
316
+ end