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