pypcapkit 1.3.3.post1__cp313-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pcapkit/__init__.py +126 -0
- pcapkit/__main__.py +138 -0
- pcapkit/all.py +136 -0
- pcapkit/const/__init__.py +81 -0
- pcapkit/const/arp/__init__.py +25 -0
- pcapkit/const/arp/hardware.py +181 -0
- pcapkit/const/arp/operation.py +131 -0
- pcapkit/const/ftp/__init__.py +25 -0
- pcapkit/const/ftp/command.py +309 -0
- pcapkit/const/ftp/return_code.py +304 -0
- pcapkit/const/hip/__init__.py +94 -0
- pcapkit/const/hip/certificate.py +77 -0
- pcapkit/const/hip/cipher.py +65 -0
- pcapkit/const/hip/di.py +59 -0
- pcapkit/const/hip/ecdsa_curve.py +59 -0
- pcapkit/const/hip/ecdsa_low_curve.py +56 -0
- pcapkit/const/hip/eddsa_curve.py +65 -0
- pcapkit/const/hip/esp_transform_suite.py +98 -0
- pcapkit/const/hip/group.py +86 -0
- pcapkit/const/hip/hi_algorithm.py +86 -0
- pcapkit/const/hip/hit_suite.py +68 -0
- pcapkit/const/hip/nat_traversal.py +62 -0
- pcapkit/const/hip/notify_message.py +200 -0
- pcapkit/const/hip/packet.py +89 -0
- pcapkit/const/hip/parameter.py +377 -0
- pcapkit/const/hip/registration.py +68 -0
- pcapkit/const/hip/registration_failure.py +84 -0
- pcapkit/const/hip/suite.py +71 -0
- pcapkit/const/hip/transport.py +59 -0
- pcapkit/const/http/__init__.py +39 -0
- pcapkit/const/http/error_code.py +95 -0
- pcapkit/const/http/frame.py +95 -0
- pcapkit/const/http/method.py +184 -0
- pcapkit/const/http/setting.py +96 -0
- pcapkit/const/http/status_code.py +294 -0
- pcapkit/const/ipv4/__init__.py +57 -0
- pcapkit/const/ipv4/classification_level.py +64 -0
- pcapkit/const/ipv4/option_class.py +55 -0
- pcapkit/const/ipv4/option_number.py +137 -0
- pcapkit/const/ipv4/protection_authority.py +63 -0
- pcapkit/const/ipv4/qs_function.py +51 -0
- pcapkit/const/ipv4/router_alert.py +251 -0
- pcapkit/const/ipv4/tos_del.py +51 -0
- pcapkit/const/ipv4/tos_ecn.py +55 -0
- pcapkit/const/ipv4/tos_pre.py +63 -0
- pcapkit/const/ipv4/tos_rel.py +51 -0
- pcapkit/const/ipv4/tos_thr.py +51 -0
- pcapkit/const/ipv4/ts_flag.py +53 -0
- pcapkit/const/ipv6/__init__.py +53 -0
- pcapkit/const/ipv6/extension_header.py +66 -0
- pcapkit/const/ipv6/option.py +137 -0
- pcapkit/const/ipv6/option_action.py +55 -0
- pcapkit/const/ipv6/qs_function.py +51 -0
- pcapkit/const/ipv6/router_alert.py +266 -0
- pcapkit/const/ipv6/routing.py +80 -0
- pcapkit/const/ipv6/seed_id.py +55 -0
- pcapkit/const/ipv6/smf_dpd_mode.py +51 -0
- pcapkit/const/ipv6/tagger_id.py +62 -0
- pcapkit/const/ipx/__init__.py +27 -0
- pcapkit/const/ipx/packet.py +72 -0
- pcapkit/const/ipx/socket.py +104 -0
- pcapkit/const/l2tp/__init__.py +21 -0
- pcapkit/const/l2tp/type.py +51 -0
- pcapkit/const/mh/__init__.py +204 -0
- pcapkit/const/mh/access_type.py +92 -0
- pcapkit/const/mh/ack_status_code.py +71 -0
- pcapkit/const/mh/ani_suboption.py +74 -0
- pcapkit/const/mh/auth_subtype.py +53 -0
- pcapkit/const/mh/binding_ack_flag.py +66 -0
- pcapkit/const/mh/binding_error.py +51 -0
- pcapkit/const/mh/binding_revocation.py +59 -0
- pcapkit/const/mh/binding_update_flag.py +81 -0
- pcapkit/const/mh/cga_extension.py +66 -0
- pcapkit/const/mh/cga_sec.py +57 -0
- pcapkit/const/mh/cga_type.py +68 -0
- pcapkit/const/mh/dhcp_support_mode.py +53 -0
- pcapkit/const/mh/dns_status_code.py +65 -0
- pcapkit/const/mh/dsmip6_tls_packet.py +62 -0
- pcapkit/const/mh/dsmipv6_home_address.py +74 -0
- pcapkit/const/mh/enumerating_algorithm.py +56 -0
- pcapkit/const/mh/fb_ack_status.py +62 -0
- pcapkit/const/mh/fb_action.py +71 -0
- pcapkit/const/mh/fb_indication_trigger.py +65 -0
- pcapkit/const/mh/fb_type.py +59 -0
- pcapkit/const/mh/flow_id_status.py +77 -0
- pcapkit/const/mh/flow_id_suboption.py +71 -0
- pcapkit/const/mh/handoff_type.py +71 -0
- pcapkit/const/mh/handover_ack_flag.py +54 -0
- pcapkit/const/mh/handover_ack_status.py +92 -0
- pcapkit/const/mh/handover_initiate_flag.py +57 -0
- pcapkit/const/mh/handover_initiate_status.py +62 -0
- pcapkit/const/mh/home_address_reply.py +71 -0
- pcapkit/const/mh/lla_code.py +63 -0
- pcapkit/const/mh/lma_mag_suboption.py +59 -0
- pcapkit/const/mh/mn_group_id.py +59 -0
- pcapkit/const/mh/mn_id_subtype.py +77 -0
- pcapkit/const/mh/operator_id.py +63 -0
- pcapkit/const/mh/option.py +260 -0
- pcapkit/const/mh/packet.py +119 -0
- pcapkit/const/mh/qos_attribute.py +89 -0
- pcapkit/const/mh/revocation_status_code.py +83 -0
- pcapkit/const/mh/revocation_trigger.py +86 -0
- pcapkit/const/mh/status_code.py +232 -0
- pcapkit/const/mh/traffic_selector.py +62 -0
- pcapkit/const/mh/upa_status.py +71 -0
- pcapkit/const/mh/upn_reason.py +80 -0
- pcapkit/const/ospf/__init__.py +27 -0
- pcapkit/const/ospf/authentication.py +65 -0
- pcapkit/const/ospf/packet.py +71 -0
- pcapkit/const/pcapng/__init__.py +51 -0
- pcapkit/const/pcapng/block_type.py +152 -0
- pcapkit/const/pcapng/filter_type.py +48 -0
- pcapkit/const/pcapng/hash_algorithm.py +59 -0
- pcapkit/const/pcapng/option_type.py +233 -0
- pcapkit/const/pcapng/record_type.py +57 -0
- pcapkit/const/pcapng/secrets_type.py +56 -0
- pcapkit/const/pcapng/verdict_type.py +53 -0
- pcapkit/const/reg/__init__.py +34 -0
- pcapkit/const/reg/apptype.py +32702 -0
- pcapkit/const/reg/ethertype.py +714 -0
- pcapkit/const/reg/linktype.py +902 -0
- pcapkit/const/reg/transtype.py +523 -0
- pcapkit/const/tcp/__init__.py +35 -0
- pcapkit/const/tcp/checksum.py +55 -0
- pcapkit/const/tcp/flags.py +73 -0
- pcapkit/const/tcp/mp_tcp_option.py +80 -0
- pcapkit/const/tcp/option.py +198 -0
- pcapkit/const/vlan/__init__.py +23 -0
- pcapkit/const/vlan/priority_level.py +71 -0
- pcapkit/corekit/__init__.py +59 -0
- pcapkit/corekit/fields/__init__.py +45 -0
- pcapkit/corekit/fields/collections.py +282 -0
- pcapkit/corekit/fields/field.py +269 -0
- pcapkit/corekit/fields/ipaddress.py +274 -0
- pcapkit/corekit/fields/misc.py +722 -0
- pcapkit/corekit/fields/numbers.py +375 -0
- pcapkit/corekit/fields/strings.py +245 -0
- pcapkit/corekit/infoclass.py +394 -0
- pcapkit/corekit/io.py +506 -0
- pcapkit/corekit/module.py +39 -0
- pcapkit/corekit/multidict.py +626 -0
- pcapkit/corekit/protochain.py +263 -0
- pcapkit/corekit/version.py +33 -0
- pcapkit/dumpkit/__init__.py +15 -0
- pcapkit/dumpkit/common.py +199 -0
- pcapkit/dumpkit/null.py +77 -0
- pcapkit/dumpkit/pcap.py +144 -0
- pcapkit/foundation/__init__.py +45 -0
- pcapkit/foundation/engines/__init__.py +36 -0
- pcapkit/foundation/engines/dpkt.py +230 -0
- pcapkit/foundation/engines/engine.py +194 -0
- pcapkit/foundation/engines/pcap.py +188 -0
- pcapkit/foundation/engines/pcapng.py +310 -0
- pcapkit/foundation/engines/pyshark.py +166 -0
- pcapkit/foundation/engines/scapy.py +161 -0
- pcapkit/foundation/extraction.py +915 -0
- pcapkit/foundation/reassembly/__init__.py +49 -0
- pcapkit/foundation/reassembly/data/__init__.py +48 -0
- pcapkit/foundation/reassembly/data/ip.py +117 -0
- pcapkit/foundation/reassembly/data/tcp.py +145 -0
- pcapkit/foundation/reassembly/ip.py +192 -0
- pcapkit/foundation/reassembly/ipv4.py +50 -0
- pcapkit/foundation/reassembly/ipv6.py +50 -0
- pcapkit/foundation/reassembly/reassembly.py +389 -0
- pcapkit/foundation/reassembly/tcp.py +249 -0
- pcapkit/foundation/registry/__init__.py +41 -0
- pcapkit/foundation/registry/foundation.py +327 -0
- pcapkit/foundation/registry/protocols.py +885 -0
- pcapkit/foundation/traceflow/__init__.py +44 -0
- pcapkit/foundation/traceflow/data/__init__.py +30 -0
- pcapkit/foundation/traceflow/data/tcp.py +105 -0
- pcapkit/foundation/traceflow/tcp.py +159 -0
- pcapkit/foundation/traceflow/traceflow.py +390 -0
- pcapkit/interface/__init__.py +22 -0
- pcapkit/interface/core.py +185 -0
- pcapkit/interface/misc.py +120 -0
- pcapkit/protocols/__init__.py +85 -0
- pcapkit/protocols/application/NotImplemented/bgp.py +0 -0
- pcapkit/protocols/application/NotImplemented/dhcp.py +0 -0
- pcapkit/protocols/application/NotImplemented/dhcpv6.py +0 -0
- pcapkit/protocols/application/NotImplemented/dns.py +0 -0
- pcapkit/protocols/application/NotImplemented/imap.py +0 -0
- pcapkit/protocols/application/NotImplemented/ldap.py +0 -0
- pcapkit/protocols/application/NotImplemented/mqtt.py +0 -0
- pcapkit/protocols/application/NotImplemented/nntp.py +0 -0
- pcapkit/protocols/application/NotImplemented/ntp.py +0 -0
- pcapkit/protocols/application/NotImplemented/onc_rpc.py +0 -0
- pcapkit/protocols/application/NotImplemented/pop.py +0 -0
- pcapkit/protocols/application/NotImplemented/rip.py +0 -0
- pcapkit/protocols/application/NotImplemented/rtp.py +0 -0
- pcapkit/protocols/application/NotImplemented/sip.py +0 -0
- pcapkit/protocols/application/NotImplemented/smtp.py +0 -0
- pcapkit/protocols/application/NotImplemented/snmp.py +0 -0
- pcapkit/protocols/application/NotImplemented/ssh.py +0 -0
- pcapkit/protocols/application/NotImplemented/telnet.py +0 -0
- pcapkit/protocols/application/NotImplemented/tls.py +0 -0
- pcapkit/protocols/application/NotImplemented/xmpp.py +0 -0
- pcapkit/protocols/application/__init__.py +34 -0
- pcapkit/protocols/application/application.py +114 -0
- pcapkit/protocols/application/ftp.py +206 -0
- pcapkit/protocols/application/http.py +176 -0
- pcapkit/protocols/application/httpv1.py +320 -0
- pcapkit/protocols/application/httpv2.py +1255 -0
- pcapkit/protocols/data/__init__.py +192 -0
- pcapkit/protocols/data/application/__init__.py +57 -0
- pcapkit/protocols/data/application/ftp.py +59 -0
- pcapkit/protocols/data/application/httpv1.py +79 -0
- pcapkit/protocols/data/application/httpv2.py +293 -0
- pcapkit/protocols/data/data.py +25 -0
- pcapkit/protocols/data/internet/__init__.py +298 -0
- pcapkit/protocols/data/internet/ah.py +31 -0
- pcapkit/protocols/data/internet/hip.py +804 -0
- pcapkit/protocols/data/internet/hopopt.py +351 -0
- pcapkit/protocols/data/internet/ipv4.py +369 -0
- pcapkit/protocols/data/internet/ipv6.py +67 -0
- pcapkit/protocols/data/internet/ipv6_frag.py +29 -0
- pcapkit/protocols/data/internet/ipv6_opts.py +368 -0
- pcapkit/protocols/data/internet/ipv6_route.py +86 -0
- pcapkit/protocols/data/internet/ipx.py +56 -0
- pcapkit/protocols/data/internet/mh.py +509 -0
- pcapkit/protocols/data/link/__init__.py +33 -0
- pcapkit/protocols/data/link/arp.py +74 -0
- pcapkit/protocols/data/link/ethernet.py +28 -0
- pcapkit/protocols/data/link/l2tp.py +63 -0
- pcapkit/protocols/data/link/ospf.py +58 -0
- pcapkit/protocols/data/link/vlan.py +42 -0
- pcapkit/protocols/data/misc/__init__.py +109 -0
- pcapkit/protocols/data/misc/null.py +18 -0
- pcapkit/protocols/data/misc/pcap/__init__.py +18 -0
- pcapkit/protocols/data/misc/pcap/frame.py +56 -0
- pcapkit/protocols/data/misc/pcap/header.py +53 -0
- pcapkit/protocols/data/misc/pcapng.py +925 -0
- pcapkit/protocols/data/misc/raw.py +25 -0
- pcapkit/protocols/data/protocol.py +32 -0
- pcapkit/protocols/data/transport/__init__.py +71 -0
- pcapkit/protocols/data/transport/tcp.py +555 -0
- pcapkit/protocols/data/transport/udp.py +29 -0
- pcapkit/protocols/internet/NotImplemented/ecn.py +0 -0
- pcapkit/protocols/internet/NotImplemented/esp.py +97 -0
- pcapkit/protocols/internet/NotImplemented/icmp.py +0 -0
- pcapkit/protocols/internet/NotImplemented/icmpv6.py +0 -0
- pcapkit/protocols/internet/NotImplemented/igmp.py +0 -0
- pcapkit/protocols/internet/NotImplemented/shim6.py +0 -0
- pcapkit/protocols/internet/__init__.py +43 -0
- pcapkit/protocols/internet/ah.py +275 -0
- pcapkit/protocols/internet/hip.py +4727 -0
- pcapkit/protocols/internet/hopopt.py +1879 -0
- pcapkit/protocols/internet/internet.py +240 -0
- pcapkit/protocols/internet/ip.py +51 -0
- pcapkit/protocols/internet/ipsec.py +50 -0
- pcapkit/protocols/internet/ipv4.py +1782 -0
- pcapkit/protocols/internet/ipv6.py +361 -0
- pcapkit/protocols/internet/ipv6_frag.py +258 -0
- pcapkit/protocols/internet/ipv6_opts.py +1890 -0
- pcapkit/protocols/internet/ipv6_route.py +710 -0
- pcapkit/protocols/internet/ipx.py +230 -0
- pcapkit/protocols/internet/mh.py +2764 -0
- pcapkit/protocols/link/NotImplemented/dsl.py +0 -0
- pcapkit/protocols/link/NotImplemented/eapol.py +1 -0
- pcapkit/protocols/link/NotImplemented/fddi.py +0 -0
- pcapkit/protocols/link/NotImplemented/isdn.py +0 -0
- pcapkit/protocols/link/NotImplemented/ndp.py +0 -0
- pcapkit/protocols/link/NotImplemented/ppp.py +0 -0
- pcapkit/protocols/link/__init__.py +35 -0
- pcapkit/protocols/link/arp.py +421 -0
- pcapkit/protocols/link/ethernet.py +248 -0
- pcapkit/protocols/link/l2tp.py +267 -0
- pcapkit/protocols/link/link.py +140 -0
- pcapkit/protocols/link/ospf.py +342 -0
- pcapkit/protocols/link/rarp.py +82 -0
- pcapkit/protocols/link/vlan.py +225 -0
- pcapkit/protocols/misc/__init__.py +37 -0
- pcapkit/protocols/misc/null.py +129 -0
- pcapkit/protocols/misc/pcap/__init__.py +17 -0
- pcapkit/protocols/misc/pcap/frame.py +478 -0
- pcapkit/protocols/misc/pcap/header.py +358 -0
- pcapkit/protocols/misc/pcapng.py +5520 -0
- pcapkit/protocols/misc/raw.py +180 -0
- pcapkit/protocols/protocol.py +1216 -0
- pcapkit/protocols/schema/__init__.py +140 -0
- pcapkit/protocols/schema/application/__init__.py +40 -0
- pcapkit/protocols/schema/application/ftp.py +21 -0
- pcapkit/protocols/schema/application/httpv1.py +21 -0
- pcapkit/protocols/schema/application/httpv2.py +384 -0
- pcapkit/protocols/schema/internet/__init__.py +294 -0
- pcapkit/protocols/schema/internet/ah.py +40 -0
- pcapkit/protocols/schema/internet/hip.py +1184 -0
- pcapkit/protocols/schema/internet/hopopt.py +679 -0
- pcapkit/protocols/schema/internet/ipv4.py +576 -0
- pcapkit/protocols/schema/internet/ipv6.py +63 -0
- pcapkit/protocols/schema/internet/ipv6_frag.py +48 -0
- pcapkit/protocols/schema/internet/ipv6_opts.py +680 -0
- pcapkit/protocols/schema/internet/ipv6_route.py +198 -0
- pcapkit/protocols/schema/internet/ipx.py +40 -0
- pcapkit/protocols/schema/internet/mh.py +718 -0
- pcapkit/protocols/schema/link/__init__.py +19 -0
- pcapkit/protocols/schema/link/arp.py +39 -0
- pcapkit/protocols/schema/link/ethernet.py +51 -0
- pcapkit/protocols/schema/link/l2tp.py +88 -0
- pcapkit/protocols/schema/link/ospf.py +90 -0
- pcapkit/protocols/schema/link/vlan.py +69 -0
- pcapkit/protocols/schema/misc/__init__.py +108 -0
- pcapkit/protocols/schema/misc/null.py +18 -0
- pcapkit/protocols/schema/misc/pcap/__init__.py +10 -0
- pcapkit/protocols/schema/misc/pcap/frame.py +51 -0
- pcapkit/protocols/schema/misc/pcap/header.py +63 -0
- pcapkit/protocols/schema/misc/pcapng.py +1689 -0
- pcapkit/protocols/schema/misc/raw.py +24 -0
- pcapkit/protocols/schema/schema.py +809 -0
- pcapkit/protocols/schema/transport/__init__.py +69 -0
- pcapkit/protocols/schema/transport/tcp.py +928 -0
- pcapkit/protocols/schema/transport/udp.py +90 -0
- pcapkit/protocols/transport/NotImplemented/dccp.py +0 -0
- pcapkit/protocols/transport/NotImplemented/rsvp.py +0 -0
- pcapkit/protocols/transport/NotImplemented/sctp.py +0 -0
- pcapkit/protocols/transport/__init__.py +27 -0
- pcapkit/protocols/transport/tcp.py +3025 -0
- pcapkit/protocols/transport/transport.py +158 -0
- pcapkit/protocols/transport/udp.py +214 -0
- pcapkit/py.typed +0 -0
- pcapkit/toolkit/__init__.py +57 -0
- pcapkit/toolkit/dpkt.py +306 -0
- pcapkit/toolkit/pcap.py +212 -0
- pcapkit/toolkit/pcapng.py +251 -0
- pcapkit/toolkit/pyshark.py +99 -0
- pcapkit/toolkit/scapy.py +297 -0
- pcapkit/utilities/__init__.py +20 -0
- pcapkit/utilities/compat.py +196 -0
- pcapkit/utilities/decorators.py +192 -0
- pcapkit/utilities/exceptions.py +365 -0
- pcapkit/utilities/logging.py +55 -0
- pcapkit/utilities/warnings.py +185 -0
- pcapkit/vendor/__init__.py +105 -0
- pcapkit/vendor/__main__.py +92 -0
- pcapkit/vendor/arp/__init__.py +27 -0
- pcapkit/vendor/arp/hardware.py +29 -0
- pcapkit/vendor/arp/operation.py +29 -0
- pcapkit/vendor/default.py +474 -0
- pcapkit/vendor/ftp/__init__.py +27 -0
- pcapkit/vendor/ftp/command.py +244 -0
- pcapkit/vendor/ftp/return_code.py +256 -0
- pcapkit/vendor/hip/__init__.py +94 -0
- pcapkit/vendor/hip/certificate.py +29 -0
- pcapkit/vendor/hip/cipher.py +29 -0
- pcapkit/vendor/hip/di.py +29 -0
- pcapkit/vendor/hip/ecdsa_curve.py +29 -0
- pcapkit/vendor/hip/ecdsa_low_curve.py +29 -0
- pcapkit/vendor/hip/eddsa_curve.py +85 -0
- pcapkit/vendor/hip/esp_transform_suite.py +29 -0
- pcapkit/vendor/hip/group.py +87 -0
- pcapkit/vendor/hip/hi_algorithm.py +29 -0
- pcapkit/vendor/hip/hit_suite.py +29 -0
- pcapkit/vendor/hip/nat_traversal.py +29 -0
- pcapkit/vendor/hip/notify_message.py +29 -0
- pcapkit/vendor/hip/packet.py +88 -0
- pcapkit/vendor/hip/parameter.py +88 -0
- pcapkit/vendor/hip/registration.py +29 -0
- pcapkit/vendor/hip/registration_failure.py +29 -0
- pcapkit/vendor/hip/suite.py +29 -0
- pcapkit/vendor/hip/transport.py +29 -0
- pcapkit/vendor/http/__init__.py +39 -0
- pcapkit/vendor/http/error_code.py +95 -0
- pcapkit/vendor/http/frame.py +91 -0
- pcapkit/vendor/http/method.py +167 -0
- pcapkit/vendor/http/setting.py +93 -0
- pcapkit/vendor/http/status_code.py +185 -0
- pcapkit/vendor/ipv4/__init__.py +57 -0
- pcapkit/vendor/ipv4/classification_level.py +91 -0
- pcapkit/vendor/ipv4/option_class.py +80 -0
- pcapkit/vendor/ipv4/option_number.py +105 -0
- pcapkit/vendor/ipv4/protection_authority.py +84 -0
- pcapkit/vendor/ipv4/qs_function.py +78 -0
- pcapkit/vendor/ipv4/router_alert.py +93 -0
- pcapkit/vendor/ipv4/tos_del.py +78 -0
- pcapkit/vendor/ipv4/tos_ecn.py +95 -0
- pcapkit/vendor/ipv4/tos_pre.py +84 -0
- pcapkit/vendor/ipv4/tos_rel.py +78 -0
- pcapkit/vendor/ipv4/tos_thr.py +77 -0
- pcapkit/vendor/ipv4/ts_flag.py +79 -0
- pcapkit/vendor/ipv6/__init__.py +53 -0
- pcapkit/vendor/ipv6/extension_header.py +171 -0
- pcapkit/vendor/ipv6/option.py +104 -0
- pcapkit/vendor/ipv6/option_action.py +90 -0
- pcapkit/vendor/ipv6/qs_function.py +78 -0
- pcapkit/vendor/ipv6/router_alert.py +93 -0
- pcapkit/vendor/ipv6/routing.py +87 -0
- pcapkit/vendor/ipv6/seed_id.py +81 -0
- pcapkit/vendor/ipv6/smf_dpd_mode.py +78 -0
- pcapkit/vendor/ipv6/tagger_id.py +81 -0
- pcapkit/vendor/ipx/__init__.py +37 -0
- pcapkit/vendor/ipx/packet.py +123 -0
- pcapkit/vendor/ipx/socket.py +125 -0
- pcapkit/vendor/l2tp/__init__.py +21 -0
- pcapkit/vendor/l2tp/type.py +78 -0
- pcapkit/vendor/mh/__init__.py +204 -0
- pcapkit/vendor/mh/access_type.py +87 -0
- pcapkit/vendor/mh/ack_status_code.py +88 -0
- pcapkit/vendor/mh/ani_suboption.py +88 -0
- pcapkit/vendor/mh/auth_subtype.py +83 -0
- pcapkit/vendor/mh/binding_ack_flag.py +148 -0
- pcapkit/vendor/mh/binding_error.py +78 -0
- pcapkit/vendor/mh/binding_revocation.py +87 -0
- pcapkit/vendor/mh/binding_update_flag.py +147 -0
- pcapkit/vendor/mh/cga_extension.py +91 -0
- pcapkit/vendor/mh/cga_sec.py +91 -0
- pcapkit/vendor/mh/cga_type.py +74 -0
- pcapkit/vendor/mh/dhcp_support_mode.py +77 -0
- pcapkit/vendor/mh/dns_status_code.py +87 -0
- pcapkit/vendor/mh/dsmip6_tls_packet.py +87 -0
- pcapkit/vendor/mh/dsmipv6_home_address.py +87 -0
- pcapkit/vendor/mh/enumerating_algorithm.py +82 -0
- pcapkit/vendor/mh/fb_ack_status.py +87 -0
- pcapkit/vendor/mh/fb_action.py +88 -0
- pcapkit/vendor/mh/fb_indication_trigger.py +87 -0
- pcapkit/vendor/mh/fb_type.py +88 -0
- pcapkit/vendor/mh/flow_id_status.py +87 -0
- pcapkit/vendor/mh/flow_id_suboption.py +87 -0
- pcapkit/vendor/mh/handoff_type.py +87 -0
- pcapkit/vendor/mh/handover_ack_flag.py +143 -0
- pcapkit/vendor/mh/handover_ack_status.py +87 -0
- pcapkit/vendor/mh/handover_initiate_flag.py +143 -0
- pcapkit/vendor/mh/handover_initiate_status.py +87 -0
- pcapkit/vendor/mh/home_address_reply.py +87 -0
- pcapkit/vendor/mh/lla_code.py +97 -0
- pcapkit/vendor/mh/lma_mag_suboption.py +88 -0
- pcapkit/vendor/mh/mn_group_id.py +87 -0
- pcapkit/vendor/mh/mn_id_subtype.py +87 -0
- pcapkit/vendor/mh/operator_id.py +87 -0
- pcapkit/vendor/mh/option.py +83 -0
- pcapkit/vendor/mh/packet.py +82 -0
- pcapkit/vendor/mh/qos_attribute.py +87 -0
- pcapkit/vendor/mh/revocation_status_code.py +87 -0
- pcapkit/vendor/mh/revocation_trigger.py +87 -0
- pcapkit/vendor/mh/status_code.py +91 -0
- pcapkit/vendor/mh/traffic_selector.py +87 -0
- pcapkit/vendor/mh/upa_status.py +87 -0
- pcapkit/vendor/mh/upn_reason.py +87 -0
- pcapkit/vendor/ospf/__init__.py +27 -0
- pcapkit/vendor/ospf/authentication.py +29 -0
- pcapkit/vendor/ospf/packet.py +29 -0
- pcapkit/vendor/pcapng/__init__.py +51 -0
- pcapkit/vendor/pcapng/block_type.py +94 -0
- pcapkit/vendor/pcapng/filter_type.py +77 -0
- pcapkit/vendor/pcapng/hash_algorithm.py +82 -0
- pcapkit/vendor/pcapng/option_type.py +287 -0
- pcapkit/vendor/pcapng/record_type.py +81 -0
- pcapkit/vendor/pcapng/secrets_type.py +81 -0
- pcapkit/vendor/pcapng/verdict_type.py +79 -0
- pcapkit/vendor/reg/__init__.py +34 -0
- pcapkit/vendor/reg/apptype.py +338 -0
- pcapkit/vendor/reg/ethertype.py +121 -0
- pcapkit/vendor/reg/linktype.py +110 -0
- pcapkit/vendor/reg/transtype.py +111 -0
- pcapkit/vendor/tcp/__init__.py +35 -0
- pcapkit/vendor/tcp/checksum.py +80 -0
- pcapkit/vendor/tcp/flags.py +149 -0
- pcapkit/vendor/tcp/mp_tcp_option.py +90 -0
- pcapkit/vendor/tcp/option.py +103 -0
- pcapkit/vendor/vlan/__init__.py +23 -0
- pcapkit/vendor/vlan/priority_level.py +97 -0
- pypcapkit-1.3.3.post1.dist-info/LICENSE +29 -0
- pypcapkit-1.3.3.post1.dist-info/METADATA +236 -0
- pypcapkit-1.3.3.post1.dist-info/RECORD +466 -0
- pypcapkit-1.3.3.post1.dist-info/WHEEL +5 -0
- pypcapkit-1.3.3.post1.dist-info/entry_points.txt +3 -0
- pypcapkit-1.3.3.post1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1216 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# mypy: disable-error-code=dict-item
|
3
|
+
"""Root Protocol
|
4
|
+
===================
|
5
|
+
|
6
|
+
.. module:: pcapkit.protocols.protocol
|
7
|
+
|
8
|
+
:mod:`pcapkit.protocols.protocol` contains
|
9
|
+
:class:`~pcapkit.protocols.protocol.Protocol` only, which is
|
10
|
+
an abstract base class for all protocol family, with pre-defined
|
11
|
+
utility arguments and methods of specified protocols.
|
12
|
+
|
13
|
+
"""
|
14
|
+
import abc
|
15
|
+
import collections
|
16
|
+
import contextlib
|
17
|
+
import enum
|
18
|
+
import functools
|
19
|
+
import io
|
20
|
+
import os
|
21
|
+
import shutil
|
22
|
+
import string
|
23
|
+
import struct
|
24
|
+
import textwrap
|
25
|
+
import urllib.parse
|
26
|
+
from typing import TYPE_CHECKING, Any, Generic, Optional, Type, TypeVar, cast, overload
|
27
|
+
|
28
|
+
import aenum
|
29
|
+
import chardet
|
30
|
+
|
31
|
+
from pcapkit.corekit.module import ModuleDescriptor
|
32
|
+
from pcapkit.corekit.protochain import ProtoChain
|
33
|
+
from pcapkit.protocols import data as data_module
|
34
|
+
from pcapkit.protocols import schema as schema_module
|
35
|
+
from pcapkit.protocols.data.data import Data
|
36
|
+
from pcapkit.protocols.data.misc.raw import Raw as Data_Raw
|
37
|
+
from pcapkit.protocols.data.protocol import Packet as Data_Packet
|
38
|
+
from pcapkit.protocols.schema.misc.raw import Raw as Schema_Raw
|
39
|
+
from pcapkit.protocols.schema.schema import Schema
|
40
|
+
from pcapkit.utilities.compat import cached_property
|
41
|
+
from pcapkit.utilities.decorators import beholder, seekset
|
42
|
+
from pcapkit.utilities.exceptions import (ProtocolNotFound, ProtocolNotImplemented, RegistryError,
|
43
|
+
StructError, UnsupportedCall)
|
44
|
+
from pcapkit.utilities.warnings import RegistryWarning, warn
|
45
|
+
|
46
|
+
if TYPE_CHECKING:
|
47
|
+
from enum import IntEnum as StdlibEnum
|
48
|
+
from typing import IO, Any, DefaultDict, Optional, Type
|
49
|
+
|
50
|
+
from aenum import IntEnum as AenumEnum
|
51
|
+
from typing_extensions import Literal, Self
|
52
|
+
|
53
|
+
__all__ = ['ProtocolBase']
|
54
|
+
|
55
|
+
_PT = TypeVar('_PT', bound='Data')
|
56
|
+
_ST = TypeVar('_ST', bound='Schema')
|
57
|
+
|
58
|
+
# readable characters' order list
|
59
|
+
readable = [ord(char) for char in filter(lambda char: not char.isspace(), string.printable)]
|
60
|
+
|
61
|
+
|
62
|
+
class ProtocolMeta(abc.ABCMeta):
|
63
|
+
"""Meta class to add dynamic support to :class:`Protocol`.
|
64
|
+
|
65
|
+
This meta class is used to generate necessary attributes for the
|
66
|
+
:class:`Protocol` class. It can be useful to reduce unnecessary
|
67
|
+
registry calls and simplify the customisation process.
|
68
|
+
|
69
|
+
"""
|
70
|
+
|
71
|
+
|
72
|
+
class ProtocolBase(Generic[_PT, _ST], metaclass=ProtocolMeta):
|
73
|
+
"""Abstract base class for all protocol family.
|
74
|
+
|
75
|
+
Note:
|
76
|
+
This class is for internal use only. For customisation, please use
|
77
|
+
:class:`Protocol` instead.
|
78
|
+
|
79
|
+
"""
|
80
|
+
|
81
|
+
if TYPE_CHECKING:
|
82
|
+
#: Parsed packet data.
|
83
|
+
_info: '_PT'
|
84
|
+
#: Raw packet data.
|
85
|
+
_data: 'bytes'
|
86
|
+
#: Source packet stream.
|
87
|
+
_file: 'IO[bytes]'
|
88
|
+
#: Next layer protocol instance.
|
89
|
+
_next: 'ProtocolBase'
|
90
|
+
#: Protocol chain instance.
|
91
|
+
_protos: 'ProtoChain'
|
92
|
+
|
93
|
+
# Internal data storage for cached properties.
|
94
|
+
__cached__: 'dict[str, Any]'
|
95
|
+
#: Protocol packet data definition.
|
96
|
+
__data__: 'Type[_PT]'
|
97
|
+
#: Protocol header schema definition.
|
98
|
+
__schema__: 'Type[_ST]'
|
99
|
+
#: Protocol header schema instance.
|
100
|
+
__header__: '_ST'
|
101
|
+
|
102
|
+
##########################################################################
|
103
|
+
# Defaults.
|
104
|
+
##########################################################################
|
105
|
+
|
106
|
+
#: Layer of protocol, can be one of ``Link``, ``Internet``, ``Transport``
|
107
|
+
#: and ``Application``. For example, the layer of
|
108
|
+
#: :class:`~pcapkit.protocols.link.ethernet.Ethernet` is ``Link``. However,
|
109
|
+
#: certain protocols are not in any layer, such as
|
110
|
+
#: :class:`~pcapkit.protocols.misc.raw.Raw`, and thus its layer is :obj:`None`.
|
111
|
+
__layer__: 'Optional[Literal["Link", "Internet", "Transport", "Application"]]' = None
|
112
|
+
|
113
|
+
#: Protocol index mapping for decoding next layer, c.f.
|
114
|
+
#: :meth:`self._decode_next_layer <pcapkit.protocols.protocol.Protocol._decode_next_layer>`
|
115
|
+
#: & :meth:`self._import_next_layer <pcapkit.protocols.protocol.Protocol._import_next_layer>`.
|
116
|
+
#: The values should be a tuple representing the module name and class name,
|
117
|
+
#: or a :class:`Protocol` subclass.
|
118
|
+
__proto__: 'DefaultDict[int, ModuleDescriptor[ProtocolBase] | Type[ProtocolBase]]' = collections.defaultdict(
|
119
|
+
lambda: ModuleDescriptor('pcapkit.protocols.misc.raw', 'Raw'),
|
120
|
+
)
|
121
|
+
|
122
|
+
##########################################################################
|
123
|
+
# Properties.
|
124
|
+
##########################################################################
|
125
|
+
|
126
|
+
# name of current protocol
|
127
|
+
@property
|
128
|
+
@abc.abstractmethod
|
129
|
+
def name(self) -> 'str':
|
130
|
+
"""Name of current protocol."""
|
131
|
+
|
132
|
+
# acronym of current protocol
|
133
|
+
@property
|
134
|
+
def alias(self) -> 'str':
|
135
|
+
"""Acronym of current protocol."""
|
136
|
+
return self.__class__.__name__
|
137
|
+
|
138
|
+
# key name for the info dict
|
139
|
+
@property
|
140
|
+
def info_name(self) -> 'str':
|
141
|
+
"""Key name of the :attr:`info` dict."""
|
142
|
+
return self.__class__.__name__.lower()
|
143
|
+
|
144
|
+
# info dict of current instance
|
145
|
+
@property
|
146
|
+
def info(self) -> '_PT':
|
147
|
+
"""Info dict of current instance."""
|
148
|
+
return self._info
|
149
|
+
|
150
|
+
# binary packet data if current instance
|
151
|
+
@property
|
152
|
+
def data(self) -> 'bytes':
|
153
|
+
"""Binary packet data of current instance."""
|
154
|
+
return self._data
|
155
|
+
|
156
|
+
# header length of current protocol
|
157
|
+
@property
|
158
|
+
@abc.abstractmethod
|
159
|
+
def length(self) -> 'int':
|
160
|
+
"""Header length of current protocol."""
|
161
|
+
|
162
|
+
# payload of current instance
|
163
|
+
@property
|
164
|
+
def payload(self) -> 'ProtocolBase':
|
165
|
+
"""Payload of current instance."""
|
166
|
+
return self._next
|
167
|
+
|
168
|
+
# name of next layer protocol
|
169
|
+
@property
|
170
|
+
def protocol(self) -> 'Optional[str]':
|
171
|
+
"""Name of next layer protocol (if any)."""
|
172
|
+
with contextlib.suppress(IndexError):
|
173
|
+
return self._protos[0]
|
174
|
+
return None
|
175
|
+
|
176
|
+
# protocol chain of current instance
|
177
|
+
@property
|
178
|
+
def protochain(self) -> 'ProtoChain':
|
179
|
+
"""Protocol chain of current instance."""
|
180
|
+
return self._protos
|
181
|
+
|
182
|
+
# packet data
|
183
|
+
@cached_property
|
184
|
+
def packet(self) -> 'Data_Packet':
|
185
|
+
"""Data_Packet data of the protocol."""
|
186
|
+
try:
|
187
|
+
return self._read_packet(header=self.length)
|
188
|
+
except UnsupportedCall:
|
189
|
+
return Data_Packet(
|
190
|
+
header=b'',
|
191
|
+
payload=self._read_packet(),
|
192
|
+
)
|
193
|
+
|
194
|
+
# schema data
|
195
|
+
@cached_property
|
196
|
+
def schema(self) -> '_ST':
|
197
|
+
"""Schema data of the protocol."""
|
198
|
+
return self.__header__
|
199
|
+
|
200
|
+
##########################################################################
|
201
|
+
# Methods.
|
202
|
+
##########################################################################
|
203
|
+
|
204
|
+
@classmethod
|
205
|
+
def id(cls) -> 'tuple[str, ...]':
|
206
|
+
"""Index ID of the protocol.
|
207
|
+
|
208
|
+
Returns:
|
209
|
+
By default, it returns the name of the protocol. In certain cases,
|
210
|
+
the method may return multiple values.
|
211
|
+
|
212
|
+
See Also:
|
213
|
+
:meth:`pcapkit.protocols.protocol.Protocol.__getitem__`
|
214
|
+
|
215
|
+
"""
|
216
|
+
return (cls.__name__,)
|
217
|
+
|
218
|
+
@abc.abstractmethod
|
219
|
+
def read(self, length: 'Optional[int]' = None, **kwargs: 'Any') -> '_PT':
|
220
|
+
"""Read (parse) packet data.
|
221
|
+
|
222
|
+
Args:
|
223
|
+
length: Length of packet data.
|
224
|
+
**kwargs: Arbitrary keyword arguments.
|
225
|
+
|
226
|
+
Returns:
|
227
|
+
Parsed packet data.
|
228
|
+
|
229
|
+
"""
|
230
|
+
|
231
|
+
@abc.abstractmethod
|
232
|
+
def make(self, **kwargs: 'Any') -> '_ST':
|
233
|
+
"""Make (construct) packet data.
|
234
|
+
|
235
|
+
Args:
|
236
|
+
**kwargs: Arbitrary keyword arguments.
|
237
|
+
|
238
|
+
Returns:
|
239
|
+
Curated protocol schema data.
|
240
|
+
|
241
|
+
"""
|
242
|
+
|
243
|
+
def pack(self, **kwargs: 'Any') -> 'bytes':
|
244
|
+
"""Pack (construct) packet data.
|
245
|
+
|
246
|
+
Args:
|
247
|
+
**kwargs: Arbitrary keyword arguments.
|
248
|
+
|
249
|
+
Returns:
|
250
|
+
Constructed packet data.
|
251
|
+
|
252
|
+
Notes:
|
253
|
+
We used a special keyword argument ``__packet__`` to pass the
|
254
|
+
global packet data to underlying methods. This is useful when
|
255
|
+
the packet data is not available in the current instance.
|
256
|
+
|
257
|
+
"""
|
258
|
+
self.__header__ = self.make(**kwargs)
|
259
|
+
packet = kwargs.get('__packet__', {}) # packet data
|
260
|
+
return self.__header__.pack(packet)
|
261
|
+
|
262
|
+
def unpack(self, length: 'Optional[int]' = None, **kwargs: 'Any') -> '_PT':
|
263
|
+
"""Unpack (parse) packet data.
|
264
|
+
|
265
|
+
Args:
|
266
|
+
length: Length of packet data.
|
267
|
+
**kwargs: Arbitrary keyword arguments.
|
268
|
+
|
269
|
+
Returns:
|
270
|
+
Parsed packet data.
|
271
|
+
|
272
|
+
Notes:
|
273
|
+
We used a special keyword argument ``__packet__`` to pass the
|
274
|
+
global packet data to underlying methods. This is useful when
|
275
|
+
the packet data is not available in the current instance.
|
276
|
+
|
277
|
+
"""
|
278
|
+
if cast('Optional[_ST]', self.__header__) is None:
|
279
|
+
packet = kwargs.get('__packet__', {}) # packet data
|
280
|
+
self.__header__ = cast('_ST', self.__schema__.unpack(self._file, length, packet)) # type: ignore[call-arg,misc]
|
281
|
+
return self.read(length, **kwargs)
|
282
|
+
|
283
|
+
@staticmethod
|
284
|
+
def decode(byte: bytes, *, encoding: 'Optional[str]' = None,
|
285
|
+
errors: 'Literal["strict", "ignore", "replace"]' = 'strict') -> 'str':
|
286
|
+
"""Decode :obj:`bytes` into :obj:`str`.
|
287
|
+
|
288
|
+
Should decoding failed using ``encoding``, the method will try again decoding
|
289
|
+
the :obj:`bytes` as ``'unicode_escape'`` with ``'replace'`` for error handling.
|
290
|
+
|
291
|
+
See Also:
|
292
|
+
The method is a wrapping function for :meth:`bytes.decode`.
|
293
|
+
|
294
|
+
Args:
|
295
|
+
byte: Source bytestring.
|
296
|
+
encoding: The encoding with which to decode the :obj:`bytes`.
|
297
|
+
If not provided, :mod:`pcapkit` will first try detecting its encoding
|
298
|
+
using |chardet|_. The fallback encoding would is **UTF-8**.
|
299
|
+
errors: The error handling scheme to use for the handling of decoding errors.
|
300
|
+
The default is ``'strict'`` meaning that decoding errors raise a
|
301
|
+
:exc:`UnicodeDecodeError`. Other possible values are ``'ignore'`` and ``'replace'``
|
302
|
+
as well as any other name registered with :func:`codecs.register_error` that
|
303
|
+
can handle :exc:`UnicodeDecodeError`.
|
304
|
+
|
305
|
+
.. |chardet| replace:: ``chardet``
|
306
|
+
.. _chardet: https://chardet.readthedocs.io
|
307
|
+
|
308
|
+
"""
|
309
|
+
charset = encoding or chardet.detect(byte)['encoding'] or 'utf-8'
|
310
|
+
try:
|
311
|
+
return byte.decode(charset, errors=errors)
|
312
|
+
except UnicodeError:
|
313
|
+
return byte.decode('unicode_escape', errors='replace')
|
314
|
+
|
315
|
+
@staticmethod
|
316
|
+
def unquote(url: str, *, encoding: 'str' = 'utf-8',
|
317
|
+
errors: 'Literal["strict", "ignore", "replace"]' = 'replace') -> 'str':
|
318
|
+
"""Unquote URLs into readable format.
|
319
|
+
|
320
|
+
Should decoding failed , the method will try again replacing ``'%'`` with ``'\\x'`` then
|
321
|
+
decoding the ``url`` as ``'unicode_escape'`` with ``'replace'`` for error handling.
|
322
|
+
|
323
|
+
See Also:
|
324
|
+
This method is a wrapper function for :func:`urllib.parse.unquote`.
|
325
|
+
|
326
|
+
Args:
|
327
|
+
url: URL string.
|
328
|
+
encoding: The encoding with which to decode the :obj:`bytes`.
|
329
|
+
errors: The error handling scheme to use for the handling of decoding errors.
|
330
|
+
The default is ``'strict'`` meaning that decoding errors raise a
|
331
|
+
:exc:`UnicodeDecodeError`. Other possible values are ``'ignore'`` and ``'replace'``
|
332
|
+
as well as any other name registered with :func:`codecs.register_error` that
|
333
|
+
can handle :exc:`UnicodeDecodeError`.
|
334
|
+
|
335
|
+
"""
|
336
|
+
try:
|
337
|
+
return urllib.parse.unquote(url, encoding=encoding, errors=errors)
|
338
|
+
except UnicodeError:
|
339
|
+
return url.replace('%', r'\x').encode().decode('unicode_escape', errors='replace')
|
340
|
+
|
341
|
+
@staticmethod
|
342
|
+
def expand_comp(value: 'str | ProtocolBase | Type[ProtocolBase]') -> 'tuple':
|
343
|
+
"""Expand protocol class to protocol name.
|
344
|
+
|
345
|
+
The method is used to expand protocol class to protocol name, in the
|
346
|
+
following manner:
|
347
|
+
|
348
|
+
1. If ``value`` is a protocol instance, the method will return the
|
349
|
+
protocol class, and the protocol names in upper case obtained from
|
350
|
+
:meth:`Protocol.id <pcapkit.protocols.protocol.Protocol.id>`.
|
351
|
+
2. If ``value`` is a protocol class, the method will return the
|
352
|
+
protocol class itself, and the protocols names in upper case
|
353
|
+
obtained from :meth:`Protocol.id <pcapkit.protocols.protocol.Protocol.id>`.
|
354
|
+
3. If ``value`` is :obj:`str`, the method will attempt to search for
|
355
|
+
the existing registered protocol class from
|
356
|
+
:data:`pcapkit.protocols.__proto__` and follow **step 2**; otherwise,
|
357
|
+
return the value itself.
|
358
|
+
|
359
|
+
Args:
|
360
|
+
value: Protocol class or name.
|
361
|
+
|
362
|
+
"""
|
363
|
+
if isinstance(value, type) and issubclass(value, ProtocolBase):
|
364
|
+
comp = (value, *(name.upper() for name in value.id()))
|
365
|
+
elif isinstance(value, ProtocolBase):
|
366
|
+
comp = (type(value), *(name.upper() for name in value.id()))
|
367
|
+
else:
|
368
|
+
from pcapkit.protocols import __proto__ as protocols_registry # pylint: disable=import-outside-toplevel # isort: skip
|
369
|
+
|
370
|
+
if (proto := protocols_registry.get(value.upper())) is not None:
|
371
|
+
comp = (proto, *(name.upper() for name in proto.id()))
|
372
|
+
else:
|
373
|
+
comp = (value.upper(),)
|
374
|
+
return comp
|
375
|
+
|
376
|
+
@classmethod
|
377
|
+
def analyze(cls, proto: 'int', payload: 'bytes', **kwargs: 'Any') -> 'ProtocolBase':
|
378
|
+
"""Analyse packet payload.
|
379
|
+
|
380
|
+
Args:
|
381
|
+
proto: Protocol registry number.
|
382
|
+
payload: Packet payload.
|
383
|
+
**kwargs: Arbitrary keyword arguments.
|
384
|
+
|
385
|
+
Returns:
|
386
|
+
Parsed payload as a :class:`~pcapkit.protocols.protocol.Protocol`
|
387
|
+
instance.
|
388
|
+
|
389
|
+
"""
|
390
|
+
protocol = cls.__proto__[proto]
|
391
|
+
if isinstance(protocol, ModuleDescriptor):
|
392
|
+
protocol = protocol.klass
|
393
|
+
cls.__proto__[proto] = protocol # update mapping upon import
|
394
|
+
|
395
|
+
payload_io = io.BytesIO(payload)
|
396
|
+
try:
|
397
|
+
report = protocol(payload_io, len(payload), **kwargs) # type: ignore[abstract]
|
398
|
+
except Exception as exc:
|
399
|
+
if isinstance(exc, StructError) and exc.eof: # pylint: disable=no-member
|
400
|
+
from pcapkit.protocols.misc.null import NoPayload as protocol # pylint: disable=import-outside-toplevel # isort: skip
|
401
|
+
else:
|
402
|
+
from pcapkit.protocols.misc.raw import Raw as protocol # pylint: disable=import-outside-toplevel # isort: skip
|
403
|
+
# error = traceback.format_exc(limit=1).strip().rsplit(os.linesep, maxsplit=1)[-1]
|
404
|
+
|
405
|
+
# log error
|
406
|
+
#logger.error(str(exc), exc_info=exc, stack_info=DEVMODE, stacklevel=stacklevel())
|
407
|
+
|
408
|
+
report = protocol(payload_io, len(payload), **kwargs) # type: ignore[abstract]
|
409
|
+
return report
|
410
|
+
|
411
|
+
@classmethod
|
412
|
+
def register(cls, code: 'int', protocol: 'ModuleDescriptor | Type[ProtocolBase]') -> 'None':
|
413
|
+
r"""Register a new protocol class.
|
414
|
+
|
415
|
+
Notes:
|
416
|
+
The full qualified class name of the new protocol class
|
417
|
+
should be as ``{protocol.module}.{protocol.name}``.
|
418
|
+
|
419
|
+
Arguments:
|
420
|
+
code: protocol code
|
421
|
+
protocol: module descriptor or a
|
422
|
+
:class:`~pcapkit.protocols.protocol.Protocol` subclass
|
423
|
+
|
424
|
+
"""
|
425
|
+
if isinstance(protocol, ModuleDescriptor):
|
426
|
+
protocol = protocol.klass
|
427
|
+
if not issubclass(protocol, ProtocolBase):
|
428
|
+
raise RegistryError(f'protocol must be a Protocol subclass, not {protocol!r}')
|
429
|
+
if code in cls.__proto__:
|
430
|
+
warn(f'protocol {code} already registered, overwriting', RegistryWarning)
|
431
|
+
cls.__proto__[code] = protocol
|
432
|
+
|
433
|
+
@classmethod
|
434
|
+
def from_schema(cls, schema: '_ST | dict[str, Any]') -> 'Self':
|
435
|
+
"""Create protocol instance from schema.
|
436
|
+
|
437
|
+
Args:
|
438
|
+
schema: Protocol schema.
|
439
|
+
|
440
|
+
Returns:
|
441
|
+
Protocol instance.
|
442
|
+
|
443
|
+
"""
|
444
|
+
if not isinstance(schema, Schema):
|
445
|
+
schema = cast('_ST', cls.__schema__.from_dict(schema))
|
446
|
+
|
447
|
+
self = cls.__new__(cls)
|
448
|
+
self.__header__ = schema
|
449
|
+
|
450
|
+
# initialize protocol instance
|
451
|
+
self.__init__(bytes(schema), len(schema)) # type: ignore[misc]
|
452
|
+
|
453
|
+
return self
|
454
|
+
|
455
|
+
@classmethod
|
456
|
+
def from_data(cls, data: '_PT | dict[str, Any]') -> 'Self':
|
457
|
+
"""Create protocol instance from data.
|
458
|
+
|
459
|
+
Args:
|
460
|
+
data: Protocol data.
|
461
|
+
|
462
|
+
Returns:
|
463
|
+
Protocol instance.
|
464
|
+
|
465
|
+
"""
|
466
|
+
if not isinstance(data, Data):
|
467
|
+
data = cast('_PT', cls.__data__.from_dict(data))
|
468
|
+
|
469
|
+
self = cls.__new__(cls)
|
470
|
+
kwargs = self._make_data(data)
|
471
|
+
|
472
|
+
# initialize protocol instance
|
473
|
+
self.__init__(**kwargs) # type: ignore[misc]
|
474
|
+
|
475
|
+
return self
|
476
|
+
|
477
|
+
##########################################################################
|
478
|
+
# Data models.
|
479
|
+
##########################################################################
|
480
|
+
|
481
|
+
def __new__(cls, *args: 'Any', **kwargs: 'Any') -> 'Self': # pylint: disable=unused-argument
|
482
|
+
self = super().__new__(cls)
|
483
|
+
|
484
|
+
# NOTE: Assign this attribute after ``__new__`` to avoid shared memory
|
485
|
+
# reference between instances.
|
486
|
+
self.__cached__ = {}
|
487
|
+
self.__header__ = None # type: ignore[assignment]
|
488
|
+
|
489
|
+
return self
|
490
|
+
|
491
|
+
@overload
|
492
|
+
def __init__(self, file: 'IO[bytes] | bytes', length: 'Optional[int]' = ..., **kwargs: 'Any') -> 'None': ...
|
493
|
+
@overload
|
494
|
+
def __init__(self, **kwargs: 'Any') -> 'None': ...
|
495
|
+
|
496
|
+
def __init__(self, file: 'Optional[IO[bytes] | bytes]' = None, length: 'Optional[int]' = None, **kwargs: 'Any') -> 'None':
|
497
|
+
"""Initialisation.
|
498
|
+
|
499
|
+
Args:
|
500
|
+
file: Source packet stream.
|
501
|
+
length: Length of packet data.
|
502
|
+
_layer (str): Parse packet until ``_layer``
|
503
|
+
(:attr:`self._exlayer <pcapkit.protocols.protocol.Protocol._exlayer>`).
|
504
|
+
_protocol (Union[str, Protocol, Type[Protocol]]): Parse packet until ``_protocol``
|
505
|
+
(:attr:`self._exproto <pcapkit.protocols.protocol.Protocol._exproto>`).
|
506
|
+
**kwargs: Arbitrary keyword arguments.
|
507
|
+
|
508
|
+
"""
|
509
|
+
#logger.debug('%s(file, %s, **%s)', type(self).__name__, length, kwargs)
|
510
|
+
|
511
|
+
#: int: File pointer.
|
512
|
+
self._seekset = io.SEEK_SET # type: int
|
513
|
+
#: str: Parse packet until such layer.
|
514
|
+
self._exlayer = kwargs.pop('_layer', None) # type: Optional[str]
|
515
|
+
#: str: Parse packet until such protocol.
|
516
|
+
self._exproto = kwargs.pop('_protocol', None) # type: Optional[str | ProtocolBase | Type[ProtocolBase]]
|
517
|
+
#: bool: If terminate parsing next layer of protocol.
|
518
|
+
self._sigterm = self._check_term_threshold()
|
519
|
+
|
520
|
+
# post-init customisations
|
521
|
+
self.__post_init__(file, length, **kwargs) # type: ignore[arg-type]
|
522
|
+
|
523
|
+
# inject packet payload to the info dict
|
524
|
+
self._info.__update__(packet=self.packet.payload)
|
525
|
+
|
526
|
+
@overload
|
527
|
+
def __post_init__(self, file: 'IO[bytes] | bytes', length: 'Optional[int]' = ..., **kwargs: 'Any') -> 'None': ...
|
528
|
+
@overload
|
529
|
+
def __post_init__(self, **kwargs: 'Any') -> 'None': ...
|
530
|
+
|
531
|
+
def __post_init__(self, file: 'Optional[IO[bytes] | bytes]' = None,
|
532
|
+
length: 'Optional[int]' = None, **kwargs: 'Any') -> 'None':
|
533
|
+
"""Post initialisation hook.
|
534
|
+
|
535
|
+
Args:
|
536
|
+
file: Source packet stream.
|
537
|
+
length: Length of packet data.
|
538
|
+
**kwargs: Arbitrary keyword arguments.
|
539
|
+
|
540
|
+
See Also:
|
541
|
+
For construction arguments, please refer to
|
542
|
+
:meth:`self.make <pcapkit.protocols.protocol.Protocol.make>`.
|
543
|
+
|
544
|
+
"""
|
545
|
+
if file is None:
|
546
|
+
_data = self.pack(**kwargs)
|
547
|
+
else:
|
548
|
+
_data = file if isinstance(file, bytes) else file.read(length) # type: ignore[arg-type]
|
549
|
+
|
550
|
+
#: bytes: Raw packet data.
|
551
|
+
self._data = _data
|
552
|
+
#: io.BytesIO: Source packet stream.
|
553
|
+
self._file = io.BytesIO(self._data)
|
554
|
+
#: pcapkit.protocols.data.data.Data: Parsed packet data.
|
555
|
+
self._info = self.unpack(length, **kwargs)
|
556
|
+
|
557
|
+
def __init_subclass__(cls, /, schema: 'Optional[Type[_ST]]' = None,
|
558
|
+
data: 'Optional[Type[_PT]]' = None, *args: 'Any', **kwargs: 'Any') -> 'None':
|
559
|
+
"""Initialisation for subclasses.
|
560
|
+
|
561
|
+
Args:
|
562
|
+
schema: Schema class.
|
563
|
+
data: Data class.
|
564
|
+
*args: Arbitrary positional arguments.
|
565
|
+
**kwargs: Arbitrary keyword arguments.
|
566
|
+
|
567
|
+
This method is called when a subclass of :class:`Protocol` is defined.
|
568
|
+
It is used to set the :attr:`self.__schema__ <pcapkit.protocols.protocol.Protocol.__schema__>`
|
569
|
+
attribute of the subclass.
|
570
|
+
|
571
|
+
Notes:
|
572
|
+
When ``schema`` and/or ``data`` is not specified, the method will first
|
573
|
+
try to find the corresponding class in the
|
574
|
+
:mod:`~pcapkit.protocols.schema` and :mod:`~pcapkit.protocols.data`
|
575
|
+
modules respectively. If the class is not found, the default
|
576
|
+
:class:`~pcapkit.protocols.schema.schema.Schema_Raw` and
|
577
|
+
:class:`~pcapkit.protocols.data.data.Data_Raw` classes will be used.
|
578
|
+
|
579
|
+
"""
|
580
|
+
super().__init_subclass__()
|
581
|
+
|
582
|
+
if schema is None:
|
583
|
+
schema = cast('Type[_ST]', getattr(schema_module, cls.__name__, Schema_Raw))
|
584
|
+
if data is None:
|
585
|
+
data = cast('Type[_PT]', getattr(data_module, cls.__name__, Data_Raw))
|
586
|
+
|
587
|
+
cls.__schema__ = schema
|
588
|
+
cls.__data__ = data
|
589
|
+
|
590
|
+
def __repr__(self) -> 'str':
|
591
|
+
"""Returns representation of parsed protocol data.
|
592
|
+
|
593
|
+
Example:
|
594
|
+
>>> protocol
|
595
|
+
<Frame alias='...' frame=(..., packet=b'...', sethernet=..., protocols='Ethernet:IPv6:Raw')>
|
596
|
+
|
597
|
+
"""
|
598
|
+
if (cached := self.__cached__.get('__repr__')) is not None:
|
599
|
+
return cached
|
600
|
+
|
601
|
+
# cache and return
|
602
|
+
repr_ = f'<{self.alias} {self.info_name}={self._info!r}>'
|
603
|
+
|
604
|
+
self.__cached__['__repr__'] = repr_
|
605
|
+
return repr_
|
606
|
+
|
607
|
+
def __str__(self) -> 'str':
|
608
|
+
"""Returns formatted hex representation of source data stream.
|
609
|
+
|
610
|
+
Example:
|
611
|
+
>>> protocol
|
612
|
+
Frame(..., packet=b"...", sethernet=..., protocols='Ethernet:IPv6:Raw')
|
613
|
+
>>> print(protocol)
|
614
|
+
00 00 00 00 00 00 00 a6 87 f9 27 93 16 ee fe 80 00 00 00 ..........'........
|
615
|
+
00 00 00 1c cd 7c 77 ba c7 46 b7 87 00 0e aa 00 00 00 00 .....|w..F.........
|
616
|
+
fe 80 00 00 00 00 00 00 1c cd 7c 77 ba c7 46 b7 01 01 a4 ..........|w..F....
|
617
|
+
5e 60 d9 6b 97 ^`.k.
|
618
|
+
|
619
|
+
"""
|
620
|
+
if (cached := self.__cached__.get('__str__')) is not None:
|
621
|
+
return cached
|
622
|
+
|
623
|
+
hexbuf = ' '.join(textwrap.wrap(self._data.hex(), 2))
|
624
|
+
strbuf = ''.join(chr(char) if char in readable else '.' for char in self._data)
|
625
|
+
|
626
|
+
number = shutil.get_terminal_size().columns // 4 - 1
|
627
|
+
length = number * 3
|
628
|
+
|
629
|
+
hexlst = textwrap.wrap(hexbuf, length)
|
630
|
+
strlst = list(iter(functools.partial(io.StringIO(strbuf).read, number), ''))
|
631
|
+
|
632
|
+
# cache and return
|
633
|
+
str_ = os.linesep.join(map(lambda x: f'{x[0].ljust(length)} {x[1]}', zip(hexlst, strlst)))
|
634
|
+
|
635
|
+
self.__cached__['__str__'] = str_
|
636
|
+
return str_
|
637
|
+
|
638
|
+
def __bytes__(self) -> 'bytes':
|
639
|
+
"""Returns source data stream in :obj:`bytes`."""
|
640
|
+
return self._data
|
641
|
+
|
642
|
+
def __len__(self) -> 'int':
|
643
|
+
"""Total length of corresponding protocol."""
|
644
|
+
if (cached := self.__cached__.get('__len__')) is not None:
|
645
|
+
return cached
|
646
|
+
|
647
|
+
# cache and return
|
648
|
+
len_ = len(self._data)
|
649
|
+
|
650
|
+
self.__cached__['__len__'] = len_
|
651
|
+
return len_
|
652
|
+
|
653
|
+
def __length_hint__(self) -> 'Optional[int]':
|
654
|
+
"""Return an estimated length for the object."""
|
655
|
+
|
656
|
+
def __iter__(self) -> 'IO[bytes]':
|
657
|
+
"""Iterate through :attr:`self._data <pcapkit.protocols.protocol.Protocol._data>`."""
|
658
|
+
return io.BytesIO(self._data)
|
659
|
+
|
660
|
+
def __getitem__(self, key: 'str | Protocol | Type[Protocol]') -> 'ProtocolBase':
|
661
|
+
"""Subscription (``getitem``) support.
|
662
|
+
|
663
|
+
* If ``key`` is a :class:`~pcapkit.protocols.protocol.Protocol` object,
|
664
|
+
the method will fetch its indexes (:meth:`self.id <pcapkit.protocols.protocol.Protocol.id>`).
|
665
|
+
* Later, search the packet's chain of protocols with the calculated ``key``.
|
666
|
+
* If no matches, then raises :exc:`~pcapkit.utilities.exceptions.ProtocolNotFound`.
|
667
|
+
|
668
|
+
Args:
|
669
|
+
key: Indexing key.
|
670
|
+
|
671
|
+
Returns:
|
672
|
+
The sub-packet from the current packet of indexed protocol.
|
673
|
+
|
674
|
+
Raises:
|
675
|
+
ProtocolNotFound: If ``key`` is not in the current packet.
|
676
|
+
|
677
|
+
See Also:
|
678
|
+
The method calls
|
679
|
+
:meth:`self.expand_comp <pcapkit.protocols.protocol.Protocol.expand_comp>`
|
680
|
+
to handle the ``key`` and expand it for robust searching.
|
681
|
+
|
682
|
+
"""
|
683
|
+
comp = self.expand_comp(key)
|
684
|
+
|
685
|
+
# if it's itself
|
686
|
+
test_comp = (type(self), *(name.upper() for name in self.id()))
|
687
|
+
for test in comp:
|
688
|
+
if test in test_comp:
|
689
|
+
return self
|
690
|
+
|
691
|
+
# then check recursively
|
692
|
+
from pcapkit.protocols.misc.null import NoPayload # pylint: disable=import-outside-toplevel
|
693
|
+
|
694
|
+
payload = self._next
|
695
|
+
while not isinstance(payload, NoPayload):
|
696
|
+
test_comp = (type(payload), *(name.upper() for name in payload.id()))
|
697
|
+
for test in comp:
|
698
|
+
if test in test_comp:
|
699
|
+
return payload
|
700
|
+
payload = payload.payload
|
701
|
+
raise ProtocolNotFound(key)
|
702
|
+
|
703
|
+
def __contains__(self, name: 'str | Protocol | Type[Protocol]') -> 'bool':
|
704
|
+
"""Returns if certain protocol is in the instance.
|
705
|
+
|
706
|
+
Args:
|
707
|
+
name: Name to search
|
708
|
+
|
709
|
+
See Also:
|
710
|
+
The method calls
|
711
|
+
:meth:`self.expand_comp <pcapkit.protocols.protocol.Protocol.expand_comp>`
|
712
|
+
to handle the ``name`` and expand it for robust searching.
|
713
|
+
|
714
|
+
"""
|
715
|
+
comp = self.expand_comp(name)
|
716
|
+
|
717
|
+
# if it's itself
|
718
|
+
test_comp = (type(self), *(name.upper() for name in self.id()))
|
719
|
+
for test in comp:
|
720
|
+
if test in test_comp:
|
721
|
+
return True
|
722
|
+
|
723
|
+
# then check recursively
|
724
|
+
from pcapkit.protocols.misc.null import NoPayload # pylint: disable=import-outside-toplevel
|
725
|
+
|
726
|
+
payload = self._next
|
727
|
+
while not isinstance(payload, NoPayload):
|
728
|
+
test_comp = (type(payload), *(name.upper() for name in payload.id()))
|
729
|
+
for test in comp:
|
730
|
+
if test in test_comp:
|
731
|
+
return True
|
732
|
+
payload = payload.payload
|
733
|
+
return False
|
734
|
+
|
735
|
+
@classmethod
|
736
|
+
@abc.abstractmethod
|
737
|
+
def __index__(cls) -> 'StdlibEnum | AenumEnum':
|
738
|
+
"""Numeral registry index of the protocol."""
|
739
|
+
|
740
|
+
@classmethod
|
741
|
+
def __eq__(cls, other: 'object') -> 'bool':
|
742
|
+
"""Returns if ``other`` is of the same protocol as the current object.
|
743
|
+
|
744
|
+
Args:
|
745
|
+
other: Comparision against the object.
|
746
|
+
|
747
|
+
"""
|
748
|
+
if isinstance(other, type) and issubclass(other, ProtocolBase):
|
749
|
+
return cls is other
|
750
|
+
if isinstance(other, ProtocolBase):
|
751
|
+
return cls.id() == other.id()
|
752
|
+
|
753
|
+
if isinstance(other, str):
|
754
|
+
test_comp = cls.expand_comp(cls)
|
755
|
+
return other.upper() in test_comp
|
756
|
+
return False
|
757
|
+
|
758
|
+
def __hash__(self) -> 'int':
|
759
|
+
"""Return the hash value for :attr:`self._data <pcapkit.protocols.protocol.Protocol._data>`."""
|
760
|
+
return hash(self._data)
|
761
|
+
|
762
|
+
##########################################################################
|
763
|
+
# Utilities.
|
764
|
+
##########################################################################
|
765
|
+
|
766
|
+
def _get_payload(self) -> 'bytes':
|
767
|
+
"""Get payload from :attr:`self.__header__ <Protocol.__header__>`.
|
768
|
+
|
769
|
+
Returns:
|
770
|
+
Payload of :attr:`self.__header__ <Protocol.__header__>` as :obj:`bytes`.
|
771
|
+
|
772
|
+
See Also:
|
773
|
+
This is a wrapper function for :meth:`pcapkit.protocols.schema.schema.Schema.get_payload`.
|
774
|
+
|
775
|
+
"""
|
776
|
+
return self.__header__.get_payload()
|
777
|
+
|
778
|
+
def _read_protos(self, size: int) -> 'Optional[StdlibEnum | AenumEnum]': # pylint: disable=unused-argument
|
779
|
+
"""Read next layer protocol type.
|
780
|
+
|
781
|
+
* If *succeed*, returns the enum of next layer protocol.
|
782
|
+
* If *fail*, returns :obj:`None`.
|
783
|
+
|
784
|
+
Arguments:
|
785
|
+
size: buffer size
|
786
|
+
|
787
|
+
"""
|
788
|
+
|
789
|
+
def _read_fileng(self, *args: 'Any', **kwargs: 'Any') -> 'bytes':
|
790
|
+
"""Read file buffer (:attr:`self._file <pcapkit.protocols.protocol.Protocol._file>`).
|
791
|
+
|
792
|
+
This method wraps the :meth:`file.read <io.BytesIO.read>` call.
|
793
|
+
|
794
|
+
Args:
|
795
|
+
*args: arbitrary positional arguments
|
796
|
+
**kwargs: arbitrary keyword arguments
|
797
|
+
|
798
|
+
Returns:
|
799
|
+
bytes: Data read from file buffer.
|
800
|
+
|
801
|
+
"""
|
802
|
+
return self._file.read(*args, **kwargs)
|
803
|
+
|
804
|
+
def _read_unpack(self, size: 'int' = 1, *, signed: 'bool' = False,
|
805
|
+
lilendian: 'bool' = False, quiet: 'bool' = False) -> 'int':
|
806
|
+
"""Read bytes and unpack for integers.
|
807
|
+
|
808
|
+
Arguments:
|
809
|
+
size: buffer size
|
810
|
+
signed: signed flag
|
811
|
+
lilendian: little-endian flag
|
812
|
+
quiet: quiet (no exception) flag
|
813
|
+
|
814
|
+
Returns:
|
815
|
+
Unpacked data upon success
|
816
|
+
|
817
|
+
Raises:
|
818
|
+
StructError: If unpack (:func:`struct.pack`) failed, and :exc:`struct.error` raised.
|
819
|
+
|
820
|
+
"""
|
821
|
+
endian = '<' if lilendian else '>'
|
822
|
+
if size == 8: # unpack to 8-byte integer (long long)
|
823
|
+
kind = 'q' if signed else 'Q'
|
824
|
+
elif size == 4: # unpack to 4-byte integer (int / long)
|
825
|
+
kind = 'i' if signed else 'I'
|
826
|
+
elif size == 2: # unpack to 2-byte integer (short)
|
827
|
+
kind = 'h' if signed else 'H'
|
828
|
+
elif size == 1: # unpack to 1-byte integer (char)
|
829
|
+
kind = 'b' if signed else 'B'
|
830
|
+
else: # do not unpack
|
831
|
+
kind = None
|
832
|
+
|
833
|
+
mem = self._file.read(size)
|
834
|
+
if not mem:
|
835
|
+
raise StructError('unpack: empty buffer', quiet=True, eof=True)
|
836
|
+
|
837
|
+
if kind is None:
|
838
|
+
end = 'little' if lilendian else 'big' # type: Literal['little', 'big']
|
839
|
+
buf = int.from_bytes(mem, end, signed=signed)
|
840
|
+
else:
|
841
|
+
fmt = f'{endian}{kind}'
|
842
|
+
try:
|
843
|
+
buf = struct.unpack(fmt, mem)[0] # pylint: disable=no-member
|
844
|
+
except struct.error as error: # pylint: disable=no-member
|
845
|
+
if quiet:
|
846
|
+
end = 'little' if lilendian else 'big'
|
847
|
+
buf = int.from_bytes(mem, end, signed=signed)
|
848
|
+
return buf
|
849
|
+
raise StructError(f'{self.__class__.__name__}: unpack failed') from error
|
850
|
+
return buf
|
851
|
+
|
852
|
+
def _read_binary(self, size: 'int' = 1) -> 'str':
|
853
|
+
"""Read bytes and convert into binaries.
|
854
|
+
|
855
|
+
Arguments:
|
856
|
+
size: buffer size
|
857
|
+
|
858
|
+
Returns:
|
859
|
+
Binary bits (``0``/``1``).
|
860
|
+
|
861
|
+
"""
|
862
|
+
bin_ = [] # type: list[str]
|
863
|
+
for _ in range(size):
|
864
|
+
byte = self._file.read(1)
|
865
|
+
bin_.append(bin(ord(byte))[2:].zfill(8))
|
866
|
+
return ''.join(bin_)
|
867
|
+
|
868
|
+
@overload
|
869
|
+
def _read_packet(self, length: 'Optional[int]' = ..., *, header: 'None' = ...) -> 'bytes': ...
|
870
|
+
@overload
|
871
|
+
def _read_packet(self, *, header: 'int', payload: 'Optional[int]' = ..., discard: 'Literal[True]') -> 'bytes': ...
|
872
|
+
@overload
|
873
|
+
def _read_packet(self, *, header: 'int', payload: 'Optional[int]' = ..., discard: 'Literal[False]' = ...) -> 'Data_Packet': ... # pylint: disable=line-too-long
|
874
|
+
|
875
|
+
@seekset # type: ignore[misc]
|
876
|
+
def _read_packet(self, length: 'Optional[int]' = None, *, header: 'Optional[int]' = None,
|
877
|
+
payload: 'Optional[int]' = None, discard: bool = False) -> 'bytes | Data_Packet':
|
878
|
+
"""Read raw packet data.
|
879
|
+
|
880
|
+
Arguments:
|
881
|
+
length: length of the packet
|
882
|
+
header: length of the packet header
|
883
|
+
payload: length of the packet payload
|
884
|
+
discard: flag if discard header data
|
885
|
+
|
886
|
+
* If ``header`` omits, returns the whole packet data in :obj:`bytes`.
|
887
|
+
* If ``discard`` is set as :data:`True`, returns the packet body (in
|
888
|
+
:obj:`bytes`) only.
|
889
|
+
* Otherwise, returns the header and payload data as
|
890
|
+
:class:`~pcapkit.protocols.data.protocol.Packet` object.
|
891
|
+
|
892
|
+
"""
|
893
|
+
if header is not None:
|
894
|
+
data_header = self._read_fileng(header)
|
895
|
+
data_payload = self._read_fileng(payload)
|
896
|
+
if discard:
|
897
|
+
return data_payload
|
898
|
+
return Data_Packet(
|
899
|
+
header=data_header,
|
900
|
+
payload=data_payload
|
901
|
+
)
|
902
|
+
return self._read_fileng(length)
|
903
|
+
|
904
|
+
@classmethod
|
905
|
+
def _make_pack(cls, integer: 'int', *, size: 'int' = 1,
|
906
|
+
signed: 'bool' = False, lilendian: 'bool' = False) -> 'bytes':
|
907
|
+
"""Pack integers to bytes.
|
908
|
+
|
909
|
+
Arguments:
|
910
|
+
integer: integer to be packed
|
911
|
+
size: buffer size
|
912
|
+
signed: signed flag
|
913
|
+
lilendian: little-endian flag
|
914
|
+
|
915
|
+
Returns:
|
916
|
+
Packed data upon success.
|
917
|
+
|
918
|
+
Raises:
|
919
|
+
StructError: If failed to pack the integer.
|
920
|
+
|
921
|
+
"""
|
922
|
+
endian = '<' if lilendian else '>'
|
923
|
+
if size == 8: # unpack to 8-byte integer (long long)
|
924
|
+
kind = 'q' if signed else 'Q'
|
925
|
+
elif size == 4: # unpack to 4-byte integer (int / long)
|
926
|
+
kind = 'i' if signed else 'I'
|
927
|
+
elif size == 2: # unpack to 2-byte integer (short)
|
928
|
+
kind = 'h' if signed else 'H'
|
929
|
+
elif size == 1: # unpack to 1-byte integer (char)
|
930
|
+
kind = 'b' if signed else 'B'
|
931
|
+
else: # do not unpack
|
932
|
+
kind = None
|
933
|
+
|
934
|
+
if kind is None:
|
935
|
+
end = 'little' if lilendian else 'big' # type: Literal['little', 'big']
|
936
|
+
buf = integer.to_bytes(size, end, signed=signed)
|
937
|
+
else:
|
938
|
+
try:
|
939
|
+
fmt = f'{endian}{kind}'
|
940
|
+
buf = struct.pack(fmt, integer) # pylint: disable=no-member
|
941
|
+
except struct.error as error: # pylint: disable=no-member
|
942
|
+
raise StructError(f'{cls.__name__}: pack failed') from error
|
943
|
+
return buf
|
944
|
+
|
945
|
+
@overload
|
946
|
+
@classmethod
|
947
|
+
def _make_index(cls, name: 'int | StdlibEnum | AenumEnum', *, pack: 'Literal[False]' = ...) -> 'int': ...
|
948
|
+
@overload
|
949
|
+
@classmethod
|
950
|
+
def _make_index(cls, name: 'int | StdlibEnum | AenumEnum', *, pack: 'Literal[True]',
|
951
|
+
size: 'int' = ..., signed: 'bool' = ..., lilendian: 'bool' = ...) -> 'bytes': ...
|
952
|
+
@overload
|
953
|
+
@classmethod
|
954
|
+
def _make_index(cls, name: 'str', default: 'Optional[int]' = ..., *,
|
955
|
+
namespace: 'Type[StdlibEnum] | Type[AenumEnum]', pack: 'Literal[False]' = ...) -> 'int': ...
|
956
|
+
@overload
|
957
|
+
@classmethod
|
958
|
+
def _make_index(cls, name: 'str', default: 'Optional[int]' = ..., *,
|
959
|
+
namespace: 'Type[StdlibEnum] | Type[AenumEnum]', pack: 'Literal[True]',
|
960
|
+
size: 'int' = ..., signed: 'bool' = ..., lilendian: 'bool' = ...) -> 'bytes': ...
|
961
|
+
@overload
|
962
|
+
@classmethod
|
963
|
+
def _make_index(cls, name: 'str', default: 'Optional[int]' = ..., *, namespace: 'dict[int, str]',
|
964
|
+
reversed: 'Literal[False]' = ..., # pylint: disable=redefined-builtin
|
965
|
+
pack: 'Literal[False]' = ...) -> 'int': ...
|
966
|
+
@overload
|
967
|
+
@classmethod
|
968
|
+
def _make_index(cls, name: 'str', default: 'Optional[int]' = ..., *, namespace: 'dict[int, str]',
|
969
|
+
reversed: 'Literal[False]' = ..., # pylint: disable=redefined-builtin
|
970
|
+
pack: 'Literal[True]', size: 'int' = ..., signed: 'bool' = ...,
|
971
|
+
lilendian: 'bool' = ...) -> 'bytes': ...
|
972
|
+
@overload
|
973
|
+
@classmethod
|
974
|
+
def _make_index(cls, name: 'str', default: 'Optional[int]' = ..., *, namespace: 'dict[str, int]',
|
975
|
+
reversed: 'Literal[True]', # pylint: disable=redefined-builtin
|
976
|
+
pack: 'Literal[False]' = ...) -> 'int': ...
|
977
|
+
@overload
|
978
|
+
@classmethod
|
979
|
+
def _make_index(cls, name: 'str', default: 'Optional[int]' = ..., *, namespace: 'dict[str, int]',
|
980
|
+
reversed: 'Literal[True]', # pylint: disable=redefined-builtin
|
981
|
+
pack: 'Literal[True]', size: 'int' = ..., signed: 'bool' = ...,
|
982
|
+
lilendian: 'bool' = ...) -> 'bytes': ...
|
983
|
+
@overload
|
984
|
+
@classmethod
|
985
|
+
def _make_index(cls, name: 'str | int | StdlibEnum | AenumEnum', default: 'Optional[int]' = ..., *,
|
986
|
+
namespace: 'Optional[dict[str, int] | dict[int, str] | Type[StdlibEnum] | Type[AenumEnum]]' = ...,
|
987
|
+
reversed: 'bool' = ..., pack: 'Literal[False]' = ...) -> 'int': ...
|
988
|
+
|
989
|
+
@classmethod
|
990
|
+
def _make_index(cls, name: 'str | int | StdlibEnum | AenumEnum', default: 'Optional[int]' = None, *,
|
991
|
+
namespace: 'Optional[dict[str, int] | dict[int, str] | Type[StdlibEnum] | Type[AenumEnum]]' = None,
|
992
|
+
reversed: 'bool' = False, # pylint: disable=redefined-builtin
|
993
|
+
pack: 'bool' = False, size: 'int' = 4, signed: 'bool' = False,
|
994
|
+
lilendian: 'bool' = False) -> 'int | bytes':
|
995
|
+
"""Return first index of ``name`` from a :obj:`dict` or enumeration.
|
996
|
+
|
997
|
+
Arguments:
|
998
|
+
name: item to be indexed
|
999
|
+
default: default value
|
1000
|
+
namespace: namespace for item
|
1001
|
+
reversed: if namespace is ``str -> int`` pairs
|
1002
|
+
pack: if need :func:`struct.pack` to pack the result
|
1003
|
+
size: buffer size
|
1004
|
+
signed: signed flag
|
1005
|
+
lilendian: little-endian flag
|
1006
|
+
|
1007
|
+
Returns:
|
1008
|
+
Index of ``name`` from a dict or enumeration. If ``pack`` is
|
1009
|
+
:data:`True`, returns :obj:`bytes`; otherwise, returns :obj:`int`.
|
1010
|
+
|
1011
|
+
Raises:
|
1012
|
+
ProtocolNotImplemented: If ``name`` is **NOT** in ``namespace``
|
1013
|
+
and ``default`` is :data:`None`.
|
1014
|
+
|
1015
|
+
"""
|
1016
|
+
if isinstance(name, (enum.Enum, aenum.Enum)):
|
1017
|
+
index = cast('int', name.value)
|
1018
|
+
elif isinstance(name, int):
|
1019
|
+
index = name
|
1020
|
+
else: # name is str
|
1021
|
+
try:
|
1022
|
+
if isinstance(namespace, type) and issubclass(namespace, (enum.IntEnum, aenum.IntEnum)):
|
1023
|
+
index = cast('int', namespace[name].value)
|
1024
|
+
elif isinstance(namespace, dict):
|
1025
|
+
if reversed:
|
1026
|
+
if TYPE_CHECKING:
|
1027
|
+
namespace = cast('dict[str, int]', namespace)
|
1028
|
+
index = namespace[name]
|
1029
|
+
else:
|
1030
|
+
if TYPE_CHECKING:
|
1031
|
+
namespace = cast('dict[int, str]', namespace)
|
1032
|
+
index = {v: k for k, v in namespace.items()}[name]
|
1033
|
+
else:
|
1034
|
+
raise KeyError(name)
|
1035
|
+
except KeyError as error:
|
1036
|
+
if default is None:
|
1037
|
+
raise ProtocolNotImplemented(f'protocol {name!r} not implemented') from error
|
1038
|
+
index = default
|
1039
|
+
|
1040
|
+
if pack:
|
1041
|
+
return cls._make_pack(index, size=size, signed=signed, lilendian=lilendian)
|
1042
|
+
return index
|
1043
|
+
|
1044
|
+
@classmethod
|
1045
|
+
def _make_data(cls, data: 'Data') -> 'dict[str, Any]':
|
1046
|
+
"""Create key-value pairs from ``data`` for protocol construction.
|
1047
|
+
|
1048
|
+
Args:
|
1049
|
+
data: protocol data
|
1050
|
+
|
1051
|
+
Returns:
|
1052
|
+
Key-value pairs for protocol construction.
|
1053
|
+
|
1054
|
+
"""
|
1055
|
+
return data.to_dict()
|
1056
|
+
|
1057
|
+
@classmethod
|
1058
|
+
def _make_payload(cls, data: 'Data') -> 'ProtocolBase':
|
1059
|
+
"""Create payload from ``data`` for protocol construction.
|
1060
|
+
|
1061
|
+
This method uses ``__next_type__`` and ``__next_name__`` to
|
1062
|
+
determine the payload type and name. If either of them is
|
1063
|
+
:data:`None`, a :class:`~pcapkit.protocols.misc.null.NoPayload`
|
1064
|
+
instance will be returned. Otherwise, the payload will be
|
1065
|
+
constructed by :meth:`Protocol.from_data <pcapkit.protocols.protocol.Protocol.from_data>`.
|
1066
|
+
|
1067
|
+
Args:
|
1068
|
+
data: protocol data
|
1069
|
+
|
1070
|
+
Returns:
|
1071
|
+
Payload for protocol construction.
|
1072
|
+
|
1073
|
+
"""
|
1074
|
+
proto = cast('Optional[Type[Protocol]]', data.get('__next_type__'))
|
1075
|
+
if proto is None or not (isinstance(proto, type) and issubclass(proto, ProtocolBase)):
|
1076
|
+
from pcapkit.protocols.misc.null import \
|
1077
|
+
NoPayload # pylint: disable=import-outside-toplevel
|
1078
|
+
return NoPayload()
|
1079
|
+
|
1080
|
+
name = cast('Optional[str]', data.get('__next_name__'))
|
1081
|
+
if name is None:
|
1082
|
+
from pcapkit.protocols.misc.null import \
|
1083
|
+
NoPayload # pylint: disable=import-outside-toplevel
|
1084
|
+
return NoPayload()
|
1085
|
+
|
1086
|
+
return proto.from_data(data[name])
|
1087
|
+
|
1088
|
+
def _decode_next_layer(self, dict_: '_PT', proto: 'int', length: 'Optional[int]' = None, *,
|
1089
|
+
packet: 'Optional[dict[str, Any]]' = None) -> '_PT':
|
1090
|
+
r"""Decode next layer protocol.
|
1091
|
+
|
1092
|
+
Arguments:
|
1093
|
+
dict\_: info buffer
|
1094
|
+
proto: next layer protocol index
|
1095
|
+
length: valid (*non-padding*) length
|
1096
|
+
packet: packet info (passed from :meth:`self.unpack <Protocol.unpack>`)
|
1097
|
+
|
1098
|
+
Returns:
|
1099
|
+
Current protocol with next layer extracted.
|
1100
|
+
|
1101
|
+
Notes:
|
1102
|
+
We added a new key ``__next_type__`` to ``dict_`` to store the
|
1103
|
+
next layer protocol type, and a new key ``__next_name__`` to
|
1104
|
+
store the next layer protocol name. These two keys will **NOT**
|
1105
|
+
be included when :meth:`Info.to_dict <pcapkit.corekit.infoclass.Info.to_dict>` is called.
|
1106
|
+
|
1107
|
+
"""
|
1108
|
+
next_ = cast('ProtocolBase', self._import_next_layer(proto, length, packet=packet)) # type: ignore[misc,call-arg,redundant-cast]
|
1109
|
+
info, chain = next_.info, next_.protochain
|
1110
|
+
|
1111
|
+
# make next layer protocol name
|
1112
|
+
layer = next_.info_name
|
1113
|
+
# proto = next_.__class__.__name__
|
1114
|
+
|
1115
|
+
# write info and protocol chain into dict
|
1116
|
+
dict_.__update__({
|
1117
|
+
layer: info,
|
1118
|
+
'__next_type__': type(next_),
|
1119
|
+
'__next_name__': layer,
|
1120
|
+
})
|
1121
|
+
self._next = next_ # pylint: disable=attribute-defined-outside-init
|
1122
|
+
self._protos = ProtoChain(self.__class__, self.alias, basis=chain) # pylint: disable=attribute-defined-outside-init
|
1123
|
+
return dict_
|
1124
|
+
|
1125
|
+
@beholder
|
1126
|
+
def _import_next_layer(self, proto: 'int', length: 'Optional[int]' = None, *,
|
1127
|
+
packet: 'Optional[dict[str, Any]]' = None) -> 'ProtocolBase':
|
1128
|
+
"""Import next layer extractor.
|
1129
|
+
|
1130
|
+
Arguments:
|
1131
|
+
proto: next layer protocol index
|
1132
|
+
length: valid (*non-padding*) length
|
1133
|
+
packet: packet info (passed from :meth:`self.unpack <Protocol.unpack>`)
|
1134
|
+
|
1135
|
+
Returns:
|
1136
|
+
Instance of next layer.
|
1137
|
+
|
1138
|
+
"""
|
1139
|
+
if TYPE_CHECKING:
|
1140
|
+
protocol: 'Type[ProtocolBase]'
|
1141
|
+
|
1142
|
+
file_ = self._get_payload()
|
1143
|
+
if length is None:
|
1144
|
+
length = len(file_)
|
1145
|
+
|
1146
|
+
if length == 0:
|
1147
|
+
from pcapkit.protocols.misc.null import NoPayload as protocol # isort: skip # pylint: disable=import-outside-toplevel
|
1148
|
+
elif self._sigterm:
|
1149
|
+
from pcapkit.protocols.misc.raw import Raw as protocol # isort: skip # pylint: disable=import-outside-toplevel
|
1150
|
+
else:
|
1151
|
+
protocol = self.__proto__[proto] # type: ignore[assignment]
|
1152
|
+
if isinstance(protocol, ModuleDescriptor):
|
1153
|
+
protocol = protocol.klass # type: ignore[unreachable]
|
1154
|
+
self.__proto__[proto] = protocol # update mapping upon import
|
1155
|
+
|
1156
|
+
next_ = protocol(file_, length, alias=proto, packet=packet,
|
1157
|
+
layer=self._exlayer, protocol=self._exproto) # type: ignore[abstract]
|
1158
|
+
return next_
|
1159
|
+
|
1160
|
+
def _check_term_threshold(self) -> bool:
|
1161
|
+
"""Check if reached termination threshold."""
|
1162
|
+
if self._exlayer is None or (layer := self.__layer__) is None:
|
1163
|
+
layer_match = False
|
1164
|
+
else:
|
1165
|
+
layer_match = layer.upper() == self._exlayer.upper()
|
1166
|
+
|
1167
|
+
if self._exproto is None:
|
1168
|
+
protocol_match = False
|
1169
|
+
else:
|
1170
|
+
protocol_match = False
|
1171
|
+
comp_test = [name.upper() for name in self.id()]
|
1172
|
+
for test in self.expand_comp(self._exproto):
|
1173
|
+
if test in comp_test:
|
1174
|
+
protocol_match = True
|
1175
|
+
break
|
1176
|
+
|
1177
|
+
return layer_match or protocol_match
|
1178
|
+
|
1179
|
+
|
1180
|
+
class Protocol(ProtocolBase, Generic[_PT, _ST]):
|
1181
|
+
"""Abstract base class for all protocol family."""
|
1182
|
+
|
1183
|
+
def __init_subclass__(cls, /, schema: 'Optional[Type[_ST]]' = None,
|
1184
|
+
data: 'Optional[Type[_PT]]' = None, *args: 'Any', **kwargs: 'Any') -> 'None':
|
1185
|
+
"""Initialisation for subclasses.
|
1186
|
+
|
1187
|
+
Args:
|
1188
|
+
schema: Schema class.
|
1189
|
+
data: Data class.
|
1190
|
+
*args: Arbitrary positional arguments.
|
1191
|
+
**kwargs: Arbitrary keyword arguments.
|
1192
|
+
|
1193
|
+
This method is called when a subclass of :class:`Protocol` is defined.
|
1194
|
+
It is used to set the :attr:`self.__schema__ <pcapkit.protocols.protocol.Protocol.__schema__>`
|
1195
|
+
attribute of the subclass.
|
1196
|
+
|
1197
|
+
Notes:
|
1198
|
+
When ``schema`` and/or ``data`` is not specified, the method will first
|
1199
|
+
try to find the corresponding class in the
|
1200
|
+
:mod:`~pcapkit.protocols.schema` and :mod:`~pcapkit.protocols.data`
|
1201
|
+
modules respectively. If the class is not found, the default
|
1202
|
+
:class:`~pcapkit.protocols.schema.schema.Schema_Raw` and
|
1203
|
+
:class:`~pcapkit.protocols.data.data.Data_Raw` classes will be used.
|
1204
|
+
|
1205
|
+
This method also registers the subclass to the protocol registry,
|
1206
|
+
i.e., :attr:`pcapkit.protocols.__proto__`.
|
1207
|
+
|
1208
|
+
See Also:
|
1209
|
+
For more information on the registry, please refer to
|
1210
|
+
:func:`pcapkit.foundation.registry.protocols.register_protocol`.
|
1211
|
+
|
1212
|
+
"""
|
1213
|
+
from pcapkit.foundation.registry.protocols import register_protocol
|
1214
|
+
register_protocol(cls)
|
1215
|
+
|
1216
|
+
return super().__init_subclass__(schema, data, *args, **kwargs)
|