lib/packetgen/packet.rb in packetgen-2.6.0 vs lib/packetgen/packet.rb in packetgen-2.7.0

- old
+ new

@@ -3,14 +3,14 @@ # See https://github.com/sdaubert/packetgen for more informations # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net> # This program is published under MIT license. # frozen_string_literal: true + require 'pcaprub' module PacketGen - # An object of type {Packet} handles a network packet. This packet may contain # multiple protocol headers, starting from MAC layer or from Network (OSI) layer. # # Creating a packet is fairly simple: # Packet.gen 'IP', src: '192.168.1.1', dst: '192.168.1.2' @@ -105,21 +105,19 @@ # @param [String] filename PcapNG or Pcap file. # @return [Array<Packet>] # @author Sylvain Daubert # @author Kent Gruber def self.read(filename) - begin - PcapNG::File.new.read_packets filename - rescue => e - raise ArgumentError, e unless File.extname(filename.downcase) == '.pcap' - packets = [] - PCAPRUB::Pcap.open_offline(filename).each_packet do |packet| - next unless packet = PacketGen.parse(packet.to_s) - packets << packet - end - packets + PcapNG::File.new.read_packets filename + rescue StandardError => e + raise ArgumentError, e unless File.extname(filename.downcase) == '.pcap' + packets = [] + PCAPRUB::Pcap.open_offline(filename).each_packet do |packet| + next unless (packet = PacketGen.parse(packet.to_s)) + packets << packet end + packets end # Write packets to +filename+ # # For more options, see {PcapNG::File}. @@ -223,11 +221,11 @@ # @return [Array] see return from {PcapNG::File#to_file} # @see File def to_f(filename) PcapNG::File.new.array_to_file(filename: filename, array: [self]) end - alias :write :to_f + alias write to_f # send packet on wire. Use first header +#to_w+ method. # @param [String] iface interface name. Default to first non-loopback interface # @param [Boolean] calc call {#calc} on packet before sending it # @param [Integer] number number of times to send the packets @@ -275,13 +273,13 @@ headers.each do |header| idx = @headers.index(header) raise FormatError, 'header not in packet!' if idx.nil? prev_header = idx > 0 ? @headers[idx - 1] : nil - next_header = (idx+1) < @headers.size ? @headers[idx + 1] : nil + next_header = (idx + 1) < @headers.size ? @headers[idx + 1] : nil @headers.delete_at(idx) - if prev_header and next_header + if prev_header && next_header add_header(next_header, previous_header: prev_header) end end rescue ArgumentError => ex raise FormatError, ex.message @@ -323,16 +321,36 @@ # @return [Boolean] def ==(other) to_s == other.to_s end + # Invert all possible fields in packet to create a reply. + # @return [self] + def reply! + @headers.each do |header| + header.reply! if header.respond_to?(:reply!) + end + self + end + + # Forge a new packet from current one with all possible fields + # inverted. The new packet may be a reply to current one. + # @return [Packet] + def reply + pkt = dup + pkt.reply! + end + private # Dup +@headers+ instance variable. Internally used by +#dup+ and +#clone+ # @return [void] - def initialize_copy(other) - @headers = @headers.dup + def initialize_copy(_other) + @headers = @headers.map(&:dup) + @headers.each do |header| + add_magic_header_method header + end end # @overload header(klass, layer=1) # @param [Class] klass # @param [Integer] layer @@ -384,21 +402,25 @@ msg << "#{prev_header.method_name}_proto_field: " msg << "value_for_#{header.method_name})" raise ArgumentError, msg end end - bindings.set(prev_header) if !bindings.empty? and !parsing + bindings.set(prev_header) if !bindings.empty? && !parsing prev_header[:body] = header end header.packet = self @headers << header unless previous_header - unless respond_to? header.method_name - self.instance_eval "def #{header.method_name}(arg=nil);" \ - "header(#{header.class}, arg); end" - end + + return if respond_to? header.method_name + add_magic_header_method header end + def add_magic_header_method(header) + self.instance_eval "def #{header.method_name}(arg=nil);" \ + "header(#{header.class}, arg); end" + end + def guess_first_header(binary_str) first_header = nil Header.all.each do |hklass| hdr = hklass.new # #read may return another object (more specific class) @@ -416,10 +438,10 @@ first_header end def decode_bottom_up decode_packet_bottom_up = true - while decode_packet_bottom_up do + while decode_packet_bottom_up last_known_hdr = @headers.last break unless last_known_hdr.respond_to? :body break if last_known_hdr.body.empty? search_header(last_known_hdr) do |nh| str = last_known_hdr.body