Example #1
0
class EnhancedPacket(BasePacketBlock):
    magic_number = 0x00000006
    schema = [
        ('interface_id', IntField(32, False)),
        ('timestamp_high', IntField(32, False)),
        ('timestamp_low', IntField(32, False)),
        ('packet_payload_info', PacketDataField()),
        ('options', OptionsField([
            (2, 'epb_flags'),  # todo: is this endianness dependent?
            (3, 'epb_hash'),  # todo: process the hash value
            (4, 'epb_dropcount', 'u64'),
        ]))
    ]

    @property
    def captured_len(self):
        return self.packet_payload_info[0]

    @property
    def packet_len(self):
        return self.packet_payload_info[1]

    @property
    def packet_data(self):
        return self.packet_payload_info[2]
Example #2
0
class InterfaceStatistics(SectionMemberBlock, BlockWithTimestampMixin,
                          BlockWithInterfaceMixin):
    """
    "The Interface Statistics Block (ISB) contains the capture statistics for a
    given interface [...]. The statistics are referred to the interface defined
    in the current Section identified by the Interface ID field."
    - pcapng spec, section 4.6. Other quoted citations are from this section
    unless otherwise noted.
    """
    magic_number = 0x00000005
    __slots__ = []
    schema = [
        ('interface_id', IntField(32, False), 0),
        ('timestamp_high', IntField(32, False), 0),
        ('timestamp_low', IntField(32, False), 0),
        (
            'options',
            OptionsField([
                Option(2, 'isb_starttime', 'u64'),  # todo: consider resolution
                Option(3, 'isb_endtime', 'u64'),
                Option(4, 'isb_ifrecv', 'u64'),
                Option(5, 'isb_ifdrop', 'u64'),
                Option(6, 'isb_filteraccept', 'u64'),
                Option(7, 'isb_osdrop', 'u64'),
                Option(8, 'isb_usrdeliv', 'u64'),
            ]),
            None),
    ]
Example #3
0
class EnhancedPacket(BasePacketBlock, BlockWithTimestampMixin):
    """
    "An Enhanced Packet Block (EPB) is the standard container for storing the
    packets coming from the network."
    - pcapng spec, section 4.3. Other quoted citations are from this section
    unless otherwise noted.
    """
    magic_number = 0x00000006
    __slots__ = []
    schema = [
        ('interface_id', IntField(32, False), 0),
        ('timestamp_high', IntField(32, False), 0),
        ('timestamp_low', IntField(32, False), 0),
        ('captured_len', IntField(32, False), 0),
        ('packet_len', IntField(32, False), 0),
        ('packet_data', PacketBytes('captured_len'), b''),
        (
            'options',
            OptionsField([
                Option(2, 'epb_flags', 'epb_flags'),
                Option(3, 'epb_hash', 'type+bytes',
                       multiple=True),  # todo: process the hash value
                Option(4, 'epb_dropcount', 'u64'),
            ]),
            None)
    ]
Example #4
0
class EnhancedPacket(BasePacketBlock, BlockWithTimestampMixin):
    """
    "An Enhanced Packet Block (EPB) is the standard container for storing the
    packets coming from the network."
    - pcapng spec, section 4.3. Other quoted citations are from this section
    unless otherwise noted.
    """

    magic_number = 0x00000006
    __slots__ = []
    schema = [
        ("interface_id", IntField(32, False), 0),
        ("timestamp_high", IntField(32, False), 0),
        ("timestamp_low", IntField(32, False), 0),
        ("captured_len", IntField(32, False), 0),
        ("packet_len", IntField(32, False), 0),
        ("packet_data", PacketBytes("captured_len"), b""),
        (
            "options",
            OptionsField(
                [
                    Option(2, "epb_flags", "epb_flags"),
                    Option(3, "epb_hash", "type+bytes", multiple=True),  # todo: process
                    Option(4, "epb_dropcount", "u64"),
                ]
            ),
            None,
        ),
    ]
Example #5
0
class Packet(BasePacketBlock):
    magic_number = 0x00000002
    schema = [
        ('interface_id', IntField(16, False)),
        ('drops_count', IntField(16, False)),
        ('timestamp_high', IntField(32, False)),
        ('timestamp_low', IntField(32, False)),
        ('packet_payload_info', PacketDataField()),
        ('options', OptionsField([
            (2, 'epb_flags', 'u32'),  # A flag!
            (3, 'epb_hash'),  # Variable size!
        ]))
    ]

    @property
    def captured_len(self):
        return self.packet_payload_info[0]

    @property
    def packet_len(self):
        return self.packet_payload_info[1]

    @property
    def packet_data(self):
        return self.packet_payload_info[2]
