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