Class: PacketFu::PcapFile

Inherits:
Struct
  • Object
show all
Includes:
StructFu
Defined in:
lib/packetfu/pcap.rb

Overview

PcapFile is a complete libpcap file struct, made up of two elements, a PcapHeader and PcapPackets.

See wiki.wireshark.org/Development/LibpcapFileFormat

PcapFile also can behave as a singleton class, which is usually the better way to handle pcap files of really any size, since it doesn’t require storing packets before handing them off to a given block. This is really the way to go.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from StructFu

#clone, #set_endianness, #sz, #typecast

Methods inherited from Struct

#force_binary

Constructor Details

#initialize(args = {}) ⇒ PcapFile

Returns a new instance of PcapFile.



318
319
320
321
# File 'lib/packetfu/pcap.rb', line 318

def initialize(args={})
  init_fields(args)
  super(args[:endian], args[:head], args[:body])
end

Instance Attribute Details

#bodyObject

Returns the value of attribute body

Returns:

  • (Object)

    the current value of body



236
237
238
# File 'lib/packetfu/pcap.rb', line 236

def body
  @body
end

#endianObject

Returns the value of attribute endian

Returns:

  • (Object)

    the current value of endian



236
237
238
# File 'lib/packetfu/pcap.rb', line 236

def endian
  @endian
end

#headObject

Returns the value of attribute head

Returns:

  • (Object)

    the current value of head



236
237
238
# File 'lib/packetfu/pcap.rb', line 236

def head
  @head
end

Class Method Details

.read(fname, &block) ⇒ Object

Takes a given file name, and reads out the packets. If given a block, it will yield back a PcapPacket object per packet found.



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/packetfu/pcap.rb', line 249

def read(fname,&block) 
  file_header = PcapHeader.new
  pcap_packets = PcapPackets.new 
  unless File.readable? fname
    raise ArgumentError, "Cannot read file `#{fname}'"
  end
  begin
  file_handle = File.open(fname, "rb")
  file_header.read file_handle.read(24)
  packet_count = 0
  pcap_packet = PcapPacket.new(:endian => file_header.endian)
  while pcap_packet.read file_handle.read(16) do
    len = pcap_packet.incl_len
    pcap_packet.data = StructFu::String.new.read(file_handle.read(len.to_i))
    packet_count += 1
    if pcap_packet.data.size < len.to_i
      warn "Packet ##{packet_count} is corrupted: expected #{len.to_i}, got #{pcap_packet.data.size}. Exiting."
      break
    end
    pcap_packets << pcap_packet.clone
    yield pcap_packets.last if block
  end
  ensure
    file_handle.close
  end
  block ? packet_count : pcap_packets
end

.read_packet_bytes(fname, &block) ⇒ Object Also known as: file_to_array

Takes a filename, and an optional block. If a block is given, yield back the raw packet data from the given file. Otherwise, return an array of parsed packets.



280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/packetfu/pcap.rb', line 280

def read_packet_bytes(fname,&block)
  count = 0
  packets = [] unless block
  read(fname) do |packet| 
    if block
      count += 1
      yield packet.data.to_s
    else
      packets << packet.data.to_s
    end
  end
  block ? count : packets
end

.read_packets(fname, &block) ⇒ Object

Takes a filename, and an optional block. If a block is given, yield back parsed packets from the given file. Otherwise, return an array of parsed packets.

This is a brazillian times faster than the old methods of extracting packets from files.



302
303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/packetfu/pcap.rb', line 302

def read_packets(fname,&block)
  count = 0
  packets = [] unless block
  read_packet_bytes(fname) do |packet| 
    if block
      count += 1
      yield Packet.parse(packet)
    else
      packets << Packet.parse(packet)
    end
  end
  block ? count : packets
end

Instance Method Details

#append(filename = 'out.pcap') ⇒ Object

Shorthand method for appending to a file. Can take either :file => ‘name.pcap’ or simply ‘name.pcap’



480
481
482
483
484
485
486
487
# File 'lib/packetfu/pcap.rb', line 480

def append(filename='out.pcap')
  if filename.kind_of?(Hash)
    f = filename[:filename] || filename[:file] || filename[:f] || 'out.pcap'
  else
    f = filename.to_s
  end
  self.to_file(:filename => f, :append => true)
end

#array_to_file(args = {}) ⇒ Object Also known as: a2f

Takes an array of packets (as generated by file_to_array), and writes them to a file. Valid arguments are:

:filename
:array      # Can either be an array of packet data, or a hash-value pair of timestamp => data.
:timestamp  # Sets an initial timestamp
:ts_inc     # Sets the increment between timestamps. Defaults to 1 second.
:append     # If true, then the packets are appended to the end of a file.


390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
# File 'lib/packetfu/pcap.rb', line 390