Example #6
0
class SectionHeader(Block):
    magic_number = 0x0a0d0d0a
    schema = [('version_major', IntField(16, False)),
              ('version_minor', IntField(16, False)),
              ('section_length', IntField(64, True)),
              ('options',
               OptionsField([
                   (2, 'shb_hardware', 'string'),
                   (3, 'shb_os', 'string'),
                   (4, 'shb_userappl', 'string'),
               ]))]

    def __init__(self, raw=None, endianness="<"):
        self._raw = raw
        self._decoded = None
        self.endianness = endianness
        self._interfaces_id = itertools.count(0)
        self.interfaces = {}
        self.interface_stats = {}

    def _decode(self):
        return struct_decode(self.schema,
                             io.BytesIO(self._raw),
                             endianness=self.endianness)

    def _encode(self, outstream):
        write_int(0x1A2B3C4D, outstream, 32)
        struct_encode(self.schema, self, outstream, endianness=self.endianness)

    def register_interface(self, interface):
        """Helper method to register an interface within this section"""
        assert isinstance(interface, InterfaceDescription)
        interface_id = next(self._interfaces_id)
        interface.interface_id = interface_id
        self.interfaces[interface_id] = interface

    def add_interface_stats(self, interface_stats):
        """Helper method to register interface stats within this section"""
        assert isinstance(interface_stats, InterfaceStatistics)
        self.interface_stats[interface_stats.interface_id] = interface_stats

    @property
    def version(self):
        return (self.version_major, self.version_minor)

    @property
    def length(self):
        return self.section_length

    def __repr__(self):
        return ('<{name} version={version} endianness={endianness} '
                'length={length} options={options}>').format(
                    name=self.__class__.__name__,
                    version='.'.join(str(x) for x in self.version),
                    endianness=repr(self.endianness),
                    length=self.length,
                    options=repr(self.options))
Example #7
0
class InterfaceDescription(SectionMemberBlock):
    magic_number = 0x00000001
    schema = [
        ('link_type', IntField(16, False)),  # todo: enc/decode
        ('reserved', RawBytes(2)),
        ('snaplen', IntField(32, False)),
        (
            'options',
            OptionsField([
                (2, 'if_name', 'string'),
                (3, 'if_description', 'string'),
                (4, 'if_IPv4addr', 'ipv4+mask'),
                (5, 'if_IPv6addr', 'ipv6+prefix'),
                (6, 'if_MACaddr', 'macaddr'),
                (7, 'if_EUIaddr', 'euiaddr'),
                (8, 'if_speed', 'u64'),
                (9, 'if_tsresol'),  # Just keep the raw data
                (10, 'if_tzone', 'u32'),
                (11, 'if_filter', 'string'),
                (12, 'if_os', 'string'),
                (13, 'if_fcslen', 'u8'),
                (14, 'if_tsoffset', 'i64'),
            ]))
    ]

    @property  # todo: cache this property
    def timestamp_resolution(self):
        # ------------------------------------------------------------
        # Resolution of timestamps. If the Most Significant Bit is
        # equal to zero, the remaining bits indicates the resolution
        # of the timestamp as as a negative power of 10 (e.g. 6 means
        # microsecond resolution, timestamps are the number of
        # microseconds since 1/1/1970). If the Most Significant Bit is
        # equal to one, the remaining bits indicates the resolution as
        # as negative power of 2 (e.g. 10 means 1/1024 of second). If
        # this option is not present, a resolution of 10^-6 is assumed
        # (i.e. timestamps have the same resolution of the standard
        # 'libpcap' timestamps).
        # ------------------------------------------------------------

        if 'if_tsresol' in self.options:
            return unpack_timestamp_resolution(self.options['if_tsresol'])

        return 1e-6

    @property
    def statistics(self):
        # todo: ensure we always have an interface id -> how??
        return self.section.interface_stats.get(self.interface_id)

    @property
    def link_type_description(self):
        try:
            return link_types.LINKTYPE_DESCRIPTIONS[self.link_type]
        except KeyError:
            return 'Unknown link type: 0x{0:04x}'.format(self.link_type)
