packetgen 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +48 -6
- data/lib/packetgen/header.rb +70 -2
- data/lib/packetgen/packet.rb +66 -33
- data/lib/packetgen/version.rb +1 -1
- data/lib/packetgen.rb +1 -0
- metadata +2 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 21f7a146ddb2ba2b7e8b86933360a97d95344fef
         | 
| 4 | 
            +
              data.tar.gz: e26c103bc3ef15ca5d7064163cd747716215a71b
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 304723a5e11714b3092606fd7f7529fd1a4d6ec0b427e364d3603b63cca20fbfc410a996d80a1a3cfb58106e855dcf5fbc76dd957b589eee6b97c1fa70379312
         | 
| 7 | 
            +
              data.tar.gz: d95e4b3fa118e33ae9296626fd2855d6d3eaad9a4d2e0fd4605cfca5bdfd193b4397fd4411164a018dfbbf37868a35026134acf01cd2fdb52729cb90c05bc7fd
         | 
    
        data/README.md
    CHANGED
    
    | @@ -94,11 +94,10 @@ pkt.is? 'TCP'   # => true | |
| 94 94 | 
             
            pkt.is? 'IP'    # => true
         | 
| 95 95 | 
             
            pkt.is? 'UDP'   # => false
         | 
| 96 96 |  | 
| 97 | 
            -
            # encapulsate/decapsulate packets | 
| 98 | 
            -
            pkt2 = PacketGen.gen('IP') | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 101 | 
            -
            pkt.decap('IP', 'ESP')                 # pkt is now inner IP/TCP packet
         | 
| 97 | 
            +
            # encapulsate/decapsulate packets
         | 
| 98 | 
            +
            pkt2 = PacketGen.gen('IP')
         | 
| 99 | 
            +
            pkt2.encapsulate pkt                   # pkt2 is now a IP/IP/TCP packet
         | 
| 100 | 
            +
            pkt2.decapsulate(pkt2.ip)              # pkt2 is now inner IP/TCP packet
         | 
| 102 101 | 
             
            ```
         | 
| 103 102 |  | 
| 104 103 | 
             
            ### Read/write PcapNG files
         | 
| @@ -112,12 +111,55 @@ pkt.write('one_packet.pcapng') | |
| 112 111 | 
             
            PacketGen.write('more_packets.pcapng', packets)
         | 
| 113 112 | 
             
            ```
         | 
| 114 113 |  | 
| 114 | 
            +
            ### Add custom header/protocol
         | 
| 115 | 
            +
            Since v1.1.0, PacketGen permits adding you own header classes.
         | 
| 116 | 
            +
            First, define the new header class. By example:
         | 
| 117 | 
            +
             | 
| 118 | 
            +
            ```ruby
         | 
| 119 | 
            +
            module MyModule
         | 
| 120 | 
            +
             class MyHeader < Struct.new(:field1, :field2)
         | 
| 121 | 
            +
               include PacketGen::StructFu
         | 
| 122 | 
            +
               include PacketGen::Header::HeaderMethods
         | 
| 123 | 
            +
               extend PacketGen::Header::HeaderClassMethods
         | 
| 124 | 
            +
               
         | 
| 125 | 
            +
               def initialize(options={})
         | 
| 126 | 
            +
                 super Int32.new(options[:field1]), Int32.new(options[:field2])
         | 
| 127 | 
            +
               end
         | 
| 128 | 
            +
               
         | 
| 129 | 
            +
               def read(str)
         | 
| 130 | 
            +
                 self[:field1].read str[0, 4]
         | 
| 131 | 
            +
                 self[:field2].read str[4, 4]
         | 
| 132 | 
            +
               end
         | 
| 133 | 
            +
             end
         | 
| 134 | 
            +
            end
         | 
| 135 | 
            +
            ```
         | 
| 136 | 
            +
             | 
| 137 | 
            +
            Then, class must be declared to PacketGen:
         | 
| 138 | 
            +
             | 
| 139 | 
            +
            ```
         | 
| 140 | 
            +
            PacketGen::Header.add_class MyModule::MyHeader
         | 
| 141 | 
            +
            ```
         | 
| 142 | 
            +
             | 
| 143 | 
            +
            Finally, bindings must be declared:
         | 
| 144 | 
            +
             | 
