Example #1
0
    def _read_opt_smf_dpd(self, code, *, desc):
        """Read IPv6-Opts ``SMF_DPD`` option.

        Structure of IPv6-Opts ``SMF_DPD`` option [:rfc:`5570`]:

        * IPv6 ``SMF_DPD`` option header in **I-DPD** mode

          .. code:: text

              0                   1                   2                   3
              0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                            ...              |0|0|0|  01000  | Opt. Data Len |
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             |0|TidTy| TidLen|             TaggerID (optional) ...           |
             +-+-+-+-+-+-+-+-+               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             |                               |            Identifier  ...
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

        * IPv6 ``SMF_DPD`` option header in **H-DPD** mode

          .. code:: text

              0                   1                   2                   3
              0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                            ...              |0|0|0| OptType | Opt. Data Len |
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             |1|    Hash Assist Value (HAV) ...
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

        Args:
            code (int): option type value

        Keyword Args:
            desc (str): option description

        Returns:
            Union[DataType_Dest_Opt_SMF_I_PDP, DataType_Dest_Opt_SMF_H_PDP]: parsed option data

        Raises:
            ProtocolError: If the option is malformed.

        """
        _type = self._read_opt_type(code)
        _size = self._read_unpack(1)
        _tidd = self._read_binary(1)

        if _tidd[0] == '0':
            _mode = 'I-DPD'
            _tidt = _TID_TYPE.get(_tidd[1:4], 'Unassigned')
            _tidl = int(_tidd[4:], base=2)

            if _tidt == _TID_TYPE.NULL:
                if _tidl != 0:
                    raise ProtocolError(
                        f'{self.alias}: [OptNo {code}] invalid format')
                _iden = self._read_fileng(_size - 1)

                opt = dict(
                    desc=desc,
                    type=_type,
                    length=_size + 2,
                    dpd_type=_mode,
                    tid_type=_tidt,
                    tid_len=_tidl,
                    id=_iden,
                )
            elif _tidt == _TID_TYPE.IPv4:
                if _tidl != 3:
                    raise ProtocolError(
                        f'{self.alias}: [OptNo {code}] invalid format')
                _tidf = self._read_fileng(4)
                _iden = self._read_fileng(_size - 4)

                opt = dict(
                    desc=desc,
                    type=_type,
                    length=_size + 2,
                    dpd_type=_mode,
                    tid_type=_tidt,
                    tid_len=_tidl,
                    tid=ipaddress.ip_address(_tidf),
                    id=_iden,
                )
            elif _tidt == _TID_TYPE.IPv6:
                if _tidl != 15:
                    raise ProtocolError(
                        f'{self.alias}: [OptNo {code}] invalid format')
                _tidf = self._read_fileng(15)
                _iden = self._read_fileng(_size - 15)

                opt = dict(
                    desc=desc,
                    type=_type,
                    length=_size + 2,
                    dpd_type=_mode,
                    tid_type=_tidt,
                    tid_len=_tidl,
                    tid=ipaddress.ip_address(_tidf),
                    id=_iden,
                )
            else:
                _tidf = self._read_unpack(_tidl + 1)
                _iden = self._read_fileng(_size - _tidl - 2)

                opt = dict(
                    desc=desc,
                    type=_type,
                    length=_size + 2,
                    dpd_type=_mode,
                    tid_type=_tidt,
                    tid_len=_tidl,
                    tid=_tidf,
                    id=_iden,
                )
        elif _tidd[0] == '1':
            _mode = 'H-DPD'
            _tidt = _TID_TYPE.get(_tidd[1:4])
            _data = self._read_binary(_size - 1)

            opt = dict(
                desc=desc,
                type=_type,
                length=_size + 2,
                dpd_type=_mode,
                tid_type=_tidt,
                hav=_tidd[1:] + _data,
            )
        else:
            raise ProtocolError(f'{self.alias}: [OptNo {code}] invalid format')

        return opt
Example #2
0
    def _read_http_push_promise(self, size, kind, flag):
        """Read HTTP/2 PUSH_PROMISE frames.

        Structure of HTTP/2 PUSH_PROMISE frame [RFC 7540]:
            +-----------------------------------------------+
            |                 Length (24)                   |
            +---------------+---------------+---------------+
            |   Type (8)    |   Flags (8)   |
            +-+-------------+---------------+-------------------------------+
            |R|                 Stream Identifier (31)                      |
            +---------------+-----------------------------------------------+
            |Pad Length? (8)|
            +-+-------------+-----------------------------------------------+
            |R|                  Promised Stream ID (31)                    |
            +-+-----------------------------+-------------------------------+
            |                   Header Block Fragment (*)                 ...
            +---------------------------------------------------------------+
            |                           Padding (*)                       ...
            +---------------------------------------------------------------+

            Octets      Bits        Name                    Description
              0           0     http.length             Length
              3          24     http.type               Type (1)
              4          32     http.flags              Flags
              5          40     -                       Reserved
              5          41     http.sid                Stream Identifier
              9          72     http.pad_len            Pad Length (Optional)
              10         80     -                       Reserved
              10         81     http.pid                Promised Stream ID
              14        112     http.frag               Header Block Fragment
              ?           ?     -                       Padding (Optional)

        """
        if size < 4:
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        _plen = 0
        _flag = dict(
            END_HEADERS=False,  # bit 2
            PADDED=False,  # bit 3
        )
        for index, bit in enumerate(flag):
            if index == 2 and bit:
                _flag['END_HEADERS'] = True
            elif index == 3 and bit:
                _flag['PADDED'] = True
                _plen = self._read_unpack(1)
            elif bit:
                raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                    quiet=True)
            else:
                continue

        if _flag['PADDED']:
            _dlen = size - _plen - 5
        else:
            _dlen = size - _plen - 4
        if _dlen < 0:
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        _rpid = self._read_binary(4)
        _frag = self._read_fileng(_dlen) or None

        if int(_rpid[0], base=2):
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        padding = self._read_binary(_plen)
        if any((int(bit, base=2) for bit in padding)):
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        data = dict(
            flags=_flag,
            pid=int(_rpid[1:], base=2),
            frag=_frag,
        )
        if _flag['PADDED']:
            data['ped_len'] = _plen

        return data
