lib/packetgen/packet.rb in packetgen-1.0.1 vs lib/packetgen/packet.rb in packetgen-1.1.0

- old
+ new

@@ -1,5 +1,6 @@ +# coding: utf-8 # This file is part of PacketGen # 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. require 'pcaprub' @@ -161,29 +162,11 @@ # @raise [ArgumentError] unknown protocol def add(protocol, options={}) klass = check_protocol(protocol) header = klass.new(options) - prev_header = @headers.last - if prev_header - binding = prev_header.class.known_headers[klass] - if binding.nil? - msg = "#{prev_header.class} knowns no layer association with #{protocol}. " - msg << "Try #{prev_header.class}.bind_layer(PacketGen::Header::#{protocol}, " - msg << "#{prev_header.protocol_name.downcase}_proto_field: " - msg << "value_for_#{protocol.downcase})" - raise ArgumentError, msg - end - prev_header[binding.key].read binding.value - prev_header.body = header - end - header.packet = self - @headers << header - unless respond_to? protocol.downcase - self.class.class_eval "def #{protocol.downcase}(arg=nil);" \ - "header('#{protocol}', arg); end" - end + add_header header self end # Check if a protocol header is embedded in packet # @return [Boolean] @@ -255,10 +238,38 @@ type = @headers.first.protocol_name raise WireError, "don't known how to send a #{type} packet on wire" end end + # Encapulate another packet in +self+ + # @param [Packet] other + # @return [self] +self+ with new headers from +other+ + # @since 1.1.0 + def encapsulate(other) + other.headers.each { |h| add_header h } + end + + # Remove headers from +self+ + # @param [Array<Header>] headers + # @return [self] +self+ with some headers removed + # @raise [FormatError] any headers not in +self+ + # @raise [FormatError] removed headers result in an unknown binding + # @since 1.1.0 + def decapsulate(*headers) + 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 + @headers.delete_at(idx) + add_header(next_header, prev_header) if prev_header and next_header + end + rescue ArgumentError => ex + raise FormatError, ex.message + end + # @return [String] def inspect str = Inspect.dashed_line(self.class) @headers.each do |header| str << header.inspect @@ -272,29 +283,27 @@ to_s == other.to_s end private - # @overload header(protocol, layer=1) - # @param [String] protocol + # @overload header(klass, layer=1) + # @param [Class] klass # @param [Integer] layer - # @overload header(protocol, options) - # @param [String] protocol + # @overload header(klass, options={}) + # @param [String] klass # @param [Hash] options + # @raise [ArgumentError] unknown option # @return [Header::Base] - # @raise [ArgumentError] unknown protocol - def header(protocol, arg) - klass = check_protocol protocol - + def header(klass, arg) headers = @headers.select { |h| h.is_a? klass } layer = arg.is_a?(Integer) ? arg : 1 header = headers[layer - 1] if arg.is_a? Hash arg.each do |key, value| unless header.respond_to? "#{key}=" - raise ArgumentError, "unknown #{key} attribute for #{header.class}" + raise ArgumentError, "unknown #{key} attribute for #{klass}" end header.send "#{key}=", value end end @@ -303,15 +312,39 @@ # check if protocol is known # @param [String] protocol # @raise [ArgumentError] unknown protocol def check_protocol(protocol) - unless Header.const_defined? protocol - raise ArgumentError, "unknown #{protocol} protocol" - end - klass = Header.const_get(protocol) - raise ArgumentError, "unknown #{protocol} protocol" unless klass.is_a? Class + klass = Header.get_header_class_by_name(protocol) + raise ArgumentError, "unknown #{protocol} protocol" if klass.nil? klass + end + + # Add a header to packet + # @param [Header::HeaderMethods] header + # @param [Header::HeaderMethods] previous_header + # @return [void] + def add_header(header, previous_header=nil) + protocol = header.protocol_name + prev_header = previous_header || @headers.last + if prev_header + binding = prev_header.class.known_headers[header.class] + if binding.nil? + msg = "#{prev_header.class} knowns no layer association with #{protocol}. " + msg << "Try #{prev_header.class}.bind_layer(PacketGen::Header::#{protocol}, " + msg << "#{prev_header.protocol_name.downcase}_proto_field: " + msg << "value_for_#{protocol.downcase})" + raise ArgumentError, msg + end + prev_header[binding.key].read binding.value + prev_header.body = header + end + header.packet = self + @headers << header unless previous_header + unless respond_to? protocol.downcase + self.class.class_eval "def #{protocol.downcase}(arg=nil);" \ + "header(#{header.class}, arg); end" + end end end end require_relative 'header'