| 145 | 
            +
            ```
         | 
| 146 | 
            +
            # bind MyHeader as IP protocol number 254 (needed by Packet#parse)
         | 
| 147 | 
            +
            PacketGen::Header::IP.bind_header MyModule::MyHeader, protocol: 254
         | 
| 148 | 
            +
            ```
         | 
| 149 | 
            +
             | 
| 150 | 
            +
            And use it:
         | 
| 151 | 
            +
             | 
| 152 | 
            +
            ```
         | 
| 153 | 
            +
            pkt = Packet.gen('IP').add('MyHeader', field1: 0x12345678)
         | 
| 154 | 
            +
            pkt.myheader.field2.read 0x01
         | 
| 155 | 
            +
            ```
         | 
| 156 | 
            +
             | 
| 115 157 | 
             
            ## Pull requests?
         | 
| 116 158 |  | 
| 117 159 | 
             
            yes
         | 
| 118 160 |  | 
| 119 161 | 
             
            ## License
         | 
| 120 | 
            -
            MIT License (see [LICENSE](https://github.com/sdaubert/packetgen/LICENSE))
         | 
| 162 | 
            +
            MIT License (see [LICENSE](https://github.com/sdaubert/packetgen/blob/master/LICENSE))
         | 
| 121 163 |  | 
| 122 164 | 
             
            Copyright © 2016 Sylvain Daubert
         | 
| 123 165 |  | 
    
        data/lib/packetgen/header.rb
    CHANGED
    
    | @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # coding: utf-8
         | 
| 1 2 | 
             
            # This file is part of PacketGen
         | 
| 2 3 | 
             
            # See https://github.com/sdaubert/packetgen for more informations
         | 
| 3 4 | 
             
            # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
         | 
| @@ -5,14 +6,81 @@ | |
| 5 6 |  | 
| 6 7 | 
             
            module PacketGen
         | 
| 7 8 | 
             
              # Namespace for protocol header classes
         | 
| 9 | 
            +
              # == Add a foreign header class
         | 
| 10 | 
            +
              # Since v1.1.0, PacketGen permits adding you own header classes.
         | 
| 11 | 
            +
              # First, define the new header class. By example:
         | 
| 12 | 
            +
              #  module MyModule
         | 
| 13 | 
            +
              #    class MyHeader < Struct.new(:field1, :field2)
         | 
| 14 | 
            +
              #      include PacketGen::StructFu
         | 
| 15 | 
            +
              #      include PacketGen::Header::HeaderMethods
         | 
| 16 | 
            +
              #      extend PacketGen::Header::HeaderClassMethods
         | 
| 17 | 
            +
              #      
         | 
| 18 | 
            +
              #      def initialize(options={})
         | 
| 19 | 
            +
              #        super Int32.new(options[:field1]), Int32.new(options[:field2])
         | 
| 20 | 
            +
              #      end
         | 
| 21 | 
            +
              #      
         | 
| 22 | 
            +
              #      def read(str)
         | 
| 23 | 
            +
              #        self[:field1].read str[0, 4]
         | 
| 24 | 
            +
              #        self[:field2].read str[4, 4]
         | 
| 25 | 
            +
              #      end
         | 
| 26 | 
            +
              #    end
         | 
| 27 | 
            +
              #   end
         | 
| 28 | 
            +
              # Then, class must be declared to PacketGen:
         | 
| 29 | 
            +
              #  PacketGen::Header.add_class MyModule::MyHeader
         | 
| 30 | 
            +
              # Finally, bindings must be declared:
         | 
| 31 | 
            +
              #  # bind MyHeader as IP protocol number 254 (needed by Packet#parse)
         | 
| 32 | 
            +
              #  PacketGen::Header::IP.bind_header MyModule::MyHeader, protocol: 254
         | 
| 33 | 
            +
              # And use it:
         | 
| 34 | 
            +
              #  pkt = Packet.gen('IP').add('MyHeader', field1: 0x12345678)
         | 
| 35 | 
            +
              #  pkt.myheader.field2.read 0x01
         | 
| 8 36 | 
             
              # @author Sylvain Daubert
         | 
| 9 37 | 
             
              module Header
         | 
| 10 38 |  | 
| 39 | 
            +
                @added_header_classes = {}
         | 
| 40 | 
            +
             | 
| 11 41 | 
             
                # Get known header classes
         | 
| 12 42 | 
             
                # @return [Array<Class>]
         | 
| 13 43 | 
             
                def self.all
         | 
| 14 | 
            -
                   | 
| 15 | 
            -
             | 
| 44 | 
            +
                  return @header_classes if @header_classes
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  @builtin ||= constants.map { |sym| const_get sym }.
         | 
| 47 | 
            +
                             select { |klass| klass < Struct && klass < HeaderMethods }
         | 
| 48 | 
            +
                  @header_classes = @builtin + @added_header_classes.values
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                # Add a foreign header class to known header classes. This is
         | 
| 52 | 
            +
                # needed by {Packet.gen} and {Packet#add}.
         | 
| 53 | 
            +
                # @param [Class] klass a header class, which should include
         | 
| 54 | 
            +
                #   {Header::HeaderMethods} and {Header::HeaderClassMethods}
         | 
| 55 | 
            +
                # @return [void]
         | 
| 56 | 
            +
                # @since 1.1.0
         | 
| 57 | 
            +
                def self.add_class(klass)
         | 
| 58 | 
            +
                  protocol_name = klass.to_s.sub(/.*::/, '')
         | 
| 59 | 
            +
                  @added_header_classes[protocol_name] = klass
         | 
| 60 | 
            +
                  @header_classes = nil
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                # Remove a foreign header (previously added by {.add_header_class}à
         | 
| 64 | 
            +
                # from known header classes.
         | 
| 65 | 
            +
                # @param [Class] klass
         | 
| 66 | 
            +
                # @return [void]
         | 
| 67 | 
            +
                # @since 1.1.0
         | 
| 68 | 
            +
                def self.remove_class(klass)
         | 
| 69 | 
            +
                  protocol_name = klass.to_s.sub(/.*::/, '')
         | 
| 70 | 
            +
                  @added_header_classes.delete protocol_name
         | 
| 71 | 
            +
                  @header_classes = nil
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                # Get header class from its name
         | 
| 75 | 
            +
                # @param [String] name
         | 
| 76 | 
            +
                # @return [Class,nil]
         | 
| 77 | 
            +
                # @since 1.1.0
         | 
| 78 | 
            +
                def self.get_header_class_by_name(name)
         | 
| 79 | 
            +
                  if Header.const_defined? name
         | 
| 80 | 
            +
                    Header.const_get name
         | 
| 81 | 
            +
                  else
         | 
| 82 | 
            +
                    @added_header_classes[name]
         | 
| 83 | 
            +
                  end
         | 
| 16 84 | 
             
                end
         | 
| 17 85 | 
             
              end
         | 
| 18 86 | 
             
            end
         | 
    
        data/lib/packetgen/packet.rb
    CHANGED
    
    | @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # coding: utf-8
         | 
| 1 2 | 
             
            # This file is part of PacketGen
         | 
| 2 3 | 
             
            # See https://github.com/sdaubert/packetgen for more informations
         | 
| 3 4 | 
             
            # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
         | 
| @@ -163,25 +164,7 @@ module PacketGen | |
| 163 164 | 
             
                  klass = check_protocol(protocol)
         | 
| 164 165 |  | 
| 165 166 | 
             
                  header = klass.new(options)
         | 
| 166 | 
            -
                   | 
| 167 | 
            -
                  if prev_header
         | 
| 168 | 
            -
                    binding = prev_header.class.known_headers[klass]
         | 
| 169 | 
            -
                    if binding.nil?
         | 
| 170 | 
            -
                      msg = "#{prev_header.class} knowns no layer association with #{protocol}. "
         | 
| 171 | 
            -
                      msg << "Try #{prev_header.class}.bind_layer(PacketGen::Header::#{protocol}, "
         | 
| 172 | 
            -
                      msg << "#{prev_header.protocol_name.downcase}_proto_field: "
         | 
| 173 | 
            -
                      msg << "value_for_#{protocol.downcase})"
         | 
| 174 | 
            -
                      raise ArgumentError, msg
         | 
| 175 | 
            -
                    end
         | 
| 176 | 
            -
                    prev_header[binding.key].read binding.value
         | 
| 177 | 
            -
                    prev_header.body = header
         | 
| 178 | 
            -
                  end
         | 
| 179 | 
            -
                  header.packet = self
         | 
| 180 | 
            -
                  @headers << header
         | 
| 181 | 
            -
                  unless respond_to? protocol.downcase
         | 
| 182 | 
            -
                    self.class.class_eval "def #{protocol.downcase}(arg=nil);" \
         | 
| 183 | 
            -
                                          "header('#{protocol}', arg); end"
         | 
| 184 | 
            -
                  end
         | 
| 167 | 
            +
                  add_header header
         | 
| 185 168 | 
             
                  self
         | 
| 186 169 | 
             
                end
         | 
| 187 170 |  | 
| @@ -257,6 +240,34 @@ module PacketGen | |
| 257 240 | 
             
                  end
         | 
| 258 241 | 
             
                end
         | 
| 259 242 |  | 
| 243 | 
            +
                # Encapulate another packet in +self+
         | 
| 244 | 
            +
                # @param [Packet] other
         | 
| 245 | 
            +
                # @return [self] +self+ with new headers from +other+
         | 
| 246 | 
            +
                # @since 1.1.0
         | 
| 247 | 
            +
                def encapsulate(other)
         | 
| 248 | 
            +
                  other.headers.each { |h| add_header h }
         | 
| 249 | 
            +
                end
         | 
| 250 | 
            +
             | 
| 251 | 
            +
                # Remove headers from +self+
         | 
| 252 | 
            +
                # @param [Array<Header>] headers
         | 
| 253 | 
            +
                # @return [self] +self+ with some headers removed
         | 
| 254 | 
            +
                # @raise [FormatError] any headers not in +self+
         | 
| 255 | 
            +
                # @raise [FormatError] removed headers result in an unknown binding
         | 
| 256 | 
            +
                # @since 1.1.0
         | 
| 257 | 
            +
                def decapsulate(*headers)
         | 
| 258 | 
            +
                  headers.each do |header|
         | 
| 259 | 
            +
                    idx = @headers.index(header)
         | 
| 260 | 
            +
                    raise FormatError, 'header not in packet!' if idx.nil?
         | 
| 261 | 
            +
             | 
| 262 | 
            +
                    prev_header = idx > 0 ? @headers[idx - 1] : nil
         | 
| 263 | 
            +
                    next_header = (idx+1) < @headers.size ? @headers[idx + 1] : nil
         | 
| 264 | 
            +
                    @headers.delete_at(idx)
         | 
| 265 | 
            +
                    add_header(next_header, prev_header) if prev_header and next_header
         | 
| 266 | 
            +
                  end
         | 
| 267 | 
            +
                rescue ArgumentError => ex
         | 
| 268 | 
            +
                  raise FormatError, ex.message
         | 
| 269 | 
            +
                end
         | 
| 270 | 
            +
             | 
| 260 271 | 
             
                # @return [String]
         | 
| 261 272 | 
             
                def inspect
         | 
| 262 273 | 
             
                  str = Inspect.dashed_line(self.class)
         | 
| @@ -274,17 +285,15 @@ module PacketGen | |
| 274 285 |  | 
| 275 286 | 
             
                private
         | 
| 276 287 |  | 
| 277 | 
            -
                # @overload header( | 
| 278 | 
            -
                #  @param [ | 
| 288 | 
            +
                # @overload header(klass, layer=1)
         | 
| 289 | 
            +
                #  @param [Class] klass
         | 
| 279 290 | 
             
                #  @param [Integer] layer
         | 
| 280 | 
            -
                # @overload header( | 
| 281 | 
            -
                #  @param [String]  | 
| 291 | 
            +
                # @overload header(klass, options={})
         | 
| 292 | 
            +
                #  @param [String] klass
         | 
| 282 293 | 
             
                #  @param [Hash] options
         | 
| 294 | 
            +
                #  @raise [ArgumentError] unknown option
         | 
| 283 295 | 
             
                # @return [Header::Base]
         | 
| 284 | 
            -
                 | 
| 285 | 
            -
                def header(protocol, arg)
         | 
| 286 | 
            -
                  klass = check_protocol protocol
         | 
| 287 | 
            -
             | 
| 296 | 
            +
                def header(klass, arg)
         | 
| 288 297 | 
             
                  headers = @headers.select { |h| h.is_a? klass }
         | 
| 289 298 | 
             
                  layer = arg.is_a?(Integer) ? arg : 1
         | 
| 290 299 | 
             
                  header = headers[layer - 1]
         | 
| @@ -292,7 +301,7 @@ module PacketGen | |
| 292 301 | 
             
                  if arg.is_a? Hash
         | 
| 293 302 | 
             
                    arg.each do |key, value|
         | 
| 294 303 | 
             
                      unless header.respond_to? "#{key}="
         | 
| 295 | 
            -
                        raise ArgumentError, "unknown #{key} attribute for #{ | 
| 304 | 
            +
                        raise ArgumentError, "unknown #{key} attribute for #{klass}"
         | 
| 296 305 | 
             
                      end
         | 
| 297 306 | 
             
                      header.send "#{key}=", value
         | 
| 298 307 | 
             
                    end
         | 
| @@ -305,13 +314,37 @@ module PacketGen | |
| 305 314 | 
             
                # @param [String] protocol
         | 
| 306 315 | 
             
                # @raise [ArgumentError] unknown protocol
         | 
| 307 316 | 
             
                def check_protocol(protocol)
         | 
| 308 | 
            -
                   | 
| 309 | 
            -
             | 
| 310 | 
            -
                  end
         | 
| 311 | 
            -
                  klass = Header.const_get(protocol)
         | 
| 312 | 
            -
                  raise ArgumentError, "unknown #{protocol} protocol" unless klass.is_a? Class
         | 
| 317 | 
            +
                  klass = Header.get_header_class_by_name(protocol)
         | 
| 318 | 
            +
                  raise ArgumentError, "unknown #{protocol} protocol" if klass.nil?
         | 
| 313 319 | 
             
                  klass
         | 
| 314 320 | 
             
                end
         | 
| 321 | 
            +
             | 
| 322 | 
            +
                # Add a header to packet
         | 
| 323 | 
            +
                # @param [Header::HeaderMethods] header
         | 
| 324 | 
            +
                # @param [Header::HeaderMethods] previous_header
         | 
| 325 | 
            +
                # @return [void]
         | 
| 326 | 
            +
                def add_header(header, previous_header=nil)
         | 
| 327 | 
            +
                  protocol = header.protocol_name
         | 
| 328 | 
            +
                  prev_header = previous_header || @headers.last
         | 
| 329 | 
            +
                  if prev_header
         | 
| 330 | 
            +
                    binding = prev_header.class.known_headers[header.class]
         | 
| 331 | 
            +
                    if binding.nil?
         | 
| 332 | 
            +
                      msg = "#{prev_header.class} knowns no layer association with #{protocol}. "
         | 
| 333 | 
            +
                      msg << "Try #{prev_header.class}.bind_layer(PacketGen::Header::#{protocol}, "
         | 
| 334 | 
            +
                      msg << "#{prev_header.protocol_name.downcase}_proto_field: "
         | 
| 335 | 
            +
                      msg << "value_for_#{protocol.downcase})"
         | 
| 336 | 
            +
                      raise ArgumentError, msg
         | 
| 337 | 
            +
                    end
         | 
| 338 | 
            +
                    prev_header[binding.key].read binding.value
         | 
| 339 | 
            +
                    prev_header.body = header
         | 
| 340 | 
            +
                  end
         | 
| 341 | 
            +
                  header.packet = self
         | 
| 342 | 
            +
                  @headers << header unless previous_header
         | 
| 343 | 
            +
                  unless respond_to? protocol.downcase
         | 
| 344 | 
            +
                    self.class.class_eval "def #{protocol.downcase}(arg=nil);" \
         | 
| 345 | 
            +
                                          "header(#{header.class}, arg); end"
         | 
| 346 | 
            +
                  end
         | 
| 347 | 
            +
                end
         | 
| 315 348 | 
             
              end
         | 
| 316 349 | 
             
            end
         | 
| 317 350 |  | 
    
        data/lib/packetgen/version.rb
    CHANGED
    
    
    
        data/lib/packetgen.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: packetgen
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1.0 | 
| 4 | 
            +
              version: 1.1.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Sylvain Daubert
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2016-12- | 
| 11 | 
            +
            date: 2016-12-28 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: pcaprub
         |