Example #3
0
    def _read_opt_calipso(self, code, *, desc):
        """Read IPv6-Opts ``CALIPSO`` option.

        Structure of IPv6-Opts ``CALIPSO`` option [:rfc:`5570`]:

        .. code:: text

            ------------------------------------------------------------
            | Next Header | Hdr Ext Len   | Option Type | Option Length|
            +-------------+---------------+-------------+--------------+
            |             CALIPSO Domain of Interpretation             |
            +-------------+---------------+-------------+--------------+
            | Cmpt Length |  Sens Level   |     Checksum (CRC-16)      |
            +-------------+---------------+-------------+--------------+
            |      Compartment Bitmap (Optional; variable length)      |
            +-------------+---------------+-------------+--------------+

        Args:
            code (int): option type value

        Keyword Args:
            desc (str): option description

        Returns:
            DataType_Dest_Opt_CALIPSO: parsed option data

        Raises:
            ProtocolError: If the option is malformed.

        """
        _type = self._read_opt_type(code)
        _size = self._read_unpack(1)
        if _size < 8 and _size % 8 != 0:
            raise ProtocolError(f'{self.alias}: [OptNo {code}] invalid format')
        _cmpt = self._read_unpack(4)
        _clen = self._read_unpack(1)
        if _clen % 2 != 0:
            raise ProtocolError(f'{self.alias}: [OptNo {code}] invalid format')
        _sens = self._read_unpack(1)
        _csum = self._read_fileng(2)

        opt = dict(
            desc=desc,
            type=_type,
            length=_size + 2,
            domain=_cmpt,
            cmpt_len=_clen * 4,
            level=_sens,
            chksum=_csum,
        )

        if _clen:
            _bmap = list()
            for _ in range(_clen // 2):
                _bmap.append(self._read_binary(8))
            opt['bitmap'] = tuple(_bmap)

        _plen = _size - _clen * 4 - 8
        if _plen:
            self._read_fileng(_plen)

        return opt
Example #4
0
    def _read_mode_ts(self, size, kind):
        """Read Time Stamp option.

        Positional arguments:
            * size - int, length of option
            * kind - int, 68 (TS)

        Returns:
            * dict -- extracted Time Stamp (TS) option

        Structure of Timestamp (TS) option [RFC 791]:
            +--------+--------+--------+--------+
            |01000100| length | pointer|oflw|flg|
            +--------+--------+--------+--------+
            |         internet address          |
            +--------+--------+--------+--------+
            |             timestamp             |
            +--------+--------+--------+--------+
            |                 .                 |
                              .
                              .

            Octets      Bits        Name                    Description
              0           0     ip.ts.kind              Kind (25)
              0           0     ip.ts.type.copy         Copied Flag (0)
              0           1     ip.ts.type.class        Option Class (0)
              0           3     ip.ts.type.number       Option Number (25)
              1           8     ip.ts.length            Length (≤40)
              2          16     ip.ts.pointer           Pointer (≥5)
              3          24     ip.ts.overflow          Overflow Octets
              3          28     ip.ts.flag              Flag
              4          32     ip.ts.ip                Internet Address
              8          64     ip.ts.timestamp         Timestamp

        """
        if size > 40 or size < 4:
            raise ProtocolError(f'{self.alias}: [Optno {kind}] invalid format')

        _tptr = self._read_unpack(1)
        _oflg = self._read_binary(1)
        _oflw = int(_oflg[:4], base=2)
        _flag = int(_oflg[4:], base=2)

        if _tptr < 5:
            raise ProtocolError(f'{self.alias}: [Optno {kind}] invalid format')

        data = dict(
            kind=kind,
            type=self._read_opt_type(kind),
            length=size,
            pointer=_tptr,
            overflow=_oflw,
            flag=_flag,
        )

        endpoint = min(_tptr, size)
        if _flag == 0:
            if (size - 4) % 4 != 0:
                raise ProtocolError(f'{self.alias}: [Optno {kind}] invalid format')
            counter = 5
            timestamp = list()
            while counter < endpoint:
                counter += 4
                time = self._read_unpack(4, lilendian=True)
                timestamp.append(datetime.datetime.fromtimestamp(time))
            data['timestamp'] = timestamp or None
        elif _flag == 1 or _flag == 3:
            if (size - 4) % 8 != 0:
                raise ProtocolError(f'{self.alias}: [Optno {kind}] invalid format')
            counter = 5
            ipaddress = list()
            timestamp = list()
            while counter < endpoint:
                counter += 8
                ipaddress.append(self._read_ipv4_addr())
                time = self._read_unpack(4, lilendian=True)
                timestamp.append(datetime.datetime.fromtimestamp(time))
            data['ip'] = ipaddress or None
            data['timestamp'] = timestamp or None
        else:
            data['data'] = self._read_fileng(size - 4) or None

        return data
Example #5
0
    def _read_http_data(self, size, kind, flag):
        """Read HTTP/2 ``DATA`` frames.

        Structure of HTTP/2 ``DATA`` frame [:rfc:`7540`]::

            +-----------------------------------------------+
            |                 Length (24)                   |
            +---------------+---------------+---------------+
            |   Type (8)    |   Flags (8)   |
            +-+-------------+---------------+-------------------------------+
            |R|                 Stream Identifier (31)                      |
            +---------------+-----------------------------------------------+
            |Pad Length? (8)|
            +---------------+-----------------------------------------------+
            |                            Data (*)                         ...
            +---------------------------------------------------------------+
            |                           Padding (*)                       ...
            +---------------------------------------------------------------+

        Args:
            size (int): length of packet data
            kind (int): packet type
            flag (str): packet flags (8 bits)

        Returns:
            DataType_HTTPv2_DATA: Parsed packet data.

        Raises:
            ProtocolError: If the packet is malformed.

        """
        _plen = 0
        _flag = dict(
            END_STREAM=False,  # bit 0
            PADDED=False,  # bit 3
        )
        for index, bit in enumerate(flag):
            if index == 0 and bit:
                _flag['END_STREAM'] = True
            elif index == 3 and bit:
                _flag['PADDED'] = True
                _plen = self._read_unpack(1)
            elif bit:
                raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                    quiet=True)
            else:
                continue

        if _plen > size - 10:
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        if _flag['PADDED']:
            _dlen = size - _plen - 1
        else:
            _dlen = size - _plen
        if _dlen < 0:
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        _data = self._read_fileng(_dlen)

        padding = self._read_binary(_plen)
        if any((int(bit, base=2) for bit in padding)):
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        data = dict(
            flags=_flag,
            data=_data,
        )
        if _flag['PADDED']:
            data['ped_len'] = _plen

        return data