Example #8
0
class InterfaceStatistics(SectionMemberBlock, BlockWithTimestampMixin,
                          BlockWithInterfaceMixin):
    magic_number = 0x00000005
    schema = [
        ('interface_id', IntField(32, False)),
        ('timestamp_high', IntField(32, False)),
        ('timestamp_low', IntField(32, False)),
        ('options', OptionsField([
            (2, 'isb_starttime', 'u64'),  # todo: consider resolution
            (3, 'isb_endtime', 'u64'),
            (4, 'isb_ifrecv', 'u64'),
            (5, 'isb_ifdrop', 'u64'),
            (6, 'isb_filteraccept', 'u64'),
            (7, 'isb_osdrop', 'u64'),
            (8, 'isb_usrdeliv', 'u64'),
        ])),
    ]
Example #9
0
 def _encode(self, outstream):
     fld_size = IntField(32, False)
     fld_data = RawBytes(0)
     if len(self.section.interfaces) > 1:
         # Spec is a bit ambiguous here. Section 4.4 says "it MUST
         # be assumed that all the Simple Packet Blocks have been captured
         # on the interface previously specified in the first Interface
         # Description Block." but later adds "A Simple Packet Block cannot
         # be present in a Section that has more than one interface because
         # of the impossibility to refer to the correct one (it does not
         # contain any Interface ID field)." Why would it say "the first"
         # IDB and not "the only" IDB if this was really forbidden?
         strictness.problem(
             "writing SimplePacket for section with multiple interfaces")
         if strictness.should_fix():
             # Can't fix this. The IDBs have already been written.
             pass
     snap_len = self.interface.snaplen
     if snap_len > 0 and snap_len < self.captured_len:
         # This isn't a strictness issue, it *will* break other readers
         # if we write more bytes than the snaplen says to expect.
         fld_size.encode(self.packet_len,
                         outstream,
                         endianness=self.section.endianness)
         fld_data.encode(self.packet_data[:snap_len], outstream)
     else:
         fld_size.encode(self.packet_len,
                         outstream,
                         endianness=self.section.endianness)
         fld_data.encode(self.packet_data, outstream)
Example #10
0
class ObsoletePacket(BasePacketBlock, BlockWithTimestampMixin):
    """
    "The Packet Block is obsolete, and MUST NOT be used in new files. [...] A
    Packet Block was a container for storing packets coming from the network."
    - pcapng spec, Appendix A. Other quoted citations are from this appendix
    unless otherwise noted.
    """

    magic_number = 0x00000002
    __slots__ = []
    schema = [
        ("interface_id", IntField(16, False), 0),
        ("drops_count", IntField(16, False), 0),
        ("timestamp_high", IntField(32, False), 0),
        ("timestamp_low", IntField(32, False), 0),
        ("captured_len", IntField(32, False), 0),
        ("packet_len", IntField(32, False), 0),
        ("packet_data", PacketBytes("captured_len"), None),
        (
            # The options have the same definitions as their epb_ equivalents
            "options",
            OptionsField(
                [
                    Option(2, "pack_flags", "epb_flags"),
                    Option(3, "pack_hash", "type+bytes", multiple=True),
                ]
            ),
            None,
        ),
    ]

    def enhanced(self):
        """Return an EnhancedPacket with this block's attributes."""
        opts_dict = dict(self.options)
        opts_dict["epb_dropcount"] = self.drops_count
        for a in ("flags", "hash"):
            try:
                opts_dict["epb_" + a] = opts_dict.pop("pack_" + a)
            except KeyError:
                pass
        return self.section.new_member(
            EnhancedPacket,
            interface_id=self.interface_id,
            timestamp_high=self.timestamp_high,
            timestamp_low=self.timestamp_low,
            packet_len=self.packet_len,
            packet_data=self.packet_data,
            options=opts_dict,
        )

    # Do this check in _write() instead of _encode() to ensure the block gets written
    # with the correct magic number.
    def _write(self, outstream):
        strictness.problem("Packet Block is obsolete and must not be used")
        if strictness.should_fix():
            self.enhanced()._write(outstream)
        else:
            super(ObsoletePacket, self)._write(outstream)
