lib/packetgen/header/base.rb in packetgen-2.8.7 vs lib/packetgen/header/base.rb in packetgen-3.0.0
- old
+ new
@@ -9,11 +9,11 @@
module Header
# @abstract Base class for all header types.
# Subclasses may define magic methods:
# * +#calc_checksum+, which computes header checksum,
# * +#calc_length+, which computes header length,
- # * +#parse?+,
+ # * {#parse?},
# * +#reply!+, which inverts needed fields to forge a response.
# @author Sylvain Daubert
class Base < Types::Fields
# @api private
# Simple class to handle a header association
@@ -76,34 +76,25 @@
# @api private
# Class to handle header associations
class Bindings
include Enumerable
- # op type
- # @return [:or,:and]
- attr_accessor :op
# @return [Array<Binding>]
attr_accessor :bindings
- # @param [:or, :and, :newstyle] operator
- def initialize(operator)
- @op = operator
+ def initialize
@bindings = []
end
def new_set
- @bindings << [] if @op == :newstyle
+ @bindings << []
end
# @param [Object] arg
- # @return [OldBindings] self
+ # @return [Bindings] self
def <<(arg)
- if op == :newstyle
- @bindings.last << arg
- else
- @bindings << arg
- end
+ @bindings.last << arg
end
# each iterator
# @return [void]
def each
@@ -115,135 +106,83 @@
# @return [Boolean]
def empty?
@bindings.empty?
end
+ # Return binding as a hash.
# @return [Hash]
def to_h
hsh = {}
each do |b|
- if b.is_a? Array
- b.each { |sb| hsh[sb.key] = sb.value }
- else
- hsh[b.key] = b.value
- end
+ b.each { |sb| hsh[sb.key] = sb.value }
end
hsh
end
# Check +fields+ responds to set of bindings
# @param [Types::Fields] fields
# @return [Boolean]
def check?(fields)
- case @op
- when :or
- empty? || @bindings.any? { |binding| binding.check?(fields) }
- when :and
- @bindings.all? { |binding| binding.check?(fields) }
- when :newstyle
- @bindings.any? { |group| group.all? { |binding| binding.check?(fields) } }
- end
+ @bindings.any? { |group| group.all? { |binding| binding.check?(fields) } }
end
# Set +fields+ to bindings value
# @param [Types::Fields] fields
# @return [void]
def set(fields)
- case @bindings.first
- when Array
- @bindings.first.each { |b| b.set fields }
- else
- @bindings.each { |b| b.set fields }
- end
+ @bindings.first.each { |b| b.set fields }
end
end
# Reference on packet which owns this header
# @return [Packet,nil]
attr_reader :packet
+ # @private
# On inheritage, create +@known_header+ class variable
# @param [Class] klass
# @return [void]
def self.inherited(klass)
super
klass.class_eval { @known_headers = {} }
end
- # Bind a upper header to current class
- # Header1.bind_header Header2, field1: 43
- # Header1.bind_header Header3, field1: 43, field2: 43
- # Header1.bind_header Header4, op: :and, field1: 43, field2: 43
- # Header1.bind_header Header5, field1: ->(v) { v.nil? ? 128 : v > 127 }
- # Header1.bind_header Header6, procs: [->(hdr) { hdr.field1 = 1 }
- # ->(hdr) { hdr.field1 == 1 && hdr.body[0..1] == "\x00\x00" }]
+ # Bind a upper header to current one.
# @param [Class] header_klass header class to bind to current class
# @param [Hash] args current class fields and their value when +header_klass+
- # is embedded in current class. Given value may be a lambda, whose alone argument
- # is the value extracted from header field (or +nil+ when lambda is used to set
- # field while adding a header).
+ # is embedded in current class.
#
- # If multiple fields are given, a special key +:op+ may be given to set parse
- # operation on this binding. By default, +:op+ is +:or+ (at least one binding
- # must match to parse it). It also may be set to +:and+ (all bindings must match
- # to parse it).
+ # Given value may be a lambda, whose alone argument is the value extracted
+ # from header field (or +nil+ when lambda is used to set field while adding
+ # a header).
#
# Special key +procs+ may be used to set 2 lambdas, the former to set
# fields, the latter to check bindings. This may be used when multiple and
# non-trivial checks should be made.
# @return [void]
- # @deprecated Use {.bind} instead.
- def self.bind_header(header_klass, args={})
- Deprecation.deprecated(self, __method__, 'bind', klass_method: true)
- op = args.delete(:op) || :or
- if @known_headers[header_klass].nil? || @known_headers[header_klass].op != op
- bindings = Bindings.new(op)
- @known_headers[header_klass] = bindings
- else
- bindings = @known_headers[header_klass]
- end
- args.each do |key, value|
- bindings << if key == :procs
- ProcBinding.new(value)
- else
- Binding.new(key, value)
- end
- end
- end
-
- # Bind a upper header to current one.
+ # @example Basic examples
# # Bind Header2 to Header1 when field1 from Header1 has a value of 42
# Header1.bind Header2, field1: 42
# # Bind Header3 to Header1 when field1 from Header1 has a value of 43
# # and field2 has value 43 or 44
# Header1.bind Header3, field1: 43, field2: 43
# Header1.bind Header3, field1: 43, field2: 44
+ # @example Defining a binding on a field using a lambda.
# # Bind Header4 to Header1 when field1 from Header1 has a value
# # greater or equal to 44. When adding a Header2 to a Header1
# # with Packet#add, force value to 44.
# Header1.bind Header4, field1: ->(v) { v.nil? ? 44 : v >= 44 }
+ # @example Defining a binding using procs key
# # Bind Header5 to Header1 when field1 from Header1 has a value of 41
# # and first two bytes of header1's body are null.
# # When adding a Header2 to a Header1 with Packet#add, force value to 44.
# Header1.bind Header5, procs: [->(hdr) { hdr.field1 = 41 }
# ->(hdr) { hdr.field1 == 41 && hdr.body[0..1] == "\x00\x00" }]
- # @param [Class] header_klass header class to bind to current class
- # @param [Hash] args current class fields and their value when +header_klass+
- # is embedded in current class.
- #
- # Given value may be a lambda, whose alone argument is the value extracted
- # from header field (or +nil+ when lambda is used to set field while adding
- # a header).
- #
- # Special key +procs+ may be used to set 2 lambdas, the former to set
- # fields, the latter to check bindings. This may be used when multiple and
- # non-trivial checks should be made.
- # @return [void]
# @since 2.7.0
def self.bind(header_klass, args={})
if @known_headers[header_klass].nil?
- bindings = Bindings.new(:newstyle)
+ bindings = Bindings.new
@known_headers[header_klass] = bindings
else
bindings = @known_headers[header_klass]
end
bindings.new_set
@@ -258,11 +197,18 @@
# Give protocol name for this class
# @return [String]
# @since 2.0.0
def self.protocol_name
- new.protocol_name
+ return @protocol_name if defined? @protocol_name
+
+ classname = to_s
+ @protocol_name = if classname.start_with?('PacketGen::Header')
+ classname.sub(/.*Header::/, '')
+ else
+ classname.sub(/.*::/, '')
+ end
end
# Helper method to calculate length of +hdr+ and set its +length+ field.
# To be used by +#calc_length+ in Base subclasses.
# @param [Base] hdr
@@ -270,11 +216,11 @@
# if +false+, only +body+ is taken into account
def self.calculate_and_set_length(hdr, header_in_size: true)
length = if header_in_size
hdr.sz
else
- hdr.body.sz
+ hdr[:body].sz
end
hdr.length = length
end
# @api private
@@ -291,26 +237,19 @@
end
# Return header protocol name
# @return [String]
def protocol_name
- return @protocol_name if @protocol_name
-
- classname = self.class.to_s
- @protocol_name = if classname.start_with?('PacketGen::Header')
- classname.sub(/.*Header::/, '')
- else
- classname.sub(/.*::/, '')
- end
+ self.class.protocol_name
end
# return header method name
# @return [String]
# @since 2.0.0
# @since 2.8.6 permit multiple nesting levels
def method_name
- return @method_name if @method_name
+ return @method_name if defined? @method_name
@method_name = protocol_name.downcase.gsub(/::/, '_')
end
# @abstract Should be redefined by subclasses. This method should check invariant
@@ -338,45 +277,47 @@
# @return [void]
# @since 2.1.4
def added_to_packet(packet) end
# @api private
- # Get +header+ id in packet headers array
+ # Get +header+ id in {Packet#headers} array
# @param [Header] header
# @return [Integer]
- # @raise FormatError +header+ not in a packet
+ # @raise [FormatError] +header+ not in a packet
def header_id(header)
raise FormatError, "header of type #{header.class} not in a packet" if packet.nil?
+
id = packet.headers.index(header)
- if id.nil?
- raise FormatError, "header of type #{header.class} not in packet #{packet}"
- end
+ raise FormatError, "header of type #{header.class} not in packet #{packet}" if id.nil?
+
id
end
# @api private
- # Get IP or IPv6 previous header from +header+
+ # Get {IP} or {IPv6} previous header from +header+
# @param [Header] header
# @return [Header]
- # @raise FormatError no IP or IPv6 header previous +header+ in packet
- # @raise FormatError +header+ not in a packet
+ # @raise [FormatError] no IP or IPv6 header previous +header+ in packet
+ # @raise [FormatError] +header+ not in a packet
def ip_header(header)
hid = header_id(header)
iph = packet.headers[0...hid].reverse.find { |h| h.is_a?(IP) || h.is_a?(IPv6) }
raise FormatError, 'no IP or IPv6 header in packet' if iph.nil?
+
iph
end
# @api private
# Get link layer header from given header
# @param [Header] header
# @return [Header]
- # @raise FormatError no link layer header in packet
- # @raise FormatError +header+ not in a packet
+ # @raise [FormatError] no link layer header in packet
+ # @raise [FormatError] +header+ not in a packet
def ll_header(header)
hid = header_id(header)
llh = packet.headers[0...hid].reverse.find { |h| h.is_a?(Eth) || h.is_a?(Dot11) }
raise FormatError, 'no link layer header in packet' if llh.nil?
+
llh
end
end
end
end