Example #6
0
    def _read_opt_smf_dpd(self, code, *, desc):
        """Read IPv6_Opts SMF_DPD option.

        Structure of IPv6_Opts SMF_DPD option [RFC 5570]:
            * IPv6 SMF_DPD Option Header in I-DPD mode
                 0                   1                   2                   3
                 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                               ...              |0|0|0|  01000  | Opt. Data Len |
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                |0|TidTy| TidLen|             TaggerId (optional) ...           |
                +-+-+-+-+-+-+-+-+               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                |                               |            Identifier  ...
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                Octets      Bits        Name                            Description
                  0           0     ipv6_opts.smf_dpd.type          Option Type
                  0           0     ipv6_opts.smf_dpd.type.value    Option Number
                  0           0     ipv6_opts.smf_dpd.type.action   Action (00)
                  0           2     ipv6_opts.smf_dpd.type.change   Change Flag (0)
                  1           8     ipv6_opts.smf_dpd.length        Length of Option Data
                  2          16     ipv6_opts.smf_dpd.dpd_type      DPD Type (0)
                  2          17     ipv6_opts.smf_dpd.tid_type      TaggerID Type
                  2          20     ipv6_opts.smf_dpd.tid_len       TaggerID Length
                  3          24     ipv6_opts.smf_dpd.tid           TaggerID
                  ?           ?     ipv6_opts.smf_dpd.id            Identifier

            * IPv6 SMF_DPD Option Header in H-DPD Mode
                 0                   1                   2                   3
                 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                               ...              |0|0|0| OptType | Opt. Data Len |
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                |1|    Hash Assist Value (HAV) ...
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                Octets      Bits        Name                        Description
                  0           0     ipv6_opts.smf_dpd.type         Option Type
                  0           0     ipv6_opts.smf_dpd.type.value   Option Number
                  0           0     ipv6_opts.smf_dpd.type.action  Action (00)
                  0           2     ipv6_opts.smf_dpd.type.change  Change Flag (0)
                  1           8     ipv6_opts.smf_dpd.length       Length of Option Data
                  2          16     ipv6_opts.smf_dpd.dpd_type     DPD Type (1)
                  2          17     ipv6_opts.smf_dpd.hav          Hash Assist Value

        """
        _type = self._read_opt_type(code)
        _size = self._read_unpack(1)
        _tidd = self._read_binary(1)

        if _tidd[0] == '0':
            _mode = 'I-DPD'
            _tidt = _TID_TYPE.get(_tidd[1:4], 'Unassigned')
            _tidl = int(_tidd[4:], base=2)
            if _tidt == 'NULL':
                if _tidl != 0:
                    raise ProtocolError(
                        f'{self.alias}: [Optno {code}] invalid format')
                _iden = self._read_fileng(_size - 1)

                opt = dict(
                    desc=desc,
                    type=_type,
                    length=_size + 2,
                    dpd_type=_mode,
                    tid_type=_tidt,
                    tid_len=_tidl,
                    id=_iden,
                )
            elif _tidt == 'IPv4':
                if _tidl != 3:
                    raise ProtocolError(
                        f'{self.alias}: [Optno {code}] invalid format')
                _tidf = self._read_fileng(4)
                _iden = self._read_fileng(_size - 4)

                opt = dict(
                    desc=desc,
                    type=_type,
                    length=_size + 2,
                    dpd_type=_mode,
                    tid_type=_tidt,
                    tid_len=_tidl,
                    tid=ipaddress.ip_address(_tidf),
                    id=_iden,
                )
            elif _tidt == 'IPv6':
                if _tidl != 15:
                    raise ProtocolError(
                        f'{self.alias}: [Optno {code}] invalid format')
                _tidf = self._read_fileng(15)
                _iden = self._read_fileng(_size - 15)

                opt = dict(
                    desc=desc,
                    type=_type,
                    length=_size + 2,
                    dpd_type=_mode,
                    tid_type=_tidt,
                    tid_len=_tidl,
                    tid=ipaddress.ip_address(_tidf),
                    id=_iden,
                )
            else:
                _tidf = self._read_unpack(_tidl + 1)
                _iden = self._read_fileng(_size - _tidl - 2)

                opt = dict(
                    desc=desc,
                    type=_type,
                    length=_size + 2,
                    dpd_type=_mode,
                    tid_type=_tidt,
                    tid_len=_tidl,
                    tid=_tidf,
                    id=_iden,
                )
        elif _tidd[0] == '1':
            _data = self._read_binary(_size - 1)

            opt = dict(
                desc=desc,
                type=_type,
                length=_size + 2,
                dpd_type=_mode,
                tid_type=_tidt,
                hav=_tidd[1:] + _data,
            )
        else:
            raise ProtocolError(f'{self.alias}: [Optno {code}] invalid format')

        return opt
Example #7
0
    def _read_opt_mpl(self, code, *, desc):
        """Read IPv6_Opts MPL option.

        Structure of IPv6_Opts MPL option [RFC 7731]:
             0                   1                   2                   3
             0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
                                            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                            |  Option Type  |  Opt Data Len |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            | S |M|V|  rsv  |   sequence    |      seed-id (optional)       |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

            Octets      Bits        Name                            Description
              0           0     ipv6_opts.mpl.type              Option Type
              0           0     ipv6_opts.mpl.type.value        Option Number
              0           0     ipv6_opts.mpl.type.action       Action (01)
              0           2     ipv6_opts.mpl.type.change       Change Flag (1)
              1           8     ipv6_opts.mpl.length            Length of Option Data
              2          16     ipv6_opts.mpl.seed_len          Seed-ID Length
              2          18     ipv6_opts.mpl.flags             MPL Option Flags
              2          18     ipv6_opts.mpl.max               Maximum SEQ Flag
              2          19     ipv6_opts.mpl.verification      Verification Flag
              2          20     -                               Reserved
              3          24     ipv6_opts.mpl.seq               Sequence
              4          32     ipv6_opts.mpl.seed_id           Seed-ID

        """
        _type = self._read_opt_type(code)
        _size = self._read_unpack(1)
        if _size < 2:
            raise ProtocolError(f'{self.alias}: [Optno {code}] invalid format')

        _type = self._read_opt_type(code)
        _size = self._read_unpack(1)
        _smvr = self._read_binary(1)
        _seqn = self._read_unpack(1)

        opt = dict(
            desc=desc,
            type=_type,
            length=_size + 2,
            seed_len=_IPv6_Opts_SEED.get(int(_smvr[:2], base=2)),
            flags=dict(
                max=True if int(_smvr[2], base=2) else False,
                verification=True if int(_smvr[3], base=2) else False,
            ),
            seq=_seqn,
        )

        _kind = _smvr[:2]
        if _kind == '00':
            if _size != 2:
                raise ProtocolError(
                    f'{self.alias}: [Optno {code}] invalid format')
        elif _kind == '01':
            if _size != 4:
                raise ProtocolError(
                    f'{self.alias}: [Optno {code}] invalid format')
            opt['seed_id'] = self._read_unpack(2)
        elif _kind == '10':
            if _size != 10:
                raise ProtocolError(
                    f'{self.alias}: [Optno {code}] invalid format')
            opt['seed_id'] = self._read_unpack(8)
        elif _kind == '11':
            if _size != 18:
                raise ProtocolError(
                    f'{self.alias}: [Optno {code}] invalid format')
            opt['seed_id'] = self._read_unpack(16)
        else:
            opt['seed_id'] = self._read_unpack(_size - 2)

        _plen = _size - opt['seed_len']
        if _plen:
            self._read_fileng(_plen)

        return opt
