@netkitty/codec 1.0.0
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.
- package/CHANGELOG.md +17 -0
- package/LICENSE +21 -0
- package/README.md +352 -0
- package/README.zh-CN.md +342 -0
- package/dist/Codec.d.ts +133 -0
- package/dist/Codec.js +632 -0
- package/dist/PacketHeaders.d.ts +189 -0
- package/dist/PacketHeaders.js +384 -0
- package/dist/abstracts/BaseHeader.d.ts +325 -0
- package/dist/abstracts/BaseHeader.js +734 -0
- package/dist/errors/CodecSchemaValidateError.d.ts +4 -0
- package/dist/errors/CodecSchemaValidateError.js +11 -0
- package/dist/headers/ADSAMS.d.ts +33 -0
- package/dist/headers/ADSAMS.js +208 -0
- package/dist/headers/AH.d.ts +28 -0
- package/dist/headers/AH.js +137 -0
- package/dist/headers/AMQP.d.ts +31 -0
- package/dist/headers/AMQP.js +316 -0
- package/dist/headers/ARP.d.ts +26 -0
- package/dist/headers/ARP.js +243 -0
- package/dist/headers/BACnetIP.d.ts +27 -0
- package/dist/headers/BACnetIP.js +97 -0
- package/dist/headers/BFD.d.ts +28 -0
- package/dist/headers/BFD.js +155 -0
- package/dist/headers/BGP.d.ts +27 -0
- package/dist/headers/BGP.js +111 -0
- package/dist/headers/BSAPIP.d.ts +33 -0
- package/dist/headers/BSAPIP.js +78 -0
- package/dist/headers/Babel.d.ts +32 -0
- package/dist/headers/Babel.js +224 -0
- package/dist/headers/BitTorrent.d.ts +28 -0
- package/dist/headers/BitTorrent.js +72 -0
- package/dist/headers/C37118.d.ts +26 -0
- package/dist/headers/C37118.js +147 -0
- package/dist/headers/CAPWAP.d.ts +38 -0
- package/dist/headers/CAPWAP.js +275 -0
- package/dist/headers/CDP.d.ts +25 -0
- package/dist/headers/CDP.js +117 -0
- package/dist/headers/CMS.d.ts +33 -0
- package/dist/headers/CMS.js +238 -0
- package/dist/headers/COTP.d.ts +30 -0
- package/dist/headers/COTP.js +193 -0
- package/dist/headers/CassandraCQL.d.ts +28 -0
- package/dist/headers/CassandraCQL.js +117 -0
- package/dist/headers/CoAP.d.ts +29 -0
- package/dist/headers/CoAP.js +140 -0
- package/dist/headers/Codesys.d.ts +32 -0
- package/dist/headers/Codesys.js +119 -0
- package/dist/headers/Collectd.d.ts +29 -0
- package/dist/headers/Collectd.js +154 -0
- package/dist/headers/DCCP.d.ts +30 -0
- package/dist/headers/DCCP.js +204 -0
- package/dist/headers/DHCP.d.ts +26 -0
- package/dist/headers/DHCP.js +148 -0
- package/dist/headers/DHCPFailover.d.ts +30 -0
- package/dist/headers/DHCPFailover.js +104 -0
- package/dist/headers/DHCPv6.d.ts +25 -0
- package/dist/headers/DHCPv6.js +145 -0
- package/dist/headers/DNP3.d.ts +27 -0
- package/dist/headers/DNP3.js +150 -0
- package/dist/headers/DNS.d.ts +55 -0
- package/dist/headers/DNS.js +309 -0
- package/dist/headers/DTLS.d.ts +33 -0
- package/dist/headers/DTLS.js +142 -0
- package/dist/headers/Diameter.d.ts +34 -0
- package/dist/headers/Diameter.js +229 -0
- package/dist/headers/EAPOL.d.ts +32 -0
- package/dist/headers/EAPOL.js +135 -0
- package/dist/headers/EIGRP.d.ts +28 -0
- package/dist/headers/EIGRP.js +165 -0
- package/dist/headers/ENIP.d.ts +31 -0
- package/dist/headers/ENIP.js +164 -0
- package/dist/headers/ESP.d.ts +27 -0
- package/dist/headers/ESP.js +140 -0
- package/dist/headers/Elasticsearch.d.ts +30 -0
- package/dist/headers/Elasticsearch.js +125 -0
- package/dist/headers/EtherCAT.d.ts +35 -0
- package/dist/headers/EtherCAT.js +150 -0
- package/dist/headers/EthernetII.d.ts +28 -0
- package/dist/headers/EthernetII.js +117 -0
- package/dist/headers/FCoE.d.ts +38 -0
- package/dist/headers/FCoE.js +210 -0
- package/dist/headers/FTP.d.ts +34 -0
- package/dist/headers/FTP.js +158 -0
- package/dist/headers/Finger.d.ts +41 -0
- package/dist/headers/Finger.js +135 -0
- package/dist/headers/FoundationFieldbusHSE.d.ts +43 -0
- package/dist/headers/FoundationFieldbusHSE.js +163 -0
- package/dist/headers/GELF.d.ts +35 -0
- package/dist/headers/GELF.js +261 -0
- package/dist/headers/GENEVE.d.ts +26 -0
- package/dist/headers/GENEVE.js +209 -0
- package/dist/headers/GESRTP.d.ts +32 -0
- package/dist/headers/GESRTP.js +141 -0
- package/dist/headers/GLBP.d.ts +29 -0
- package/dist/headers/GLBP.js +159 -0
- package/dist/headers/GRE.d.ts +27 -0
- package/dist/headers/GRE.js +181 -0
- package/dist/headers/GTPU.d.ts +25 -0
- package/dist/headers/GTPU.js +203 -0
- package/dist/headers/GTPv2C.d.ts +30 -0
- package/dist/headers/GTPv2C.js +281 -0
- package/dist/headers/GitProtocol.d.ts +34 -0
- package/dist/headers/GitProtocol.js +141 -0
- package/dist/headers/Goose.d.ts +115 -0
- package/dist/headers/Goose.js +1067 -0
- package/dist/headers/Gopher.d.ts +43 -0
- package/dist/headers/Gopher.js +182 -0
- package/dist/headers/HARTIP.d.ts +30 -0
- package/dist/headers/HARTIP.js +145 -0
- package/dist/headers/HSR.d.ts +27 -0
- package/dist/headers/HSR.js +105 -0
- package/dist/headers/HSRP.d.ts +26 -0
- package/dist/headers/HSRP.js +173 -0
- package/dist/headers/HTTP.d.ts +32 -0
- package/dist/headers/HTTP.js +153 -0
- package/dist/headers/HTTP2.d.ts +34 -0
- package/dist/headers/HTTP2.js +190 -0
- package/dist/headers/IAX2.d.ts +33 -0
- package/dist/headers/IAX2.js +274 -0
- package/dist/headers/ICMP.d.ts +15 -0
- package/dist/headers/ICMP.js +148 -0
- package/dist/headers/ICMPv6.d.ts +15 -0
- package/dist/headers/ICMPv6.js +129 -0
- package/dist/headers/IEC104_I_Frame.d.ts +23 -0
- package/dist/headers/IEC104_I_Frame.js +6489 -0
- package/dist/headers/IEC104_S_Frame.d.ts +26 -0
- package/dist/headers/IEC104_S_Frame.js +135 -0
- package/dist/headers/IEC104_U_Frame.d.ts +27 -0
- package/dist/headers/IEC104_U_Frame.js +176 -0
- package/dist/headers/IEC61850SampledValues.d.ts +20 -0
- package/dist/headers/IEC61850SampledValues.js +420 -0
- package/dist/headers/IGMP.d.ts +31 -0
- package/dist/headers/IGMP.js +274 -0
- package/dist/headers/IMAP.d.ts +32 -0
- package/dist/headers/IMAP.js +185 -0
- package/dist/headers/IPFIX.d.ts +29 -0
- package/dist/headers/IPFIX.js +156 -0
- package/dist/headers/IPv4.d.ts +20 -0
- package/dist/headers/IPv4.js +370 -0
- package/dist/headers/IPv6.d.ts +29 -0
- package/dist/headers/IPv6.js +209 -0
- package/dist/headers/IPv6HopByHopOptions.d.ts +81 -0
- package/dist/headers/IPv6HopByHopOptions.js +495 -0
- package/dist/headers/IRC.d.ts +34 -0
- package/dist/headers/IRC.js +175 -0
- package/dist/headers/ISAKMP.d.ts +29 -0
- package/dist/headers/ISAKMP.js +197 -0
- package/dist/headers/ISCSI.d.ts +39 -0
- package/dist/headers/ISCSI.js +289 -0
- package/dist/headers/ISIS.d.ts +25 -0
- package/dist/headers/ISIS.js +79 -0
- package/dist/headers/ISOSession.d.ts +27 -0
- package/dist/headers/ISOSession.js +343 -0
- package/dist/headers/Ident.d.ts +42 -0
- package/dist/headers/Ident.js +184 -0
- package/dist/headers/KNXnetIP.d.ts +30 -0
- package/dist/headers/KNXnetIP.js +135 -0
- package/dist/headers/Kafka.d.ts +35 -0
- package/dist/headers/Kafka.js +128 -0
- package/dist/headers/Kerberos.d.ts +30 -0
- package/dist/headers/Kerberos.js +217 -0
- package/dist/headers/L2TP.d.ts +26 -0
- package/dist/headers/L2TP.js +297 -0
- package/dist/headers/LACP.d.ts +31 -0
- package/dist/headers/LACP.js +151 -0
- package/dist/headers/LDAP.d.ts +31 -0
- package/dist/headers/LDAP.js +226 -0
- package/dist/headers/LDP.d.ts +31 -0
- package/dist/headers/LDP.js +138 -0
- package/dist/headers/LISP.d.ts +30 -0
- package/dist/headers/LISP.js +103 -0
- package/dist/headers/LLC.d.ts +27 -0
- package/dist/headers/LLC.js +81 -0
- package/dist/headers/LLDP.d.ts +26 -0
- package/dist/headers/LLDP.js +126 -0
- package/dist/headers/LLMNR.d.ts +23 -0
- package/dist/headers/LLMNR.js +40 -0
- package/dist/headers/LPD.d.ts +31 -0
- package/dist/headers/LPD.js +159 -0
- package/dist/headers/MACsec.d.ts +34 -0
- package/dist/headers/MACsec.js +178 -0
- package/dist/headers/MDNS.d.ts +26 -0
- package/dist/headers/MDNS.js +45 -0
- package/dist/headers/MGCP.d.ts +30 -0
- package/dist/headers/MGCP.js +155 -0
- package/dist/headers/MMS.d.ts +29 -0
- package/dist/headers/MMS.js +243 -0
- package/dist/headers/MPLS.d.ts +31 -0
- package/dist/headers/MPLS.js +145 -0
- package/dist/headers/MQTT.d.ts +30 -0
- package/dist/headers/MQTT.js +173 -0
- package/dist/headers/MQTTSN.d.ts +34 -0
- package/dist/headers/MQTTSN.js +177 -0
- package/dist/headers/MarkerProtocol.d.ts +32 -0
- package/dist/headers/MarkerProtocol.js +153 -0
- package/dist/headers/Megaco.d.ts +32 -0
- package/dist/headers/Megaco.js +144 -0
- package/dist/headers/Memcached.d.ts +44 -0
- package/dist/headers/Memcached.js +285 -0
- package/dist/headers/ModbusTCP.d.ts +25 -0
- package/dist/headers/ModbusTCP.js +99 -0
- package/dist/headers/ModbusUDP.d.ts +26 -0
- package/dist/headers/ModbusUDP.js +100 -0
- package/dist/headers/MongoDB.d.ts +33 -0
- package/dist/headers/MongoDB.js +143 -0
- package/dist/headers/MySQL.d.ts +30 -0
- package/dist/headers/MySQL.js +114 -0
- package/dist/headers/NATS.d.ts +29 -0
- package/dist/headers/NATS.js +135 -0
- package/dist/headers/NBDS.d.ts +34 -0
- package/dist/headers/NBDS.js +176 -0
- package/dist/headers/NBNS.d.ts +34 -0
- package/dist/headers/NBNS.js +78 -0
- package/dist/headers/NBSS.d.ts +36 -0
- package/dist/headers/NBSS.js +137 -0
- package/dist/headers/NFS.d.ts +36 -0
- package/dist/headers/NFS.js +273 -0
- package/dist/headers/NHRP.d.ts +32 -0
- package/dist/headers/NHRP.js +154 -0
- package/dist/headers/NNTP.d.ts +35 -0
- package/dist/headers/NNTP.js +162 -0
- package/dist/headers/NTP.d.ts +18 -0
- package/dist/headers/NTP.js +73 -0
- package/dist/headers/NetFlowV5.d.ts +28 -0
- package/dist/headers/NetFlowV5.js +205 -0
- package/dist/headers/OLSR.d.ts +31 -0
- package/dist/headers/OLSR.js +213 -0
- package/dist/headers/OPCUA.d.ts +33 -0
- package/dist/headers/OPCUA.js +165 -0
- package/dist/headers/OPCUAPubSub.d.ts +33 -0
- package/dist/headers/OPCUAPubSub.js +144 -0
- package/dist/headers/OSPF.d.ts +31 -0
- package/dist/headers/OSPF.js +365 -0
- package/dist/headers/OmronFINS.d.ts +31 -0
- package/dist/headers/OmronFINS.js +90 -0
- package/dist/headers/OpenFlow.d.ts +30 -0
- package/dist/headers/OpenFlow.js +113 -0
- package/dist/headers/OpenVPN.d.ts +35 -0
- package/dist/headers/OpenVPN.js +214 -0
- package/dist/headers/PCWorx.d.ts +35 -0
- package/dist/headers/PCWorx.js +115 -0
- package/dist/headers/PIM.d.ts +27 -0
- package/dist/headers/PIM.js +129 -0
- package/dist/headers/POP3.d.ts +31 -0
- package/dist/headers/POP3.js +152 -0
- package/dist/headers/POWERLINK.d.ts +28 -0
- package/dist/headers/POWERLINK.js +116 -0
- package/dist/headers/PPPoEDiscovery.d.ts +29 -0
- package/dist/headers/PPPoEDiscovery.js +198 -0
- package/dist/headers/PPPoESession.d.ts +28 -0
- package/dist/headers/PPPoESession.js +125 -0
- package/dist/headers/PPTP.d.ts +30 -0
- package/dist/headers/PPTP.js +122 -0
- package/dist/headers/PROFINETRT.d.ts +28 -0
- package/dist/headers/PROFINETRT.js +74 -0
- package/dist/headers/PTP.d.ts +34 -0
- package/dist/headers/PTP.js +213 -0
- package/dist/headers/PostgreSQL.d.ts +40 -0
- package/dist/headers/PostgreSQL.js +236 -0
- package/dist/headers/QUIC.d.ts +34 -0
- package/dist/headers/QUIC.js +308 -0
- package/dist/headers/RADIUS.d.ts +24 -0
- package/dist/headers/RADIUS.js +138 -0
- package/dist/headers/RDP.d.ts +27 -0
- package/dist/headers/RDP.js +93 -0
- package/dist/headers/RFB.d.ts +39 -0
- package/dist/headers/RFB.js +151 -0
- package/dist/headers/RGoose.d.ts +72 -0
- package/dist/headers/RGoose.js +340 -0
- package/dist/headers/RIP.d.ts +29 -0
- package/dist/headers/RIP.js +133 -0
- package/dist/headers/RIPng.d.ts +26 -0
- package/dist/headers/RIPng.js +140 -0
- package/dist/headers/RMCP.d.ts +26 -0
- package/dist/headers/RMCP.js +155 -0
- package/dist/headers/RSVP.d.ts +31 -0
- package/dist/headers/RSVP.js +263 -0
- package/dist/headers/RTCP.d.ts +34 -0
- package/dist/headers/RTCP.js +217 -0
- package/dist/headers/RTP.d.ts +33 -0
- package/dist/headers/RTP.js +291 -0
- package/dist/headers/RTPS.d.ts +33 -0
- package/dist/headers/RTPS.js +182 -0
- package/dist/headers/RTSP.d.ts +36 -0
- package/dist/headers/RTSP.js +184 -0
- package/dist/headers/RawData.d.ts +17 -0
- package/dist/headers/RawData.js +43 -0
- package/dist/headers/Redis.d.ts +32 -0
- package/dist/headers/Redis.js +202 -0
- package/dist/headers/Rlogin.d.ts +42 -0
- package/dist/headers/Rlogin.js +139 -0
- package/dist/headers/Rsync.d.ts +28 -0
- package/dist/headers/Rsync.js +117 -0
- package/dist/headers/S7comm.d.ts +36 -0
- package/dist/headers/S7comm.js +244 -0
- package/dist/headers/SCTP.d.ts +35 -0
- package/dist/headers/SCTP.js +174 -0
- package/dist/headers/SFlow.d.ts +29 -0
- package/dist/headers/SFlow.js +242 -0
- package/dist/headers/SIP.d.ts +27 -0
- package/dist/headers/SIP.js +146 -0
- package/dist/headers/SLMP.d.ts +36 -0
- package/dist/headers/SLMP.js +154 -0
- package/dist/headers/SLP.d.ts +30 -0
- package/dist/headers/SLP.js +207 -0
- package/dist/headers/SLPv1.d.ts +32 -0
- package/dist/headers/SLPv1.js +138 -0
- package/dist/headers/SMB1.d.ts +37 -0
- package/dist/headers/SMB1.js +200 -0
- package/dist/headers/SMB2.d.ts +36 -0
- package/dist/headers/SMB2.js +203 -0
- package/dist/headers/SMPP.d.ts +33 -0
- package/dist/headers/SMPP.js +119 -0
- package/dist/headers/SMTP.d.ts +33 -0
- package/dist/headers/SMTP.js +157 -0
- package/dist/headers/SNAP.d.ts +22 -0
- package/dist/headers/SNAP.js +68 -0
- package/dist/headers/SNMP.d.ts +28 -0
- package/dist/headers/SNMP.js +302 -0
- package/dist/headers/SOCKS4.d.ts +42 -0
- package/dist/headers/SOCKS4.js +302 -0
- package/dist/headers/SOCKS5.d.ts +45 -0
- package/dist/headers/SOCKS5.js +223 -0
- package/dist/headers/SSDP.d.ts +27 -0
- package/dist/headers/SSDP.js +161 -0
- package/dist/headers/SSH.d.ts +44 -0
- package/dist/headers/SSH.js +274 -0
- package/dist/headers/STOMP.d.ts +48 -0
- package/dist/headers/STOMP.js +177 -0
- package/dist/headers/STP.d.ts +26 -0
- package/dist/headers/STP.js +150 -0
- package/dist/headers/STUN.d.ts +25 -0
- package/dist/headers/STUN.js +183 -0
- package/dist/headers/SercosIII.d.ts +43 -0
- package/dist/headers/SercosIII.js +199 -0
- package/dist/headers/Skinny.d.ts +32 -0
- package/dist/headers/Skinny.js +139 -0
- package/dist/headers/StatsD.d.ts +27 -0
- package/dist/headers/StatsD.js +134 -0
- package/dist/headers/SunRPC.d.ts +34 -0
- package/dist/headers/SunRPC.js +268 -0
- package/dist/headers/Syslog.d.ts +21 -0
- package/dist/headers/Syslog.js +91 -0
- package/dist/headers/TCP.d.ts +19 -0
- package/dist/headers/TCP.js +905 -0
- package/dist/headers/TFTP.d.ts +28 -0
- package/dist/headers/TFTP.js +224 -0
- package/dist/headers/TLS_Alert.d.ts +24 -0
- package/dist/headers/TLS_Alert.js +639 -0
- package/dist/headers/TLS_ApplicationData.d.ts +24 -0
- package/dist/headers/TLS_ApplicationData.js +170 -0
- package/dist/headers/TLS_ChangeCipherSpec.d.ts +23 -0
- package/dist/headers/TLS_ChangeCipherSpec.js +168 -0
- package/dist/headers/TLS_Handshake.d.ts +65 -0
- package/dist/headers/TLS_Handshake.js +860 -0
- package/dist/headers/TLS_Heartbeat.d.ts +27 -0
- package/dist/headers/TLS_Heartbeat.js +247 -0
- package/dist/headers/TPKT.d.ts +26 -0
- package/dist/headers/TPKT.js +99 -0
- package/dist/headers/TacacsPlus.d.ts +27 -0
- package/dist/headers/TacacsPlus.js +133 -0
- package/dist/headers/Telnet.d.ts +40 -0
- package/dist/headers/Telnet.js +193 -0
- package/dist/headers/Teredo.d.ts +40 -0
- package/dist/headers/Teredo.js +251 -0
- package/dist/headers/TimeProtocol.d.ts +25 -0
- package/dist/headers/TimeProtocol.js +53 -0
- package/dist/headers/UDP.d.ts +19 -0
- package/dist/headers/UDP.js +171 -0
- package/dist/headers/VLAN_802dot1Q.d.ts +24 -0
- package/dist/headers/VLAN_802dot1Q.js +96 -0
- package/dist/headers/VRRP.d.ts +26 -0
- package/dist/headers/VRRP.js +309 -0
- package/dist/headers/VXLAN.d.ts +24 -0
- package/dist/headers/VXLAN.js +75 -0
- package/dist/headers/WCCP.d.ts +29 -0
- package/dist/headers/WCCP.js +117 -0
- package/dist/headers/WHOIS.d.ts +37 -0
- package/dist/headers/WHOIS.js +126 -0
- package/dist/headers/WSDiscovery.d.ts +29 -0
- package/dist/headers/WSDiscovery.js +118 -0
- package/dist/headers/WakeOnLAN.d.ts +30 -0
- package/dist/headers/WakeOnLAN.js +126 -0
- package/dist/headers/WebSocket.d.ts +26 -0
- package/dist/headers/WebSocket.js +173 -0
- package/dist/headers/WireGuard.d.ts +38 -0
- package/dist/headers/WireGuard.js +216 -0
- package/dist/headers/XMPP.d.ts +37 -0
- package/dist/headers/XMPP.js +149 -0
- package/dist/headers/Zabbix.d.ts +35 -0
- package/dist/headers/Zabbix.js +153 -0
- package/dist/headers/cms/AcsiPdu.d.ts +45 -0
- package/dist/headers/cms/AcsiPdu.js +112 -0
- package/dist/headers/cms/PerDecoder.d.ts +68 -0
- package/dist/headers/cms/PerDecoder.js +211 -0
- package/dist/headers/cms/PerReader.d.ts +64 -0
- package/dist/headers/cms/PerReader.js +155 -0
- package/dist/helper/BufferToHex.d.ts +5 -0
- package/dist/helper/BufferToHex.js +10 -0
- package/dist/helper/BufferToIP.d.ts +12 -0
- package/dist/helper/BufferToIP.js +23 -0
- package/dist/helper/BufferToNumber.d.ts +18 -0
- package/dist/helper/BufferToNumber.js +47 -0
- package/dist/helper/HexToBuffer.d.ts +7 -0
- package/dist/helper/HexToBuffer.js +27 -0
- package/dist/helper/HexToNumber.d.ts +18 -0
- package/dist/helper/HexToNumber.js +48 -0
- package/dist/helper/IPToBuffer.d.ts +12 -0
- package/dist/helper/IPToBuffer.js +34 -0
- package/dist/helper/NumberToBERBuffer.d.ts +18 -0
- package/dist/helper/NumberToBERBuffer.js +31 -0
- package/dist/helper/NumberToBERHex.d.ts +18 -0
- package/dist/helper/NumberToBERHex.js +52 -0
- package/dist/helper/NumberToBuffer.d.ts +18 -0
- package/dist/helper/NumberToBuffer.js +38 -0
- package/dist/helper/NumberToHex.d.ts +18 -0
- package/dist/helper/NumberToHex.js +35 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.js +113 -0
- package/dist/lib/FlexibleObject.d.ts +27 -0
- package/dist/lib/FlexibleObject.js +113 -0
- package/dist/lib/GetBERIntegerLengthFromBuffer.d.ts +6 -0
- package/dist/lib/GetBERIntegerLengthFromBuffer.js +16 -0
- package/dist/lib/ProcessPacketPostHandlers.d.ts +13 -0
- package/dist/lib/ProcessPacketPostHandlers.js +53 -0
- package/dist/lib/SortPostHandlers.d.ts +7 -0
- package/dist/lib/SortPostHandlers.js +11 -0
- package/dist/lib/StringContentEncodingEnum.d.ts +10 -0
- package/dist/lib/StringContentEncodingEnum.js +14 -0
- package/dist/schema/ProtocolFieldJSONSchema.d.ts +35 -0
- package/dist/schema/ProtocolFieldJSONSchema.js +2 -0
- package/dist/schema/ProtocolFieldJSONSchemaDefinition.d.ts +2 -0
- package/dist/schema/ProtocolFieldJSONSchemaDefinition.js +2 -0
- package/dist/schema/ProtocolJSONSchema.d.ts +32 -0
- package/dist/schema/ProtocolJSONSchema.js +2 -0
- package/dist/types/CodecData.d.ts +7 -0
- package/dist/types/CodecData.js +2 -0
- package/dist/types/CodecDecodeResult.d.ts +10 -0
- package/dist/types/CodecDecodeResult.js +2 -0
- package/dist/types/CodecEncodeInput.d.ts +2 -0
- package/dist/types/CodecEncodeInput.js +2 -0
- package/dist/types/CodecEncodeResult.d.ts +5 -0
- package/dist/types/CodecEncodeResult.js +2 -0
- package/dist/types/CodecErrorInfo.d.ts +5 -0
- package/dist/types/CodecErrorInfo.js +2 -0
- package/dist/types/CodecModule.d.ts +2 -0
- package/dist/types/CodecModule.js +2 -0
- package/dist/types/CodecModuleConstructor.d.ts +2 -0
- package/dist/types/CodecModuleConstructor.js +2 -0
- package/dist/types/CodecSchema.d.ts +6 -0
- package/dist/types/CodecSchema.js +2 -0
- package/dist/types/DemuxProducer.d.ts +23 -0
- package/dist/types/DemuxProducer.js +2 -0
- package/dist/types/Dissection.d.ts +23 -0
- package/dist/types/Dissection.js +2 -0
- package/dist/types/HeaderTreeNode.d.ts +4 -0
- package/dist/types/HeaderTreeNode.js +2 -0
- package/dist/types/LayerGraph.d.ts +32 -0
- package/dist/types/LayerGraph.js +2 -0
- package/dist/types/PostHandlerItem.d.ts +4 -0
- package/dist/types/PostHandlerItem.js +2 -0
- package/package.json +141 -0
|
@@ -0,0 +1,734 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BaseHeader = void 0;
|
|
4
|
+
const SortPostHandlers_1 = require("../lib/SortPostHandlers");
|
|
5
|
+
const FlexibleObject_1 = require("../lib/FlexibleObject");
|
|
6
|
+
const ajv_1 = require("ajv");
|
|
7
|
+
const CodecSchemaValidateError_1 = require("../errors/CodecSchemaValidateError");
|
|
8
|
+
const BufferToNumber_1 = require("../helper/BufferToNumber");
|
|
9
|
+
const NumberToBuffer_1 = require("../helper/NumberToBuffer");
|
|
10
|
+
const BufferToHex_1 = require("../helper/BufferToHex");
|
|
11
|
+
const HexToBuffer_1 = require("../helper/HexToBuffer");
|
|
12
|
+
const BufferToIP_1 = require("../helper/BufferToIP");
|
|
13
|
+
const IPToBuffer_1 = require("../helper/IPToBuffer");
|
|
14
|
+
const StringContentEncodingEnum_1 = require("../lib/StringContentEncodingEnum");
|
|
15
|
+
const CONSTRUCTOR_VALIDATE_KEY = '__validate';
|
|
16
|
+
//Per-codec-class cached MATCH probe. Content-heuristic selection calls MATCH() on many candidates
|
|
17
|
+
//per layer; each used to build a whole fresh instance (rebuilding the SCHEMA) just to run match().
|
|
18
|
+
//Since match() only inspects the previous layer and the raw buffer, one reusable probe per class
|
|
19
|
+
//suffices — see BaseHeader.MATCH / bindContext.
|
|
20
|
+
const MATCH_PROBE_KEY = '__matchProbe';
|
|
21
|
+
/**
|
|
22
|
+
* The base class every protocol header extends, and the surface a custom-codec author works against.
|
|
23
|
+
*
|
|
24
|
+
* A subclass declares a `SCHEMA` (an executable JSON Schema whose fields carry per-field
|
|
25
|
+
* `decode`/`encode` closures), a stable `id`/`name`/`nickname`, and a `match()` that decides —
|
|
26
|
+
* by inspecting the previously decoded layers (`prevCodecModule`/`prevCodecModules`) — whether
|
|
27
|
+
* this header applies at the current offset. Optional `matchKeys`/`demuxProducers` wire the
|
|
28
|
+
* header into the codec's O(1) dispatch table instead of the content-heuristic chain.
|
|
29
|
+
*
|
|
30
|
+
* Field closures read and write the shared packet buffer through the header-relative helpers
|
|
31
|
+
* `readBytes`/`writeBytes` (whole octets) and `readBits`/`writeBits` (bit fields), which
|
|
32
|
+
* auto-expand the buffer on write, and store values on the path-tracking `instance`. Malformed
|
|
33
|
+
* input is reported via `recordError()` rather than thrown, so decode/encode never crash. Headers
|
|
34
|
+
* register cross-field/cross-layer fixups (lengths, checksums) with the post-handler methods
|
|
35
|
+
* (`addPostSelfEncodeHandler`/`addPostSelfDecodeHandler` for layer-internal, `addPostPacketEncodeHandler`/
|
|
36
|
+
* `addPostPacketDecodeHandler` for packet-level).
|
|
37
|
+
*/
|
|
38
|
+
class BaseHeader {
|
|
39
|
+
static get CODEC_INSTANCE() {
|
|
40
|
+
return new this();
|
|
41
|
+
}
|
|
42
|
+
static CREATE_CODEC_INSTANCE_WITCH_CODEC_MODULES(codecData, codecModules) {
|
|
43
|
+
return this.CREATE_INSTANCE(codecData, codecModules);
|
|
44
|
+
}
|
|
45
|
+
static get PROTOCOL_ID() {
|
|
46
|
+
return this.CODEC_INSTANCE.id;
|
|
47
|
+
}
|
|
48
|
+
static get PROTOCOL_NAME() {
|
|
49
|
+
return this.CODEC_INSTANCE.name;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Demux keys this header registers in the codec dispatch table.
|
|
53
|
+
* Empty (the default) means the header is matched by its content-heuristic
|
|
54
|
+
* match() instead of by an upper-layer demultiplexing value.
|
|
55
|
+
*/
|
|
56
|
+
static get MATCH_KEYS() {
|
|
57
|
+
return this.CODEC_INSTANCE.matchKeys;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Demux keys this header produces for its child layer (from its own field values). Reversed by the
|
|
61
|
+
* codec to route the next layer, and read by the editor projection. Empty (the default) means this
|
|
62
|
+
* header is a leaf — nothing demuxes off it.
|
|
63
|
+
*/
|
|
64
|
+
static get DEMUX_PRODUCERS() {
|
|
65
|
+
return this.CODEC_INSTANCE.demuxProducers;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* When true, this header is also placed in the content-heuristic fallback list even though it has
|
|
69
|
+
* demux matchKeys. Lets a protocol with a reliable content signature (TLS, IEC104) take the O(1)
|
|
70
|
+
* bucket on its well-known port yet still be recognized on any other port via its match(). Default
|
|
71
|
+
* false: matchKeys → bucket only; no matchKeys → heuristic only.
|
|
72
|
+
*/
|
|
73
|
+
static get HEURISTIC_FALLBACK() {
|
|
74
|
+
return this.CODEC_INSTANCE.heuristicFallback;
|
|
75
|
+
}
|
|
76
|
+
/** Higher is tried first within a demux bucket and within the heuristic chain; ties keep registration order. */
|
|
77
|
+
static get MATCH_PRIORITY() {
|
|
78
|
+
return this.CODEC_INSTANCE.matchPriority;
|
|
79
|
+
}
|
|
80
|
+
static get PROTOCOL_SCHEMA() {
|
|
81
|
+
const schema = JSON.parse(JSON.stringify(this.CODEC_INSTANCE.SCHEMA));
|
|
82
|
+
if (!Object.hasOwn(this, CONSTRUCTOR_VALIDATE_KEY)) {
|
|
83
|
+
const validate = new ajv_1.Ajv({
|
|
84
|
+
strict: false,
|
|
85
|
+
useDefaults: true,
|
|
86
|
+
coerceTypes: true
|
|
87
|
+
}).compile(schema);
|
|
88
|
+
Object.defineProperty(this, CONSTRUCTOR_VALIDATE_KEY, {
|
|
89
|
+
enumerable: false,
|
|
90
|
+
configurable: false,
|
|
91
|
+
value: validate
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return schema;
|
|
95
|
+
}
|
|
96
|
+
static MATCH(codecData, codecModules) {
|
|
97
|
+
//Reuse one cached probe instance per codec class instead of rebuilding a full instance (and its
|
|
98
|
+
//SCHEMA) for every candidate. match() only reads the previous layer and the raw buffer, never
|
|
99
|
+
//this header's SCHEMA/instance, so rebinding context is enough. selectCodec runs synchronously,
|
|
100
|
+
//so the shared probe never overlaps across concurrent decodes.
|
|
101
|
+
let probe = Object.hasOwn(this, MATCH_PROBE_KEY) ? this[MATCH_PROBE_KEY] : undefined;
|
|
102
|
+
if (!probe) {
|
|
103
|
+
probe = new this();
|
|
104
|
+
Object.defineProperty(this, MATCH_PROBE_KEY, { enumerable: false, configurable: false, value: probe });
|
|
105
|
+
}
|
|
106
|
+
probe.bindContext(codecData, codecModules ? codecModules : []);
|
|
107
|
+
return probe.match();
|
|
108
|
+
}
|
|
109
|
+
static CREATE_INSTANCE(codecData, codecModules) {
|
|
110
|
+
return new this(codecData, codecModules);
|
|
111
|
+
}
|
|
112
|
+
//Dissect-only instrumentation: when #byteRanges is non-null, each field read records the packet
|
|
113
|
+
//span it covered (keyed by the field being decoded). Off (null) during normal decode/encode, so
|
|
114
|
+
//the hot path is untouched. See enableByteRangeTracking() / getByteRanges().
|
|
115
|
+
#byteRanges;
|
|
116
|
+
#currentFieldPath;
|
|
117
|
+
/**
|
|
118
|
+
* Turn on byte-range collection for a dissect pass (call before decode()). Additive and
|
|
119
|
+
* read-only: it changes nothing about the decoded values, only records where each field lives.
|
|
120
|
+
*/
|
|
121
|
+
enableByteRangeTracking() {
|
|
122
|
+
this.#byteRanges = new Map();
|
|
123
|
+
}
|
|
124
|
+
/** The byte span each field occupied, or null if this header was not dissected. */
|
|
125
|
+
getByteRanges() {
|
|
126
|
+
return this.#byteRanges;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Entire packet buffer data getter
|
|
130
|
+
*/
|
|
131
|
+
get packet() {
|
|
132
|
+
//Expose only the logical bytes. When physical === logical (the decode path never grows the
|
|
133
|
+
//buffer) return it directly with zero allocation; only slice a view when encode left headroom.
|
|
134
|
+
const buffer = this.codecData.packet;
|
|
135
|
+
const length = this.codecData.packetLength;
|
|
136
|
+
return (length === undefined || length === buffer.length) ? buffer : buffer.subarray(0, length);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Entire packet buffer data setter
|
|
140
|
+
* @param packet
|
|
141
|
+
*/
|
|
142
|
+
set packet(packet) {
|
|
143
|
+
this.codecData.packet = packet;
|
|
144
|
+
this.codecData.packetLength = packet.length;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* The end position of this header in the entire packet's buffer
|
|
148
|
+
*/
|
|
149
|
+
get endPos() {
|
|
150
|
+
return this.startPos + this.headerLength;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Readonly Header length
|
|
154
|
+
*/
|
|
155
|
+
get length() {
|
|
156
|
+
return this.headerLength;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Post packet handlers
|
|
160
|
+
* @protected
|
|
161
|
+
*/
|
|
162
|
+
get postPacketHandlers() {
|
|
163
|
+
if (this.codecData.postHandlers[this.headerIndex] === undefined)
|
|
164
|
+
this.codecData.postHandlers[this.headerIndex] = [];
|
|
165
|
+
return this.codecData.postHandlers[this.headerIndex];
|
|
166
|
+
}
|
|
167
|
+
constructor(codecData, codecModules) {
|
|
168
|
+
/**
|
|
169
|
+
* Current header is a protocol or not
|
|
170
|
+
*/
|
|
171
|
+
this.isProtocol = true;
|
|
172
|
+
/**
|
|
173
|
+
* Upper-layer demultiplexing keys that select this header, e.g.
|
|
174
|
+
* ['ethertype:0800'] or ['ipproto:6']. Registered in the codec dispatch
|
|
175
|
+
* table for O(1) selection during decode. Leave empty for headers that
|
|
176
|
+
* must inspect their own bytes to match (TLS, IEC104, tunnels); those fall
|
|
177
|
+
* back to the content-heuristic match() list.
|
|
178
|
+
*/
|
|
179
|
+
this.matchKeys = [];
|
|
180
|
+
/**
|
|
181
|
+
* Demux keys this header produces for its child (see DemuxProducer). Declared per-schema so a new
|
|
182
|
+
* demux dimension (port, GUID, …) is a per-protocol declaration, not a core change. Default: none
|
|
183
|
+
* (a leaf header). Example: Ethernet declares `[{field:'etherType', namespace:'ethertype',
|
|
184
|
+
* kind:'string'}]`; IPv4 declares `[{field:'protocol', namespace:'ipproto', kind:'uint'}]`.
|
|
185
|
+
*/
|
|
186
|
+
this.demuxProducers = [];
|
|
187
|
+
/**
|
|
188
|
+
* Also register in the content-heuristic fallback list despite having matchKeys — so a
|
|
189
|
+
* content-signed protocol keeps working off its well-known port. See static HEURISTIC_FALLBACK.
|
|
190
|
+
*/
|
|
191
|
+
this.heuristicFallback = false;
|
|
192
|
+
/** Selection priority within a bucket / the heuristic chain (higher first; ties keep registration order). */
|
|
193
|
+
this.matchPriority = 0;
|
|
194
|
+
/**
|
|
195
|
+
* Encode/Decode error info objects
|
|
196
|
+
*/
|
|
197
|
+
this.errors = [];
|
|
198
|
+
/**
|
|
199
|
+
* Header schema instance
|
|
200
|
+
*/
|
|
201
|
+
this.instance = new FlexibleObject_1.FlexibleObject();
|
|
202
|
+
//Dissect-only instrumentation: when #byteRanges is non-null, each field read records the packet
|
|
203
|
+
//span it covered (keyed by the field being decoded). Off (null) during normal decode/encode, so
|
|
204
|
+
//the hot path is untouched. See enableByteRangeTracking() / getByteRanges().
|
|
205
|
+
this.#byteRanges = null;
|
|
206
|
+
this.#currentFieldPath = '';
|
|
207
|
+
/**
|
|
208
|
+
* The start position of this header in the entire packet's buffer
|
|
209
|
+
*/
|
|
210
|
+
this.startPos = 0;
|
|
211
|
+
/**
|
|
212
|
+
* Header length
|
|
213
|
+
* @protected
|
|
214
|
+
*/
|
|
215
|
+
this.headerLength = 0;
|
|
216
|
+
/**
|
|
217
|
+
* Current header index in packet headers
|
|
218
|
+
* @protected
|
|
219
|
+
*/
|
|
220
|
+
this.headerIndex = 0;
|
|
221
|
+
/**
|
|
222
|
+
* Registered post encode handlers (CodecModule)
|
|
223
|
+
* @protected
|
|
224
|
+
*/
|
|
225
|
+
this.postSelfEncodeHandlers = [];
|
|
226
|
+
/**
|
|
227
|
+
* Registered post decode handlers (CodecModule)
|
|
228
|
+
* @protected
|
|
229
|
+
*/
|
|
230
|
+
this.postSelfDecodeHandlers = [];
|
|
231
|
+
this.bindContext(codecData, codecModules);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* (Re)bind this instance's decode/match context. Extracted from the constructor so the static
|
|
235
|
+
* MATCH probe can reuse a single cached instance across heuristic candidates instead of rebuilding
|
|
236
|
+
* the whole SCHEMA for each — match() only reads the previous layer and the raw buffer.
|
|
237
|
+
*/
|
|
238
|
+
bindContext(codecData, codecModules) {
|
|
239
|
+
this.codecData = codecData;
|
|
240
|
+
this.startPos = codecData?.startPos ? codecData.startPos : 0;
|
|
241
|
+
codecModules = codecModules ? codecModules : [];
|
|
242
|
+
this.codecModules = codecModules;
|
|
243
|
+
this.prevCodecModules = [...codecModules];
|
|
244
|
+
const prevCodecModuleIndex = this.prevCodecModules.length - 1;
|
|
245
|
+
this.prevCodecModule = this.prevCodecModules[prevCodecModuleIndex > -1 ? prevCodecModuleIndex : 0];
|
|
246
|
+
this.headerIndex = this.prevCodecModules.length;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Get packet data offset
|
|
250
|
+
* @param offset
|
|
251
|
+
* @protected
|
|
252
|
+
*/
|
|
253
|
+
getPacketOffset(offset) {
|
|
254
|
+
return this.startPos + offset;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Internal read bytes from buffer
|
|
258
|
+
* @param offset
|
|
259
|
+
* @param length
|
|
260
|
+
* @param expandBufferLength
|
|
261
|
+
* @param changeHeaderLength
|
|
262
|
+
* @private
|
|
263
|
+
*/
|
|
264
|
+
#readBytes(offset, length, expandBufferLength, changeHeaderLength) {
|
|
265
|
+
const packetOffset = this.getPacketOffset(offset);
|
|
266
|
+
let readEndPos = packetOffset + length;
|
|
267
|
+
const codecData = this.codecData;
|
|
268
|
+
const logicalLength = codecData.packetLength ?? codecData.packet.length;
|
|
269
|
+
if (logicalLength < readEndPos) {
|
|
270
|
+
if (expandBufferLength) {
|
|
271
|
+
if (codecData.packet.length < readEndPos) {
|
|
272
|
+
//Amortized doubling: a run of small field writes grows the buffer O(log n) times
|
|
273
|
+
//instead of reallocating + copying the whole packet on every write (O(n^2)). Only the
|
|
274
|
+
//logical bytes are copied forward; the headroom stays zero-filled from Buffer.alloc.
|
|
275
|
+
const capacity = Math.max(readEndPos, codecData.packet.length * 2);
|
|
276
|
+
const grown = Buffer.alloc(capacity, 0);
|
|
277
|
+
codecData.packet.copy(grown, 0, 0, logicalLength);
|
|
278
|
+
codecData.packet = grown;
|
|
279
|
+
}
|
|
280
|
+
codecData.packetLength = readEndPos;
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
readEndPos = logicalLength;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const headerLength = readEndPos - this.startPos;
|
|
287
|
+
if (changeHeaderLength)
|
|
288
|
+
this.headerLength = this.headerLength < headerLength ? headerLength : this.headerLength;
|
|
289
|
+
//Dissect instrumentation: attribute this read's absolute span to the field being decoded,
|
|
290
|
+
//extending the field's range if it reads more than once (e.g. an address + its value).
|
|
291
|
+
if (this.#byteRanges && this.#currentFieldPath) {
|
|
292
|
+
const existing = this.#byteRanges.get(this.#currentFieldPath);
|
|
293
|
+
if (existing) {
|
|
294
|
+
const start = Math.min(existing.offset, packetOffset);
|
|
295
|
+
const end = Math.max(existing.offset + existing.length, packetOffset + length);
|
|
296
|
+
this.#byteRanges.set(this.#currentFieldPath, { offset: start, length: end - start });
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
this.#byteRanges.set(this.#currentFieldPath, { offset: packetOffset, length: length });
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
//Clamp the returned view to readEndPos (which the branches above already clamped to the logical
|
|
303
|
+
//length on a non-expanding out-of-range read), so encode-time forward reads past the write
|
|
304
|
+
//high-water mark stay truncated exactly as before rather than exposing zero-filled headroom.
|
|
305
|
+
return this.codecData.packet.subarray(packetOffset, readEndPos);
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Read bytes from buffer
|
|
309
|
+
* @param offset
|
|
310
|
+
* @param length
|
|
311
|
+
* @param dryRun
|
|
312
|
+
* @protected
|
|
313
|
+
*/
|
|
314
|
+
readBytes(offset, length, dryRun = false) {
|
|
315
|
+
return this.#readBytes(offset, length, false, !dryRun);
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Write bytes to buffer
|
|
319
|
+
* @param offset
|
|
320
|
+
* @param buffer
|
|
321
|
+
* @protected
|
|
322
|
+
*/
|
|
323
|
+
writeBytes(offset, buffer) {
|
|
324
|
+
const packetOffset = this.getPacketOffset(offset);
|
|
325
|
+
const writeEndPos = packetOffset + buffer.length;
|
|
326
|
+
this.#readBytes(offset, buffer.length, true, true);
|
|
327
|
+
this.codecData.packet.fill(buffer, packetOffset, writeEndPos);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Read bits from buffer
|
|
331
|
+
* @param offset
|
|
332
|
+
* @param length
|
|
333
|
+
* @param bitOffset
|
|
334
|
+
* @param bitLength
|
|
335
|
+
* @protected
|
|
336
|
+
*/
|
|
337
|
+
readBits(offset, length, bitOffset, bitLength) {
|
|
338
|
+
const buffer = this.#readBytes(offset, length, false, true);
|
|
339
|
+
//MSB-first bit extraction over a `length`-octet window. Available bytes occupy the low end;
|
|
340
|
+
//missing (truncated) high bits read as 0.
|
|
341
|
+
const shift = length * 8 - bitOffset - bitLength;
|
|
342
|
+
if (shift < 0)
|
|
343
|
+
return 0;
|
|
344
|
+
if (length <= 4) {
|
|
345
|
+
//A window of ≤4 octets (≤32 bits) fits a JS number exactly — skip BigInt boxing. Use
|
|
346
|
+
//arithmetic (not <</>>, which are 32-bit-signed in JS) so 32-bit widths stay correct.
|
|
347
|
+
let narrow = 0;
|
|
348
|
+
for (const byte of buffer)
|
|
349
|
+
narrow = narrow * 256 + byte;
|
|
350
|
+
return Math.floor(narrow / 2 ** shift) % (2 ** bitLength);
|
|
351
|
+
}
|
|
352
|
+
//Wider windows (48/64-bit GOOSE/SV fields) keep full precision via BigInt.
|
|
353
|
+
let value = 0n;
|
|
354
|
+
for (const byte of buffer)
|
|
355
|
+
value = (value << 8n) | BigInt(byte);
|
|
356
|
+
const mask = (1n << BigInt(bitLength)) - 1n;
|
|
357
|
+
return Number((value >> BigInt(shift)) & mask);
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Write bits to buffer
|
|
361
|
+
* @param offset
|
|
362
|
+
* @param length
|
|
363
|
+
* @param bitOffset
|
|
364
|
+
* @param bitLength
|
|
365
|
+
* @param value
|
|
366
|
+
* @protected
|
|
367
|
+
*/
|
|
368
|
+
writeBits(offset, length, bitOffset, bitLength, value) {
|
|
369
|
+
const buffer = this.#readBytes(offset, length, true, true);
|
|
370
|
+
//Overlay `bitLength` bits of `value` at `bitOffset` (MSB-first) into the octet window. Only
|
|
371
|
+
//the low `bitLength` bits of value are written (out-of-range values wrap, matching a
|
|
372
|
+
//fixed-width field), and encode never crashes.
|
|
373
|
+
const out = Buffer.alloc(buffer.length);
|
|
374
|
+
if (length <= 4) {
|
|
375
|
+
//≤32-bit window: plain number arithmetic, no BigInt boxing. Split the window into the
|
|
376
|
+
//untouched high part, the target field, and the low part, then reassemble — all via
|
|
377
|
+
//multiply/divide/modulo to stay correct at 32-bit widths (JS bit-ops are 32-bit-signed).
|
|
378
|
+
let current = 0;
|
|
379
|
+
for (const byte of buffer)
|
|
380
|
+
current = current * 256 + byte;
|
|
381
|
+
const shift = buffer.length * 8 - bitOffset - bitLength;
|
|
382
|
+
const modulo = 2 ** bitLength;
|
|
383
|
+
const divisor = 2 ** shift;
|
|
384
|
+
const field = (((Math.trunc(Number(value)) || 0) % modulo) + modulo) % modulo;
|
|
385
|
+
const blockSize = modulo * divisor;
|
|
386
|
+
let written = Math.floor(current / blockSize) * blockSize + field * divisor + current % divisor;
|
|
387
|
+
for (let i = buffer.length - 1; i >= 0; i--) {
|
|
388
|
+
out[i] = written % 256;
|
|
389
|
+
written = Math.floor(written / 256);
|
|
390
|
+
}
|
|
391
|
+
this.writeBytes(offset, out);
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
//Wider windows keep exact via BigInt.
|
|
395
|
+
let current = 0n;
|
|
396
|
+
for (const byte of buffer)
|
|
397
|
+
current = (current << 8n) | BigInt(byte);
|
|
398
|
+
const shift = BigInt(buffer.length * 8 - bitOffset - bitLength);
|
|
399
|
+
const fieldMask = (1n << BigInt(bitLength)) - 1n;
|
|
400
|
+
const field = (BigInt(Math.trunc(Number(value)) || 0) & fieldMask) << shift;
|
|
401
|
+
const written = (current & ~(fieldMask << shift)) | field;
|
|
402
|
+
let v = written;
|
|
403
|
+
for (let i = buffer.length - 1; i >= 0; i--) {
|
|
404
|
+
out[i] = Number(v & 0xffn);
|
|
405
|
+
v >>= 8n;
|
|
406
|
+
}
|
|
407
|
+
this.writeBytes(offset, out);
|
|
408
|
+
}
|
|
409
|
+
// ===== Field building blocks =====
|
|
410
|
+
// Declarative field factories: one call yields the whole {type,label,min,max,decode,encode}
|
|
411
|
+
// schema field, generating the decode/encode twin from a single declaration so the byte offset
|
|
412
|
+
// and width live in one place (not duplicated across two hand-mirrored closures — the source of
|
|
413
|
+
// the historical decode≠encode bugs). Closures re-resolve this.instance[name] at run time because
|
|
414
|
+
// encode swaps in a fresh instance. Irregular fields keep their hand-written closures (escape hatch).
|
|
415
|
+
/**
|
|
416
|
+
* An unsigned big-endian integer field of `byteLength` octets at `offset`. Decode reads it into
|
|
417
|
+
* `name`; encode clamps to the field's range (recording an error, never throwing) and writes it —
|
|
418
|
+
* byte-for-byte identical to the hand-written pattern it replaces.
|
|
419
|
+
* @protected
|
|
420
|
+
*/
|
|
421
|
+
static fieldUInt(name, offset, byteLength, label) {
|
|
422
|
+
const maximum = byteLength === 1 ? 255 : byteLength === 2 ? 65535 : 4294967295;
|
|
423
|
+
const read = byteLength === 1 ? BufferToNumber_1.BufferToUInt8 : byteLength === 2 ? BufferToNumber_1.BufferToUInt16 : BufferToNumber_1.BufferToUInt32;
|
|
424
|
+
const write = byteLength === 1 ? NumberToBuffer_1.UInt8ToBuffer : byteLength === 2 ? NumberToBuffer_1.UInt16ToBuffer : NumberToBuffer_1.UInt32ToBuffer;
|
|
425
|
+
return {
|
|
426
|
+
type: 'integer',
|
|
427
|
+
label: label,
|
|
428
|
+
minimum: 0,
|
|
429
|
+
maximum: maximum,
|
|
430
|
+
decode: function () {
|
|
431
|
+
this.instance[name].setValue(read(this.readBytes(offset, byteLength)));
|
|
432
|
+
},
|
|
433
|
+
encode: function () {
|
|
434
|
+
const node = this.instance[name];
|
|
435
|
+
let value = node.getValue(0, (nodePath) => this.recordError(nodePath, 'Not Found'));
|
|
436
|
+
if (value > maximum) {
|
|
437
|
+
this.recordError(node.getPath(), `Maximum value is ${maximum}`);
|
|
438
|
+
value = maximum;
|
|
439
|
+
}
|
|
440
|
+
if (value < 0) {
|
|
441
|
+
this.recordError(node.getPath(), 'Minimum value is 0');
|
|
442
|
+
value = 0;
|
|
443
|
+
}
|
|
444
|
+
node.setValue(value);
|
|
445
|
+
this.writeBytes(offset, write(value));
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* A signed 8-bit integer field at `offset` (e.g. NTP poll/precision — power-of-two exponents).
|
|
451
|
+
* Decode reads it as int8 into `name`; encode clamps to [-128, 127] (recording an error, never
|
|
452
|
+
* throwing) and writes it — byte-for-byte identical to a hand-written int8 field for in-range values.
|
|
453
|
+
* @protected
|
|
454
|
+
*/
|
|
455
|
+
static fieldInt8(name, offset, label) {
|
|
456
|
+
return {
|
|
457
|
+
type: 'integer',
|
|
458
|
+
label: label,
|
|
459
|
+
minimum: -128,
|
|
460
|
+
maximum: 127,
|
|
461
|
+
decode: function () {
|
|
462
|
+
this.instance[name].setValue((0, BufferToNumber_1.BufferToInt8)(this.readBytes(offset, 1)));
|
|
463
|
+
},
|
|
464
|
+
encode: function () {
|
|
465
|
+
const node = this.instance[name];
|
|
466
|
+
let value = node.getValue(0, (nodePath) => this.recordError(nodePath, 'Not Found'));
|
|
467
|
+
if (value > 127) {
|
|
468
|
+
this.recordError(node.getPath(), 'Maximum value is 127');
|
|
469
|
+
value = 127;
|
|
470
|
+
}
|
|
471
|
+
if (value < -128) {
|
|
472
|
+
this.recordError(node.getPath(), 'Minimum value is -128');
|
|
473
|
+
value = -128;
|
|
474
|
+
}
|
|
475
|
+
node.setValue(value);
|
|
476
|
+
this.writeBytes(offset, (0, NumberToBuffer_1.Int8ToBuffer)(value));
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* A raw byte field of `byteLength` octets at `offset`, kept verbatim as a lower-case hex string
|
|
482
|
+
* (e.g. NTP timestamps / reference id — bytes an editor should see and round-trip untouched).
|
|
483
|
+
* Decode reads the bytes to hex; encode writes the hex back (default: all-zero). Byte-for-byte
|
|
484
|
+
* identical to the hand-written pattern it replaces.
|
|
485
|
+
* @protected
|
|
486
|
+
*/
|
|
487
|
+
static fieldHex(name, offset, byteLength, label) {
|
|
488
|
+
const zero = '00'.repeat(byteLength);
|
|
489
|
+
return {
|
|
490
|
+
type: 'string',
|
|
491
|
+
label: label,
|
|
492
|
+
contentEncoding: StringContentEncodingEnum_1.StringContentEncodingEnum.HEX,
|
|
493
|
+
decode: function () {
|
|
494
|
+
this.instance[name].setValue((0, BufferToHex_1.BufferToHex)(this.readBytes(offset, byteLength)));
|
|
495
|
+
},
|
|
496
|
+
encode: function () {
|
|
497
|
+
this.writeBytes(offset, (0, HexToBuffer_1.HexToBuffer)(this.instance[name].getValue(zero)));
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* A 4-octet IPv4 address field at `offset`, stored as a dotted-quad string (e.g. DHCP
|
|
503
|
+
* ciaddr/yiaddr/siaddr/giaddr). Decode reads it to `a.b.c.d`; encode writes it back (default
|
|
504
|
+
* '0.0.0.0', recording a Not Found error if the value is missing). Byte-for-byte identical to the
|
|
505
|
+
* hand-written IPv4-address pattern (see IPv4.sip/dip).
|
|
506
|
+
* @protected
|
|
507
|
+
*/
|
|
508
|
+
static fieldIPv4(name, offset, label) {
|
|
509
|
+
return {
|
|
510
|
+
type: 'string',
|
|
511
|
+
label: label,
|
|
512
|
+
//Match IPv4.sip/dip exactly: the same length bounds + ipv4 content encoding, so Ajv
|
|
513
|
+
//validation and the editor's field metadata are identical to the hand-written pattern.
|
|
514
|
+
minLength: 7,
|
|
515
|
+
maxLength: 15,
|
|
516
|
+
contentEncoding: StringContentEncodingEnum_1.StringContentEncodingEnum.IPv4,
|
|
517
|
+
decode: function () {
|
|
518
|
+
this.instance[name].setValue((0, BufferToIP_1.BufferToIPv4)(this.readBytes(offset, 4)));
|
|
519
|
+
},
|
|
520
|
+
encode: function () {
|
|
521
|
+
const node = this.instance[name];
|
|
522
|
+
const value = node.getValue('0.0.0.0', (nodePath) => this.recordError(nodePath, 'Not Found'));
|
|
523
|
+
node.setValue(value);
|
|
524
|
+
this.writeBytes(offset, (0, IPToBuffer_1.IPv4ToBuffer)(value));
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Get field codecs from schema tree
|
|
530
|
+
* @param schema
|
|
531
|
+
* @param codecName
|
|
532
|
+
* @param execBeforeSubCodecs
|
|
533
|
+
* @param codecs
|
|
534
|
+
* @protected
|
|
535
|
+
*/
|
|
536
|
+
getFieldCodecs(schema, codecName, execBeforeSubCodecs, codecs = [], paths = null, pathPrefix = '') {
|
|
537
|
+
if (!schema.properties)
|
|
538
|
+
return codecs;
|
|
539
|
+
for (const propertyName of Object.keys(schema.properties)) {
|
|
540
|
+
const fieldSchema = schema.properties[propertyName];
|
|
541
|
+
const codec = fieldSchema[codecName];
|
|
542
|
+
//Push the raw closure (no async double-wrap) and skip fields with no codec entirely, so
|
|
543
|
+
//decode/encode call each synchronously and only await genuine Promises. When `paths` is
|
|
544
|
+
//provided (dissect only) a dotted field path is collected in parallel; otherwise there is
|
|
545
|
+
//zero extra cost on the hot path (no per-field object, no string concat).
|
|
546
|
+
const path = paths ? (pathPrefix ? `${pathPrefix}.${propertyName}` : propertyName) : '';
|
|
547
|
+
if (execBeforeSubCodecs && codec) {
|
|
548
|
+
codecs.push(codec);
|
|
549
|
+
if (paths)
|
|
550
|
+
paths.push(path);
|
|
551
|
+
}
|
|
552
|
+
if (fieldSchema.properties)
|
|
553
|
+
this.getFieldCodecs(fieldSchema, codecName, execBeforeSubCodecs, codecs, paths, path);
|
|
554
|
+
if (!execBeforeSubCodecs && codec) {
|
|
555
|
+
codecs.push(codec);
|
|
556
|
+
if (paths)
|
|
557
|
+
paths.push(path);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
return codecs;
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Record encode/decode error
|
|
564
|
+
* @param path
|
|
565
|
+
* @param message
|
|
566
|
+
* @protected
|
|
567
|
+
*/
|
|
568
|
+
recordError(path, message) {
|
|
569
|
+
this.errors.push({
|
|
570
|
+
id: this.id,
|
|
571
|
+
path: path,
|
|
572
|
+
message: message
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Register post encode handler for current codec
|
|
577
|
+
* @param handler
|
|
578
|
+
* @param priority
|
|
579
|
+
* @protected
|
|
580
|
+
*/
|
|
581
|
+
addPostSelfEncodeHandler(handler, priority = 0) {
|
|
582
|
+
this.postSelfEncodeHandlers.push({
|
|
583
|
+
priority: priority,
|
|
584
|
+
handler: handler
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Register post decode handler for current codec
|
|
589
|
+
* @param handler
|
|
590
|
+
* @param priority
|
|
591
|
+
* @protected
|
|
592
|
+
*/
|
|
593
|
+
addPostSelfDecodeHandler(handler, priority = 0) {
|
|
594
|
+
this.postSelfDecodeHandlers.push({
|
|
595
|
+
priority: priority,
|
|
596
|
+
handler: handler
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Register post encode handler for packet
|
|
601
|
+
* @description Registered handler call sequence: LIFO (Last In First Out)
|
|
602
|
+
* @param handler
|
|
603
|
+
* @param priority
|
|
604
|
+
* @protected
|
|
605
|
+
*/
|
|
606
|
+
addPostPacketEncodeHandler(handler, priority = 0) {
|
|
607
|
+
this.postPacketHandlers.push({
|
|
608
|
+
priority: priority,
|
|
609
|
+
handler: handler
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Register post decode handler for packet
|
|
614
|
+
* @description Registered handler call sequence: FIFO (First In First Out)
|
|
615
|
+
* @param handler
|
|
616
|
+
* @param priority
|
|
617
|
+
* @protected
|
|
618
|
+
*/
|
|
619
|
+
addPostPacketDecodeHandler(handler, priority = 0) {
|
|
620
|
+
this.postPacketHandlers.push({
|
|
621
|
+
priority: priority,
|
|
622
|
+
handler: handler
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Validate input json node is valid
|
|
627
|
+
* @param headerTreeNode
|
|
628
|
+
*/
|
|
629
|
+
validate(headerTreeNode) {
|
|
630
|
+
let validate = this.constructor[CONSTRUCTOR_VALIDATE_KEY];
|
|
631
|
+
if (!validate) {
|
|
632
|
+
validate = new ajv_1.Ajv({
|
|
633
|
+
strict: false,
|
|
634
|
+
useDefaults: true,
|
|
635
|
+
coerceTypes: true
|
|
636
|
+
}).compile(this.SCHEMA);
|
|
637
|
+
this.constructor[CONSTRUCTOR_VALIDATE_KEY] = validate;
|
|
638
|
+
}
|
|
639
|
+
const isValid = validate(headerTreeNode);
|
|
640
|
+
if (!isValid) {
|
|
641
|
+
let errorObject;
|
|
642
|
+
if (validate.errors)
|
|
643
|
+
errorObject = validate.errors[0];
|
|
644
|
+
const errorMessage = errorObject?.message ? errorObject.message : 'Unknown Error';
|
|
645
|
+
throw new CodecSchemaValidateError_1.CodecSchemaValidateError(errorMessage);
|
|
646
|
+
}
|
|
647
|
+
return headerTreeNode;
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Decode packet header field by field
|
|
651
|
+
*/
|
|
652
|
+
async decode() {
|
|
653
|
+
const paths = this.#byteRanges ? [] : null;
|
|
654
|
+
const decodes = this.getFieldCodecs(this.SCHEMA, 'decode', true, [], paths);
|
|
655
|
+
for (let i = 0; i < decodes.length; i++) {
|
|
656
|
+
//Decode must never throw (error-accumulation contract): a truncated/corrupt packet can
|
|
657
|
+
//make a field's byte read run past the buffer or hit an invalid value. Contain that to a
|
|
658
|
+
//recorded error so the remaining fields and layers still decode best-effort.
|
|
659
|
+
//Call synchronously and only await genuine Promises — the vast majority of field
|
|
660
|
+
//closures are synchronous, so this avoids a promise allocation + scheduler turn per field.
|
|
661
|
+
if (paths)
|
|
662
|
+
this.#currentFieldPath = paths[i];
|
|
663
|
+
try {
|
|
664
|
+
//Invoke with the current instance as `this`. Arrow closures (headers not yet migrated)
|
|
665
|
+
//ignore the binding and use their captured lexical this — the same instance — so this is
|
|
666
|
+
//behaviour-neutral for them; migrated headers use plain functions whose dynamic `this`
|
|
667
|
+
//this provides, letting their SCHEMA be class-cached instead of rebuilt per packet.
|
|
668
|
+
const result = decodes[i].call(this);
|
|
669
|
+
if (result && typeof result.then === 'function')
|
|
670
|
+
await result;
|
|
671
|
+
}
|
|
672
|
+
catch (e) {
|
|
673
|
+
this.recordError('', `Decode error: ${e.message}`);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
//Post-handler reads (cross-field/cross-layer fixups) are not a single field's bytes — clear
|
|
677
|
+
//the tracked path so they are not attributed to whichever field decoded last.
|
|
678
|
+
this.#currentFieldPath = '';
|
|
679
|
+
const postSelfDecodeHandlers = (0, SortPostHandlers_1.SortPostHandlers)(this.postSelfDecodeHandlers);
|
|
680
|
+
let postDecodeHandler = postSelfDecodeHandlers.shift();
|
|
681
|
+
while (postDecodeHandler) {
|
|
682
|
+
try {
|
|
683
|
+
await postDecodeHandler.handler();
|
|
684
|
+
}
|
|
685
|
+
catch (e) {
|
|
686
|
+
this.recordError('', `Decode error: ${e.message}`);
|
|
687
|
+
}
|
|
688
|
+
postDecodeHandler = postSelfDecodeHandlers.shift();
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Encode packet header field by field
|
|
693
|
+
*/
|
|
694
|
+
async encode() {
|
|
695
|
+
const encodes = this.getFieldCodecs(this.SCHEMA, 'encode', false);
|
|
696
|
+
for (const encode of encodes) {
|
|
697
|
+
//Shape validation is the deliberate fast-fail at the encode entry point (Ajv, in
|
|
698
|
+
//Codec.#encode); it runs before we get here. Past that, a field's encode closure must
|
|
699
|
+
//not crash the whole encode on an edge-case value — contain it to a recorded error so
|
|
700
|
+
//the packet still assembles best-effort (matches the decode contract).
|
|
701
|
+
//Synchronous fast-path (see decode): only await a genuinely returned Promise.
|
|
702
|
+
try {
|
|
703
|
+
//See decode: .call(this) is neutral for arrow closures, dynamic-`this` for migrated ones.
|
|
704
|
+
const result = encode.call(this);
|
|
705
|
+
if (result && typeof result.then === 'function')
|
|
706
|
+
await result;
|
|
707
|
+
}
|
|
708
|
+
catch (e) {
|
|
709
|
+
this.recordError('', `Encode error: ${e.message}`);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
const postSelfEncodeHandlers = (0, SortPostHandlers_1.SortPostHandlers)(this.postSelfEncodeHandlers);
|
|
713
|
+
let postEncodeHandler = postSelfEncodeHandlers.shift();
|
|
714
|
+
while (postEncodeHandler) {
|
|
715
|
+
try {
|
|
716
|
+
await postEncodeHandler.handler();
|
|
717
|
+
}
|
|
718
|
+
catch (e) {
|
|
719
|
+
this.recordError('', `Encode error: ${e.message}`);
|
|
720
|
+
}
|
|
721
|
+
postEncodeHandler = postSelfEncodeHandlers.shift();
|
|
722
|
+
}
|
|
723
|
+
//Encode grows `packet` with headroom (amortized doubling). Restore it to the exact logical
|
|
724
|
+
//length so every reader of codecData.packet — packet-level post handlers, the encode result,
|
|
725
|
+
//and single-layer callers — sees no trailing headroom. subarray is a view (no copy); a
|
|
726
|
+
//following layer simply re-grows from here.
|
|
727
|
+
const encodedLength = this.codecData.packetLength;
|
|
728
|
+
if (encodedLength !== undefined && encodedLength < this.codecData.packet.length) {
|
|
729
|
+
this.codecData.packet = this.codecData.packet.subarray(0, encodedLength);
|
|
730
|
+
}
|
|
731
|
+
this.codecData.packetLength = undefined;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
exports.BaseHeader = BaseHeader;
|