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