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.
Files changed (466) hide show
  1. pcapkit/__init__.py +126 -0
  2. pcapkit/__main__.py +138 -0
  3. pcapkit/all.py +136 -0
  4. pcapkit/const/__init__.py +81 -0
  5. pcapkit/const/arp/__init__.py +25 -0
  6. pcapkit/const/arp/hardware.py +181 -0
  7. pcapkit/const/arp/operation.py +131 -0
  8. pcapkit/const/ftp/__init__.py +25 -0
  9. pcapkit/const/ftp/command.py +309 -0
  10. pcapkit/const/ftp/return_code.py +304 -0
  11. pcapkit/const/hip/__init__.py +94 -0
  12. pcapkit/const/hip/certificate.py +77 -0
  13. pcapkit/const/hip/cipher.py +65 -0
  14. pcapkit/const/hip/di.py +59 -0
  15. pcapkit/const/hip/ecdsa_curve.py +59 -0
  16. pcapkit/const/hip/ecdsa_low_curve.py +56 -0
  17. pcapkit/const/hip/eddsa_curve.py +65 -0
  18. pcapkit/const/hip/esp_transform_suite.py +98 -0
  19. pcapkit/const/hip/group.py +86 -0
  20. pcapkit/const/hip/hi_algorithm.py +86 -0
  21. pcapkit/const/hip/hit_suite.py +68 -0
  22. pcapkit/const/hip/nat_traversal.py +62 -0
  23. pcapkit/const/hip/notify_message.py +200 -0
  24. pcapkit/const/hip/packet.py +89 -0
  25. pcapkit/const/hip/parameter.py +377 -0
  26. pcapkit/const/hip/registration.py +68 -0
  27. pcapkit/const/hip/registration_failure.py +84 -0
  28. pcapkit/const/hip/suite.py +71 -0
  29. pcapkit/const/hip/transport.py +59 -0
  30. pcapkit/const/http/__init__.py +39 -0
  31. pcapkit/const/http/error_code.py +95 -0
  32. pcapkit/const/http/frame.py +95 -0
  33. pcapkit/const/http/method.py +184 -0
  34. pcapkit/const/http/setting.py +96 -0
  35. pcapkit/const/http/status_code.py +294 -0
  36. pcapkit/const/ipv4/__init__.py +57 -0
  37. pcapkit/const/ipv4/classification_level.py +64 -0
  38. pcapkit/const/ipv4/option_class.py +55 -0
  39. pcapkit/const/ipv4/option_number.py +137 -0
  40. pcapkit/const/ipv4/protection_authority.py +63 -0
  41. pcapkit/const/ipv4/qs_function.py +51 -0
  42. pcapkit/const/ipv4/router_alert.py +251 -0
  43. pcapkit/const/ipv4/tos_del.py +51 -0
  44. pcapkit/const/ipv4/tos_ecn.py +55 -0
  45. pcapkit/const/ipv4/tos_pre.py +63 -0
  46. pcapkit/const/ipv4/tos_rel.py +51 -0
  47. pcapkit/const/ipv4/tos_thr.py +51 -0
  48. pcapkit/const/ipv4/ts_flag.py +53 -0
  49. pcapkit/const/ipv6/__init__.py +53 -0
  50. pcapkit/const/ipv6/extension_header.py +66 -0
  51. pcapkit/const/ipv6/option.py +137 -0
  52. pcapkit/const/ipv6/option_action.py +55 -0
  53. pcapkit/const/ipv6/qs_function.py +51 -0
  54. pcapkit/const/ipv6/router_alert.py +266 -0
  55. pcapkit/const/ipv6/routing.py +80 -0
  56. pcapkit/const/ipv6/seed_id.py +55 -0
  57. pcapkit/const/ipv6/smf_dpd_mode.py +51 -0
  58. pcapkit/const/ipv6/tagger_id.py +62 -0
  59. pcapkit/const/ipx/__init__.py +27 -0
  60. pcapkit/const/ipx/packet.py +72 -0
  61. pcapkit/const/ipx/socket.py +104 -0
  62. pcapkit/const/l2tp/__init__.py +21 -0
  63. pcapkit/const/l2tp/type.py +51 -0
  64. pcapkit/const/mh/__init__.py +204 -0
  65. pcapkit/const/mh/access_type.py +92 -0
  66. pcapkit/const/mh/ack_status_code.py +71 -0
  67. pcapkit/const/mh/ani_suboption.py +74 -0
  68. pcapkit/const/mh/auth_subtype.py +53 -0
  69. pcapkit/const/mh/binding_ack_flag.py +66 -0
  70. pcapkit/const/mh/binding_error.py +51 -0
  71. pcapkit/const/mh/binding_revocation.py +59 -0
  72. pcapkit/const/mh/binding_update_flag.py +81 -0
  73. pcapkit/const/mh/cga_extension.py +66 -0
  74. pcapkit/const/mh/cga_sec.py +57 -0
  75. pcapkit/const/mh/cga_type.py +68 -0
  76. pcapkit/const/mh/dhcp_support_mode.py +53 -0
  77. pcapkit/const/mh/dns_status_code.py +65 -0
  78. pcapkit/const/mh/dsmip6_tls_packet.py +62 -0
  79. pcapkit/const/mh/dsmipv6_home_address.py +74 -0
  80. pcapkit/const/mh/enumerating_algorithm.py +56 -0
  81. pcapkit/const/mh/fb_ack_status.py +62 -0
  82. pcapkit/const/mh/fb_action.py +71 -0
  83. pcapkit/const/mh/fb_indication_trigger.py +65 -0
  84. pcapkit/const/mh/fb_type.py +59 -0
  85. pcapkit/const/mh/flow_id_status.py +77 -0
  86. pcapkit/const/mh/flow_id_suboption.py +71 -0
  87. pcapkit/const/mh/handoff_type.py +71 -0
  88. pcapkit/const/mh/handover_ack_flag.py +54 -0
  89. pcapkit/const/mh/handover_ack_status.py +92 -0
  90. pcapkit/const/mh/handover_initiate_flag.py +57 -0
  91. pcapkit/const/mh/handover_initiate_status.py +62 -0
  92. pcapkit/const/mh/home_address_reply.py +71 -0
  93. pcapkit/const/mh/lla_code.py +63 -0
  94. pcapkit/const/mh/lma_mag_suboption.py +59 -0
  95. pcapkit/const/mh/mn_group_id.py +59 -0
  96. pcapkit/const/mh/mn_id_subtype.py +77 -0
  97. pcapkit/const/mh/operator_id.py +63 -0
  98. pcapkit/const/mh/option.py +260 -0
  99. pcapkit/const/mh/packet.py +119 -0
  100. pcapkit/const/mh/qos_attribute.py +89 -0
  101. pcapkit/const/mh/revocation_status_code.py +83 -0
  102. pcapkit/const/mh/revocation_trigger.py +86 -0
  103. pcapkit/const/mh/status_code.py +232 -0
  104. pcapkit/const/mh/traffic_selector.py +62 -0
  105. pcapkit/const/mh/upa_status.py +71 -0
  106. pcapkit/const/mh/upn_reason.py +80 -0
  107. pcapkit/const/ospf/__init__.py +27 -0
  108. pcapkit/const/ospf/authentication.py +65 -0
  109. pcapkit/const/ospf/packet.py +71 -0
  110. pcapkit/const/pcapng/__init__.py +51 -0
  111. pcapkit/const/pcapng/block_type.py +152 -0
  112. pcapkit/const/pcapng/filter_type.py +48 -0
  113. pcapkit/const/pcapng/hash_algorithm.py +59 -0
  114. pcapkit/const/pcapng/option_type.py +233 -0
  115. pcapkit/const/pcapng/record_type.py +57 -0
  116. pcapkit/const/pcapng/secrets_type.py +56 -0
  117. pcapkit/const/pcapng/verdict_type.py +53 -0
  118. pcapkit/const/reg/__init__.py +34 -0
  119. pcapkit/const/reg/apptype.py +32702 -0
  120. pcapkit/const/reg/ethertype.py +714 -0
  121. pcapkit/const/reg/linktype.py +902 -0
  122. pcapkit/const/reg/transtype.py +523 -0
  123. pcapkit/const/tcp/__init__.py +35 -0
  124. pcapkit/const/tcp/checksum.py +55 -0
  125. pcapkit/const/tcp/flags.py +73 -0
  126. pcapkit/const/tcp/mp_tcp_option.py +80 -0
  127. pcapkit/const/tcp/option.py +198 -0
  128. pcapkit/const/vlan/__init__.py +23 -0
  129. pcapkit/const/vlan/priority_level.py +71 -0
  130. pcapkit/corekit/__init__.py +59 -0
  131. pcapkit/corekit/fields/__init__.py +45 -0
  132. pcapkit/corekit/fields/collections.py +282 -0
  133. pcapkit/corekit/fields/field.py +269 -0
  134. pcapkit/corekit/fields/ipaddress.py +274 -0
  135. pcapkit/corekit/fields/misc.py +722 -0
  136. pcapkit/corekit/fields/numbers.py +375 -0
  137. pcapkit/corekit/fields/strings.py +245 -0
  138. pcapkit/corekit/infoclass.py +394 -0
  139. pcapkit/corekit/io.py +506 -0
  140. pcapkit/corekit/module.py +39 -0
  141. pcapkit/corekit/multidict.py +626 -0
  142. pcapkit/corekit/protochain.py +263 -0
  143. pcapkit/corekit/version.py +33 -0
  144. pcapkit/dumpkit/__init__.py +15 -0
  145. pcapkit/dumpkit/common.py +199 -0
  146. pcapkit/dumpkit/null.py +77 -0
  147. pcapkit/dumpkit/pcap.py +144 -0
  148. pcapkit/foundation/__init__.py +45 -0
  149. pcapkit/foundation/engines/__init__.py +36 -0
  150. pcapkit/foundation/engines/dpkt.py +230 -0
  151. pcapkit/foundation/engines/engine.py +194 -0
  152. pcapkit/foundation/engines/pcap.py +188 -0
  153. pcapkit/foundation/engines/pcapng.py +310 -0
  154. pcapkit/foundation/engines/pyshark.py +166 -0
  155. pcapkit/foundation/engines/scapy.py +161 -0
  156. pcapkit/foundation/extraction.py +915 -0
  157. pcapkit/foundation/reassembly/__init__.py +49 -0
  158. pcapkit/foundation/reassembly/data/__init__.py +48 -0
  159. pcapkit/foundation/reassembly/data/ip.py +117 -0
  160. pcapkit/foundation/reassembly/data/tcp.py +145 -0
  161. pcapkit/foundation/reassembly/ip.py +192 -0
  162. pcapkit/foundation/reassembly/ipv4.py +50 -0
  163. pcapkit/foundation/reassembly/ipv6.py +50 -0
  164. pcapkit/foundation/reassembly/reassembly.py +389 -0
  165. pcapkit/foundation/reassembly/tcp.py +249 -0
  166. pcapkit/foundation/registry/__init__.py +41 -0
  167. pcapkit/foundation/registry/foundation.py +327 -0
  168. pcapkit/foundation/registry/protocols.py +885 -0
  169. pcapkit/foundation/traceflow/__init__.py +44 -0
  170. pcapkit/foundation/traceflow/data/__init__.py +30 -0
  171. pcapkit/foundation/traceflow/data/tcp.py +105 -0
  172. pcapkit/foundation/traceflow/tcp.py +159 -0
  173. pcapkit/foundation/traceflow/traceflow.py +390 -0
  174. pcapkit/interface/__init__.py +22 -0
  175. pcapkit/interface/core.py +185 -0
  176. pcapkit/interface/misc.py +120 -0
  177. pcapkit/protocols/__init__.py +85 -0
  178. pcapkit/protocols/application/NotImplemented/bgp.py +0 -0
  179. pcapkit/protocols/application/NotImplemented/dhcp.py +0 -0
  180. pcapkit/protocols/application/NotImplemented/dhcpv6.py +0 -0
  181. pcapkit/protocols/application/NotImplemented/dns.py +0 -0
  182. pcapkit/protocols/application/NotImplemented/imap.py +0 -0
  183. pcapkit/protocols/application/NotImplemented/ldap.py +0 -0
  184. pcapkit/protocols/application/NotImplemented/mqtt.py +0 -0
  185. pcapkit/protocols/application/NotImplemented/nntp.py +0 -0
  186. pcapkit/protocols/application/NotImplemented/ntp.py +0 -0
  187. pcapkit/protocols/application/NotImplemented/onc_rpc.py +0 -0
  188. pcapkit/protocols/application/NotImplemented/pop.py +0 -0
  189. pcapkit/protocols/application/NotImplemented/rip.py +0 -0
  190. pcapkit/protocols/application/NotImplemented/rtp.py +0 -0
  191. pcapkit/protocols/application/NotImplemented/sip.py +0 -0
  192. pcapkit/protocols/application/NotImplemented/smtp.py +0 -0
  193. pcapkit/protocols/application/NotImplemented/snmp.py +0 -0
  194. pcapkit/protocols/application/NotImplemented/ssh.py +0 -0
  195. pcapkit/protocols/application/NotImplemented/telnet.py +0 -0
  196. pcapkit/protocols/application/NotImplemented/tls.py +0 -0
  197. pcapkit/protocols/application/NotImplemented/xmpp.py +0 -0
  198. pcapkit/protocols/application/__init__.py +34 -0
  199. pcapkit/protocols/application/application.py +114 -0
  200. pcapkit/protocols/application/ftp.py +206 -0
  201. pcapkit/protocols/application/http.py +176 -0
  202. pcapkit/protocols/application/httpv1.py +320 -0
  203. pcapkit/protocols/application/httpv2.py +1255 -0
  204. pcapkit/protocols/data/__init__.py +192 -0
  205. pcapkit/protocols/data/application/__init__.py +57 -0
  206. pcapkit/protocols/data/application/ftp.py +59 -0
  207. pcapkit/protocols/data/application/httpv1.py +79 -0
  208. pcapkit/protocols/data/application/httpv2.py +293 -0
  209. pcapkit/protocols/data/data.py +25 -0
  210. pcapkit/protocols/data/internet/__init__.py +298 -0
  211. pcapkit/protocols/data/internet/ah.py +31 -0
  212. pcapkit/protocols/data/internet/hip.py +804 -0
  213. pcapkit/protocols/data/internet/hopopt.py +351 -0
  214. pcapkit/protocols/data/internet/ipv4.py +369 -0
  215. pcapkit/protocols/data/internet/ipv6.py +67 -0
  216. pcapkit/protocols/data/internet/ipv6_frag.py +29 -0
  217. pcapkit/protocols/data/internet/ipv6_opts.py +368 -0
  218. pcapkit/protocols/data/internet/ipv6_route.py +86 -0
  219. pcapkit/protocols/data/internet/ipx.py +56 -0
  220. pcapkit/protocols/data/internet/mh.py +509 -0
  221. pcapkit/protocols/data/link/__init__.py +33 -0
  222. pcapkit/protocols/data/link/arp.py +74 -0
  223. pcapkit/protocols/data/link/ethernet.py +28 -0
  224. pcapkit/protocols/data/link/l2tp.py +63 -0
  225. pcapkit/protocols/data/link/ospf.py +58 -0
  226. pcapkit/protocols/data/link/vlan.py +42 -0
  227. pcapkit/protocols/data/misc/__init__.py +109 -0
  228. pcapkit/protocols/data/misc/null.py +18 -0
  229. pcapkit/protocols/data/misc/pcap/__init__.py +18 -0
  230. pcapkit/protocols/data/misc/pcap/frame.py +56 -0
  231. pcapkit/protocols/data/misc/pcap/header.py +53 -0
  232. pcapkit/protocols/data/misc/pcapng.py +925 -0
  233. pcapkit/protocols/data/misc/raw.py +25 -0
  234. pcapkit/protocols/data/protocol.py +32 -0
  235. pcapkit/protocols/data/transport/__init__.py +71 -0
  236. pcapkit/protocols/data/transport/tcp.py +555 -0
  237. pcapkit/protocols/data/transport/udp.py +29 -0
  238. pcapkit/protocols/internet/NotImplemented/ecn.py +0 -0
  239. pcapkit/protocols/internet/NotImplemented/esp.py +97 -0
  240. pcapkit/protocols/internet/NotImplemented/icmp.py +0 -0
  241. pcapkit/protocols/internet/NotImplemented/icmpv6.py +0 -0
  242. pcapkit/protocols/internet/NotImplemented/igmp.py +0 -0
  243. pcapkit/protocols/internet/NotImplemented/shim6.py +0 -0
  244. pcapkit/protocols/internet/__init__.py +43 -0
  245. pcapkit/protocols/internet/ah.py +275 -0
  246. pcapkit/protocols/internet/hip.py +4727 -0
  247. pcapkit/protocols/internet/hopopt.py +1879 -0
  248. pcapkit/protocols/internet/internet.py +240 -0
  249. pcapkit/protocols/internet/ip.py +51 -0
  250. pcapkit/protocols/internet/ipsec.py +50 -0
  251. pcapkit/protocols/internet/ipv4.py +1782 -0
  252. pcapkit/protocols/internet/ipv6.py +361 -0
  253. pcapkit/protocols/internet/ipv6_frag.py +258 -0
  254. pcapkit/protocols/internet/ipv6_opts.py +1890 -0
  255. pcapkit/protocols/internet/ipv6_route.py +710 -0
  256. pcapkit/protocols/internet/ipx.py +230 -0
  257. pcapkit/protocols/internet/mh.py +2764 -0
  258. pcapkit/protocols/link/NotImplemented/dsl.py +0 -0
  259. pcapkit/protocols/link/NotImplemented/eapol.py +1 -0
  260. pcapkit/protocols/link/NotImplemented/fddi.py +0 -0
  261. pcapkit/protocols/link/NotImplemented/isdn.py +0 -0
  262. pcapkit/protocols/link/NotImplemented/ndp.py +0 -0
  263. pcapkit/protocols/link/NotImplemented/ppp.py +0 -0
  264. pcapkit/protocols/link/__init__.py +35 -0
  265. pcapkit/protocols/link/arp.py +421 -0
  266. pcapkit/protocols/link/ethernet.py +248 -0
  267. pcapkit/protocols/link/l2tp.py +267 -0
  268. pcapkit/protocols/link/link.py +140 -0
  269. pcapkit/protocols/link/ospf.py +342 -0
  270. pcapkit/protocols/link/rarp.py +82 -0
  271. pcapkit/protocols/link/vlan.py +225 -0
  272. pcapkit/protocols/misc/__init__.py +37 -0
  273. pcapkit/protocols/misc/null.py +129 -0
  274. pcapkit/protocols/misc/pcap/__init__.py +17 -0
  275. pcapkit/protocols/misc/pcap/frame.py +478 -0
  276. pcapkit/protocols/misc/pcap/header.py +358 -0
  277. pcapkit/protocols/misc/pcapng.py +5520 -0
  278. pcapkit/protocols/misc/raw.py +180 -0
  279. pcapkit/protocols/protocol.py +1216 -0
  280. pcapkit/protocols/schema/__init__.py +140 -0
  281. pcapkit/protocols/schema/application/__init__.py +40 -0
  282. pcapkit/protocols/schema/application/ftp.py +21 -0
  283. pcapkit/protocols/schema/application/httpv1.py +21 -0
  284. pcapkit/protocols/schema/application/httpv2.py +384 -0
  285. pcapkit/protocols/schema/internet/__init__.py +294 -0
  286. pcapkit/protocols/schema/internet/ah.py +40 -0
  287. pcapkit/protocols/schema/internet/hip.py +1184 -0
  288. pcapkit/protocols/schema/internet/hopopt.py +679 -0
  289. pcapkit/protocols/schema/internet/ipv4.py +576 -0
  290. pcapkit/protocols/schema/internet/ipv6.py +63 -0
  291. pcapkit/protocols/schema/internet/ipv6_frag.py +48 -0
  292. pcapkit/protocols/schema/internet/ipv6_opts.py +680 -0
  293. pcapkit/protocols/schema/internet/ipv6_route.py +198 -0
  294. pcapkit/protocols/schema/internet/ipx.py +40 -0
  295. pcapkit/protocols/schema/internet/mh.py +718 -0
  296. pcapkit/protocols/schema/link/__init__.py +19 -0
  297. pcapkit/protocols/schema/link/arp.py +39 -0
  298. pcapkit/protocols/schema/link/ethernet.py +51 -0
  299. pcapkit/protocols/schema/link/l2tp.py +88 -0
  300. pcapkit/protocols/schema/link/ospf.py +90 -0
  301. pcapkit/protocols/schema/link/vlan.py +69 -0
  302. pcapkit/protocols/schema/misc/__init__.py +108 -0
  303. pcapkit/protocols/schema/misc/null.py +18 -0
  304. pcapkit/protocols/schema/misc/pcap/__init__.py +10 -0
  305. pcapkit/protocols/schema/misc/pcap/frame.py +51 -0
  306. pcapkit/protocols/schema/misc/pcap/header.py +63 -0
  307. pcapkit/protocols/schema/misc/pcapng.py +1689 -0
  308. pcapkit/protocols/schema/misc/raw.py +24 -0
  309. pcapkit/protocols/schema/schema.py +809 -0
  310. pcapkit/protocols/schema/transport/__init__.py +69 -0
  311. pcapkit/protocols/schema/transport/tcp.py +928 -0
  312. pcapkit/protocols/schema/transport/udp.py +90 -0
  313. pcapkit/protocols/transport/NotImplemented/dccp.py +0 -0
  314. pcapkit/protocols/transport/NotImplemented/rsvp.py +0 -0
  315. pcapkit/protocols/transport/NotImplemented/sctp.py +0 -0
  316. pcapkit/protocols/transport/__init__.py +27 -0
  317. pcapkit/protocols/transport/tcp.py +3025 -0
  318. pcapkit/protocols/transport/transport.py +158 -0
  319. pcapkit/protocols/transport/udp.py +214 -0
  320. pcapkit/py.typed +0 -0
  321. pcapkit/toolkit/__init__.py +57 -0
  322. pcapkit/toolkit/dpkt.py +306 -0
  323. pcapkit/toolkit/pcap.py +212 -0
  324. pcapkit/toolkit/pcapng.py +251 -0
  325. pcapkit/toolkit/pyshark.py +99 -0
  326. pcapkit/toolkit/scapy.py +297 -0
  327. pcapkit/utilities/__init__.py +20 -0
  328. pcapkit/utilities/compat.py +196 -0
  329. pcapkit/utilities/decorators.py +192 -0
  330. pcapkit/utilities/exceptions.py +365 -0
  331. pcapkit/utilities/logging.py +55 -0
  332. pcapkit/utilities/warnings.py +185 -0
  333. pcapkit/vendor/__init__.py +105 -0
  334. pcapkit/vendor/__main__.py +92 -0
  335. pcapkit/vendor/arp/__init__.py +27 -0
  336. pcapkit/vendor/arp/hardware.py +29 -0
  337. pcapkit/vendor/arp/operation.py +29 -0
  338. pcapkit/vendor/default.py +474 -0
  339. pcapkit/vendor/ftp/__init__.py +27 -0
  340. pcapkit/vendor/ftp/command.py +244 -0
  341. pcapkit/vendor/ftp/return_code.py +256 -0
  342. pcapkit/vendor/hip/__init__.py +94 -0
  343. pcapkit/vendor/hip/certificate.py +29 -0
  344. pcapkit/vendor/hip/cipher.py +29 -0
  345. pcapkit/vendor/hip/di.py +29 -0
  346. pcapkit/vendor/hip/ecdsa_curve.py +29 -0
  347. pcapkit/vendor/hip/ecdsa_low_curve.py +29 -0
  348. pcapkit/vendor/hip/eddsa_curve.py +85 -0
  349. pcapkit/vendor/hip/esp_transform_suite.py +29 -0
  350. pcapkit/vendor/hip/group.py +87 -0
  351. pcapkit/vendor/hip/hi_algorithm.py +29 -0
  352. pcapkit/vendor/hip/hit_suite.py +29 -0
  353. pcapkit/vendor/hip/nat_traversal.py +29 -0
  354. pcapkit/vendor/hip/notify_message.py +29 -0
  355. pcapkit/vendor/hip/packet.py +88 -0
  356. pcapkit/vendor/hip/parameter.py +88 -0
  357. pcapkit/vendor/hip/registration.py +29 -0
  358. pcapkit/vendor/hip/registration_failure.py +29 -0
  359. pcapkit/vendor/hip/suite.py +29 -0
  360. pcapkit/vendor/hip/transport.py +29 -0
  361. pcapkit/vendor/http/__init__.py +39 -0
  362. pcapkit/vendor/http/error_code.py +95 -0
  363. pcapkit/vendor/http/frame.py +91 -0
  364. pcapkit/vendor/http/method.py +167 -0
  365. pcapkit/vendor/http/setting.py +93 -0
  366. pcapkit/vendor/http/status_code.py +185 -0
  367. pcapkit/vendor/ipv4/__init__.py +57 -0
  368. pcapkit/vendor/ipv4/classification_level.py +91 -0
  369. pcapkit/vendor/ipv4/option_class.py +80 -0
  370. pcapkit/vendor/ipv4/option_number.py +105 -0
  371. pcapkit/vendor/ipv4/protection_authority.py +84 -0
  372. pcapkit/vendor/ipv4/qs_function.py +78 -0
  373. pcapkit/vendor/ipv4/router_alert.py +93 -0
  374. pcapkit/vendor/ipv4/tos_del.py +78 -0
  375. pcapkit/vendor/ipv4/tos_ecn.py +95 -0
  376. pcapkit/vendor/ipv4/tos_pre.py +84 -0
  377. pcapkit/vendor/ipv4/tos_rel.py +78 -0
  378. pcapkit/vendor/ipv4/tos_thr.py +77 -0
  379. pcapkit/vendor/ipv4/ts_flag.py +79 -0
  380. pcapkit/vendor/ipv6/__init__.py +53 -0
  381. pcapkit/vendor/ipv6/extension_header.py +171 -0
  382. pcapkit/vendor/ipv6/option.py +104 -0
  383. pcapkit/vendor/ipv6/option_action.py +90 -0
  384. pcapkit/vendor/ipv6/qs_function.py +78 -0
  385. pcapkit/vendor/ipv6/router_alert.py +93 -0
  386. pcapkit/vendor/ipv6/routing.py +87 -0
  387. pcapkit/vendor/ipv6/seed_id.py +81 -0
  388. pcapkit/vendor/ipv6/smf_dpd_mode.py +78 -0
  389. pcapkit/vendor/ipv6/tagger_id.py +81 -0
  390. pcapkit/vendor/ipx/__init__.py +37 -0
  391. pcapkit/vendor/ipx/packet.py +123 -0
  392. pcapkit/vendor/ipx/socket.py +125 -0
  393. pcapkit/vendor/l2tp/__init__.py +21 -0
  394. pcapkit/vendor/l2tp/type.py +78 -0
  395. pcapkit/vendor/mh/__init__.py +204 -0
  396. pcapkit/vendor/mh/access_type.py +87 -0
  397. pcapkit/vendor/mh/ack_status_code.py +88 -0
  398. pcapkit/vendor/mh/ani_suboption.py +88 -0
  399. pcapkit/vendor/mh/auth_subtype.py +83 -0
  400. pcapkit/vendor/mh/binding_ack_flag.py +148 -0
  401. pcapkit/vendor/mh/binding_error.py +78 -0
  402. pcapkit/vendor/mh/binding_revocation.py +87 -0
  403. pcapkit/vendor/mh/binding_update_flag.py +147 -0
  404. pcapkit/vendor/mh/cga_extension.py +91 -0
  405. pcapkit/vendor/mh/cga_sec.py +91 -0
  406. pcapkit/vendor/mh/cga_type.py +74 -0
  407. pcapkit/vendor/mh/dhcp_support_mode.py +77 -0
  408. pcapkit/vendor/mh/dns_status_code.py +87 -0
  409. pcapkit/vendor/mh/dsmip6_tls_packet.py +87 -0
  410. pcapkit/vendor/mh/dsmipv6_home_address.py +87 -0
  411. pcapkit/vendor/mh/enumerating_algorithm.py +82 -0
  412. pcapkit/vendor/mh/fb_ack_status.py +87 -0
  413. pcapkit/vendor/mh/fb_action.py +88 -0
  414. pcapkit/vendor/mh/fb_indication_trigger.py +87 -0
  415. pcapkit/vendor/mh/fb_type.py +88 -0
  416. pcapkit/vendor/mh/flow_id_status.py +87 -0
  417. pcapkit/vendor/mh/flow_id_suboption.py +87 -0
  418. pcapkit/vendor/mh/handoff_type.py +87 -0
  419. pcapkit/vendor/mh/handover_ack_flag.py +143 -0
  420. pcapkit/vendor/mh/handover_ack_status.py +87 -0
  421. pcapkit/vendor/mh/handover_initiate_flag.py +143 -0
  422. pcapkit/vendor/mh/handover_initiate_status.py +87 -0
  423. pcapkit/vendor/mh/home_address_reply.py +87 -0
  424. pcapkit/vendor/mh/lla_code.py +97 -0
  425. pcapkit/vendor/mh/lma_mag_suboption.py +88 -0
  426. pcapkit/vendor/mh/mn_group_id.py +87 -0
  427. pcapkit/vendor/mh/mn_id_subtype.py +87 -0
  428. pcapkit/vendor/mh/operator_id.py +87 -0
  429. pcapkit/vendor/mh/option.py +83 -0
  430. pcapkit/vendor/mh/packet.py +82 -0
  431. pcapkit/vendor/mh/qos_attribute.py +87 -0
  432. pcapkit/vendor/mh/revocation_status_code.py +87 -0
  433. pcapkit/vendor/mh/revocation_trigger.py +87 -0
  434. pcapkit/vendor/mh/status_code.py +91 -0
  435. pcapkit/vendor/mh/traffic_selector.py +87 -0
  436. pcapkit/vendor/mh/upa_status.py +87 -0
  437. pcapkit/vendor/mh/upn_reason.py +87 -0
  438. pcapkit/vendor/ospf/__init__.py +27 -0
  439. pcapkit/vendor/ospf/authentication.py +29 -0
  440. pcapkit/vendor/ospf/packet.py +29 -0
  441. pcapkit/vendor/pcapng/__init__.py +51 -0
  442. pcapkit/vendor/pcapng/block_type.py +94 -0
  443. pcapkit/vendor/pcapng/filter_type.py +77 -0
  444. pcapkit/vendor/pcapng/hash_algorithm.py +82 -0
  445. pcapkit/vendor/pcapng/option_type.py +287 -0
  446. pcapkit/vendor/pcapng/record_type.py +81 -0
  447. pcapkit/vendor/pcapng/secrets_type.py +81 -0
  448. pcapkit/vendor/pcapng/verdict_type.py +79 -0
  449. pcapkit/vendor/reg/__init__.py +34 -0
  450. pcapkit/vendor/reg/apptype.py +338 -0
  451. pcapkit/vendor/reg/ethertype.py +121 -0
  452. pcapkit/vendor/reg/linktype.py +110 -0
  453. pcapkit/vendor/reg/transtype.py +111 -0
  454. pcapkit/vendor/tcp/__init__.py +35 -0
  455. pcapkit/vendor/tcp/checksum.py +80 -0
  456. pcapkit/vendor/tcp/flags.py +149 -0
  457. pcapkit/vendor/tcp/mp_tcp_option.py +90 -0
  458. pcapkit/vendor/tcp/option.py +103 -0
  459. pcapkit/vendor/vlan/__init__.py +23 -0
  460. pcapkit/vendor/vlan/priority_level.py +97 -0
  461. pypcapkit-1.3.3.post1.dist-info/LICENSE +29 -0
  462. pypcapkit-1.3.3.post1.dist-info/METADATA +236 -0
  463. pypcapkit-1.3.3.post1.dist-info/RECORD +466 -0
  464. pypcapkit-1.3.3.post1.dist-info/WHEEL +5 -0
  465. pypcapkit-1.3.3.post1.dist-info/entry_points.txt +3 -0
  466. 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]