Example #8
0
    def _read_mode_qs(self, size, kind):
        """Read Quick Start option.

        Structure of Quick-Start (QS) option [:rfc:`4782`]:

        * A Quick-Start Request

          .. code:: text

              0                   1                   2                   3
              0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             |   Option      |  Length=8     | Func. | Rate  |   QS TTL      |
             |               |               | 0000  |Request|               |
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             |                        QS Nonce                           | R |
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

        * Report of Approved Rate

          .. code:: text

              0                   1                   2                   3
              0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             |   Option      |  Length=8     | Func. | Rate  |   Not Used    |
             |               |               | 1000  | Report|               |
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             |                        QS Nonce                           | R |
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

        Arguments:
            size (int): length of option
            kind (Literal[25]): option kind value (QS)

        Returns:
            DataType_Opt_QuickStart: extracted Quick Start option

        Raises:
            ProtocolError: If the option is malformed.

        """
        if size != 8:
            raise ProtocolError(f'{self.alias}: [OptNo {kind}] invalid format')

        _type = self._read_opt_type(kind)
        _fcrr = self._read_binary(1)
        _func = int(_fcrr[:4], base=2)
        _rate = int(_fcrr[4:], base=2)
        _ttlv = self._read_unpack(1)
        _nonr = self._read_binary(4)
        _qsnn = int(_nonr[:30], base=2)

        if _func not in (0, 8):
            raise ProtocolError(f'{self.alias}: [OptNo {kind}] invalid format')

        data = dict(
            kind=kind,
            type=_type,
            length=size,
            func=QS_FUNC.get(_func),
            rate=40000 * (2 ** _rate) / 1000,
            ttl=None if _func else _rate,
            nounce=_qsnn,
        )

        return data
Example #9
0
    def _read_mode_ts(self, size, kind):
        """Read Time Stamp option.

        Structure of Timestamp (TS) option [:rfc:`791`]::

            +--------+--------+--------+--------+
            |01000100| length | pointer|oflw|flg|
            +--------+--------+--------+--------+
            |         internet address          |
            +--------+--------+--------+--------+
            |             timestamp             |
            +--------+--------+--------+--------+
            |                 .                 |
                              .
                              .

        Arguments:
            size (int): length of option
            kind (Literal[68]): option kind value (TS)

        Returns:
            DataType_Opt_TimeStamp: extracted Time Stamp option

        Raises:
            ProtocolError: If the option is malformed.

        """
        if size > 40 or size < 4:
            raise ProtocolError(f'{self.alias}: [OptNo {kind}] invalid format')

        _tptr = self._read_unpack(1)
        _oflg = self._read_binary(1)
        _oflw = int(_oflg[:4], base=2)
        _flag = int(_oflg[4:], base=2)

        if _tptr < 5:
            raise ProtocolError(f'{self.alias}: [OptNo {kind}] invalid format')

        data = dict(
            kind=kind,
            type=self._read_opt_type(kind),
            length=size,
            pointer=_tptr,
            overflow=_oflw,
            flag=_flag,
        )

        endpoint = min(_tptr, size)
        if _flag == 0:
            if (size - 4) % 4 != 0:
                raise ProtocolError(f'{self.alias}: [OptNo {kind}] invalid format')
            counter = 5
            timestamp = list()
            while counter < endpoint:
                counter += 4
                time = self._read_unpack(4, lilendian=True)
                timestamp.append(datetime.datetime.fromtimestamp(time))
            data['timestamp'] = timestamp or None
        elif _flag in (1, 3):
            if (size - 4) % 8 != 0:
                raise ProtocolError(f'{self.alias}: [OptNo {kind}] invalid format')
            counter = 5
            ipaddress = list()  # pylint: disable=redefined-outer-name
            timestamp = list()
            while counter < endpoint:
                counter += 8
                ipaddress.append(self._read_ipv4_addr())
                time = self._read_unpack(4, lilendian=True)
                timestamp.append(datetime.datetime.fromtimestamp(time))
            data['ip'] = tuple(ipaddress) or None
            data['timestamp'] = tuple(timestamp) or None
        else:
            data['data'] = self._read_fileng(size - 4) or None

        return data
Example #10
0
    def read(self, length=None, **kwargs):  # pylint: disable=unused-argument
        """Read Hypertext Transfer Protocol (HTTP/2).

        Structure of HTTP/2 packet [:rfc:`7540`]::

            +-----------------------------------------------+
            |                 Length (24)                   |
            +---------------+---------------+---------------+
            |   Type (8)    |   Flags (8)   |
            +-+-------------+---------------+-------------------------------+
            |R|                 Stream Identifier (31)                      |
            +=+=============================================================+
            |                   Frame Payload (0...)                      ...
            +---------------------------------------------------------------+

        Args:
            length (Optional[int]): Length of packet data.

        Keyword Args:
            **kwargs: Arbitrary keyword arguments.

        Returns:
            DataType_HTTPv2: Parsed packet data.

        Raises:
            ProtocolError: If the packet is malformed.

        """
        if length is None:
            length = len(self)

        if length < 9:
            raise ProtocolError('HTTP/2: invalid format', quiet=True)

        _tlen = self._read_unpack(3)
        _type = self._read_unpack(1)
        _flag = self._read_binary(1)
        _rsid = self._read_binary(4)

        if _tlen != length:
            raise ProtocolError(f'HTTP/2: [Type {_type}] invalid format',
                                quiet=True)

        if int(_rsid[0], base=2):
            raise ProtocolError(f'HTTP/2: [Type {_type}] invalid format',
                                quiet=True)

        http = dict(
            length=_tlen,
            type=_HTTP_TYPE.get(_type),
            sid=int(_rsid[1:], base=2),
            packet=self._read_packet(_tlen),
        )

        if http['type'] is None:
            raise ProtocolError(f'HTTP/2: [Type {_type}] invalid format',
                                quiet=True)

        if http['type'] in ('SETTINGS', 'PING') and http['sid'] != 0:
            raise ProtocolError(f'HTTP/2: [Type {_type}] invalid format',
                                quiet=True)

        _http = _HTTP_FUNC[_type](self, _tlen, _type, _flag)
        http.update(_http)

        return http
Example #11
0
    def _read_mode_route(self, size, kind):
        """Read options with route data.

        Structure of these options [:rfc:`791`]:

        * Loose Source Route

          .. code:: text

             +--------+--------+--------+---------//--------+
             |10000011| length | pointer|     route data    |
             +--------+--------+--------+---------//--------+

        * Strict Source Route

          .. code:: text

             +--------+--------+--------+---------//--------+
             |10001001| length | pointer|     route data    |
             +--------+--------+--------+---------//--------+

        * Record Route

          .. code:: text

             +--------+--------+--------+---------//--------+
             |00000111| length | pointer|     route data    |
             +--------+--------+--------+---------//--------+

        Arguments:
            size (int): length of option
            kind (Literal[7, 131, 137]): option kind value (RR/LSR/SSR)

        Returns:
            DataType_Opt_Route_Data: extracted option with route data

        Raises:
            ProtocolError: If the option is malformed.

        """
        if size < 3 or (size - 3) % 4 != 0:
            raise ProtocolError(f'{self.alias}: [OptNo {kind}] invalid format')

        _rptr = self._read_unpack(1)
        if _rptr < 4:
            raise ProtocolError(f'{self.alias}: [OptNo {kind}] invalid format')

        data = dict(
            kind=kind,
            type=self._read_opt_type(kind),
            length=size,
            pointer=_rptr,
        )

        counter = 4
        address = list()
        endpoint = min(_rptr, size)
        while counter < endpoint:
            counter += 4
            address.append(self._read_ipv4_addr())
        data['data'] = tuple(address) or None

        return data
