class PacketGen::PcapNG::File
PcapNG::File
is a complete Pcap-NG file handler. @author Sylvain Daubert
Constants
- BLOCK_TYPES
@private
- KNOWN_LINK_TYPES
Known link types
Attributes
Get file sections @return [Array]
Public Class Methods
# File lib/packetgen/pcapng/file.rb, line 36 def initialize @sections = [] end
Public Instance Methods
Shorthand method for appending to a file. @param [#to_s] filename @return [Array] see return value from {#to_file}
# File lib/packetgen/pcapng/file.rb, line 223 def append(filename='out.pcapng') self.to_file(filename.to_s, append: true) end
@deprecated Prefer use of {#read_array} or {#read_hash}. @overload array_to_file
(ary)
Update {File} object with packets. @param [Array] ary as generated by {#file_to_array} or Array of Packet objects. Update {File} object without writing file on disk @return [self]
@overload array_to_file
(options={})
Update {File} and/or write it to a file @param [Hash] options @option options [String] :file file written on disk only if given @option options [Array] :array can either be an array of packet data, or a hash-value pair of timestamp => data. @option options [Time] :timestamp set an initial timestamp @option options [Integer] :ts_inc set the increment between timestamps. Defaults to 1 @option options [Boolean] :append if +true+, append packets to the end of the file @return [Array] see return value from {#to_file}
# File lib/packetgen/pcapng/file.rb, line 245 def array_to_file(options={}) filename, ary, ts, ts_inc, append = array_to_file_options(options) section = create_new_shb_section ary.each do |pkt| classify_block(section, epb_from_pkt(pkt, section, ts)) ts += ts_inc end if filename self.to_f(filename, append: append) else self end end
Clear the contents of the Pcapng::File. @return [void]
# File lib/packetgen/pcapng/file.rb, line 140 def clear @sections.clear end
@deprecated
Prefer use of {#to_a} or {#to_h}.
Translates a {File} into an array of packets. @param [Hash] options @option options [String] :file if given, object is cleared and filename
is analyzed before generating array. Else, array is generated from +self+
@option options [Boolean] :keep_timestamps if true
(default value: false
),
generates an array of hashes, each one with timestamp as key and packet as value. There is one hash per packet.
@return [Array<Packet>,Array<Hash>]
# File lib/packetgen/pcapng/file.rb, line 154 def file_to_array(options={}) Deprecation.deprecated(self.class, __method__) file = options[:file] || options[:filename] reread file ary = [] blk = if options[:keep_timestamps] || options[:keep_ts] proc { |pkt| { pkt.timestamp => pkt.data.to_s } } else proc { |pkt| pkt.data.to_s } end each_packet_with_interface do |pkt, _itf| ary << blk.call(pkt) end ary end
@return [String] @since 3.1.6
# File lib/packetgen/pcapng/file.rb, line 297 def inspect str = +'' sections.each do |section| str << section.inspect section.interfaces.each do |itf| str << itf.inspect itf.packets.each { |block| str << block.inspect } end end str end
Read a string to populate the object. Note that this appends new blocks to the Pcapng::File object. @param [String] str @return [self]
# File lib/packetgen/pcapng/file.rb, line 44 def read(str) PacketGen.force_binary(str) io = StringIO.new(str) parse_section(io) self end
Clear the contents of the Pcapng::File prior to reading in a new string. This string should contain a Section Header
Block
and an Interface Description Block
to create a conform pcapng file. @param [String] str @return [self]
# File lib/packetgen/pcapng/file.rb, line 56 def read!(str) clear read(str) end
Update current object from an array of packets @param [Array<Packet>] packets @param [Time, nil] timestamp initial timestamp, used for first packet @param [Numeric, nil] ts_inc timestamp increment, in seconds, to increment
initial timestamp for each packet
@return [self] @note if timestamp
and/or ts_inc
are nil, {SPB} sections are created
for each packet, else {EPB} ones are used
@since 3.1.6
# File lib/packetgen/pcapng/file.rb, line 271 def read_array(packets, timestamp: nil, ts_inc: nil) ts = timestamp section = create_new_shb_section packets.each do |pkt| block = create_block_from_pkt(pkt, section, ts, ts_inc) classify_block(section, block) ts = update_ts(ts, ts_inc) end self end
Update current object from a hash of packets and timestamps @param [Hash{Time => Packet}] hsh @return [self] @since 3.1.6
# File lib/packetgen/pcapng/file.rb, line 286 def read_hash(hsh) section = create_new_shb_section hsh.each do |ts, pkt| block = create_block_from_pkt(pkt, section, ts, 0) classify_block(section, block) end self end
Give an array of raw packets (raw data from packets). If a block is given, yield raw packet data from the given file. @overload read_packet_bytes
(fname)
@param [String] fname pcapng file name @return [Array] array of packet raw data
@overload read_packet_bytes
(fname)
@param [String] fname pcapng file name @yieldparam [String] raw packet raw data @yieldparam [Integer] interface's link_type from which packet was captured @return [Integer] number of packets
@raise [ArgumentError] cannot read fname
# File lib/packetgen/pcapng/file.rb, line 93 def read_packet_bytes(fname, &blk) packets = [] unless blk count = readfile(fname) do |packet| if blk yield packet.data.to_s, packet.interface.link_type else packets << packet.data.to_s end end blk ? count : packets end
Return an array of parsed packets. If a block is given, yield parsed packets from the given file. @overload read_packets
(fname)
@param [String] fname pcapng file name @return [Array<Packet>]
@overload read_packets
(fname)
@param [String] fname pcapng file name @yieldparam [Packet] packet @return [Integer] number of packets
@raise [ArgumentError] cannot read fname
# File lib/packetgen/pcapng/file.rb, line 117 def read_packets(fname, &blk) packets = [] unless blk count = read_packet_bytes(fname) do |packet, link_type| parsed_pkt = parse_packet(packet, link_type) if blk yield parsed_pkt else packets << parsed_pkt end end blk ? count : packets end
Read a given file and analyze it. If given a block, it will yield PcapNG::EPB
or PcapNG::SPB
objects. This is the only way to get packet timestamps. @param [String] fname pcapng file name @yieldparam [EPB,SPB] block @return [Integer] return number of yielded blocks (only if a block is given) @raise [ArgumentError] cannot read fname
# File lib/packetgen/pcapng/file.rb, line 68 def readfile(fname, &blk) raise ArgumentError, "cannot read file #{fname}" unless ::File.readable?(fname) ::File.open(fname, 'rb') { |f| parse_section(f) until f.eof? } return unless blk count = 0 each_packet_with_interface do |pkt, _itf| count += 1 yield pkt end count end
Translates a {File} into an array of packets. @return [Array<Packet>] @since 3.1.6
# File lib/packetgen/pcapng/file.rb, line 176 def to_a ary = [] each_packet_with_interface do |pkt, itf| ary << parse_packet(pkt.data.to_s, itf.link_type) end ary end
Writes the {File} to a file. @param [Hash] options @option options [Boolean] :append (default: false
) if set to true
,
the packets are appended to the file, rather than overwriting it
@return [Array] array of 2 elements: filename and size written @todo for 4.0, replace options
by append
kwarg
# File lib/packetgen/pcapng/file.rb, line 206 def to_file(filename, options={}) mode = (options[:append] && ::File.exist?(filename)) ? 'ab' : 'wb' ::File.open(filename, mode) { |f| f.write(self.to_s) } [filename, self.to_s.size] end
Translates a {File} into a hash with timestamps as keys. @note Only packets from {EPB} sections are extracted, as {SPB} ones do not have timestamp. @return [Hash{Time => Packet}] @since 3.1.6
# File lib/packetgen/pcapng/file.rb, line 189 def to_h hsh = {} each_packet_with_interface do |pkt, itf| next if pkt.is_a?(SPB) hsh[pkt.timestamp] = parse_packet(pkt.data.to_s, itf.link_type) end hsh end
Return the object as a String @return [String]
# File lib/packetgen/pcapng/file.rb, line 134 def to_s @sections.map(&:to_s).join end
Shorthand method for writing to a file. @param [#to_s] filename @return [Array] see return value from {#to_file}
# File lib/packetgen/pcapng/file.rb, line 216 def write(filename='out.pcapng') self.to_file(filename.to_s, append: false) end
Private Instance Methods
# File lib/packetgen/pcapng/file.rb, line 380 def array_to_file_options(options) case options when Hash array_to_file_options_from_hash(options) when Array [nil, options, Time.now, 1, false] else raise ArgumentError, 'unknown argument. Need either a Hash or Array' end end
Extract and check options for array_to_file
# File lib/packetgen/pcapng/file.rb, line 392 def array_to_file_options_from_hash(options) %i[filename arr ts].each do |deprecated_opt| Deprecation.deprecated_option(self.class, :array_to_file, deprecated_opt) if options[deprecated_opt] end filename = options[:filename] || options[:file] ary = options[:array] || options[:arr] raise ArgumentError, ':array parameter needs to be an array' unless ary.is_a? Array ts = options[:timestamp] || options[:ts] || Time.now ts_inc = options[:ts_inc] || 1 append = !options[:append].nil? [filename, ary, ts, ts_inc, append] end
Compute tsh and tsl from ts
# File lib/packetgen/pcapng/file.rb, line 418 def calc_ts(timeslot, ts_resol) this_ts = (timeslot / ts_resol).to_i [this_ts >> 32, this_ts & 0xffffffff] end
Classify block from its type @param [SHB] shb header of current section @param [Block] block block to classify @return [void]
# File lib/packetgen/pcapng/file.rb, line 366 def classify_block(shb, block) case block when SHB @sections << block when IDB shb << block when SPB, EPB ifid = block.is_a?(EPB) ? block.interface_id : 0 shb.interfaces[ifid] << block else shb.add_unknown_block(block) end end
# File lib/packetgen/pcapng/file.rb, line 431 def create_block_from_pkt(pkt, section, timestamp, ts_inc) if timestamp.nil? || ts_inc.nil? spb_from_pkt(pkt, section) else epb_from_pkt(pkt, section, timestamp) end end
# File lib/packetgen/pcapng/file.rb, line 408 def create_new_shb_section section = SHB.new @sections << section itf = IDB.new(endian: section.endian) classify_block section, itf section end
Iterate over each xPB with its associated interface @return [void] @yieldparam [String] xpb @yieldparam [IDB] itf
# File lib/packetgen/pcapng/file.rb, line 477 def each_packet_with_interface sections.each do |section| section.interfaces.each do |itf| itf.packets.each { |xpb| yield xpb, itf } end end end
@todo remove hash case when array_to_file
will be removed
# File lib/packetgen/pcapng/file.rb, line 449 def epb_from_pkt(pkt, section, timestamp) this_ts, this_data = case pkt when Hash [pkt.keys.first.to_i, pkt.values.first.to_s] else [timestamp.to_r, pkt.to_s] end this_cap_len = this_data.size this_tsh, this_tsl = calc_ts(this_ts, section.interfaces.last.ts_resol) EPB.new(endian: section.endian, interface_id: 0, tsh: this_tsh, tsl: this_tsl, cap_len: this_cap_len, orig_len: this_cap_len, data: this_data) end
Guess class to use from type @param [Types::Int] type @return [Block]
# File lib/packetgen/pcapng/file.rb, line 358 def guess_block_type(type) BLOCK_TYPES.key?(type.to_i) ? BLOCK_TYPES[type.to_i] : UnknownBlock end
Parse a block from its type @param [Types::Int32] type @param [IO] io stream from which parse block @param [SHB] shb header of current section @return [Block]
# File lib/packetgen/pcapng/file.rb, line 349 def parse(type, io, shb) block = guess_block_type(type).new(endian: shb.endian) classify_block shb, block block.read(io) end
# File lib/packetgen/pcapng/file.rb, line 485 def parse_packet(data, link_type) Packet.parse(data, first_header: KNOWN_LINK_TYPES[link_type]) end
Parse a section. A section is made of at least a SHB
. It than may contain others blocks, such as IDB
, SPB
or EPB
. @param [IO] io @return [void]
# File lib/packetgen/pcapng/file.rb, line 316 def parse_section(io) shb = parse_shb(SHB.new, io) raise InvalidFileError, 'no Section header found' unless shb.is_a?(SHB) to_parse = if shb.section_len.to_i != 0xffffffffffffffff # Section length is defined StringIO.new(io.read(shb.section_len.to_i)) else # section length is undefined io end until to_parse.eof? shb = @sections.last parse_shb shb, to_parse end end
# File lib/packetgen/pcapng/file.rb, line 424 def reread(filename) return if filename.nil? clear readfile filename end
# File lib/packetgen/pcapng/file.rb, line 439 def spb_from_pkt(pkt, section) pkt_s = pkt.to_s size = pkt_s.size SPB.new(endian: section.endian, block_len: size, orig_len: size, data: pkt_s) end
# File lib/packetgen/pcapng/file.rb, line 467 def update_ts(timestamp, ts_inc) return nil if timestamp.nil? || ts_inc.nil? timestamp + ts_inc end