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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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)