Example #11
0
def test_decode_simple_struct():
    schema = [
        ('rawbytes', RawBytes(12)),
        ('int32s', IntField(32, True)),
        ('int32u', IntField(32, False)),
        ('int16s', IntField(16, True)),
        ('int16u', IntField(16, False)),
    ]

    stream = io.BytesIO()
    stream.write('Hello world!')
    stream.write(struct.pack('>i', -1234))
    stream.write(struct.pack('>I', 1234))
    stream.write(struct.pack('>h', -789))
    stream.write(struct.pack('>H', 789))

    stream.seek(0)
    decoded = struct_decode(schema, stream, '>')

    assert decoded['rawbytes'] == 'Hello world!'
    assert decoded['int32s'] == -1234
    assert decoded['int32u'] == 1234
    assert decoded['int16s'] == -789
    assert decoded['int16u'] == 789
Example #12
0
def test_decode_simple_struct():
    schema = [
        ("rawbytes", RawBytes(12), b""),
        ("int32s", IntField(32, True), 0),
        ("int32u", IntField(32, False), 0),
        ("int16s", IntField(16, True), 0),
        ("int16u", IntField(16, False), 0),
    ]

    stream = io.BytesIO()
    stream.write(b"Hello world!")
    stream.write(struct.pack(">i", -1234))
    stream.write(struct.pack(">I", 1234))
    stream.write(struct.pack(">h", -789))
    stream.write(struct.pack(">H", 789))

    stream.seek(0)
    decoded = struct_decode(schema, stream, ">")

    assert decoded["rawbytes"] == b"Hello world!"
    assert decoded["int32s"] == -1234
    assert decoded["int32u"] == 1234
    assert decoded["int16s"] == -789
    assert decoded["int16u"] == 789
Example #13
0
class ObsoletePacket(BasePacketBlock, BlockWithTimestampMixin):
    """
    "The Packet Block is obsolete, and MUST NOT be used in new files. [...] A
    Packet Block was a container for storing packets coming from the network."
    - pcapng spec, Appendix A. Other quoted citations are from this appendix
    unless otherwise noted.
    """
    magic_number = 0x00000002
    __slots__ = []
    schema = [
        ('interface_id', IntField(16, False), 0),
        ('drops_count', IntField(16, False), 0),
        ('timestamp_high', IntField(32, False), 0),
        ('timestamp_low', IntField(32, False), 0),
        ('captured_len', IntField(32, False), 0),
        ('packet_len', IntField(32, False), 0),
        ('packet_data', PacketBytes('captured_len'), b''),
        (
            'options',
            OptionsField([
                Option(2, 'pack_flags',
                       'epb_flags'),  # Same definition as epb_flags
                Option(3, 'pack_hash', 'type+bytes',
                       multiple=True),  # Same definition as epb_hash
            ]),
            None)
    ]

    def enhanced(self):
        """Return an EnhancedPacket with this block's attributes."""
        opts_dict = dict(self.options)
        opts_dict['epb_dropcount'] = self.drops_count
        for a in ('flags', 'hash'):
            try:
                opts_dict['epb_' + a] = opts_dict.pop('pack_' + a)
            except KeyError:
                pass
        return self.section.new_member(EnhancedPacket,
                                       interface_id=self.interface_id,
                                       timestamp_high=self.timestamp_high,
                                       timestamp_low=self.timestamp_low,
                                       packet_len=self.packet_len,
                                       packet_data=self.packet_data,
                                       options=opts_dict)

    def write(self, outstream):
        strictness.problem("Packet Block is obsolete and must not be used")
        if strictness.should_fix():
            self.enhanced().write(outstream)
        else:
            super(ObsoletePacket, self).write(outstream)