Example #12
0
    def _read_http_push_promise(self, size, kind, flag):
        """Read HTTP/2 ``PUSH_PROMISE`` frames.

        Structure of HTTP/2 ``PUSH_PROMISE`` frame [:rfc:`7540`]::

            +-----------------------------------------------+
            |                 Length (24)                   |
            +---------------+---------------+---------------+
            |   Type (8)    |   Flags (8)   |
            +-+-------------+---------------+-------------------------------+
            |R|                 Stream Identifier (31)                      |
            +---------------+-----------------------------------------------+
            |Pad Length? (8)|
            +-+-------------+-----------------------------------------------+
            |R|                  Promised Stream ID (31)                    |
            +-+-----------------------------+-------------------------------+
            |                   Header Block Fragment (*)                 ...
            +---------------------------------------------------------------+
            |                           Padding (*)                       ...
            +---------------------------------------------------------------+

        Args:
            size (int): length of packet data
            kind (int): packet type
            flag (str): packet flags (8 bits)

        Returns:
            DataType_HTTPv2_PUSH_PROMISE: Parsed packet data.

        Raises:
            ProtocolError: If the packet is malformed.

        """
        if size < 4:
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        _plen = 0
        _flag = dict(
            END_HEADERS=False,  # bit 2
            PADDED=False,  # bit 3
        )
        for index, bit in enumerate(flag):
            if index == 2 and bit:
                _flag['END_HEADERS'] = True
            elif index == 3 and bit:
                _flag['PADDED'] = True
                _plen = self._read_unpack(1)
            elif bit:
                raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                    quiet=True)
            else:
                continue

        if _flag['PADDED']:
            _dlen = size - _plen - 5
        else:
            _dlen = size - _plen - 4
        if _dlen < 0:
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        _rpid = self._read_binary(4)
        _frag = self._read_fileng(_dlen) or None

        if int(_rpid[0], base=2):
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        padding = self._read_binary(_plen)
        if any((int(bit, base=2) for bit in padding)):
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        data = dict(
            flags=_flag,
            pid=int(_rpid[1:], base=2),
            frag=_frag,
        )
        if _flag['PADDED']:
            data['pad_len'] = _plen

        return data
Example #13
0
    def _read_http_settings(self, size, kind, flag):
        """Read HTTP/2 ``SETTINGS`` frames.

        Structure of HTTP/2 ``SETTINGS`` frame [:rfc:`7540`]::

            +-----------------------------------------------+
            |                 Length (24)                   |
            +---------------+---------------+---------------+
            |   Type (8)    |   Flags (8)   |
            +-+-------------+---------------+-------------------------------+
            |R|                 Stream Identifier (31)                      |
            +---------------------------------------------------------------+
            |       Identifier (16)         |
            +-------------------------------+-------------------------------+
            |                        Value (32)                             |
            +---------------------------------------------------------------+
            |                          ......                               |

        Args:
            size (int): length of packet data
            kind (int): packet type
            flag (str): packet flags (8 bits)

        Returns:
            DataType_HTTPv2_SETTINGS: Parsed packet data.

        Raises:
            ProtocolError: If the packet is malformed.

        """
        if size % 5 != 0:
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        _flag = dict(
            ACK=False,  # bit 0
        )
        for index, bit in enumerate(flag):
            if index == 0 and bit:
                _flag['ACK'] = True
            elif bit:
                raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                    quiet=True)
            else:
                continue

        if _flag['ACK'] and size:
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        _list = list()
        _para = dict()
        counter = 0
        while counter < size:
            _stid = self._read_unpack(1)
            _pval = self._read_unpack(4)
            _pkey = _PARA_NAME.get(_stid, 'Unsigned')

            _name = _pkey.name
            if _pkey in _para:
                if isinstance(_para[_name], tuple):
                    _para[_name] += (_pval, )
                else:
                    _para[_name] = (_para[_name], _pval)
            else:
                _para[_name] = _pval
                _list.append(_pkey)

        data = dict(
            flags=_flag,
            settings=tuple(_pkey),
        )
        data.update(_para)

        return data
Example #14
0
    def _read_http_headers(self, size, kind, flag):
        """Read HTTP/2 ``HEADERS`` frames.

        Structure of HTTP/2 ``HEADERS`` frame [:rfc:`7540`]::

            +-----------------------------------------------+
            |                 Length (24)                   |
            +---------------+---------------+---------------+
            |   Type (8)    |   Flags (8)   |
            +-+-------------+---------------+-------------------------------+
            |R|                 Stream Identifier (31)                      |
            +---------------+-----------------------------------------------+
            |Pad Length? (8)|
            +-+-------------+-----------------------------------------------+
            |E|                 Stream Dependency? (31)                     |
            +-+-------------+-----------------------------------------------+
            |  Weight? (8)  |
            +-+-------------+-----------------------------------------------+
            |                   Header Block Fragment (*)                 ...
            +---------------------------------------------------------------+
            |                           Padding (*)                       ...
            +---------------------------------------------------------------+

        Args:
            size (int): length of packet data
            kind (int): packet type
            flag (str): packet flags (8 bits)

        Returns:
            DataType_HTTPv2_HEADERS: Parsed packet data.

        Raises:
            ProtocolError: If the packet is malformed.

        """
        _plen = 0
        _elen = 0
        _flag = dict(
            END_STREAM=False,  # bit 0
            END_HEADERS=False,  # bit 2
            PADDED=False,  # bit 3
            PRIORITY=False,  # bit 5
        )
        for index, bit in enumerate(flag):
            if index == 0 and bit:
                _flag['END_STREAM'] = True
            elif index == 2 and bit:
                _flag['END_HEADERS'] = True
            elif index == 3 and bit:
                _flag['PADDED'] = True
                _plen = self._read_unpack(1)
            elif index == 5 and bit:
                _flag['PRIORITY'] = True
                _edep = self._read_binary(4)
                _wght = self._read_unpack(1)
                _elen = 5
            elif bit:
                raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                    quiet=True)
            else:
                continue

        if _flag['PADDED']:
            _dlen = size - _plen - _elen - 1
        else:
            _dlen = size - _plen - _elen
        if _dlen < 0:
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        _frag = self._read_fileng(_dlen) or None

        padding = self._read_binary(_plen)
        if any((int(bit, base=2) for bit in padding)):
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        data = dict(
            flags=_flag,
            frag=_frag,
        )
        if _flag['PADDED']:
            data['pad_len'] = _plen
        if _flag['PRIORITY']:
            data['exclusive'] = bool(int(_edep[0], base=2))
            data['deps'] = int(_edep[1:], base=2)
            data['weight'] = _wght + 1

        return data