def array_to_file(args={})
  if args.kind_of? Hash
    filename = args[:filename] || args[:file] || args[:f]
    arr = args[:array] || args[:arr] || args[:a]
    ts = args[:timestamp] || args[:ts] || Time.now.to_i
    ts_inc = args[:timestamp_increment] || args[:ts_inc] || 1
    append = !!args[:append]
  elsif args.kind_of? Array
    arr = args
    filename = append = nil
  else
    raise ArgumentError, "Unknown argument. Need either a Hash or Array."
  end
  unless arr.kind_of? Array
    raise ArgumentError, "Need an array to read packets from"
  end
  arr.each_with_index do |p,i|
    if p.kind_of? Hash # Binary timestamps are included
      this_ts = p.keys.first
      this_incl_len = p.values.first.size
      this_orig_len = this_incl_len
      this_data = p.values.first
    else # it's an array
      this_ts = Timestamp.new(:endian => self[:endian], :sec => ts + (ts_inc * i)).to_s
      this_incl_len = p.to_s.size
      this_orig_len = this_incl_len
      this_data = p.to_s
    end
    this_pkt = PcapPacket.new({:endian => self[:endian],
                              :timestamp => this_ts,
                              :incl_len => this_incl_len,
                              :orig_len => this_orig_len,
                              :data => this_data }
                             )
    self[:body] << this_pkt
  end
  if filename
    self.to_f(:filename => filename, :append => append)
  else
    self
  end
end

#array_to_file!(arr) ⇒ Object Also known as: a2f!

Just like array_to_file, but clears any existing packets from the array first.



436
437
438
439
# File 'lib/packetfu/pcap.rb', line 436

def array_to_file!(arr)
  clear
  array_to_file(arr)
end

#clearObject

Clears the contents of the PcapFile.



336
337
338
# File 'lib/packetfu/pcap.rb', line 336

def clear
  self[:body].clear
end

#file_to_array(args = {}) ⇒ Object Also known as: f2a

file_to_array() translates a libpcap file into an array of packets. Note that this strips out pcap timestamps – if you’d like to retain timestamps and other libpcap file information, you will want to use read() instead.



368
369
370
371
372
373
374
375
376
377
378
# File 'lib/packetfu/pcap.rb', line 368

def file_to_array(args={})
  filename = args[:filename] || args[:file] || args[:f]
  if filename
    self.read! File.open(filename, "rb") {|f| f.read}
  end
  if args[:keep_timestamps] || args[:keep_ts] || args[:ts]
    self[:body].map {|x| {x.timestamp.to_s => x.data.to_s} }
  else
    self[:body].map {|x| x.data.to_s}
  end
end

#init_fields(args = {}) ⇒ Object

Called by initialize to set the initial fields.



324
325
326
327
328
# File 'lib/packetfu/pcap.rb', line 324

def init_fields(args={})
  args[:head] = PcapHeader.new(:endian => args[:endian]).read(args[:head])
  args[:body] = PcapPackets.new(:endian => args[:endian]).read(args[:body])
  return args
end

#read(str) ⇒ Object

Reads a string to populate the object. Note that this appends new packets to any existing packets in the PcapFile.



342
343
344
345
346
347
# File 'lib/packetfu/pcap.rb', line 342

def read(str)
  force_binary(str)
  self[:head].read str[0,24]
  self[:body].read str
  self
end

#read!(str) ⇒ Object

Clears the contents of the PcapFile prior to reading in a new string.



350
351
352
353
354
# File 'lib/packetfu/pcap.rb', line 350

def read!(str)
  clear  
  force_binary(str)
  self.read str
end

#readfile(file) ⇒ Object

A shorthand method for opening a file and reading in the packets. Note that readfile clears any existing packets, since that seems to be the typical use.



359
360
361
362
# File 'lib/packetfu/pcap.rb', line 359

def readfile(file)
  fdata = File.open(file, "rb") {|f| f.read}
  self.read! fdata
end

#to_file(args = {}) ⇒ Object Also known as: to_f

Writes the PcapFile to a file. Takes the following arguments:

:filename # The file to write to.
:append   # If set to true, the packets are appended to the file, rather than overwriting.


447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
# File 'lib/packetfu/pcap.rb', line 447

def to_file(args={})
  filename = args[:filename] || args[:file] || args[:f]
  unless (!filename.nil? || filename.kind_of?(String))
    raise ArgumentError, "Need a :filename for #{self.class}"
  end
  append = args[:append]
  if append
    if File.exists? filename
      File.open(filename,'ab') {|file| file.write(self.body.to_s)}
    else
      File.open(filename,'wb') {|file| file.write(self.to_s)}
    end
  else
    File.open(filename,'wb') {|file| file.write(self.to_s)}
  end
  [filename, self.body.sz, self.body.size]
end

#to_sObject

Returns the object in string form.



331
332
333
# File 'lib/packetfu/pcap.rb', line 331

def to_s
  self[:head].to_s + self[:body].map {|p| p.to_s}.join
end

#write(filename = 'out.pcap') ⇒ Object

Shorthand method for writing to a file. Can take either :file => ‘name.pcap’ or simply ‘name.pcap’



469
470
471
472
473
474
475
476
# File 'lib/packetfu/pcap.rb', line 469

def write(filename='out.pcap')
  if filename.kind_of?(Hash)
    f = filename[:filename] || filename[:file] || filename[:f] || 'out.pcap'
  else
    f = filename.to_s
  end
  self.to_file(:filename => f.to_s, :append => false)
end