Example #14
0
def test_unpack_dummy_packet():
    schema = [
        ('a_string', RawBytes(8), ''),
        ('a_number', IntField(32, False), 0),
        ('options', OptionsField([]), None),
        ('pb_captured_len', IntField(32, False), 0),
        ('pb_orig_len', IntField(32, False), 0),
        ('packet_data', PacketBytes('pb_captured_len'), b''),
        ('spb_orig_len', IntField(32, False), 0),
        ('simple_packet_data', PacketBytes('spb_orig_len'), b''),
        ('name_res', ListField(NameResolutionRecordField()), []),
        ('another_number', IntField(32, False), 0),
    ]

    # Note: NULLs are for padding!
    data = io.BytesIO(
        b'\x01\x23\x45\x67\x89\xab\xcd\xef'
        b'\x00\x00\x01\x00'

        # Options
        b'\x00\x01\x00\x0cHello world!'
        b'\x00\x01\x00\x0fSpam eggs bacon\x00'
        b'\x00\x02\x00\x0fSome other text\x00'
        b'\x00\x00\x00\x00'

        # Enhanced Packet data
        b'\x00\x00\x00\x12'
        b'\x00\x01\x00\x00'
        b'These are 18 bytes\x00\x00'

        # Simple packet data
        b'\x00\x00\x00\x0d'
        b'Simple packet\x00\x00\x00'

        # List of name resolution items
        b'\x00\x01'  # IPv4
        b'\x00\x13'  # Length: 19bytes
        b'\x0a\x22\x33\x44www.example.com\x00'  # 19 bytes (10.34.51.68)

        b'\x00\x01'  # IPv4
        b'\x00\x13'  # Length: 19bytes
        b'\xc0\xa8\x14\x01www.example.org\x00'  # 19 bytes (192.168.20.1)

        b'\x00\x02'  # IPv6
        b'\x00\x1e'  # 30 bytes
        b'\x00\x11\x22\x33\x44\x55\x66\x77'
        b'\x88\x99\xaa\xbb\xcc\xdd\xee\xff'
        b'v6.example.net\x00\x00'

        b'\x00\x00\x00\x00'  # End marker

        # Another number, to check end
        b'\xaa\xbb\xcc\xdd'
    )

    unpacked = struct_decode(schema, data, endianness='>')
    assert unpacked['a_string'] == b'\x01\x23\x45\x67\x89\xab\xcd\xef'
    assert unpacked['a_number'] == 0x100

    assert isinstance(unpacked['options'], Options)
    assert len(unpacked['options']) == 2
    assert unpacked['options']['opt_comment'] == 'Hello world!'
    assert unpacked['options'][2] == b'Some other text'

    assert unpacked['pb_captured_len'] == 0x12
    assert unpacked['pb_orig_len'] == 0x10000
    assert unpacked['packet_data'] == b'These are 18 bytes'

    assert unpacked['spb_orig_len'] == 13
    assert unpacked['simple_packet_data'] == b'Simple packet'

    assert unpacked['name_res'] == [
        {'address': '10.34.51.68',
         'names': ['www.example.com'], 'type': 1},
        {'address': '192.168.20.1',
         'names': ['www.example.org'], 'type': 1},
        {'type': 2,
            'address': '11:2233:4455:6677:8899:aabb:ccdd:eeff',
         'names': ['v6.example.net']}]
Example #15
0
class SimplePacket(BasePacketBlock):
    """
    "The Simple Packet Block (SPB) is a lightweight container for storing the
    packets coming from the network."
    - pcapng spec, section 4.4. Other quoted citations are from this section
    unless otherwise noted.
    """
    magic_number = 0x00000003
    __slots__ = []
    schema = [
        ('packet_len', IntField(32, False), 0),  # NOT the captured length
        ('packet_data', PacketBytes('captured_len'),
         b''),  # we don't actually use this
    ]

    readonly_fields = set(('captured_len', 'interface_id'))

    def _decode(self):
        """Decodes the raw data of this block into its fields"""
        stream = six.BytesIO(self._raw)
        self._decoded = struct_decode(self.schema[:1], stream,
                                      self.section.endianness)
        # Now we can get our ``captured_len`` property which is required
        # to really know how much data we can load
        self._decoded['packet_data'] = RawBytes(self.captured_len).load(
            stream, self.section.endianness)
        del self._raw

    @property
    def interface_id(self):
        """
        "The Simple Packet Block does not contain the Interface ID field.
        Therefore, it MUST be assumed that all the Simple Packet Blocks have
        been captured on the interface previously specified in the first
        Interface Description Block."
        """
        return 0

    @property
    def captured_len(self):
        """
        "...the SnapLen value MUST be used to determine the size of the Packet
        Data field length."
        """
        snap_len = self.interface.snaplen
        if snap_len == 0:  # unlimited
            return self.packet_len
        else:
            return min(snap_len, self.packet_len)

    def _encode(self, outstream):
        fld_size = IntField(32, False)
        fld_data = RawBytes(0)
        if len(self.section.interfaces) > 1:
            # Spec is a bit ambiguous here. Section 4.4 says "it MUST
            # be assumed that all the Simple Packet Blocks have been captured
            # on the interface previously specified in the first Interface
            # Description Block." but later adds "A Simple Packet Block cannot
            # be present in a Section that has more than one interface because
            # of the impossibility to refer to the correct one (it does not
            # contain any Interface ID field)." Why would it say "the first"
            # IDB and not "the only" IDB if this was really forbidden?
            strictness.problem(
                "writing SimplePacket for section with multiple interfaces")
            if strictness.should_fix():
                # Can't fix this. The IDBs have already been written.
                pass
        snap_len = self.interface.snaplen
        if snap_len > 0 and snap_len < self.captured_len:
            # This isn't a strictness issue, it *will* break other readers
            # if we write more bytes than the snaplen says to expect.
            fld_size.encode(self.packet_len,
                            outstream,
                            endianness=self.section.endianness)
            fld_data.encode(self.packet_data[:snap_len], outstream)
        else:
            fld_size.encode(self.packet_len,
                            outstream,
                            endianness=self.section.endianness)
            fld_data.encode(self.packet_data, outstream)