Example #15
0
    def _read_opt_qs(self, code, *, desc):  # pylint: disable=unused-argument
        """Read IPv6-Opts Quick Start option.

        Structure of IPv6-Opts Quick-Start option [:rfc:`4782`]:

        * A Quick-Start Request:

          .. code:: text

              0                   1                   2                   3
              0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             |   Option      |  Length=6     | Func. | Rate  |   QS TTL      |
             |               |               | 0000  |Request|               |
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             |                        QS Nonce                           | R |
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

        * Report of Approved Rate:

          .. code:: text

              0                   1                   2                   3
              0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             |   Option      |  Length=6     | Func. | Rate  |   Not Used    |
             |               |               | 1000  | Report|               |
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             |                        QS Nonce                           | R |
             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

        Args:
            code (int): option type value

        Keyword Args:
            desc (str): option description

        Returns:
            DataType_Dest_Opt_QS: parsed option data

        Raises:
            ProtocolError: If the option is malformed.

        """
        _type = self._read_opt_type(code)
        _size = self._read_unpack(1)
        if _size != 6:
            raise ProtocolError(f'{self.alias}: [OptNo {code}] invalid format')

        _fcrr = self._read_binary(1)
        _func = int(_fcrr[:4], base=2)
        _rate = int(_fcrr[4:], base=2)
        _ttlv = self._read_unpack(1)
        _nonr = self._read_binary(4)
        _qsnn = int(_nonr[:30], base=2)

        if _func not in (0, 8):
            raise ProtocolError(f'{self.alias}: [OptNo {code}] invalid format')

        data = dict(
            type=_type,
            length=_size + 2,
            func=_QS_FUNC.get(_func),
            rate=40000 * (2**_rate) / 1000,
            ttl=None if _func else _rate,
            nounce=_qsnn,
        )

        return data
Example #16
0
    def read_http(self, length):
        """Read Hypertext Transfer Protocol (HTTP/2).

        Structure of HTTP/2 packet [RFC 7540]:
            +-----------------------------------------------+
            |                 Length (24)                   |
            +---------------+---------------+---------------+
            |   Type (8)    |   Flags (8)   |
            +-+-------------+---------------+-------------------------------+
            |R|                 Stream Identifier (31)                      |
            +=+=============================================================+
            |                   Frame Payload (0...)                      ...
            +---------------------------------------------------------------+

            Octets      Bits        Name                    Description
              0           0     http.length             Length
              3          24     http.type               Type
              4          32     http.flags              Flags
              5          40     -                       Reserved
              5          41     http.sid                Stream Identifier
              9          72     http.payload            Frame Payload

        """
        if length is None:
            length = len(self)

        if length < 9:
            raise ProtocolError(f'HTTP/2: invalid format', quiet=True)

        _tlen = self._read_unpack(3)
        _type = self._read_unpack(1)
        _flag = self._read_binary(1)
        _rsid = self._read_binary(4)

        if _tlen != length:
            raise ProtocolError(f'HTTP/2: [Type {_type}] invalid format',
                                quiet=True)

        if int(_rsid[0], base=2):
            raise ProtocolError(f'HTTP/2: [Type {_type}] invalid format',
                                quiet=True)

        http = dict(
            length=_tlen,
            type=_HTTP_TYPE.get(_type),
            sid=int(_rsid[1:], base=2),
            packet=self._read_packet(_tlen),
        )

        if http['type'] is None:
            raise ProtocolError(f'HTTP/2: [Type {_type}] invalid format',
                                quiet=True)

        if http['type'] in ('SETTINGS', 'PING') and http['sid'] != 0:
            raise ProtocolError(f'HTTP/2: [Type {_type}] invalid format',
                                quiet=True)

        _http = _HTTP_FUNC[_type](self, _tlen, _type, _flag)
        http.update(_http)

        return http
Example #17
0
    def _read_opt_mpl(self, code, *, desc):
        """Read IPv6-Opts ``MPL`` option.

        Structure of IPv6-Opts ``MPL`` option [:rfc:`7731`]:

        .. code:: text

             0                   1                   2                   3
             0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
                                            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                            |  Option Type  |  Opt Data Len |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            | S |M|V|  rsv  |   sequence    |      seed-id (optional)       |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

        Args:
            code (int): option type value

        Keyword Args:
            desc (str): option description

        Returns:
            DataType_Dest_Opt_MPL: parsed option data

        Raises:
            ProtocolError: If the option is malformed.

        """
        _type = self._read_opt_type(code)
        _size = self._read_unpack(1)
        if _size < 2:
            raise ProtocolError(f'{self.alias}: [OptNo {code}] invalid format')
        _smvr = self._read_binary(1)
        _seqn = self._read_unpack(1)

        opt = dict(
            desc=desc,
            type=_type,
            length=_size + 2,
            seed_len=_IPv6_Opts_SEED.get(int(_smvr[:2], base=2)),
            flags=dict(
                max=bool(int(_smvr[2], base=2)),
                verification=bool(int(_smvr[3], base=2)),
            ),
            seq=_seqn,
        )

        _kind = _smvr[:2]
        if _kind == '00':
            if _size != 2:
                raise ProtocolError(
                    f'{self.alias}: [OptNo {code}] invalid format')
        elif _kind == '01':
            if _size != 4:
                raise ProtocolError(
                    f'{self.alias}: [OptNo {code}] invalid format')
            opt['seed_id'] = self._read_unpack(2)
        elif _kind == '10':
            if _size != 10:
                raise ProtocolError(
                    f'{self.alias}: [OptNo {code}] invalid format')
            opt['seed_id'] = self._read_unpack(8)
        elif _kind == '11':
            if _size != 18:
                raise ProtocolError(
                    f'{self.alias}: [OptNo {code}] invalid format')
            opt['seed_id'] = self._read_unpack(16)
        else:
            opt['seed_id'] = self._read_unpack(_size - 2)

        _plen = _size - opt['seed_len']
        if _plen:
            self._read_fileng(_plen)

        return opt
Example #18
0
    def _read_http_data(self, size, kind, flag):
        """Read HTTP/2 DATA frames.

        Structure of HTTP/2 DATA frame [RFC 7540]:
            +-----------------------------------------------+
            |                 Length (24)                   |
            +---------------+---------------+---------------+
            |   Type (8)    |   Flags (8)   |
            +-+-------------+---------------+-------------------------------+
            |R|                 Stream Identifier (31)                      |
            +---------------+-----------------------------------------------+
            |Pad Length? (8)|
            +---------------+-----------------------------------------------+
            |                            Data (*)                         ...
            +---------------------------------------------------------------+
            |                           Padding (*)                       ...
            +---------------------------------------------------------------+

            Octets      Bits        Name                    Description
              0           0     http.length             Length
              3          24     http.type               Type (0)
              4          32     http.flags              Flags
              5          40     -                       Reserved
              5          41     http.sid                Stream Identifier
              9          72     http.pad_len            Pad Length (Optional)
              10         80     http.data               Data
              ?           ?     -                       Padding (Optional)

        """
        _plen = 0
        _flag = dict(
            END_STREAM=False,  # bit 0
            PADDED=False,  # bit 3
        )
        for index, bit in enumerate(flag):
            if index == 0 and bit:
                _flag['END_STREAM'] = True
            elif index == 3 and bit:
                _flag['PADDED'] = True
                _plen = self._read_unpack(1)
            elif bit:
                raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                    quiet=True)
            else:
                continue

        if _plen > size - 10:
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        if _flag['PADDED']:
            _dlen = size - _plen - 1
        else:
            _dlen = size - _plen
        if _dlen < 0:
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        _data = self._read_fileng(_dlen)

        padding = self._read_binary(_plen)
        if any((int(bit, base=2) for bit in padding)):
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        data = dict(
            flags=_flag,
            data=_data,
        )
        if _flag['PADDED']:
            data['ped_len'] = _plen

        return data
