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,809 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
"""schema for protocol headers"""
|
3
|
+
|
4
|
+
import abc
|
5
|
+
import collections
|
6
|
+
import collections.abc
|
7
|
+
import io
|
8
|
+
import itertools
|
9
|
+
import sys
|
10
|
+
from typing import TYPE_CHECKING, Any, Generic, TypeVar, cast, final
|
11
|
+
|
12
|
+
from pcapkit.corekit.fields.collections import ListField, OptionField
|
13
|
+
from pcapkit.corekit.fields.field import FieldBase, NoValue
|
14
|
+
from pcapkit.corekit.fields.misc import ConditionalField, ForwardMatchField, PayloadField
|
15
|
+
from pcapkit.corekit.fields.strings import PaddingField
|
16
|
+
from pcapkit.corekit.infoclass import FinalisedState
|
17
|
+
from pcapkit.utilities.compat import Mapping
|
18
|
+
from pcapkit.utilities.decorators import prepare
|
19
|
+
from pcapkit.utilities.exceptions import NoDefaultValue, ProtocolUnbound, stacklevel
|
20
|
+
from pcapkit.utilities.warnings import SchemaWarning, UnknownFieldWarning, warn
|
21
|
+
|
22
|
+
if TYPE_CHECKING:
|
23
|
+
from collections import OrderedDict
|
24
|
+
from enum import Enum
|
25
|
+
from typing import IO, Any, Callable, DefaultDict, Iterable, Iterator, Optional, Type
|
26
|
+
|
27
|
+
from typing_extensions import Self
|
28
|
+
|
29
|
+
__all__ = ['Schema', 'EnumSchema', 'schema_final']
|
30
|
+
|
31
|
+
_VT = TypeVar('_VT')
|
32
|
+
_ET = TypeVar('_ET', bound='Enum')
|
33
|
+
_ST = TypeVar('_ST', bound='Type[Schema]')
|
34
|
+
|
35
|
+
|
36
|
+
def schema_final(cls: '_ST', *, _finalised: 'bool' = True) -> '_ST':
|
37
|
+
"""Finalise schema class.
|
38
|
+
|
39
|
+
This decorator function is used to generate necessary
|
40
|
+
attributes and methods for the decorated :class:`Schema`
|
41
|
+
class. It can be useful to reduce runtime generation
|
42
|
+
time as well as caching already generated attributes.
|
43
|
+
|
44
|
+
Notes:
|
45
|
+
The decorator should only be used on the *final*
|
46
|
+
class, otherwise, any subclasses derived from a
|
47
|
+
finalised schema class will not be re-finalised.
|
48
|
+
|
49
|
+
Args:
|
50
|
+
cls: Schema class.
|
51
|
+
_finalised: Whether to make the schema class finalised.
|
52
|
+
|
53
|
+
Returns:
|
54
|
+
Finalised schema class.
|
55
|
+
|
56
|
+
:meta decorator:
|
57
|
+
"""
|
58
|
+
if cls.__finalised__ == FinalisedState.FINAL:
|
59
|
+
warn(f'{cls.__name__}: schema has been finalised; now skipping',
|
60
|
+
SchemaWarning, stacklevel=stacklevel())
|
61
|
+
return cls
|
62
|
+
|
63
|
+
temp = ['__map__', '__map_reverse__', '__builtin__',
|
64
|
+
'__fields__', '__buffer__', '__updated__',
|
65
|
+
'__payload__', '__finalised__']
|
66
|
+
temp.extend(cls.__additional__)
|
67
|
+
for obj in cls.mro():
|
68
|
+
temp.extend(el for el in dir(obj) if el not in cls.__fields__)
|
69
|
+
cls.__builtin__ = set(temp)
|
70
|
+
cls.__excluded__.extend(cls.__builtin__)
|
71
|
+
|
72
|
+
args_ = [f'{key}=NoValue' for key in cls.__fields__]
|
73
|
+
dict_ = [f'{key}={key}' for key in cls.__fields__]
|
74
|
+
|
75
|
+
# NOTE: We shall only attempt to generate ``__init__`` method
|
76
|
+
# if the class does not define such method.
|
77
|
+
if not hasattr(cls, '__init__'):
|
78
|
+
# NOTE: We only generate typed ``__init__`` method if only the class
|
79
|
+
# has field definition from any of itself and its base classes.
|
80
|
+
if args_:
|
81
|
+
# NOTE: The following code is to make the ``__init__`` method work.
|
82
|
+
# It is inspired from the :func:`dataclasses._create_fn` function.
|
83
|
+
init_ = (
|
84
|
+
f'def __create_fn__():\n'
|
85
|
+
f' def __init__(self, {", ".join(args_)}, *, __packet__=None):\n'
|
86
|
+
f' self.__update__({", ".join(dict_)})\n'
|
87
|
+
f' self.__post_init__(__packet__)\n'
|
88
|
+
f' return __init__\n'
|
89
|
+
)
|
90
|
+
else:
|
91
|
+
init_ = (
|
92
|
+
'def __create_fn__():\n'
|
93
|
+
' def __init__(self, dict_=None, *, __packet__=None, **kwargs):\n'
|
94
|
+
' self.__update__(dict_, **kwargs)\n'
|
95
|
+
' self.__post_init__(__packet__)\n'
|
96
|
+
' return __init__\n'
|
97
|
+
)
|
98
|
+
|
99
|
+
ns = {} # type: dict[str, Any]
|
100
|
+
exec(init_, None, ns) # pylint: disable=exec-used # nosec
|
101
|
+
|
102
|
+
cls.__init__ = ns['__create_fn__']() # type: ignore[misc]
|
103
|
+
cls.__init__.__qualname__ = f'{cls.__name__}.__init__' # type: ignore[misc]
|
104
|
+
|
105
|
+
if not _finalised:
|
106
|
+
cls.__finalised__ = FinalisedState.BASE
|
107
|
+
return cls
|
108
|
+
|
109
|
+
cls.__finalised__ = FinalisedState.FINAL
|
110
|
+
return final(cls)
|
111
|
+
|
112
|
+
|
113
|
+
class SchemaMeta(abc.ABCMeta):
|
114
|
+
"""Meta class to add dynamic support to :class:`Schema`.
|
115
|
+
|
116
|
+
This meta class is used to generate necessary attributes for the
|
117
|
+
:class:`Schema` class. It can be useful to reduce runtime generation
|
118
|
+
cost as well as caching already generated attributes.
|
119
|
+
|
120
|
+
* :attr:`Schema.__fields__` is a dictionary of field names and their
|
121
|
+
corresponding :class:`~pcapkit.corekit.fields.field.Field` objects,
|
122
|
+
which are used to define and parse the protocol headers. The field
|
123
|
+
dictionary will automatically be populated from the class attributes
|
124
|
+
of the :class:`Schema` class, and the field names will be the same
|
125
|
+
as the attribute names.
|
126
|
+
|
127
|
+
.. seealso::
|
128
|
+
|
129
|
+
This is implemented thru setting up the initial field dictionary
|
130
|
+
in the |prepare|_ method, and then inherit the field
|
131
|
+
dictionaries from the base classes.
|
132
|
+
|
133
|
+
Later, during the class creation, the
|
134
|
+
:meth:`Field.__set_name__ <pcapkit.corekit.fields.field.FieldBase.__set_name__>`
|
135
|
+
method will be called to set the field name for each field object,
|
136
|
+
as well as to add the field object to the field dictionary.
|
137
|
+
|
138
|
+
.. |prepare| replace:: :meth:`__prepare__`
|
139
|
+
.. _prepare: https://docs.python.org/3/reference/datamodel.html#preparing-the-class-namespace
|
140
|
+
|
141
|
+
* :attr:`Schema.__additional__` and :attr:`Schema.__excluded__` are
|
142
|
+
lists of additional and excluded field names, which are used to
|
143
|
+
determine certain names to be included or excluded from the field
|
144
|
+
dictionary. They will be automatically populated from the class
|
145
|
+
attributes of the :class:`Schema` class and its base classes.
|
146
|
+
|
147
|
+
.. note::
|
148
|
+
|
149
|
+
This is implemented thru the :meth:`~object.__new__` method, which
|
150
|
+
will inherit the additional and excluded field names from the base
|
151
|
+
classes, as well as populating the additional and excluded field
|
152
|
+
from the subclass attributes.
|
153
|
+
|
154
|
+
.. code-block:: python
|
155
|
+
|
156
|
+
class A(Schema):
|
157
|
+
__additional__ = ['a', 'b']
|
158
|
+
|
159
|
+
class B(A):
|
160
|
+
__additional__ = ['c', 'd']
|
161
|
+
|
162
|
+
class C(B):
|
163
|
+
__additional__ = ['e', 'f']
|
164
|
+
|
165
|
+
print(A.__additional__) # ['a', 'b']
|
166
|
+
print(B.__additional__) # ['a', 'b', 'c', 'd']
|
167
|
+
print(C.__additional__) # ['a', 'b', 'c', 'd', 'e', 'f']
|
168
|
+
|
169
|
+
"""
|
170
|
+
|
171
|
+
@classmethod
|
172
|
+
def __prepare__(cls, name: 'str', bases: 'tuple[type, ...]', /, **kwds: 'Any') -> 'Mapping[str, object]':
|
173
|
+
"""Prepare the namespace for the schema class.
|
174
|
+
|
175
|
+
Args:
|
176
|
+
name: Name of the schema class.
|
177
|
+
bases: Base classes of the schema class.
|
178
|
+
**kwds: Additional keyword arguments at class definition.
|
179
|
+
|
180
|
+
This method is used to create the initial field dictionary
|
181
|
+
:attr:`~Schema.__fields__` for the schema class.
|
182
|
+
|
183
|
+
"""
|
184
|
+
fields = collections.OrderedDict()
|
185
|
+
for base in bases:
|
186
|
+
if hasattr(base, '__fields__'):
|
187
|
+
fields.update(base.__fields__)
|
188
|
+
return collections.OrderedDict(__fields__=fields)
|
189
|
+
|
190
|
+
def __new__(cls, name: 'str', bases: 'tuple[type, ...]', attrs: 'dict[str, Any]', **kwargs: 'Any') -> 'Type[Schema]':
|
191
|
+
"""Create the schema class.
|
192
|
+
|
193
|
+
Args:
|
194
|
+
name: Schema class name.
|
195
|
+
bases: Schema class bases.
|
196
|
+
attrs: Schema class attributes.
|
197
|
+
**kwargs: Arbitrary keyword arguments in class definition.
|
198
|
+
|
199
|
+
This method is used to inherit the :attr:`~Schema.__additional__` and
|
200
|
+
:attr:`~Schema.__excluded__` fields from the base classes, as well as
|
201
|
+
populating both fields from the subclass attributes.
|
202
|
+
|
203
|
+
"""
|
204
|
+
if '__additional__' not in attrs:
|
205
|
+
attrs['__additional__'] = []
|
206
|
+
if '__excluded__' not in attrs:
|
207
|
+
attrs['__excluded__'] = []
|
208
|
+
|
209
|
+
for base in bases:
|
210
|
+
if hasattr(base, '__additional__'):
|
211
|
+
attrs['__additional__'].extend(name for name in base.__additional__ if name not in attrs['__additional__'])
|
212
|
+
if hasattr(base, '__excluded__'):
|
213
|
+
attrs['__excluded__'].extend(name for name in base.__excluded__ if name not in attrs['__excluded__'])
|
214
|
+
|
215
|
+
# NOTE: for unknown reason, the following code will cause an error
|
216
|
+
# for duplicated keyword arguments in class definition.
|
217
|
+
if sys.version_info < (3, 11):
|
218
|
+
return type.__new__(cls, name, bases, attrs, **kwargs)
|
219
|
+
return super().__new__(cls, name, bases, attrs, **kwargs) # type: ignore[return-value]
|
220
|
+
|
221
|
+
|
222
|
+
class Schema(Mapping[str, _VT], Generic[_VT], metaclass=SchemaMeta):
|
223
|
+
"""Schema for protocol headers."""
|
224
|
+
|
225
|
+
if TYPE_CHECKING:
|
226
|
+
#: Mapping of name conflicts with builtin methods (original names to
|
227
|
+
#: transformed names).
|
228
|
+
__map__: 'dict[str, str]'
|
229
|
+
#: Mapping of name conflicts with builtin methods (transformed names to
|
230
|
+
#: original names).
|
231
|
+
__map_reverse__: 'dict[str, str]'
|
232
|
+
#: List of builtin methods.
|
233
|
+
__builtin__: 'set[str]'
|
234
|
+
#: Mapping of fields.
|
235
|
+
__fields__: 'OrderedDict[str, FieldBase]'
|
236
|
+
#: Mapping of field names to packed values.
|
237
|
+
__buffer__: 'dict[str, bytes]'
|
238
|
+
#: Flag for whether the schema is recently updated.
|
239
|
+
__updated__: 'bool'
|
240
|
+
|
241
|
+
#: Flag for finalised class initialisation.
|
242
|
+
__finalised__: 'FinalisedState' = FinalisedState.NONE
|
243
|
+
|
244
|
+
#: Field name of the payload.
|
245
|
+
__payload__: 'str' = 'payload'
|
246
|
+
#: List of additional built-in names.
|
247
|
+
__additional__: 'list[str]' = []
|
248
|
+
#: List of names to be excluded from :obj:`dict` conversion.
|
249
|
+
__excluded__: 'list[str]' = []
|
250
|
+
|
251
|
+
def __new__(cls, *args: '_VT', **kwargs: '_VT') -> 'Self': # pylint: disable=unused-argument
|
252
|
+
"""Create a new instance.
|
253
|
+
|
254
|
+
The class will try to automatically generate ``__init__`` method with
|
255
|
+
the same signature as specified in class variables' type annotations,
|
256
|
+
which is inspired by :pep:`557` (:mod:`dataclasses`).
|
257
|
+
|
258
|
+
Args:
|
259
|
+
*args: Arbitrary positional arguments.
|
260
|
+
**kwargs: Arbitrary keyword arguments.
|
261
|
+
|
262
|
+
"""
|
263
|
+
if cls.__finalised__ == FinalisedState.NONE:
|
264
|
+
cls = schema_final(cls, _finalised=False)
|
265
|
+
self = super().__new__(cls)
|
266
|
+
|
267
|
+
# NOTE: We define the ``__map__`` and ``__map_reverse__`` attributes
|
268
|
+
# here under ``self`` to avoid them being considered as class variables
|
269
|
+
# and thus being shared by all instances.
|
270
|
+
super().__setattr__(self, '__map__', {})
|
271
|
+
super().__setattr__(self, '__map_reverse__', {})
|
272
|
+
|
273
|
+
# NOTE: We only create the attributes for the instance itself,
|
274
|
+
# to avoid creating shared attributes for the class.
|
275
|
+
super().__setattr__(self, '__buffer__', {name: b'' for name in self.__fields__.keys()})
|
276
|
+
super().__setattr__(self, '__updated__', True)
|
277
|
+
|
278
|
+
return self
|
279
|
+
|
280
|
+
def __post_init__(self, packet: 'Optional[dict[str, Any]]' = None) -> 'None':
|
281
|
+
for name, field in self.__fields__.items():
|
282
|
+
if self.__dict__[name] in (NoValue, None):
|
283
|
+
self.__dict__[name] = field.default
|
284
|
+
self.pack(packet)
|
285
|
+
|
286
|
+
def __update__(self, dict_: 'Optional[Mapping[str, _VT] | Iterable[tuple[str, _VT]]]' = None,
|
287
|
+
**kwargs: '_VT') -> 'None':
|
288
|
+
# NOTE: Keys with the same names as the class's builtin methods will be
|
289
|
+
# renamed with the class name prefixed as mangled class variables
|
290
|
+
# implicitly and internally. Such mapping information will be stored
|
291
|
+
# within: attr: `__map__` attribute.
|
292
|
+
|
293
|
+
__name__ = type(self).__name__ # pylint: disable=redefined-builtin
|
294
|
+
|
295
|
+
if dict_ is None:
|
296
|
+
data_iter = kwargs.items() # type: Iterable[tuple[str, Any]]
|
297
|
+
elif isinstance(dict_, collections.abc.Mapping):
|
298
|
+
data_iter = itertools.chain(dict_.items(), kwargs.items())
|
299
|
+
else:
|
300
|
+
data_iter = itertools.chain(dict_, kwargs.items())
|
301
|
+
|
302
|
+
for (key, value) in data_iter:
|
303
|
+
if key not in self.__buffer__:
|
304
|
+
warn(f'{key!r} is not a valid field name', UnknownFieldWarning)
|
305
|
+
continue
|
306
|
+
|
307
|
+
if key in self.__builtin__:
|
308
|
+
new_key = f'_{__name__}{key}'
|
309
|
+
|
310
|
+
# NOTE: We keep record of the mapping bidirectionally.
|
311
|
+
self.__map__[key] = new_key
|
312
|
+
self.__map_reverse__[new_key] = key
|
313
|
+
|
314
|
+
key = new_key
|
315
|
+
|
316
|
+
# if key in self.__dict__:
|
317
|
+
# raise KeyExists(f'{key!r} already exists')
|
318
|
+
|
319
|
+
# NOTE: We don't rewrite the key names here, just keep the
|
320
|
+
# original ones, even though they might break on the ``.``
|
321
|
+
# (:obj:`getattr`) operator.
|
322
|
+
|
323
|
+
# if isinstance(key, str):
|
324
|
+
# key = re.sub(r'\W', '_', key)
|
325
|
+
self.__dict__[key] = value
|
326
|
+
|
327
|
+
self.__updated__ = True
|
328
|
+
|
329
|
+
__init__ = __update__
|
330
|
+
|
331
|
+
def __str__(self) -> 'str':
|
332
|
+
temp = [] # type: list[str]
|
333
|
+
for (key, value) in self.__dict__.items():
|
334
|
+
if key in self.__excluded__:
|
335
|
+
continue
|
336
|
+
|
337
|
+
out_key = self.__map_reverse__.get(key, key)
|
338
|
+
temp.append(f'{out_key}={value}')
|
339
|
+
args = ', '.join(temp)
|
340
|
+
return f'{type(self).__name__}({args})'
|
341
|
+
|
342
|
+
def __repr__(self) -> 'str':
|
343
|
+
temp = [] # type: list[str]
|
344
|
+
for (key, value) in self.__dict__.items():
|
345
|
+
if key in self.__excluded__:
|
346
|
+
continue
|
347
|
+
|
348
|
+
out_key = self.__map_reverse__.get(key, key)
|
349
|
+
if isinstance(value, Schema):
|
350
|
+
temp.append(f'{out_key}={type(value).__name__}(...)')
|
351
|
+
else:
|
352
|
+
temp.append(f'{out_key}={value!r}')
|
353
|
+
args = ', '.join(temp)
|
354
|
+
return f'{type(self).__name__}({args})'
|
355
|
+
|
356
|
+
def __bytes__(self) -> 'bytes':
|
357
|
+
if self.__updated__:
|
358
|
+
self.pack()
|
359
|
+
|
360
|
+
buffer = [] # type: list[bytes]
|
361
|
+
for name in self.__fields__.keys():
|
362
|
+
value = self.__buffer__[name]
|
363
|
+
buffer.append(value)
|
364
|
+
return b''.join(buffer)
|
365
|
+
|
366
|
+
def __len__(self) -> 'int':
|
367
|
+
return len(self.__bytes__())
|
368
|
+
|
369
|
+
def __iter__(self) -> 'Iterator[str]':
|
370
|
+
for key in self.__dict__:
|
371
|
+
if key in self.__builtin__:
|
372
|
+
continue
|
373
|
+
yield self.__map_reverse__.get(key, key)
|
374
|
+
|
375
|
+
def __getitem__(self, name: 'str') -> '_VT':
|
376
|
+
if name in self.__fields__:
|
377
|
+
key = self.__map__.get(name, name)
|
378
|
+
return self.__dict__[key]
|
379
|
+
return super().__getitem__(name)
|
380
|
+
|
381
|
+
def __setattr__(self, name: 'str', value: '_VT') -> 'None':
|
382
|
+
if name in self.__fields__:
|
383
|
+
key = self.__map__.get(name, name)
|
384
|
+
self.__dict__[key] = value
|
385
|
+
self.__updated__ = True
|
386
|
+
return
|
387
|
+
return super().__setattr__(name, value)
|
388
|
+
|
389
|
+
def __delattr__(self, name: 'str') -> 'None':
|
390
|
+
if name in self.__fields__:
|
391
|
+
key = self.__map__.get(name, name)
|
392
|
+
del self.__dict__[key]
|
393
|
+
self.__updated__ = True
|
394
|
+
return
|
395
|
+
return super().__delattr__(name)
|
396
|
+
|
397
|
+
@classmethod
|
398
|
+
def from_dict(cls, dict_: 'Optional[Mapping[str, _VT] | Iterable[tuple[str, _VT]]]' = None,
|
399
|
+
**kwargs: '_VT') -> 'Self':
|
400
|
+
r"""Create a new instance.
|
401
|
+
|
402
|
+
* If ``dict_`` is present and has a ``.keys()`` method, then does:
|
403
|
+
``for k in dict_: self[k] = dict_[k]``.
|
404
|
+
* If ``dict_`` is present and has no ``.keys()`` method, then does:
|
405
|
+
``for k, v in dict_: self[k] = v``.
|
406
|
+
* If ``dict_`` is not present, then does:
|
407
|
+
``for k, v in kwargs.items(): self[k] = v``.
|
408
|
+
|
409
|
+
Args:
|
410
|
+
dict\_: Source data.
|
411
|
+
**kwargs: Arbitrary keyword arguments.
|
412
|
+
|
413
|
+
"""
|
414
|
+
self = cls.__new__(cls)
|
415
|
+
self.__update__(dict_, **kwargs)
|
416
|
+
self.__post_init__()
|
417
|
+
return self
|
418
|
+
|
419
|
+
def to_dict(self) -> 'dict[str, _VT]':
|
420
|
+
"""Convert :class:`Schema` into :obj:`dict`.
|
421
|
+
|
422
|
+
Important:
|
423
|
+
We only convert nested :class:`Schema` objects into :obj:`dict` if
|
424
|
+
they are the direct value of the :class:`Schema` object's attribute.
|
425
|
+
Should such :class:`Schema` objects be nested within other data,
|
426
|
+
types, such as :obj:`list`, :obj:`tuple`, :obj:`set`, etc., we
|
427
|
+
shall not convert them into :obj:`dict` and remain them intact.
|
428
|
+
|
429
|
+
"""
|
430
|
+
dict_ = {} # type: dict[str, Any]
|
431
|
+
for (key, value) in self.__dict__.items():
|
432
|
+
if key in self.__excluded__:
|
433
|
+
continue
|
434
|
+
|
435
|
+
out_key = self.__map_reverse__.get(key, key)
|
436
|
+
if isinstance(value, Schema):
|
437
|
+
dict_[out_key] = value.to_dict()
|
438
|
+
else:
|
439
|
+
dict_[out_key] = value
|
440
|
+
return dict_
|
441
|
+
|
442
|
+
def to_bytes(self) -> 'bytes':
|
443
|
+
"""Convert :class:`Schema` into :obj:`bytes`."""
|
444
|
+
return self.__bytes__()
|
445
|
+
|
446
|
+
def get_payload(self, name: 'Optional[str]' = None) -> 'bytes':
|
447
|
+
"""Get payload of :class:`Schema`.
|
448
|
+
|
449
|
+
Args:
|
450
|
+
name: Name of the payload field.
|
451
|
+
|
452
|
+
Returns:
|
453
|
+
Payload of :class:`Schema` as :obj:`bytes`.
|
454
|
+
|
455
|
+
"""
|
456
|
+
if name is None:
|
457
|
+
name = self.__payload__
|
458
|
+
|
459
|
+
field = self.__fields__.get(name)
|
460
|
+
if field is None:
|
461
|
+
raise ProtocolUnbound(f'unknown field: {name!r}')
|
462
|
+
if not isinstance(field, PayloadField):
|
463
|
+
raise ProtocolUnbound(f'not a payload field: {name!r}')
|
464
|
+
return self.__buffer__[name]
|
465
|
+
|
466
|
+
def pack(self, packet: 'Optional[dict[str, Any]]' = None) -> 'bytes':
|
467
|
+
"""Pack :class:`Schema` into :obj:`bytes`.
|
468
|
+
|
469
|
+
Args:
|
470
|
+
packet: Packet data.
|
471
|
+
|
472
|
+
Returns:
|
473
|
+
Packed :class:`Schema` as :obj:`bytes`.
|
474
|
+
|
475
|
+
Notes:
|
476
|
+
Since we do not know the length of the packet, we use a
|
477
|
+
reasonable default value ``-1`` for the ``__length__``
|
478
|
+
field, as the :class:`~pcapkit.corekit.fields.field.Field`
|
479
|
+
class will consider negative value as a placeholder.
|
480
|
+
|
481
|
+
If you want to pack the packet with the correct length,
|
482
|
+
please provide the ``__length__`` value before packing.
|
483
|
+
|
484
|
+
"""
|
485
|
+
if packet is None:
|
486
|
+
packet = {}
|
487
|
+
|
488
|
+
packet.update(self.__dict__)
|
489
|
+
self.pre_unpack(packet)
|
490
|
+
|
491
|
+
if '__length__' not in packet:
|
492
|
+
packet['__length__'] = -1 # reasonable default value
|
493
|
+
|
494
|
+
for field in self.__fields__.values():
|
495
|
+
field = field(packet)
|
496
|
+
|
497
|
+
if isinstance(field, PayloadField):
|
498
|
+
from pcapkit.protocols.protocol import \
|
499
|
+
Protocol # pylint: disable=import-outside-toplevel
|
500
|
+
|
501
|
+
data = getattr(self, field.name, None)
|
502
|
+
if data is None:
|
503
|
+
self.__buffer__[field.name] = b''
|
504
|
+
elif isinstance(data, Protocol):
|
505
|
+
self.__buffer__[field.name] = bytes(data)
|
506
|
+
elif isinstance(data, bytes):
|
507
|
+
self.__buffer__[field.name] = data
|
508
|
+
elif isinstance(data, Schema):
|
509
|
+
self.__buffer__[field.name] = data.pack(packet)
|
510
|
+
else:
|
511
|
+
raise ProtocolUnbound(f'unsupported type {type(data)}')
|
512
|
+
continue
|
513
|
+
|
514
|
+
if isinstance(field, ListField):
|
515
|
+
data = getattr(self, field.name, None)
|
516
|
+
if data is None:
|
517
|
+
self.__buffer__[field.name] = b''
|
518
|
+
elif isinstance(data, bytes):
|
519
|
+
self.__buffer__[field.name] = data
|
520
|
+
elif isinstance(data, list):
|
521
|
+
self.__buffer__[field.name] = field.pack(data, packet)
|
522
|
+
else:
|
523
|
+
raise ProtocolUnbound(f'unsupported type {type(data)}')
|
524
|
+
continue
|
525
|
+
|
526
|
+
if isinstance(field, PaddingField):
|
527
|
+
self.__buffer__[field.name] = bytes(field.length)
|
528
|
+
continue
|
529
|
+
|
530
|
+
if isinstance(field, ConditionalField):
|
531
|
+
if not field.test(packet):
|
532
|
+
self.__buffer__[field.name] = b''
|
533
|
+
continue
|
534
|
+
field = field.field(packet)
|
535
|
+
|
536
|
+
if isinstance(field, ForwardMatchField):
|
537
|
+
self.__buffer__[field.name] = b''
|
538
|
+
continue
|
539
|
+
|
540
|
+
value = getattr(self, field.name)
|
541
|
+
try:
|
542
|
+
temp = field.pack(value, packet)
|
543
|
+
except NoDefaultValue:
|
544
|
+
temp = bytes(field.length)
|
545
|
+
self.__buffer__[field.name] = temp
|
546
|
+
|
547
|
+
self.post_process(packet)
|
548
|
+
self.__updated__ = False
|
549
|
+
return self.__bytes__()
|
550
|
+
|
551
|
+
def pre_pack(self, packet: 'dict[str, Any]') -> 'None':
|
552
|
+
"""Prepare ``packet`` data for packing process.
|
553
|
+
|
554
|
+
Args:
|
555
|
+
packet: packet data
|
556
|
+
|
557
|
+
Note:
|
558
|
+
This method is expected to directly modify any data stored
|
559
|
+
in the ``packet`` and thus no return is required.
|
560
|
+
|
561
|
+
"""
|
562
|
+
|
563
|
+
@classmethod
|
564
|
+
@prepare
|
565
|
+
def unpack(cls, data: 'bytes | IO[bytes]',
|
566
|
+
length: 'Optional[int]' = None,
|
567
|
+
packet: 'Optional[dict[str, Any]]' = None) -> 'Self':
|
568
|
+
"""Unpack :obj:`bytes` into :class:`Schema`.
|
569
|
+
|
570
|
+
Args:
|
571
|
+
data: Packed data.
|
572
|
+
length: Length of data.
|
573
|
+
packet: Unpacked data.
|
574
|
+
|
575
|
+
Returns:
|
576
|
+
Unpacked data as :class:`Schema`.
|
577
|
+
|
578
|
+
Notes:
|
579
|
+
We used a ``__length__`` key in ``packet`` to record the length
|
580
|
+
of the remaining data, which is used to determine the length of
|
581
|
+
the payload field.
|
582
|
+
|
583
|
+
And a ``__padding_length__`` key in the ``packet`` to record the
|
584
|
+
length of the padding field after an
|
585
|
+
:class:`~pcapkit.corekit.fields.collections.OptionField`, which
|
586
|
+
is used to potentially determine the length of the remaining
|
587
|
+
padding field data.
|
588
|
+
|
589
|
+
"""
|
590
|
+
# force cast arg type since decorator changed their signatures
|
591
|
+
if TYPE_CHECKING:
|
592
|
+
data = cast('IO[bytes]', data)
|
593
|
+
length = cast('int', length)
|
594
|
+
packet = cast('dict[str, Any]', packet)
|
595
|
+
|
596
|
+
self = cls.__new__(cls)
|
597
|
+
for field in self.__fields__.values():
|
598
|
+
field = field(packet)
|
599
|
+
|
600
|
+
if isinstance(field, PayloadField):
|
601
|
+
payload_length = field.length or cast('int', packet['__length__'])
|
602
|
+
|
603
|
+
payload = data.read(payload_length)
|
604
|
+
self.__buffer__[field.name] = payload
|
605
|
+
|
606
|
+
packet['__length__'] -= field.length
|
607
|
+
packet[field.name] = payload
|
608
|
+
|
609
|
+
setattr(self, field.name, payload)
|
610
|
+
continue
|
611
|
+
|
612
|
+
if isinstance(field, PaddingField):
|
613
|
+
byte = data.read(field.length)
|
614
|
+
self.__buffer__[field.name] = byte
|
615
|
+
|
616
|
+
packet[field.name] = byte
|
617
|
+
packet['__length__'] -= field.length
|
618
|
+
|
619
|
+
setattr(self, field.name, byte)
|
620
|
+
continue
|
621
|
+
|
622
|
+
if isinstance(field, ConditionalField):
|
623
|
+
if not field.test(packet):
|
624
|
+
self.__buffer__[field.name] = b''
|
625
|
+
setattr(self, field.name, None)
|
626
|
+
packet[field.name] = NoValue
|
627
|
+
continue
|
628
|
+
field = field.field(packet)
|
629
|
+
|
630
|
+
byte = data.read(field.length)
|
631
|
+
self.__buffer__[field.name] = byte
|
632
|
+
|
633
|
+
value = field.unpack(byte, packet.copy())
|
634
|
+
setattr(self, field.name, value)
|
635
|
+
|
636
|
+
packet[field.name] = value
|
637
|
+
|
638
|
+
if isinstance(field, OptionField):
|
639
|
+
packet['__option_padding__'] = field.option_padding
|
640
|
+
|
641
|
+
if isinstance(field, ForwardMatchField):
|
642
|
+
data.seek(-field.length, io.SEEK_CUR)
|
643
|
+
else:
|
644
|
+
packet['__length__'] -= field.length
|
645
|
+
|
646
|
+
if packet['__length__'] < 0:
|
647
|
+
warn(f'packet length < 0: {packet["__length__"]}',
|
648
|
+
SchemaWarning, stacklevel=stacklevel())
|
649
|
+
|
650
|
+
self.__updated__ = False
|
651
|
+
return self
|
652
|
+
|
653
|
+
@classmethod
|
654
|
+
def pre_unpack(cls, packet: 'dict[str, Any]') -> 'None':
|
655
|
+
"""Prepare ``packet`` data for unpacking process.
|
656
|
+
|
657
|
+
Args:
|
658
|
+
packet: packet data
|
659
|
+
|
660
|
+
Note:
|
661
|
+
This method is expected to directly modify any data stored
|
662
|
+
in the ``packet`` and thus no return is required.
|
663
|
+
|
664
|
+
"""
|
665
|
+
|
666
|
+
def post_process(self, packet: 'dict[str, Any]') -> 'Schema':
|
667
|
+
"""Revise ``schema`` data after packing and/or unpacking process.
|
668
|
+
|
669
|
+
Args:
|
670
|
+
packet: Unpacked data.
|
671
|
+
|
672
|
+
Returns:
|
673
|
+
Revised schema.
|
674
|
+
|
675
|
+
"""
|
676
|
+
return self
|
677
|
+
|
678
|
+
|
679
|
+
class EnumMeta(SchemaMeta, Generic[_ET]):
|
680
|
+
"""Meta class to add dynamic support for :class:`EnumSchema`.
|
681
|
+
|
682
|
+
This meta class is used to generate necessary attributes for the
|
683
|
+
:class:`SchemaMeta` class. It can be useful to reduce runtime generation
|
684
|
+
cost as well as caching already generated attributes.
|
685
|
+
|
686
|
+
* :attr:`~EnumSchema.registry` is added to subclasses as an *immutable*
|
687
|
+
proxy (similar to :class:`property`, but on class variables) to the
|
688
|
+
:attr:`EnumSchema.__enum__` mapping.
|
689
|
+
|
690
|
+
"""
|
691
|
+
|
692
|
+
if TYPE_CHECKING:
|
693
|
+
#: Mapping of enumeration numbers to schemas (**internal use only**).
|
694
|
+
__enum__: 'DefaultDict[_ET, Type[EnumSchema]]'
|
695
|
+
|
696
|
+
@property
|
697
|
+
def registry(cls) -> 'DefaultDict[_ET, Type[EnumSchema]]':
|
698
|
+
"""Mapping of enumeration numbers to schemas."""
|
699
|
+
return cls.__enum__
|
700
|
+
|
701
|
+
|
702
|
+
class EnumSchema(Schema, Generic[_ET], metaclass=EnumMeta):
|
703
|
+
""":class:`Schema` with enumeration mapping support.
|
704
|
+
|
705
|
+
Examples:
|
706
|
+
|
707
|
+
To create an enumeration mapping supported schema, simply
|
708
|
+
|
709
|
+
.. code-block:: python
|
710
|
+
|
711
|
+
class MySchema(EnumSchema[MyEnum]):
|
712
|
+
|
713
|
+
# optional, set the default schema for enumeration mapping
|
714
|
+
# if the enumeration number is not found in the mapping
|
715
|
+
__default__ = lambda: UnknownSchema # by default, None
|
716
|
+
|
717
|
+
then, you can use inheritance to create a list of schemas
|
718
|
+
for this given enumeration mapping:
|
719
|
+
|
720
|
+
.. code-block:: python
|
721
|
+
|
722
|
+
class OneSchema(MySchema, code=MyEnum.ONE):
|
723
|
+
...
|
724
|
+
|
725
|
+
class MultipleSchema(MySchema, code=[MyEnum.TWO, MyEnum.THREE]):
|
726
|
+
...
|
727
|
+
|
728
|
+
or optionally, using the :meth:`register` method to register a
|
729
|
+
schema to the enumeration mapping:
|
730
|
+
|
731
|
+
.. code-block:: python
|
732
|
+
|
733
|
+
MySchema.register(MyEnum.ZERO, ZeroSchema)
|
734
|
+
|
735
|
+
And now you can access the enumeration mapping via the :attr:`registry`
|
736
|
+
property (more specifically, class attribute):
|
737
|
+
|
738
|
+
.. code-block:: python
|
739
|
+
|
740
|
+
>>> MySchema.registry[MyEnum.ONE] # OneSchema
|
741
|
+
|
742
|
+
"""
|
743
|
+
|
744
|
+
__additional__ = ['__enum__', '__default__']
|
745
|
+
__excluded__ = ['__enum__', '__default__']
|
746
|
+
|
747
|
+
#: Callback to return the default schema for enumeration mapping,
|
748
|
+
#: by default is a ``lambda: None`` statement.
|
749
|
+
__default__: 'Callable[[], Type[Self]]' = lambda: None # type: ignore[assignment,return-value]
|
750
|
+
|
751
|
+
if TYPE_CHECKING:
|
752
|
+
#: Mapping of enumeration numbers to schemas.
|
753
|
+
__enum__: 'DefaultDict[_ET, Type[Self]]'
|
754
|
+
|
755
|
+
@property
|
756
|
+
def registry(self) -> 'DefaultDict[_ET, Type[Self]]':
|
757
|
+
"""Mapping of enumeration numbers to schemas.
|
758
|
+
|
759
|
+
Note:
|
760
|
+
This property is also available as a class
|
761
|
+
attribute.
|
762
|
+
|
763
|
+
"""
|
764
|
+
return self.__enum__
|
765
|
+
|
766
|
+
def __init_subclass__(cls, /, code: 'Optional[_ET | Iterable[_ET]]' = None, *args: 'Any', **kwargs: 'Any') -> 'None':
|
767
|
+
"""Register enumeration to :attr:`registry` mapping.
|
768
|
+
|
769
|
+
Args:
|
770
|
+
code: Enumeration code. It can be either a single enumeration
|
771
|
+
or a list of enumerations.
|
772
|
+
*args: Arbitrary positional arguments.
|
773
|
+
**kwargs: Arbitrary keyword arguments.
|
774
|
+
|
775
|
+
If ``code`` is provided, the subclass will be registered to the
|
776
|
+
:attr:`registry` mapping with the given ``code``. If ``code`` is
|
777
|
+
not given, the subclass will not be registered.
|
778
|
+
|
779
|
+
Notes:
|
780
|
+
If :attr:`__enum__` is not yet defined at function call,
|
781
|
+
it will automatically be defined as a :class:`collections.defaultdict`
|
782
|
+
object, with the default value set to :attr:`__default__`.
|
783
|
+
|
784
|
+
If intended to customise the :attr:`__enum__` mapping,
|
785
|
+
it is possible to override the :meth:`__init_subclass__` method and
|
786
|
+
define :attr:`__enum__` manually.
|
787
|
+
|
788
|
+
"""
|
789
|
+
if not hasattr(cls, '__enum__'):
|
790
|
+
cls.__enum__ = collections.defaultdict(cls.__default__)
|
791
|
+
|
792
|
+
if code is not None:
|
793
|
+
if isinstance(code, collections.abc.Iterable):
|
794
|
+
for _code in code:
|
795
|
+
cls.__enum__[_code] = (cls) # type: ignore[index]
|
796
|
+
else:
|
797
|
+
cls.__enum__[code] = (cls) # type: ignore[index]
|
798
|
+
super().__init_subclass__()
|
799
|
+
|
800
|
+
@classmethod
|
801
|
+
def register(cls, code: '_ET', schema: 'Type[Self]') -> 'None':
|
802
|
+
"""Register enumetaion to :attr:`__enum__` mapping.
|
803
|
+
|
804
|
+
Args:
|
805
|
+
code: Enumetaion code.
|
806
|
+
schema: Enumetaion schema.
|
807
|
+
|
808
|
+
"""
|
809
|
+
cls.__enum__[code] = schema # type: ignore[index]
|