Example #16
0
def test_unpack_dummy_packet():
    schema = [
        ("a_string", RawBytes(8), ""),
        ("a_number", IntField(32, False), 0),
        ("options", OptionsField([]), None),
        ("pb_captured_len", IntField(32, False), 0),
        ("pb_orig_len", IntField(32, False), 0),
        ("packet_data", PacketBytes("pb_captured_len"), b""),
        ("spb_orig_len", IntField(32, False), 0),
        ("simple_packet_data", PacketBytes("spb_orig_len"), b""),
        ("name_res", ListField(NameResolutionRecordField()), []),
        ("another_number", IntField(32, False), 0),
    ]

    # Note: NULLs are for padding!
    data = io.BytesIO(
        b"\x01\x23\x45\x67\x89\xab\xcd\xef"
        b"\x00\x00\x01\x00"
        # Options
        b"\x00\x01\x00\x0cHello world!"
        b"\x00\x01\x00\x0fSpam eggs bacon\x00"
        b"\x00\x02\x00\x0fSome other text\x00"
        b"\x00\x00\x00\x00"
        # Enhanced Packet data
        b"\x00\x00\x00\x12"
        b"\x00\x01\x00\x00"
        b"These are 18 bytes\x00\x00"
        # Simple packet data
        b"\x00\x00\x00\x0d"
        b"Simple packet\x00\x00\x00"
        # List of name resolution items
        b"\x00\x01"  # IPv4
        b"\x00\x13"  # Length: 19bytes
        b"\x0a\x22\x33\x44www.example.com\x00"  # 19 bytes (10.34.51.68)
        b"\x00\x01"  # IPv4
        b"\x00\x13"  # Length: 19bytes
        b"\xc0\xa8\x14\x01www.example.org\x00"  # 19 bytes (192.168.20.1)
        b"\x00\x02"  # IPv6
        b"\x00\x1e"  # 30 bytes
        b"\x00\x11\x22\x33\x44\x55\x66\x77"
        b"\x88\x99\xaa\xbb\xcc\xdd\xee\xff"
        b"v6.example.net\x00\x00"
        b"\x00\x00\x00\x00"  # End marker
        # Another number, to check end
        b"\xaa\xbb\xcc\xdd"
    )

    unpacked = struct_decode(schema, data, endianness=">")
    assert unpacked["a_string"] == b"\x01\x23\x45\x67\x89\xab\xcd\xef"
    assert unpacked["a_number"] == 0x100

    assert isinstance(unpacked["options"], Options)
    assert len(unpacked["options"]) == 2
    assert unpacked["options"]["opt_comment"] == "Hello world!"
    assert unpacked["options"][2] == b"Some other text"

    assert unpacked["pb_captured_len"] == 0x12
    assert unpacked["pb_orig_len"] == 0x10000
    assert unpacked["packet_data"] == b"These are 18 bytes"

    assert unpacked["spb_orig_len"] == 13
    assert unpacked["simple_packet_data"] == b"Simple packet"

    assert unpacked["name_res"] == [
        {"address": "10.34.51.68", "names": ["www.example.com"], "type": 1},
        {"address": "192.168.20.1", "names": ["www.example.org"], "type": 1},
        {
            "type": 2,
            "address": "11:2233:4455:6677:8899:aabb:ccdd:eeff",
            "names": ["v6.example.net"],
        },
    ]