Example #19
0
    def _read_opt_qs(self, code, *, desc):
        """Read IPv6_Opts Quick Start option.

        Structure of IPv6_Opts Quick-Start option [RFC 4782]:
            * A Quick-Start Request.
                 0                   1                   2                   3
                 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                |   Option      |  Length=6     | Func. | Rate  |   QS TTL      |
                |               |               | 0000  |Request|               |
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                |                        QS Nonce                           | R |
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            * Report of Approved Rate.
                 0                   1                   2                   3
                 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                |   Option      |  Length=6     | Func. | Rate  |   Not Used    |
                |               |               | 1000  | Report|               |
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                |                        QS Nonce                           | R |
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

            Octets      Bits        Name                        Description
              0           0     ipv6_opts.qs.type           Option Type
              0           0     ipv6_opts.qs.type.value     Option Number
              0           0     ipv6_opts.qs.type.action    Action (00)
              0           2     ipv6_opts.qs.type.change    Change Flag (1)
              1           8     ipv6_opts.qs.length         Length of Option Data
              2          16     ipv6_opts.qs.func           Function (0/8)
              2          20     ipv6_opts.qs.rate           Rate Request / Report (in Kbps)
              3          24     ipv6_opts.qs.ttl            QS TTL / None
              4          32     ipv6_opts.qs.nounce         QS Nounce
              7          62     -                           Reserved

        """
        _type = self._read_opt_type(code)
        _size = self._read_unpack(1)
        if _size != 6:
            raise ProtocolError(f'{self.alias}: [Optno {code}] invalid format')

        _fcrr = self._read_binary(1)
        _func = int(_fcrr[:4], base=2)
        _rate = int(_fcrr[4:], base=2)
        _ttlv = self._read_unpack(1)
        _nonr = self._read_binary(4)
        _qsnn = int(_nonr[:30], base=2)

        if _func != 0 and _func != 8:
            raise ProtocolError(f'{self.alias}: [Optno {code}] invalid format')

        data = dict(
            type=_type,
            length=_size + 2,
            func=_QS_FUNC.get(_func),
            rate=40000 * (2**_rate) / 1000,
            ttl=None if _func else _rate,
            nounce=_qsnn,
        )

        return data
Example #20
0
    def _read_http_headers(self, size, kind, flag):
        """Read HTTP/2 HEADERS frames.

        Structure of HTTP/2 HEADERS frame [RFC 7540]:
            +-----------------------------------------------+
            |                 Length (24)                   |
            +---------------+---------------+---------------+
            |   Type (8)    |   Flags (8)   |
            +-+-------------+---------------+-------------------------------+
            |R|                 Stream Identifier (31)                      |
            +---------------+-----------------------------------------------+
            |Pad Length? (8)|
            +-+-------------+-----------------------------------------------+
            |E|                 Stream Dependency? (31)                     |
            +-+-------------+-----------------------------------------------+
            |  Weight? (8)  |
            +-+-------------+-----------------------------------------------+
            |                   Header Block Fragment (*)                 ...
            +---------------------------------------------------------------+
            |                           Padding (*)                       ...
            +---------------------------------------------------------------+

            Octets      Bits        Name                    Description
              0           0     http.length             Length
              3          24     http.type               Type (1)
              4          32     http.flags              Flags
              5          40     -                       Reserved
              5          41     http.sid                Stream Identifier
              9          72     http.pad_len            Pad Length (Optional)
              10         80     http.exclusive          Exclusive Flag
              10         81     http.deps               Stream Dependency (Optional)
              14        112     http.weight             Weight (Optional)
              15        120     http.frag               Header Block Fragment
              ?           ?     -                       Padding (Optional)

        """
        _plen = 0
        _elen = 0
        _flag = dict(
            END_STREAM=False,  # bit 0
            END_HEADERS=False,  # bit 2
            PADDED=False,  # bit 3
            PRIORITY=False,  # bit 5
        )
        for index, bit in enumerate(flag):
            if index == 0 and bit:
                _flag['END_STREAM'] = True
            elif index == 2 and bit:
                _flag['END_HEADERS'] = True
            elif index == 3 and bit:
                _flag['PADDED'] = True
                _plen = self._read_unpack(1)
            elif index == 5 and bit:
                _flag['PRIORITY'] = True
                _edep = self._read_binary(4)
                _wght = self._read_unpack(1)
                _elen = 5
            elif bit:
                raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                    quiet=True)
            else:
                continue

        if _flag['PADDED']:
            _dlen = size - _plen - _elen - 1
        else:
            _dlen = size - _plen - _elen
        if _dlen < 0:
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        _frag = self._read_fileng(_dlen) or None

        padding = self._read_binary(_plen)
        if any((int(bit, base=2) for bit in padding)):
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        data = dict(
            flags=_flag,
            frag=_frag,
        )
        if _flag['PADDED']:
            data['ped_len'] = _plen
        if _flag['PRIORITY']:
            data['exclusive'] = True if int(_edep[0], base=2) else False
            data['deps'] = int(_edep[1:], base=2)
            data['weight'] = _wght + 1

        return data
Example #21
0
    def _read_mode_qs(self, size, kind):
        """Read Quick Start option.

        Positional arguments:
            * size - int, length of option
            * kind - int, 25 (QS)

        Returns:
            * dict -- extracted Quick Start (QS) option

        Structure of Quick-Start (QS) option [RFC 4782]:
            * A Quick-Start Request.
                 0                   1                   2                   3
                 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                |   Option      |  Length=8     | Func. | Rate  |   QS TTL      |
                |               |               | 0000  |Request|               |
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                |                        QS Nonce                           | R |
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            * Report of Approved Rate.
                 0                   1                   2                   3
                 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                |   Option      |  Length=8     | Func. | Rate  |   Not Used    |
                |               |               | 1000  | Report|               |
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                |                        QS Nonce                           | R |
                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

            Octets      Bits        Name                    Description
              0           0     ip.qs.kind              Kind (25)
              0           0     ip.qs.type.copy         Copied Flag (0)
              0           1     ip.qs.type.class        Option Class (0)
              0           3     ip.qs.type.number       Option Number (25)
              1           8     ip.qs.length            Length (8)
              2          16     ip.qs.func              Function (0/8)
              2          20     ip.qs.rate              Rate Request / Report (in Kbps)
              3          24     ip.qs.ttl               QS TTL / None
              4          32     ip.qs.nounce            QS Nounce
              7          62     -                       Reserved (\x00\x00)

        """
        if size != 8:
            raise ProtocolError(f'{self.alias}: [Optno {kind}] invalid format')

        _type = self._read_opt_type(kind)
        _fcrr = self._read_binary(1)
        _func = int(_fcrr[:4], base=2)
        _rate = int(_fcrr[4:], base=2)
        _ttlv = self._read_unpack(1)
        _nonr = self._read_binary(4)
        _qsnn = int(_nonr[:30], base=2)

        if _func != 0 and _func != 8:
            raise ProtocolError(f'{self.alias}: [Optno {kind}] invalid format')

        data = dict(
            kind=kind,
            type=_type,
            length=size,
            func=QS_FUNC.get(_func),
            rate=40000 * (2 ** _rate) / 1000,
            ttl=None if _func else _rate,
            nounce=_qsnn,
        )

        return data
