lib/packetgen/packet.rb in packetgen-3.3.3 vs lib/packetgen/packet.rb in packetgen-4.0.0
- old
+ new
@@ -59,24 +59,24 @@
# Create a new Packet
# @param [String] protocol base protocol for packet
# @param [Hash] options specific options for +protocol+
# @return [Packet]
def self.gen(protocol, options={})
- self.new.add protocol, options
+ self.new.add(protocol, options)
end
# Parse a binary string and generate a Packet from it.
# # auto-detect first header
- # Packet.parse str
+ # Packet.parse(str)
# # force decoding a Ethernet header for first header
- # Packet.parse str, first_header: 'Eth'
+ # Packet.parse(str, first_header: 'Eth')
# @param [String] binary_str
# @param [String,nil] first_header First protocol header. +nil+ means discover it!
# @return [Packet]
# @raise [ArgumentError] +first_header+ is an unknown header
def self.parse(binary_str, first_header: nil)
- new.parse binary_str, first_header: first_header
+ new.parse(binary_str, first_header: first_header)
end
# Capture packets from wire.
# Same arguments as {Capture#initialize}
# @see Capture#initialize
@@ -110,20 +110,27 @@
raise ArgumentError, e unless File.extname(filename.downcase) == '.pcap'
Pcap.read(filename)
end
- # Write packets to +filename+
+ # Write packets to +filename+, as pcap or PcapNG file
#
# For more options, see {PcapNG::File}.
# @param [String] filename
# @param [Array<Packet>] packets packets to write
# @return [void]
+ # @since 4.0.0 write a pcap file if filename extension is +.pcap+
+ # @author Sylvain Daubert
+ # @author LemonTree55 (pcap)
def self.write(filename, packets)
- pf = PcapNG::File.new
- pf.array_to_file packets
- pf.to_f filename
+ if filename.end_with?('.pcap')
+ Pcap.write(filename, packets)
+ else
+ pf = PcapNG::File.new
+ pf.read_array(packets)
+ pf.to_f(filename)
+ end
end
# @private
def initialize
@headers = []
@@ -140,11 +147,11 @@
klass = check_protocol(protocol)
# options[:packet]= self is speedier than options.merge(packet: self)
options[:packet] = self
header = klass.new(options)
- add_header header
+ add_header(header)
self
end
# Insert a header in packet
# @param [Header] prev header after which insert new one
@@ -157,11 +164,11 @@
nxt = prev.body
# options[:packet]= self is speedier than options.merge(packet: self)
options[:packet] = self
header = klass.new(options)
- add_header header, previous_header: prev
+ add_header(header, previous_header: prev)
idx = headers.index(prev) + 1
headers[idx, 0] = header
header[:body] = nxt
self
end
@@ -171,41 +178,44 @@
# pkt.is?('IP') #=> true
# pkt.is?('TCP') #=> false
# @return [Boolean]
# @raise [ArgumentError] unknown protocol
def is?(protocol)
- klass = check_protocol protocol
+ klass = check_protocol(protocol)
headers.any?(klass)
end
# Recalculate all packet checksums
# @return [void]
def calc_checksum
headers.reverse_each do |header|
- header.calc_checksum if header.respond_to? :calc_checksum
+ header.calc_checksum if header.respond_to?(:calc_checksum)
end
end
# Recalculate all packet length fields
# @return [void]
def calc_length
headers.reverse_each do |header|
- header.calc_length if header.respond_to? :calc_length
+ header.calc_length if header.respond_to?(:calc_length)
end
end
# Recalculate all calculatable fields (for now: length and checksum)
# @return [void]
+ # @author LemonTree55
def calc
- calc_length
- calc_checksum
+ headers.reverse_each do |header|
+ header.calc_length if header.respond_to?(:calc_length)
+ header.calc_checksum if header.respond_to?(:calc_checksum)
+ end
end
# Get packet body
# @return [Types]
def body
- last_header[:body] if last_header.respond_to? :body
+ last_header[:body] if last_header.respond_to?(:body)
end
# Set packet body
# @param [String] str
# @return [void]
@@ -227,26 +237,26 @@
PcapNG::File.new.read_array([self]).to_f(filename)
end
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 [String,nil] iface interface name. Default to first non-loopback interface
# @param [Boolean] calc if +true+, call {#calc} on packet before sending it.
# @param [Integer] number number of times to send the packets
# @param [Integer,Float] interval time, in seconds, between sending 2 packets
# @return [void]
# @since 2.1.4 add `calc`, `number` and `interval` parameters
# @since 3.0.0 +calc+ defaults to +true+
def to_w(iface=nil, calc: true, number: 1, interval: 1)
iface ||= PacketGen.default_iface
- if first_header.respond_to? :to_w
+ if first_header.respond_to?(:to_w)
self.calc if calc
number.times do
first_header.to_w(iface)
- sleep interval if number > 1
+ sleep(interval) if number > 1
end
else
type = first_header.protocol_name
raise WireError, "don't known how to send a #{type} packet on wire"
end
@@ -255,16 +265,16 @@
# Encapulate another packet in +self+
# @param [Packet] other
# @param [Boolean] parsing set to +true+ to not update last current header field
# from binding with first other's one. Use only when current header field
# has its value set accordingly.
- # @return [self] +self+ with new headers from +other+
+ # @return [self] +self+ updated with new headers from +other+
# @raise [BindingError] do not known how to encapsulate
# @since 1.1.0
def encapsulate(other, parsing: false)
other.headers.each_with_index do |h, i|
- add_header h, parsing: i.positive? || parsing
+ add_header(h, parsing: i.positive? || parsing)
end
end
# Remove headers from +self+
# @param [Array<Header>] hdrs
@@ -284,12 +294,12 @@
raise FormatError, e.message
end
# Parse a binary string and populate Packet from it.
# @param [String] binary_str
- # @param [String,nil] first_header First protocol header. +nil+ means discover it!
- # @return [Packet] self
+ # @param [String,nil] first_header First protocol header name. +nil+ means discover it!
+ # @return [self]
# @raise [ParseError] +first_header+ is an unknown header
# @raise [BindingError] unknwon binding between some headers
def parse(binary_str, first_header: nil)
headers.clear
@@ -297,11 +307,11 @@
# No decoding forced for first header. Have to guess it!
first_header = guess_first_header(binary_str)
raise ParseError, "cannot identify first header in string: #{binary_str.inspect}" if first_header.nil?
end
- add first_header
+ add(first_header)
headers[-1, 1] = last_header.read(binary_str)
# Decode upper headers recursively
decode_bottom_up
self
@@ -315,31 +325,33 @@
str << header.inspect
end
str << Inspect.inspect_body(body)
end
+ # Check equality at binary level
# @param [Packet] other
# @return [Boolean]
def ==(other)
to_s == other.to_s
end
+ # +true+ is {#==} is +true+ with another packet, or if +other+ is a protocol name String, whose protocol is in Packet.
# @param [Packet] other
# @return [Boolean]
# @since 3.1.2
def ===(other)
case other
when PacketGen::Packet
self == other
when String
- is? other
+ is?(other)
else
false
end
end
- # Invert all possible fields in packet to create a reply.
+ # Invert all possible attributes.in packet to create a reply.
# @return [self]
# @since 2.7.0
def reply!
headers.each do |header|
header.reply! if header.respond_to?(:reply!)
@@ -361,11 +373,11 @@
# Dup +@headers+ instance variable. Internally used by +#dup+ and +#clone+
# @return [void]
def initialize_copy(_other)
@headers = headers.map(&:dup)
headers.each do |header|
- add_magic_header_method header
+ add_magic_header_method(header)
end
invalidate_header_cache
end
# Give first header of packet
@@ -408,21 +420,24 @@
idx = header_index(hdr)
(idx + 1) < headers.size ? headers[idx + 1] : nil
end
# @overload header(klass, layer=1)
+ # Get a header given its class and its layer (example: IP-in-IP encapsulation)
# @param [Class] klass
# @param [Integer] layer
# @overload header(klass, options={})
+ # Get a header given its class, and set some attributes
# @param [String] klass
- # @param [Hash] options
- # @raise [ArgumentError] unknown option
- # @return [Header::Base]
+ # @param [Hash] options attributes to set
+ # @raise [ArgumentError] unknown attribute
+ # @return [Header::Base,nil]
def header(klass, arg)
layer = arg.is_a?(Integer) ? arg : 1
header = find_header(klass, layer)
- return header unless arg.is_a? Hash
+ return nil if header.nil?
+ return header unless arg.is_a?(Hash)
arg.each do |key, value|
raise ArgumentError, "unknown #{key} attribute for #{klass}" unless header.respond_to?(:"#{key}=")
header.send(:"#{key}=", value)
@@ -432,16 +447,18 @@
end
# Get header from cache, or find it in packet
# @param [Class] klass
# @param [Integer] layer
- # @return [Header::Base]
+ # @return [Header::Base,nil]
def find_header(klass, layer)
header = fetch_header_from_cache(klass, layer)
return header if header
- header = headers.select { |h| h.is_a? klass }[layer - 1]
+ header = headers.select { |h| h.is_a?(klass) }[layer - 1]
+ return nil if header.nil?
+
add_header_to_cache(header, klass, layer)
header
end
# check if protocol is known
@@ -466,13 +483,13 @@
add_to_previous_header(prev_header, header, parsing) if prev_header
header.packet = self
headers << header unless previous_header
- return if respond_to? header.method_name
+ return if respond_to?(header.method_name)
- add_magic_header_method header
+ add_magic_header_method(header)
end
# Bind +header+ to +prev_header+.
# @param [Headerable] prev_header
# @param [Headerable] header
@@ -495,11 +512,11 @@
"header(#{header.class}, arg); end"
end
# Try to guess header from +binary_str+
# @param [String] binary_str
- # @return [String] header/protocol name
+ # @return [String,nil] header/protocol name, or nil if cannot be guessed
def guess_first_header(binary_str)
first_header = nil
Header.all.each do |hklass|
hdr = hklass.new(packet: self)
# #read may return another object (more specific class)
@@ -527,10 +544,10 @@
nheader = nh.new(packet: self)
nheader = nheader.read(last_known_hdr.body)
next unless nheader.parse?
- add_header nheader, parsing: true
+ add_header(nheader, parsing: true)
break if last_header == last_known_hdr
end
end
# Search a upper header for +hdr+