Example #17
0
class InterfaceDescription(SectionMemberBlock):
    """
    "An Interface Description Block (IDB) is the container for information
    describing an interface on which packet data is captured."
    - pcapng spec, section 4.2. Other quoted citations are from this section
    unless otherwise noted.
    """
    magic_number = 0x00000001
    __slots__ = []
    schema = [
        ('link_type', IntField(16, False), 0),  # todo: enc/decode
        ('reserved', IntField(16, False), 0),
        ('snaplen', IntField(32, False), 0),
        (
            'options',
            OptionsField([
                Option(2, 'if_name', 'string'),
                Option(3, 'if_description', 'string'),
                Option(4, 'if_IPv4addr', 'ipv4+mask', multiple=True),
                Option(5, 'if_IPv6addr', 'ipv6+prefix', multiple=True),
                Option(6, 'if_MACaddr', 'macaddr'),
                Option(7, 'if_EUIaddr', 'euiaddr'),
                Option(8, 'if_speed', 'u64'),
                Option(9, 'if_tsresol', 'bytes'),  # Just keep the raw data
                Option(10, 'if_tzone', 'u32'),
                Option(11, 'if_filter', 'type+bytes'),
                Option(12, 'if_os', 'string'),
                Option(13, 'if_fcslen', 'u8'),
                Option(14, 'if_tsoffset', 'i64'),
                Option(15, 'if_hardware', 'string'),
            ]),
            None)
    ]

    @property  # todo: cache this property
    def timestamp_resolution(self):
        # ------------------------------------------------------------
        # Resolution of timestamps. If the Most Significant Bit is
        # equal to zero, the remaining bits indicates the resolution
        # of the timestamp as as a negative power of 10 (e.g. 6 means
        # microsecond resolution, timestamps are the number of
        # microseconds since 1/1/1970). If the Most Significant Bit is
        # equal to one, the remaining bits indicates the resolution as
        # as negative power of 2 (e.g. 10 means 1/1024 of second). If
        # this option is not present, a resolution of 10^-6 is assumed
        # (i.e. timestamps have the same resolution of the standard
        # 'libpcap' timestamps).
        # ------------------------------------------------------------

        if 'if_tsresol' in self.options:
            return unpack_timestamp_resolution(self.options['if_tsresol'])

        return 1e-6

    @property
    def statistics(self):
        # todo: ensure we always have an interface id -> how??
        return self.section.interface_stats.get(self.interface_id)

    @property
    def link_type_description(self):
        try:
            return link_types.LINKTYPE_DESCRIPTIONS[self.link_type]
        except KeyError:
            return 'Unknown link type: 0x{0:04x}'.format(self.link_type)
Example #18
0
class SectionHeader(Block):
    """
    "The Section Header Block (SHB) is mandatory. It identifies the beginning
    of a section of the capture file. The Section Header Block does not contain
    data but it rather identifies a list of blocks (interfaces, packets) that
    are logically correlated."
    - pcapng spec, section 4.1. Other quoted citations are from this section
    unless otherwise noted.
    """
    magic_number = 0x0a0d0d0a
    __slots__ = [
        'endianness',
        '_interfaces_id',
        'interfaces',
        'interface_stats',
    ]
    schema = [('version_major', IntField(16, False), 1),
              ('version_minor', IntField(16, False), 0),
              ('section_length', IntField(64, True), -1),
              ('options',
               OptionsField([
                   Option(2, 'shb_hardware', 'string'),
                   Option(3, 'shb_os', 'string'),
                   Option(4, 'shb_userappl', 'string'),
               ]), None)]

    def __init__(self, endianness="<", **kwargs):
        super(SectionHeader, self).__init__(**kwargs)
        self.endianness = endianness
        self._interfaces_id = itertools.count(0)
        self.interfaces = {}
        self.interface_stats = {}

    def _encode(self, outstream):
        write_int(0x1A2B3C4D, outstream, 32, endianness=self.endianness)
        super(SectionHeader, self)._encode(outstream)

    def new_member(self, cls, **kwargs):
        """Helper method to create a block that's a member of this section"""
        assert issubclass(cls, SectionMemberBlock)
        blk = cls(section=self, **kwargs)
        # Some blocks (eg. SPB) don't have options
        if any([x[0] == 'options' for x in blk.schema]):
            blk.options.endianness = self.endianness
        if isinstance(blk, InterfaceDescription):
            self.register_interface(blk)
        elif isinstance(blk, InterfaceStatistics):
            self.add_interface_stats(blk)
        return blk

    def register_interface(self, interface):
        """Helper method to register an interface within this section"""
        assert isinstance(interface, InterfaceDescription)
        interface_id = next(self._interfaces_id)
        interface.interface_id = interface_id
        self.interfaces[interface_id] = interface

    def add_interface_stats(self, interface_stats):
        """Helper method to register interface stats within this section"""
        assert isinstance(interface_stats, InterfaceStatistics)
        self.interface_stats[interface_stats.interface_id] = interface_stats

    @property
    def version(self):
        return (self.version_major, self.version_minor)

    @property
    def length(self):
        return self.section_length

    # Block.decode() assumes all blocks have sections -- technically true...
    @property
    def section(self):
        return self

    def __repr__(self):
        return ('<{name} version={version} endianness={endianness} '
                'length={length} options={options}>').format(
                    name=self.__class__.__name__,
                    version='.'.join(str(x) for x in self.version),
                    endianness=repr(self.endianness),
                    length=self.length,
                    options=repr(self.options))