Example #22
0
    def _read_http_settings(self, size, kind, flag):
        """Read HTTP/2 SETTINGS frames.

        Structure of HTTP/2 SETTINGS frame [RFC 7540]:
            +-----------------------------------------------+
            |                 Length (24)                   |
            +---------------+---------------+---------------+
            |   Type (8)    |   Flags (8)   |
            +-+-------------+---------------+-------------------------------+
            |R|                 Stream Identifier (31)                      |
            +---------------------------------------------------------------+
            |       Identifier (16)         |
            +-------------------------------+-------------------------------+
            |                        Value (32)                             |
            +---------------------------------------------------------------+
            |                          ......                               |

            Octets      Bits        Name                    Description
              0           0     http.length             Length
              3          24     http.type               Type (2)
              4          32     http.flags              Flags
              5          40     -                       Reserved
              5          41     http.sid                Stream Identifier
              9          72     http.settings           Settings
              9          72     http.settings.id        Identifier
              10         80     http.settings.value     Value

        """
        if size % 5 != 0:
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        _flag = dict(
            ACK=False,  # bit 0
        )
        for index, bit in enumerate(flag):
            if index == 0 and bit:
                _flag['ACK'] = True
            elif bit:
                raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                    quiet=True)
            else:
                continue

        if _flag['ACK'] and size:
            raise ProtocolError(f'HTTP/2: [Type {kind}] invalid format',
                                quiet=True)

        _para = dict()
        counter = 0
        while counter < size:
            _stid = self._read_unpack(1)
            _pval = self._read_unpack(4)
            _pkey = _PARA_NAME.get(_stid, 'Unsigned')
            if _pkey in _para:
                if isinstance(_para[_pkey], tuple):
                    _para[_pkey] += (_pval, )
                else:
                    _para[_pkey] = (_para[_pkey], _pval)
            else:
                _para[_pkey] = _pval

        data = dict(flags=_flag, )
        data.update(_para)

        return data
Example #23
0
    def _read_mode_sec(self, size, kind):
        """Read options with security info.

        Positional arguments:
            size - int, length of option
            kind - int, 130 (SEC )/ 133 (ESEC)

        Returns:
            * dict -- extracted option with security info (E/SEC)

        Structure of these options:
            * [RFC 1108] Security (SEC)
                +------------+------------+------------+-------------//----------+
                |  10000010  |  XXXXXXXX  |  SSSSSSSS  |  AAAAAAA[1]    AAAAAAA0 |
                |            |            |            |         [0]             |
                +------------+------------+------------+-------------//----------+
                  TYPE = 130     LENGTH   CLASSIFICATION         PROTECTION
                                               LEVEL              AUTHORITY
                                                                    FLAGS
            * [RFC 1108] Extended Security (ESEC):
                +------------+------------+------------+-------//-------+
                |  10000101  |  000LLLLL  |  AAAAAAAA  |  add sec info  |
                +------------+------------+------------+-------//-------+
                 TYPE = 133      LENGTH     ADDITIONAL      ADDITIONAL
                                           SECURITY INFO     SECURITY
                                            FORMAT CODE        INFO

            Octets      Bits        Name                    Description
              0           0     ip.sec.kind             Kind (130)
              0           0     ip.sec.type.copy        Copied Flag (1)
              0           1     ip.sec.type.class       Option Class (0)
              0           3     ip.sec.type.number      Option Number (2)
              1           8     ip.sec.length           Length (≥3)
              2          16     ip.sec.level            Classification Level
              3          24     ip.sec.flags            Protection Authority Flags

        """
        if size < 3:
            raise ProtocolError(f'{self.alias}: [Optno {kind}] invalid format')

        _clvl = self._read_unpack(1)

        data = dict(
            kind=kind,
            type=self._read_opt_type(kind),
            length=size,
            level=_CLASSIFICATION_LEVEL.get(_clvl, _clvl),
        )

        if size > 3:
            _list = list()
            for counter in range(3, size):
                _flag = self._read_binary(1)
                if (counter < size - 1 and not int(_flag[7], base=2)) \
                        or (counter == size - 1 and int(_flag[7], base=2)):
                    raise ProtocolError(f'{self.alias}: [Optno {kind}] invalid format')

                _dict = dict()
                for (index, bit) in enumerate(_flag[:5]):
                    _auth = _PROTECTION_AUTHORITY.get(index)
                    _dict[_auth] = True if int(bit, base=2) else False
                _list.append(Info(_dict))
            data['flags'] = tuple(_list)

        return data
Example #24
0
    def read_ah(self, length, version, extension):
        """Read Authentication Header.

        Structure of AH header [:rfc:`4302`]::

             0                   1                   2                   3
             0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            | Next Header   |  Payload Len  |          RESERVED             |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |                 Security Parameters Index (SPI)               |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |                    Sequence Number Field                      |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |                                                               |
            +                Integrity Check Value-ICV (variable)           |
            |                                                               |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

        Args:
            length (int): packet length

        Returns:
            DataType_AH: Parsed packet data.

        """
        if length is None:
            length = len(self)

        _next = self._read_protos(1)
        _plen = self._read_unpack(1)
        _resv = self._read_fileng(2)
        _scpi = self._read_unpack(4)
        _dsnf = self._read_unpack(4)

        # ICV length & value
        _tlen = _plen * 4 - 2
        _vlen = _tlen - 12
        _chkv = self._read_fileng(_vlen)

        ah = dict(
            next=_next,
            length=_tlen,
            spi=_scpi,
            seq=_dsnf,
            icv=_chkv,
        )

        if version == 6:
            _plen = 8 - (_tlen % 8)
        elif version == 4:
            _plen = 4 - (_tlen % 4)
        else:
            raise VersionError(f'Unknown IP version {version}')

        if _plen:   # explicit padding in need
            padding = self._read_binary(_plen)
            if any((int(bit, base=2) for bit in padding)):
                raise ProtocolError(f'{self.alias}: invalid format')

        length -= ah['length']
        ah['packet'] = self._read_packet(header=ah['length'], payload=length)

        if extension:
            self._protos = None
            return ah
        return self._decode_next_layer(ah, _next, length)