Example #19
0
def test_unpack_dummy_packet():
    schema = [
        ('a_string', RawBytes(8)),
        ('a_number', IntField(32, False)),
        ('options', OptionsField([])),
        ('packet_data', PacketDataField()),
        ('simple_packet_data', SimplePacketDataField()),
        ('name_res', ListField(NameResolutionRecordField())),
        ('another_number', IntField(32, False)),
    ]

    # Note: NULLs are for padding!
    data = io.BytesIO(
        '\x01\x23\x45\x67\x89\xab\xcd\xef'
        '\x00\x00\x01\x00'

        # Options
        '\x00\x01\x00\x0cHello world!'
        '\x00\x01\x00\x0fSpam eggs bacon\x00'
        '\x00\x02\x00\x0fSome other text\x00'
        '\x00\x00\x00\x00'

        # Enhanced Packet data
        '\x00\x00\x00\x12'
        '\x00\x01\x00\x00'
        'These are 18 bytes\x00\x00'

        # Simple packet data
        '\x00\x00\x00\x0d'
        'Simple packet\x00\x00\x00'

        # List of name resolution items
        '\x00\x01'  # IPv4
        '\x00\x13'  # Length: 19bytes
        '\x0a\x22\x33\x44www.example.com\x00'  # 19 bytes (10.34.51.68)

        '\x00\x01'  # IPv4
        '\x00\x13'  # Length: 19bytes
        '\xc0\xa8\x14\x01www.example.org\x00'  # 19 bytes (192.168.20.1)

        '\x00\x02'  # IPv6
        '\x00\x1e'  # 30 bytes
        '\x00\x11\x22\x33\x44\x55\x66\x77'
        '\x88\x99\xaa\xbb\xcc\xdd\xee\xff'
        'v6.example.net\x00\x00'

        '\x00\x00\x00\x00'  # End marker

        # Another number, to check end
        '\xaa\xbb\xcc\xdd'
    )

    unpacked = struct_decode(schema, data, endianness='>')
    assert unpacked['a_string'] == '\x01\x23\x45\x67\x89\xab\xcd\xef'
    assert unpacked['a_number'] == 0x100

    assert isinstance(unpacked['options'], Options)
    assert len(unpacked['options']) == 2
    assert unpacked['options']['opt_comment'] == 'Hello world!'
    assert unpacked['options'][2] == 'Some other text'

    assert unpacked['packet_data'] == (0x12, 0x10000, 'These are 18 bytes')

    assert unpacked['simple_packet_data'] == (13, 'Simple packet')

    assert unpacked['name_res'] == [
        {'address': '\x0a\x22\x33\x44', 'name': 'www.example.com', 'type': 1},
        {'address': '\xc0\xa8\x14\x01', 'name': 'www.example.org', 'type': 1},
        {'type': 2,
         'address': '\x00\x11\x22\x33\x44\x55\x66\x77'
                    '\x88\x99\xaa\xbb\xcc\xdd\xee\xff',
         'name': 'v6.example.net'}]