@honeybbq/teamspeak-client 0.3.0 → 0.3.1

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 (64) hide show
  1. package/dist/address-BR-5unVo.cjs.map +1 -1
  2. package/dist/address-BfmovVpp.js.map +1 -1
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/command-BLe-C2L2.js.map +1 -1
  5. package/dist/command-DAK4pUBP.cjs.map +1 -1
  6. package/dist/commands.d.ts.map +1 -1
  7. package/dist/crypt-handshake-CQkQreLx.cjs.map +1 -1
  8. package/dist/crypt-handshake-DziOY4Lq.js.map +1 -1
  9. package/dist/crypt-init2-D70ha4LB.js.map +1 -1
  10. package/dist/crypt-init2-DparLekm.cjs.map +1 -1
  11. package/dist/crypto/crypt.d.ts.map +1 -1
  12. package/dist/crypto/eax.d.ts.map +1 -1
  13. package/dist/crypto/identity.d.ts.map +1 -1
  14. package/dist/crypto-Bg02m4ZE.js.map +1 -1
  15. package/dist/crypto-CDlweUL3.cjs.map +1 -1
  16. package/dist/discovery/resolver.d.ts.map +1 -1
  17. package/dist/errors.d.ts.map +1 -1
  18. package/dist/handler-DI_abe9c.js.map +1 -1
  19. package/dist/handler-DuwoO5RC.cjs.map +1 -1
  20. package/dist/handshake/license.d.ts.map +1 -1
  21. package/dist/index.cjs +2 -2
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.mjs +96 -65
  24. package/dist/index.mjs.map +1 -1
  25. package/dist/parser-CL33R9tr.cjs.map +1 -1
  26. package/dist/parser-DZMv3Jm-.js.map +1 -1
  27. package/dist/primitives-cNNAeFCo.cjs.map +1 -1
  28. package/dist/primitives-xIr6Tjk4.js.map +1 -1
  29. package/dist/resolver-6B8dI9gr.js.map +1 -1
  30. package/dist/resolver-DCcmyWSK.cjs.map +1 -1
  31. package/dist/throttle.d.ts.map +1 -1
  32. package/dist/transfer.d.ts.map +1 -1
  33. package/dist/transport/generation-window.d.ts.map +1 -1
  34. package/dist/transport/handler.d.ts.map +1 -1
  35. package/dist/transport/quicklz.d.ts.map +1 -1
  36. package/package.json +4 -5
  37. package/dist/address.test.d.ts +0 -2
  38. package/dist/address.test.d.ts.map +0 -1
  39. package/dist/api.test.d.ts +0 -2
  40. package/dist/api.test.d.ts.map +0 -1
  41. package/dist/command/command.test.d.ts +0 -2
  42. package/dist/command/command.test.d.ts.map +0 -1
  43. package/dist/crypto/eax.test.d.ts +0 -2
  44. package/dist/crypto/eax.test.d.ts.map +0 -1
  45. package/dist/crypto/identity.test.d.ts +0 -2
  46. package/dist/crypto/identity.test.d.ts.map +0 -1
  47. package/dist/handshake.test.d.ts +0 -2
  48. package/dist/handshake.test.d.ts.map +0 -1
  49. package/dist/helpers.test.d.ts +0 -2
  50. package/dist/helpers.test.d.ts.map +0 -1
  51. package/dist/integration.test.d.ts +0 -12
  52. package/dist/integration.test.d.ts.map +0 -1
  53. package/dist/notifications.test.d.ts +0 -2
  54. package/dist/notifications.test.d.ts.map +0 -1
  55. package/dist/throttle.test.d.ts +0 -2
  56. package/dist/throttle.test.d.ts.map +0 -1
  57. package/dist/transport/generation-window.test.d.ts +0 -2
  58. package/dist/transport/generation-window.test.d.ts.map +0 -1
  59. package/dist/transport/packet.test.d.ts +0 -2
  60. package/dist/transport/packet.test.d.ts.map +0 -1
  61. package/dist/transport/quicklz.test.d.ts +0 -2
  62. package/dist/transport/quicklz.test.d.ts.map +0 -1
  63. package/dist/transport/whisper.test.d.ts +0 -2
  64. package/dist/transport/whisper.test.d.ts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"crypto-CDlweUL3.cjs","names":[],"sources":["../src/crypto/eax.ts","../src/crypto/crypt.ts"],"sourcesContent":["import { createCipheriv, timingSafeEqual } from \"node:crypto\";\nimport { EAXTagMismatchError } from \"../errors.js\";\n\nconst EAX_TAG_SIZE = 8;\nconst EAX_BLOCK_SIZE = 16;\n\n/**\n * AES-EAX AEAD implementation for TS3 (64-bit tag, AES-128).\n * Uses AES-CMAC for authentication and AES-CTR for encryption.\n */\nexport class EAX {\n readonly #key: Uint8Array;\n\n constructor(key: Uint8Array) {\n if (key.length !== 16) throw new Error(\"EAX requires a 16-byte key\");\n this.#key = key;\n }\n\n encrypt(\n nonce: Uint8Array,\n header: Uint8Array,\n plaintext: Uint8Array,\n ): [ciphertext: Uint8Array, tag: Uint8Array] {\n const nStar = this.#cmac(0, nonce);\n const hStar = this.#cmac(1, header);\n\n const ciphertext = aesCtr(this.#key, nStar, plaintext);\n\n const cStar = this.#cmac(2, ciphertext);\n\n const tag = new Uint8Array(EAX_TAG_SIZE);\n for (let i = 0; i < EAX_TAG_SIZE; i++) {\n tag[i] = nStar[i]! ^ hStar[i]! ^ cStar[i]!;\n }\n\n return [ciphertext, tag];\n }\n\n decrypt(\n nonce: Uint8Array,\n header: Uint8Array,\n ciphertext: Uint8Array,\n tag: Uint8Array,\n ): Uint8Array {\n const nStar = this.#cmac(0, nonce);\n const hStar = this.#cmac(1, header);\n const cStar = this.#cmac(2, ciphertext);\n\n const expected = new Uint8Array(EAX_TAG_SIZE);\n for (let i = 0; i < EAX_TAG_SIZE; i++) {\n expected[i] = nStar[i]! ^ hStar[i]! ^ cStar[i]!;\n }\n\n if (!timingSafeEqual(Buffer.from(expected), Buffer.from(tag.slice(0, EAX_TAG_SIZE)))) {\n throw new EAXTagMismatchError();\n }\n\n return aesCtr(this.#key, nStar, ciphertext);\n }\n\n /**\n * AES-CMAC per RFC 4493:\n * input = [0…0 tag] ++ data (one block prefix)\n */\n #cmac(tagByte: number, data: Uint8Array): Uint8Array {\n const input = new Uint8Array(EAX_BLOCK_SIZE + data.length);\n // first block: zeros then tag byte at the last position\n input[EAX_BLOCK_SIZE - 1] = tagByte;\n input.set(data, EAX_BLOCK_SIZE);\n\n return aesCmac(this.#key, input);\n }\n}\n\n// ---- AES-CTR ----------------------------------------------------------------\n\nfunction aesCtr(key: Uint8Array, iv: Uint8Array, data: Uint8Array): Uint8Array {\n const cipher = createCipheriv(\"aes-128-ctr\", Buffer.from(key), Buffer.from(iv));\n return Buffer.concat([cipher.update(Buffer.from(data)), cipher.final()]);\n}\n\n// ---- AES-CMAC (RFC 4493) ----------------------------------------------------\n\nfunction aesEcb(key: Uint8Array, block: Uint8Array): Uint8Array {\n // Node.js does not expose ECB directly; simulate via CTR with zero IV and\n // a zero-input (CTR of 0-block XOR with input = ECB)\n const cipher = createCipheriv(\"aes-128-ecb\", Buffer.from(key), null);\n cipher.setAutoPadding(false);\n return Buffer.concat([cipher.update(Buffer.from(block)), cipher.final()]);\n}\n\nfunction xorBlock(a: Uint8Array, b: Uint8Array): Uint8Array {\n const out = new Uint8Array(EAX_BLOCK_SIZE);\n for (let i = 0; i < EAX_BLOCK_SIZE; i++) out[i] = a[i]! ^ b[i]!;\n return out;\n}\n\nfunction shiftLeft1(b: Uint8Array): Uint8Array {\n const out = new Uint8Array(EAX_BLOCK_SIZE);\n let carry = 0;\n for (let i = EAX_BLOCK_SIZE - 1; i >= 0; i--) {\n const shifted = ((b[i]! << 1) | carry) & 0xff;\n carry = b[i]! >> 7;\n out[i] = shifted;\n }\n return out;\n}\n\nconst RB = new Uint8Array(EAX_BLOCK_SIZE);\nRB[EAX_BLOCK_SIZE - 1] = 0x87;\n\nfunction generateSubkeys(key: Uint8Array): [Uint8Array, Uint8Array] {\n const zero = new Uint8Array(EAX_BLOCK_SIZE);\n const L = aesEcb(key, zero);\n\n let K1 = shiftLeft1(L);\n if ((L[0]! & 0x80) !== 0) K1 = xorBlock(K1, RB);\n\n let K2 = shiftLeft1(K1);\n if ((K1[0]! & 0x80) !== 0) K2 = xorBlock(K2, RB);\n\n return [K1, K2];\n}\n\nexport function aesCmac(key: Uint8Array, message: Uint8Array): Uint8Array {\n const [K1, K2] = generateSubkeys(key);\n\n const n = Math.max(1, Math.ceil(message.length / EAX_BLOCK_SIZE));\n const lastBlockComplete = message.length > 0 && message.length % EAX_BLOCK_SIZE === 0;\n\n let X: Uint8Array = new Uint8Array(EAX_BLOCK_SIZE);\n\n for (let i = 0; i < n - 1; i++) {\n const block = message.slice(i * EAX_BLOCK_SIZE, (i + 1) * EAX_BLOCK_SIZE);\n X = Uint8Array.from(aesEcb(key, xorBlock(X, block)));\n }\n\n const lastBlock = new Uint8Array(EAX_BLOCK_SIZE);\n const lastStart = (n - 1) * EAX_BLOCK_SIZE;\n const lastSlice = message.slice(lastStart);\n lastBlock.set(lastSlice);\n\n let Mn: Uint8Array;\n if (lastBlockComplete) {\n Mn = xorBlock(lastBlock, K1);\n } else {\n if (lastSlice.length < EAX_BLOCK_SIZE) lastBlock[lastSlice.length] = 0x80;\n Mn = xorBlock(lastBlock, K2);\n }\n\n return Uint8Array.from(aesEcb(key, xorBlock(X, Mn)));\n}\n","import { createHash, createECDH } from \"node:crypto\";\nimport { FakeSignatureMismatchError } from \"../errors.js\";\nimport type { Identity } from \"./identity.js\";\nimport { importPublicKey } from \"./identity.js\";\nimport { EAX } from \"./eax.js\";\n\nconst FAKE_SIGNATURE_SIZE = 8;\nconst IV_ALPHA_SIZE = 10;\nconst INIT1_PACKET_TYPE = 8;\nconst PACKET_TYPE_MASK = 0x0f;\n\nconst INIT1_MAC = Buffer.from(\"TS3INIT1\");\n\nexport interface KeyNonce {\n key: Uint8Array;\n nonce: Uint8Array;\n gen: number;\n}\n\n/** Precomputed dummy key/nonce matching the TS3 client pre-crypto placeholder. */\nconst DUMMY_KEY = Buffer.from(\"c:\\\\windows\\\\syste\");\nconst DUMMY_NONCE = Buffer.from(\"m\\\\firewall32.cpl\");\n\nexport class Crypt {\n readonly identity: Identity;\n\n ivStruct: Uint8Array = new Uint8Array(0);\n fakeSignature: Uint8Array = new Uint8Array(FAKE_SIGNATURE_SIZE);\n alphaTmp: Uint8Array = new Uint8Array(0);\n cryptoInitComplete = false;\n\n readonly #cachedKeys = new Map<bigint, KeyNonce>();\n\n constructor(identity: Identity) {\n this.identity = identity;\n }\n\n solveRsaChallenge(data: Uint8Array, offset: number, level: number): Uint8Array {\n if (level < 0 || level > 1_000_000) {\n throw new Error(\"RSA challenge level out of range\");\n }\n\n const xBytes = data.slice(offset, offset + 64);\n const nBytes = data.slice(offset + 64, offset + 128);\n\n let y = bytesToBigInt(xBytes);\n const n = bytesToBigInt(nBytes);\n\n for (let i = 0; i < level; i++) {\n y = (y * y) % n;\n }\n\n return bigIntToBytes(y, 64);\n }\n\n initCrypto(alpha: string, beta: string, omega: string): void {\n const alphaBytes = Buffer.from(alpha, \"base64\");\n const betaBytes = Buffer.from(beta, \"base64\");\n const omegaBytes = Buffer.from(omega, \"base64\");\n\n const serverPubKey = importPublicKey(omegaBytes);\n const sharedSecret = this.#getSharedSecret(serverPubKey);\n\n this.setSharedSecret(alphaBytes, betaBytes, sharedSecret);\n }\n\n setSharedSecret(alpha: Uint8Array, beta: Uint8Array, sharedKey: Uint8Array): void {\n this.ivStruct = new Uint8Array(IV_ALPHA_SIZE + beta.length);\n for (let i = 0; i < IV_ALPHA_SIZE; i++) {\n const a = sharedKey[i];\n const b = alpha[i];\n this.ivStruct[i] = ((a !== undefined ? a : 0) ^ (b !== undefined ? b : 0)) & 0xff;\n }\n for (let i = 0; i < beta.length; i++) {\n const a = sharedKey[IV_ALPHA_SIZE + i];\n const b = beta[i];\n this.ivStruct[IV_ALPHA_SIZE + i] =\n ((a !== undefined ? a : 0) ^ (b !== undefined ? b : 0)) & 0xff;\n }\n\n const digest = createHash(\"sha1\").update(this.ivStruct).digest();\n this.fakeSignature = new Uint8Array(digest.buffer, digest.byteOffset, FAKE_SIGNATURE_SIZE);\n this.cryptoInitComplete = true;\n }\n\n getKeyNonce(\n fromServer: boolean,\n packetID: number,\n generationID: number,\n packetType: number,\n dummy: boolean,\n ): [key: Uint8Array, nonce: Uint8Array] {\n if (dummy) {\n return [Uint8Array.from(DUMMY_KEY), Uint8Array.from(DUMMY_NONCE)];\n }\n\n const cacheKey = makeCacheKey(fromServer, packetType, generationID);\n let kn = this.#cachedKeys.get(cacheKey);\n\n if (kn === undefined) {\n const tmpToHash = new Uint8Array(6 + this.ivStruct.length);\n tmpToHash[0] = fromServer ? 0x30 : 0x31;\n tmpToHash[1] = packetType & PACKET_TYPE_MASK;\n new DataView(tmpToHash.buffer).setUint32(2, generationID, false);\n tmpToHash.set(this.ivStruct, 6);\n\n const hash = createHash(\"sha256\").update(tmpToHash).digest();\n kn = {\n key: Uint8Array.from(hash.slice(0, 16)),\n nonce: Uint8Array.from(hash.slice(16, 32)),\n gen: generationID,\n };\n this.#cachedKeys.set(cacheKey, kn);\n }\n\n const key = Uint8Array.from(kn.key);\n key[0] = (key[0] !== undefined ? key[0] : 0) ^ ((packetID >> 8) & 0xff);\n key[1] = (key[1] !== undefined ? key[1] : 0) ^ (packetID & 0xff);\n\n return [key, kn.nonce];\n }\n\n encrypt(\n packetType: number,\n packetID: number,\n generationID: number,\n header: Uint8Array,\n plaintext: Uint8Array,\n dummy: boolean,\n unencrypted: boolean,\n ): [ciphertext: Uint8Array, mac: Uint8Array] {\n if (packetType === INIT1_PACKET_TYPE) {\n return [plaintext, INIT1_MAC];\n }\n if (unencrypted) {\n return [plaintext, this.fakeSignature];\n }\n\n const [key, nonce] = this.getKeyNonce(false, packetID, generationID, packetType, dummy);\n const eax = new EAX(key);\n return eax.encrypt(nonce, header, plaintext);\n }\n\n decrypt(\n packetType: number,\n packetID: number,\n generationID: number,\n header: Uint8Array,\n ciphertext: Uint8Array,\n tag: Uint8Array,\n dummy: boolean,\n unencrypted: boolean,\n ): Uint8Array {\n if (packetType === INIT1_PACKET_TYPE) {\n return ciphertext;\n }\n if (unencrypted) {\n const fsSub = tag.slice(0, FAKE_SIGNATURE_SIZE);\n if (!Buffer.from(fsSub).equals(Buffer.from(this.fakeSignature))) {\n throw new FakeSignatureMismatchError();\n }\n return ciphertext;\n }\n\n const [key, nonce] = this.getKeyNonce(true, packetID, generationID, packetType, dummy);\n const eax = new EAX(key);\n return eax.decrypt(nonce, header, ciphertext, tag);\n }\n\n #getSharedSecret(serverPubKey: import(\"node:crypto\").KeyObject): Uint8Array {\n const privJwk = this.identity.privateKey.export({ format: \"jwk\" }) as {\n d: string;\n };\n const pubJwk = serverPubKey.export({ format: \"jwk\" }) as {\n x: string;\n y: string;\n };\n\n const ecdh = createECDH(\"prime256v1\");\n const dBytes = Buffer.from(privJwk.d, \"base64url\");\n ecdh.setPrivateKey(dBytes);\n\n const xBytes = base64UrlToBytes(pubJwk.x, 32);\n const yBytes = base64UrlToBytes(pubJwk.y, 32);\n const uncompressed = Buffer.alloc(65);\n uncompressed[0] = 0x04;\n xBytes.copy(uncompressed, 1);\n yBytes.copy(uncompressed, 33);\n\n const rawSecret = ecdh.computeSecret(uncompressed);\n return createHash(\"sha1\").update(rawSecret).digest();\n }\n}\n\n// ---- Helpers ----------------------------------------------------------------\n\nfunction makeCacheKey(fromServer: boolean, packetType: number, generationID: number): bigint {\n let key = 0n;\n if (fromServer) key |= 1n << 40n;\n key |= BigInt(packetType & PACKET_TYPE_MASK) << 32n;\n key |= BigInt(generationID);\n return key;\n}\n\nfunction bytesToBigInt(bytes: Uint8Array): bigint {\n let result = 0n;\n for (const b of bytes) result = (result << 8n) | BigInt(b);\n return result;\n}\n\nfunction bigIntToBytes(value: bigint, size: number): Uint8Array {\n const result = new Uint8Array(size);\n let v = value;\n for (let i = size - 1; i >= 0; i--) {\n result[i] = Number(v & 0xffn);\n v >>= 8n;\n }\n return result;\n}\n\nfunction base64UrlToBytes(b64url: string, size: number): Buffer {\n const buf = Buffer.from(b64url, \"base64url\");\n if (buf.length === size) return buf;\n const padded = Buffer.alloc(size);\n buf.copy(padded, size - buf.length);\n return padded;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"crypto-CDlweUL3.cjs","names":["timingSafeEqual","EAXTagMismatchError","createCipheriv","importPublicKey","createHash","FakeSignatureMismatchError","createECDH","require_primitives","node_crypto","EAX_TAG_SIZE","EAX_BLOCK_SIZE","EAX","key","#key","nStar","#cmac","nonce","hStar","header","ciphertext","aesCtr","plaintext","cStar","tag","i","expected","input","data","tagByte","aesCmac","cipher","iv","aesEcb","block","xorBlock","out","a","b","shiftLeft1","carry","shifted","RB","generateSubkeys","L","K1","K2","n","message","lastBlockComplete","X","lastBlock","lastStart","lastSlice","Mn","FAKE_SIGNATURE_SIZE","IV_ALPHA_SIZE","INIT1_PACKET_TYPE","PACKET_TYPE_MASK","INIT1_MAC","DUMMY_KEY","DUMMY_NONCE","Crypt","identity","level","xBytes","offset","nBytes","y","bytesToBigInt","bigIntToBytes","alphaBytes","alpha","betaBytes","beta","omegaBytes","omega","serverPubKey","sharedSecret","#getSharedSecret","sharedKey","digest","dummy","cacheKey","makeCacheKey","fromServer","packetType","generationID","kn","#cachedKeys","tmpToHash","hash","packetID","unencrypted","fsSub","privJwk","pubJwk","ecdh","dBytes","base64UrlToBytes","yBytes","uncompressed","rawSecret","result","bytes","size","v","value","buf","b64url","padded"],"sources":["../src/crypto/eax.ts","../src/crypto/crypt.ts"],"sourcesContent":["import { createCipheriv, timingSafeEqual } from \"node:crypto\";\nimport { EAXTagMismatchError } from \"../errors.js\";\n\nconst EAX_TAG_SIZE = 8;\nconst EAX_BLOCK_SIZE = 16;\n\n/**\n * AES-EAX AEAD implementation for TS3 (64-bit tag, AES-128).\n * Uses AES-CMAC for authentication and AES-CTR for encryption.\n */\nexport class EAX {\n readonly #key: Uint8Array;\n\n constructor(key: Uint8Array) {\n if (key.length !== 16) throw new Error(\"EAX requires a 16-byte key\");\n this.#key = key;\n }\n\n encrypt(\n nonce: Uint8Array,\n header: Uint8Array,\n plaintext: Uint8Array,\n ): [ciphertext: Uint8Array, tag: Uint8Array] {\n const nStar = this.#cmac(0, nonce);\n const hStar = this.#cmac(1, header);\n\n const ciphertext = aesCtr(this.#key, nStar, plaintext);\n\n const cStar = this.#cmac(2, ciphertext);\n\n const tag = new Uint8Array(EAX_TAG_SIZE);\n for (let i = 0; i < EAX_TAG_SIZE; i++) {\n tag[i] = nStar[i]! ^ hStar[i]! ^ cStar[i]!;\n }\n\n return [ciphertext, tag];\n }\n\n decrypt(\n nonce: Uint8Array,\n header: Uint8Array,\n ciphertext: Uint8Array,\n tag: Uint8Array,\n ): Uint8Array {\n const nStar = this.#cmac(0, nonce);\n const hStar = this.#cmac(1, header);\n const cStar = this.#cmac(2, ciphertext);\n\n const expected = new Uint8Array(EAX_TAG_SIZE);\n for (let i = 0; i < EAX_TAG_SIZE; i++) {\n expected[i] = nStar[i]! ^ hStar[i]! ^ cStar[i]!;\n }\n\n if (!timingSafeEqual(Buffer.from(expected), Buffer.from(tag.slice(0, EAX_TAG_SIZE)))) {\n throw new EAXTagMismatchError();\n }\n\n return aesCtr(this.#key, nStar, ciphertext);\n }\n\n /**\n * AES-CMAC per RFC 4493:\n * input = [0…0 tag] ++ data (one block prefix)\n */\n #cmac(tagByte: number, data: Uint8Array): Uint8Array {\n const input = new Uint8Array(EAX_BLOCK_SIZE + data.length);\n // first block: zeros then tag byte at the last position\n input[EAX_BLOCK_SIZE - 1] = tagByte;\n input.set(data, EAX_BLOCK_SIZE);\n\n return aesCmac(this.#key, input);\n }\n}\n\n// ---- AES-CTR ----------------------------------------------------------------\n\nfunction aesCtr(key: Uint8Array, iv: Uint8Array, data: Uint8Array): Uint8Array {\n const cipher = createCipheriv(\"aes-128-ctr\", Buffer.from(key), Buffer.from(iv));\n return Buffer.concat([cipher.update(Buffer.from(data)), cipher.final()]);\n}\n\n// ---- AES-CMAC (RFC 4493) ----------------------------------------------------\n\nfunction aesEcb(key: Uint8Array, block: Uint8Array): Uint8Array {\n // Node.js does not expose ECB directly; simulate via CTR with zero IV and\n // a zero-input (CTR of 0-block XOR with input = ECB)\n const cipher = createCipheriv(\"aes-128-ecb\", Buffer.from(key), null);\n cipher.setAutoPadding(false);\n return Buffer.concat([cipher.update(Buffer.from(block)), cipher.final()]);\n}\n\nfunction xorBlock(a: Uint8Array, b: Uint8Array): Uint8Array {\n const out = new Uint8Array(EAX_BLOCK_SIZE);\n for (let i = 0; i < EAX_BLOCK_SIZE; i++) out[i] = a[i]! ^ b[i]!;\n return out;\n}\n\nfunction shiftLeft1(b: Uint8Array): Uint8Array {\n const out = new Uint8Array(EAX_BLOCK_SIZE);\n let carry = 0;\n for (let i = EAX_BLOCK_SIZE - 1; i >= 0; i--) {\n const shifted = ((b[i]! << 1) | carry) & 0xff;\n carry = b[i]! >> 7;\n out[i] = shifted;\n }\n return out;\n}\n\nconst RB = new Uint8Array(EAX_BLOCK_SIZE);\nRB[EAX_BLOCK_SIZE - 1] = 0x87;\n\nfunction generateSubkeys(key: Uint8Array): [Uint8Array, Uint8Array] {\n const zero = new Uint8Array(EAX_BLOCK_SIZE);\n const L = aesEcb(key, zero);\n\n let K1 = shiftLeft1(L);\n if ((L[0]! & 0x80) !== 0) K1 = xorBlock(K1, RB);\n\n let K2 = shiftLeft1(K1);\n if ((K1[0]! & 0x80) !== 0) K2 = xorBlock(K2, RB);\n\n return [K1, K2];\n}\n\nexport function aesCmac(key: Uint8Array, message: Uint8Array): Uint8Array {\n const [K1, K2] = generateSubkeys(key);\n\n const n = Math.max(1, Math.ceil(message.length / EAX_BLOCK_SIZE));\n const lastBlockComplete = message.length > 0 && message.length % EAX_BLOCK_SIZE === 0;\n\n let X: Uint8Array = new Uint8Array(EAX_BLOCK_SIZE);\n\n for (let i = 0; i < n - 1; i++) {\n const block = message.slice(i * EAX_BLOCK_SIZE, (i + 1) * EAX_BLOCK_SIZE);\n X = Uint8Array.from(aesEcb(key, xorBlock(X, block)));\n }\n\n const lastBlock = new Uint8Array(EAX_BLOCK_SIZE);\n const lastStart = (n - 1) * EAX_BLOCK_SIZE;\n const lastSlice = message.slice(lastStart);\n lastBlock.set(lastSlice);\n\n let Mn: Uint8Array;\n if (lastBlockComplete) {\n Mn = xorBlock(lastBlock, K1);\n } else {\n if (lastSlice.length < EAX_BLOCK_SIZE) lastBlock[lastSlice.length] = 0x80;\n Mn = xorBlock(lastBlock, K2);\n }\n\n return Uint8Array.from(aesEcb(key, xorBlock(X, Mn)));\n}\n","import { createHash, createECDH } from \"node:crypto\";\nimport { FakeSignatureMismatchError } from \"../errors.js\";\nimport type { Identity } from \"./identity.js\";\nimport { importPublicKey } from \"./identity.js\";\nimport { EAX } from \"./eax.js\";\n\nconst FAKE_SIGNATURE_SIZE = 8;\nconst IV_ALPHA_SIZE = 10;\nconst INIT1_PACKET_TYPE = 8;\nconst PACKET_TYPE_MASK = 0x0f;\n\nconst INIT1_MAC = Buffer.from(\"TS3INIT1\");\n\nexport interface KeyNonce {\n key: Uint8Array;\n nonce: Uint8Array;\n gen: number;\n}\n\n/** Precomputed dummy key/nonce matching the TS3 client pre-crypto placeholder. */\nconst DUMMY_KEY = Buffer.from(\"c:\\\\windows\\\\syste\");\nconst DUMMY_NONCE = Buffer.from(\"m\\\\firewall32.cpl\");\n\nexport class Crypt {\n readonly identity: Identity;\n\n ivStruct: Uint8Array = new Uint8Array(0);\n fakeSignature: Uint8Array = new Uint8Array(FAKE_SIGNATURE_SIZE);\n alphaTmp: Uint8Array = new Uint8Array(0);\n cryptoInitComplete = false;\n\n readonly #cachedKeys = new Map<bigint, KeyNonce>();\n\n constructor(identity: Identity) {\n this.identity = identity;\n }\n\n solveRsaChallenge(data: Uint8Array, offset: number, level: number): Uint8Array {\n if (level < 0 || level > 1_000_000) {\n throw new Error(\"RSA challenge level out of range\");\n }\n\n const xBytes = data.slice(offset, offset + 64);\n const nBytes = data.slice(offset + 64, offset + 128);\n\n let y = bytesToBigInt(xBytes);\n const n = bytesToBigInt(nBytes);\n\n for (let i = 0; i < level; i++) {\n y = (y * y) % n;\n }\n\n return bigIntToBytes(y, 64);\n }\n\n initCrypto(alpha: string, beta: string, omega: string): void {\n const alphaBytes = Buffer.from(alpha, \"base64\");\n const betaBytes = Buffer.from(beta, \"base64\");\n const omegaBytes = Buffer.from(omega, \"base64\");\n\n const serverPubKey = importPublicKey(omegaBytes);\n const sharedSecret = this.#getSharedSecret(serverPubKey);\n\n this.setSharedSecret(alphaBytes, betaBytes, sharedSecret);\n }\n\n setSharedSecret(alpha: Uint8Array, beta: Uint8Array, sharedKey: Uint8Array): void {\n this.ivStruct = new Uint8Array(IV_ALPHA_SIZE + beta.length);\n for (let i = 0; i < IV_ALPHA_SIZE; i++) {\n const a = sharedKey[i];\n const b = alpha[i];\n this.ivStruct[i] = ((a !== undefined ? a : 0) ^ (b !== undefined ? b : 0)) & 0xff;\n }\n for (let i = 0; i < beta.length; i++) {\n const a = sharedKey[IV_ALPHA_SIZE + i];\n const b = beta[i];\n this.ivStruct[IV_ALPHA_SIZE + i] =\n ((a !== undefined ? a : 0) ^ (b !== undefined ? b : 0)) & 0xff;\n }\n\n const digest = createHash(\"sha1\").update(this.ivStruct).digest();\n this.fakeSignature = new Uint8Array(digest.buffer, digest.byteOffset, FAKE_SIGNATURE_SIZE);\n this.cryptoInitComplete = true;\n }\n\n getKeyNonce(\n fromServer: boolean,\n packetID: number,\n generationID: number,\n packetType: number,\n dummy: boolean,\n ): [key: Uint8Array, nonce: Uint8Array] {\n if (dummy) {\n return [Uint8Array.from(DUMMY_KEY), Uint8Array.from(DUMMY_NONCE)];\n }\n\n const cacheKey = makeCacheKey(fromServer, packetType, generationID);\n let kn = this.#cachedKeys.get(cacheKey);\n\n if (kn === undefined) {\n const tmpToHash = new Uint8Array(6 + this.ivStruct.length);\n tmpToHash[0] = fromServer ? 0x30 : 0x31;\n tmpToHash[1] = packetType & PACKET_TYPE_MASK;\n new DataView(tmpToHash.buffer).setUint32(2, generationID, false);\n tmpToHash.set(this.ivStruct, 6);\n\n const hash = createHash(\"sha256\").update(tmpToHash).digest();\n kn = {\n key: Uint8Array.from(hash.slice(0, 16)),\n nonce: Uint8Array.from(hash.slice(16, 32)),\n gen: generationID,\n };\n this.#cachedKeys.set(cacheKey, kn);\n }\n\n const key = Uint8Array.from(kn.key);\n key[0] = (key[0] !== undefined ? key[0] : 0) ^ ((packetID >> 8) & 0xff);\n key[1] = (key[1] !== undefined ? key[1] : 0) ^ (packetID & 0xff);\n\n return [key, kn.nonce];\n }\n\n encrypt(\n packetType: number,\n packetID: number,\n generationID: number,\n header: Uint8Array,\n plaintext: Uint8Array,\n dummy: boolean,\n unencrypted: boolean,\n ): [ciphertext: Uint8Array, mac: Uint8Array] {\n if (packetType === INIT1_PACKET_TYPE) {\n return [plaintext, INIT1_MAC];\n }\n if (unencrypted) {\n return [plaintext, this.fakeSignature];\n }\n\n const [key, nonce] = this.getKeyNonce(false, packetID, generationID, packetType, dummy);\n const eax = new EAX(key);\n return eax.encrypt(nonce, header, plaintext);\n }\n\n decrypt(\n packetType: number,\n packetID: number,\n generationID: number,\n header: Uint8Array,\n ciphertext: Uint8Array,\n tag: Uint8Array,\n dummy: boolean,\n unencrypted: boolean,\n ): Uint8Array {\n if (packetType === INIT1_PACKET_TYPE) {\n return ciphertext;\n }\n if (unencrypted) {\n const fsSub = tag.slice(0, FAKE_SIGNATURE_SIZE);\n if (!Buffer.from(fsSub).equals(Buffer.from(this.fakeSignature))) {\n throw new FakeSignatureMismatchError();\n }\n return ciphertext;\n }\n\n const [key, nonce] = this.getKeyNonce(true, packetID, generationID, packetType, dummy);\n const eax = new EAX(key);\n return eax.decrypt(nonce, header, ciphertext, tag);\n }\n\n #getSharedSecret(serverPubKey: import(\"node:crypto\").KeyObject): Uint8Array {\n const privJwk = this.identity.privateKey.export({ format: \"jwk\" }) as {\n d: string;\n };\n const pubJwk = serverPubKey.export({ format: \"jwk\" }) as {\n x: string;\n y: string;\n };\n\n const ecdh = createECDH(\"prime256v1\");\n const dBytes = Buffer.from(privJwk.d, \"base64url\");\n ecdh.setPrivateKey(dBytes);\n\n const xBytes = base64UrlToBytes(pubJwk.x, 32);\n const yBytes = base64UrlToBytes(pubJwk.y, 32);\n const uncompressed = Buffer.alloc(65);\n uncompressed[0] = 0x04;\n xBytes.copy(uncompressed, 1);\n yBytes.copy(uncompressed, 33);\n\n const rawSecret = ecdh.computeSecret(uncompressed);\n return createHash(\"sha1\").update(rawSecret).digest();\n }\n}\n\n// ---- Helpers ----------------------------------------------------------------\n\nfunction makeCacheKey(fromServer: boolean, packetType: number, generationID: number): bigint {\n let key = 0n;\n if (fromServer) key |= 1n << 40n;\n key |= BigInt(packetType & PACKET_TYPE_MASK) << 32n;\n key |= BigInt(generationID);\n return key;\n}\n\nfunction bytesToBigInt(bytes: Uint8Array): bigint {\n let result = 0n;\n for (const b of bytes) result = (result << 8n) | BigInt(b);\n return result;\n}\n\nfunction bigIntToBytes(value: bigint, size: number): Uint8Array {\n const result = new Uint8Array(size);\n let v = value;\n for (let i = size - 1; i >= 0; i--) {\n result[i] = Number(v & 0xffn);\n v >>= 8n;\n }\n return result;\n}\n\nfunction base64UrlToBytes(b64url: string, size: number): Buffer {\n const buf = Buffer.from(b64url, \"base64url\");\n if (buf.length === size) return buf;\n const padded = Buffer.alloc(size);\n buf.copy(padded, size - buf.length);\n return padded;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/discovery/resolver.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAQhD,qBAAa,QAAQ;;gBAGP,IAAI,GAAE,MAAmB;IAE/B,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CA+GhF"}
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/discovery/resolver.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAQhD,qBAAa,QAAQ;;IAGnB,YAAY,IAAI,GAAE,MAAmB,EAAI;IAEnC,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CA+C9E;CAgEF"}
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAIpD;AAED,qBAAa,WAAY,SAAQ,cAAc;IAC7C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;gBAEnB,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;CAM9C;AAED,qBAAa,mBAAoB,SAAQ,cAAc;IACrD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM;CAK5B;AAED,qBAAa,qBAAsB,SAAQ,cAAc;;CAKxD;AAED,qBAAa,mBAAoB,SAAQ,cAAc;;CAKtD;AAED,qBAAa,0BAA2B,SAAQ,cAAc;;CAK7D;AAED,qBAAa,iBAAkB,SAAQ,cAAc;gBACvC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAIpD;AAED,qBAAa,wBAAyB,SAAQ,cAAc;;CAK3D;AAED,qBAAa,eAAgB,SAAQ,cAAc;gBACrC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAIpD;AAED,qBAAa,oBAAqB,SAAQ,cAAc;gBAC1C,OAAO,SAA4B;CAIhD"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,cAAe,SAAQ,KAAK;IACvC,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,EAGlD;CACF;AAED,qBAAa,WAAY,SAAQ,cAAc;IAC7C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAE/B,YAAY,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAK5C;CACF;AAED,qBAAa,mBAAoB,SAAQ,cAAc;IACrD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,YAAY,OAAO,EAAE,MAAM,EAI1B;CACF;AAED,qBAAa,qBAAsB,SAAQ,cAAc;IACvD,cAGC;CACF;AAED,qBAAa,mBAAoB,SAAQ,cAAc;IACrD,cAGC;CACF;AAED,qBAAa,0BAA2B,SAAQ,cAAc;IAC5D,cAGC;CACF;AAED,qBAAa,iBAAkB,SAAQ,cAAc;IACnD,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,EAGlD;CACF;AAED,qBAAa,wBAAyB,SAAQ,cAAc;IAC1D,cAGC;CACF;AAED,qBAAa,eAAgB,SAAQ,cAAc;IACjD,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,EAGlD;CACF;AAED,qBAAa,oBAAqB,SAAQ,cAAc;IACtD,YAAY,OAAO,SAA4B,EAG9C;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"handler-DI_abe9c.js","names":[],"sources":["../src/transport/packet.ts","../src/transport/generation-window.ts","../src/transport/quicklz.ts","../src/transport/handler.ts"],"sourcesContent":["export const enum PacketType {\n Voice = 0,\n VoiceWhisper = 1,\n Command = 2,\n CommandLow = 3,\n Ping = 4,\n Pong = 5,\n Ack = 6,\n AckLow = 7,\n Init1 = 8,\n}\n\nexport const enum PacketFlags {\n Fragmented = 0x10,\n NewProtocol = 0x20,\n Compressed = 0x40,\n Unencrypted = 0x80,\n}\n\nexport interface Packet {\n /** Type byte combined with flags (low nibble = type, high nibble = flags). */\n typeFlagged: number;\n id: number;\n clientID: number;\n generationID: number;\n data: Uint8Array;\n receivedAt: number; // Date.now()\n}\n\nexport function packetType(p: Packet): PacketType {\n return (p.typeFlagged & 0x0f) as PacketType;\n}\n\nexport function packetFlags(p: Packet): number {\n return p.typeFlagged & 0xf0;\n}\n\nexport function isUnencrypted(p: Packet): boolean {\n return (packetFlags(p) & PacketFlags.Unencrypted) !== 0;\n}\n\n/** Build the 5-byte client-to-server header: [packetID(2), clientID(2), typeFlagged(1)]. */\nexport function buildC2SHeader(p: Packet): Uint8Array {\n const header = new Uint8Array(5);\n const view = new DataView(header.buffer);\n view.setUint16(0, p.id, false);\n view.setUint16(2, p.clientID, false);\n header[4] = p.typeFlagged;\n return header;\n}\n\n/** Parse a 3-byte server-to-client header. */\nexport function parseS2CHeader(raw: Uint8Array): Pick<Packet, \"id\" | \"typeFlagged\"> {\n const view = new DataView(raw.buffer, raw.byteOffset, raw.byteLength);\n return {\n id: view.getUint16(0, false),\n typeFlagged: raw[2]!,\n };\n}\n\n/** Parse a 5-byte client-to-server header. */\nexport function parseC2SHeader(raw: Uint8Array): Pick<Packet, \"id\" | \"clientID\" | \"typeFlagged\"> {\n const view = new DataView(raw.buffer, raw.byteOffset, raw.byteLength);\n return {\n id: view.getUint16(0, false),\n clientID: view.getUint16(2, false),\n typeFlagged: raw[4]!,\n };\n}\n","export class GenerationWindow {\n #mappedBaseOffset = 0;\n #generation = 0;\n readonly #mod: number;\n readonly #receiveWindow: number;\n\n constructor(mod: number, windowSize: number) {\n this.#mod = mod;\n this.#receiveWindow = windowSize;\n }\n\n get generation(): number {\n return this.#generation;\n }\n\n advance(amount: number): void {\n if (amount <= 0) return;\n const newBaseOffset = this.#mappedBaseOffset + amount;\n const genStep = Math.floor(newBaseOffset / this.#mod);\n if (genStep > 0) {\n this.#generation = Math.min(this.#generation + genStep, 0xffff_ffff);\n }\n this.#mappedBaseOffset = newBaseOffset % this.#mod;\n }\n\n advanceToExcluded(mappedValue: number): void {\n let moveDist = mappedValue - this.#mappedBaseOffset;\n if (moveDist < 0) moveDist += this.#mod;\n this.advance(moveDist + 1);\n }\n\n syncTo(mappedValue: number): void {\n let moveDist = mappedValue - this.#mappedBaseOffset;\n if (moveDist < 0) moveDist += this.#mod;\n this.advance(moveDist);\n }\n\n isInWindow(mappedValue: number): boolean {\n const maxOffset = this.#mappedBaseOffset + this.#receiveWindow;\n if (maxOffset < this.#mod) {\n return mappedValue >= this.#mappedBaseOffset && mappedValue < maxOffset;\n }\n return mappedValue >= this.#mappedBaseOffset || mappedValue < maxOffset - this.#mod;\n }\n\n mappedToIndex(mappedValue: number): number {\n if (this.#isNextGen(mappedValue)) {\n return mappedValue + this.#mod - this.#mappedBaseOffset;\n }\n return mappedValue - this.#mappedBaseOffset;\n }\n\n isOldPacket(mappedValue: number): boolean {\n return this.mappedToIndex(mappedValue) < 0;\n }\n\n isFuturePacket(mappedValue: number): boolean {\n return this.mappedToIndex(mappedValue) >= this.#receiveWindow;\n }\n\n #isNextGen(mappedValue: number): boolean {\n return (\n this.#mappedBaseOffset > this.#mod - this.#receiveWindow &&\n mappedValue < this.#mappedBaseOffset + this.#receiveWindow - this.#mod\n );\n }\n\n getGeneration(mappedValue: number): number {\n if (this.#isNextGen(mappedValue)) return this.#generation + 1;\n return this.#generation;\n }\n\n reset(): void {\n this.#mappedBaseOffset = 0;\n this.#generation = 0;\n }\n}\n","const TABLE_SIZE = 4096;\n\ninterface QlzState {\n control: number;\n sourcePos: number;\n destPos: number;\n nextHashed: number;\n}\n\nexport class Qlz {\n #hashtable = new Int32Array(TABLE_SIZE);\n\n decompress(data: Uint8Array): Uint8Array {\n const { headerLen, decompressedSize, flags } = parseQlzHeader(data);\n\n const dest = new Uint8Array(decompressedSize);\n\n if ((flags & 0x01) === 0) {\n dest.set(data.slice(headerLen, headerLen + decompressedSize));\n return dest;\n }\n\n this.#hashtable.fill(0);\n\n const state: QlzState = {\n control: 1,\n sourcePos: headerLen,\n destPos: 0,\n nextHashed: 0,\n };\n\n while (this.#ensureControl(data, state)) {\n if ((state.control & 1) !== 0) {\n if (!this.#processReference(data, dest, state)) break;\n } else {\n if (this.#processLiteral(data, dest, decompressedSize, state)) break;\n }\n }\n\n return dest;\n }\n\n #ensureControl(data: Uint8Array, st: QlzState): boolean {\n if (st.control !== 1) return true;\n if (st.sourcePos + 4 > data.length) return false;\n st.control =\n (data[st.sourcePos]! |\n (data[st.sourcePos + 1]! << 8) |\n (data[st.sourcePos + 2]! << 16) |\n (data[st.sourcePos + 3]! << 24)) >>>\n 0;\n st.sourcePos += 4;\n return true;\n }\n\n #processReference(data: Uint8Array, dest: Uint8Array, st: QlzState): boolean {\n st.control = (st.control >>> 1) >>> 0;\n if (st.sourcePos + 2 > data.length) return false;\n\n const b1 = data[st.sourcePos]!;\n const b2 = data[st.sourcePos + 1]!;\n st.sourcePos += 2;\n\n const hash = (b1 >> 4) | (b2 << 4);\n let matchlen = b1 & 0x0f;\n if (matchlen !== 0) {\n matchlen += 2;\n } else {\n if (st.sourcePos >= data.length) return false;\n matchlen = data[st.sourcePos]!;\n st.sourcePos++;\n }\n\n const offset = this.#hashtable[hash]!;\n for (let i = 0; i < matchlen; i++) {\n if (st.destPos < dest.length && offset + i < st.destPos) {\n dest[st.destPos] = dest[offset + i]!;\n st.destPos++;\n }\n }\n\n const end = st.destPos + 1 - matchlen;\n this.#updateHashtable(dest, st, end);\n st.nextHashed = st.destPos;\n\n return true;\n }\n\n #processLiteral(\n data: Uint8Array,\n dest: Uint8Array,\n decompressedSize: number,\n st: QlzState,\n ): boolean {\n const threshold = Math.max(decompressedSize, 10) - 10;\n if (st.destPos >= threshold) {\n while (st.destPos < decompressedSize) {\n if (st.control === 1) {\n st.sourcePos += 4;\n if (st.sourcePos > data.length) break;\n st.control =\n (data[st.sourcePos - 4]! |\n (data[st.sourcePos - 3]! << 8) |\n (data[st.sourcePos - 2]! << 16) |\n (data[st.sourcePos - 1]! << 24)) >>>\n 0;\n }\n if (st.sourcePos >= data.length) break;\n dest[st.destPos++] = data[st.sourcePos++]!;\n st.control = (st.control >>> 1) >>> 0;\n }\n return true;\n }\n\n if (st.sourcePos >= data.length || st.destPos >= dest.length) return true;\n\n dest[st.destPos++] = data[st.sourcePos++]!;\n st.control = (st.control >>> 1) >>> 0;\n\n const end = Math.max(st.destPos - 2, 0);\n this.#updateHashtable(dest, st, end);\n if (st.nextHashed < end) st.nextHashed = end;\n\n return false;\n }\n\n #updateHashtable(dest: Uint8Array, st: QlzState, end: number): void {\n while (st.nextHashed < end) {\n if (st.nextHashed + 3 > dest.length) break;\n const v =\n (dest[st.nextHashed]! |\n (dest[st.nextHashed + 1]! << 8) |\n (dest[st.nextHashed + 2]! << 16)) >>>\n 0;\n const hash = ((v >> 12) ^ v) & 0xfff;\n this.#hashtable[hash] = st.nextHashed;\n st.nextHashed++;\n }\n }\n}\n\nfunction parseQlzHeader(data: Uint8Array): {\n headerLen: number;\n decompressedSize: number;\n flags: number;\n} {\n if (data.length < 3) throw new Error(\"QuickLZ: data too short\");\n\n const flags = data[0]!;\n const level = (flags >> 2) & 0x03;\n if (level !== 1) throw new Error(\"QuickLZ: only level 1 is supported\");\n\n const headerLen = (flags & 0x02) !== 0 ? 9 : 3;\n if (data.length < headerLen) throw new Error(\"QuickLZ: data too short for header\");\n\n let decompressedSize: number;\n if ((flags & 0x02) !== 0) {\n decompressedSize = (data[5]! | (data[6]! << 8) | (data[7]! << 16) | (data[8]! << 24)) >>> 0;\n } else {\n decompressedSize = data[2]!;\n }\n\n return { headerLen, decompressedSize, flags };\n}\n","import { createSocket, type Socket as UdpSocket } from \"node:dgram\";\nimport { isIP } from \"node:net\";\nimport { splitHostPort } from \"../address.js\";\nimport type { Crypt } from \"../crypto/crypt.js\";\nimport type { Logger } from \"../types.js\";\nimport { noopLogger } from \"../types.js\";\nimport {\n type Packet,\n PacketType,\n PacketFlags,\n packetType,\n packetFlags,\n buildC2SHeader,\n parseS2CHeader,\n} from \"./packet.js\";\nimport { GenerationWindow } from \"./generation-window.js\";\nimport { Qlz } from \"./quicklz.js\";\nimport { processInit1 } from \"../handshake/crypt-handshake.js\";\n\nconst MAX_OUT_PACKET_SIZE = 500;\nconst RECEIVE_PACKET_WINDOW_SIZE = 1024;\nconst PING_INTERVAL_MS = 5_000;\nconst PACKET_TIMEOUT_MS = 60_000;\nconst MAX_RETRY_INTERVAL_MS = 1_000;\nconst HEADER_SIZE = 5;\nconst TAG_SIZE = 8;\nconst VOICE_HEADER_SIZE = 3;\nconst WHISPER_HEADER_SIZE = 5;\nconst RESEND_BASE_INTERVAL_MS = 500;\nconst RESEND_LOOP_INTERVAL_MS = 100;\n\ninterface ResendPacket {\n packet: Packet;\n firstSend: number;\n lastSend: number;\n retryCount: number;\n nextInterval: number;\n}\n\nexport class PacketHandler {\n onPacket: ((p: Packet) => void) | null = null;\n onClosed: ((err: Error | null) => void) | null = null;\n\n readonly #crypt: Crypt;\n readonly #logger: Logger;\n\n #conn: UdpSocket | null = null;\n #clientID = 0;\n #closed = false;\n #stopPing: (() => void) | null = null;\n #stopResend: (() => void) | null = null;\n #lastMessageReceived = Date.now();\n\n #packetCounter = new Uint16Array(9);\n #generationCounter = new Uint32Array(9);\n\n #recvWindowCommand = new GenerationWindow(1 << 16, RECEIVE_PACKET_WINDOW_SIZE);\n #recvWindowCommandLow = new GenerationWindow(1 << 16, RECEIVE_PACKET_WINDOW_SIZE);\n #sendWindowCommand = new GenerationWindow(1 << 16, RECEIVE_PACKET_WINDOW_SIZE);\n #sendWindowCommandLow = new GenerationWindow(1 << 16, RECEIVE_PACKET_WINDOW_SIZE);\n\n #commandQueue = new Map<number, Packet>();\n #commandLowQueue = new Map<number, Packet>();\n #ackManager = new Map<number, ResendPacket>();\n #initPacketCheck: ResendPacket | null = null;\n\n constructor(crypt: Crypt, logger: Logger = noopLogger) {\n this.#crypt = crypt;\n this.#logger = logger;\n }\n\n setClientID(id: number): void {\n this.#clientID = id;\n }\n\n connect(addr: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const { host, port } = splitHostPort(addr);\n\n const socket = createSocket(isIP(host) === 6 ? \"udp6\" : \"udp4\");\n const onError = (error: Error) => {\n socket.close();\n reject(error);\n };\n socket.once(\"error\", onError);\n socket.connect(parseInt(port, 10), host, () => {\n socket.off(\"error\", onError);\n this.start(socket);\n resolve();\n });\n });\n }\n\n start(conn: UdpSocket): void {\n this.#conn = conn;\n this.#closed = false;\n this.#lastMessageReceived = Date.now();\n\n conn.on(\"message\", (msg) => {\n this.#lastMessageReceived = Date.now();\n this.#handleRawPacket(new Uint8Array(msg.buffer, msg.byteOffset, msg.byteLength));\n });\n conn.on(\"error\", (err) => {\n this.#logger.error(\"udp error\", err);\n this.#triggerClose(err);\n });\n conn.on(\"close\", () => this.#triggerClose(null));\n\n this.#packetCounter[PacketType.Command] = 1;\n this.#packetCounter[PacketType.Init1] = 101;\n\n const init1Data = processInit1(this.#crypt, null);\n if (init1Data) this.#sendPacketRaw(PacketType.Init1, init1Data, 0);\n\n // Ping loop\n const pingTimer = setInterval(() => {\n if (this.#crypt.cryptoInitComplete) {\n this.sendPacket(PacketType.Ping, new Uint8Array(0), PacketFlags.Unencrypted);\n }\n }, PING_INTERVAL_MS);\n this.#stopPing = () => clearInterval(pingTimer);\n\n // Resend loop\n const resendTimer = setInterval(() => this.#checkResends(), RESEND_LOOP_INTERVAL_MS);\n this.#stopResend = () => clearInterval(resendTimer);\n }\n\n receivedFinalInitAck(): void {\n this.#initPacketCheck = null;\n }\n\n sendPacket(pType: PacketType, data: Uint8Array, flags: number): void {\n const dummy = !this.#crypt.cryptoInitComplete;\n if (data.length > 487 && pType !== PacketType.Voice && pType !== PacketType.VoiceWhisper) {\n this.#sendSplitPacket(pType, data, flags, dummy);\n return;\n }\n this.#sendPacketRaw(pType, data, flags, dummy);\n }\n\n sendVoicePacket(data: Uint8Array, codec: number): void {\n const pID = this.#packetCounter[PacketType.Voice]!;\n const pGen = this.#generationCounter[PacketType.Voice]!;\n this.#packetCounter[PacketType.Voice] = (pID + 1) & 0xffff;\n if (this.#packetCounter[PacketType.Voice] === 0) {\n this.#generationCounter[PacketType.Voice]!++;\n }\n\n const payloadLen = VOICE_HEADER_SIZE + data.length;\n const voicePayload = new Uint8Array(payloadLen);\n new DataView(voicePayload.buffer).setUint16(0, pID, false);\n voicePayload[2] = codec;\n voicePayload.set(data, VOICE_HEADER_SIZE);\n\n const p: Packet = {\n typeFlagged: PacketType.Voice | PacketFlags.Unencrypted,\n id: pID,\n clientID: this.#clientID,\n generationID: pGen,\n data: voicePayload,\n receivedAt: 0,\n };\n\n const header = buildC2SHeader(p);\n const final = new Uint8Array(TAG_SIZE + HEADER_SIZE + payloadLen);\n final.set(this.#crypt.fakeSignature, 0);\n final.set(header, TAG_SIZE);\n final.set(voicePayload, TAG_SIZE + HEADER_SIZE);\n this.#write(final);\n }\n\n /** Send an unencrypted TeamSpeak voice whisper packet to up to 32 clients. */\n sendWhisperPacket(data: Uint8Array, targetClientIds: number[], codec: number): void {\n if (targetClientIds.length === 0) throw new Error(\"whisper requires at least one target\");\n if (targetClientIds.length > 32) throw new Error(\"too many whisper targets\");\n if (targetClientIds.some((id) => !Number.isInteger(id) || id < 0 || id > 0xffff)) {\n throw new RangeError(\"invalid whisper target\");\n }\n\n const pID = this.#packetCounter[PacketType.VoiceWhisper]!;\n const pGen = this.#generationCounter[PacketType.VoiceWhisper]!;\n this.#packetCounter[PacketType.VoiceWhisper] = (pID + 1) & 0xffff;\n if (this.#packetCounter[PacketType.VoiceWhisper] === 0) {\n this.#generationCounter[PacketType.VoiceWhisper] = (pGen + 1) >>> 0;\n }\n\n const payloadLen = WHISPER_HEADER_SIZE + targetClientIds.length * 2 + data.length;\n const whisperPayload = new Uint8Array(payloadLen);\n const view = new DataView(whisperPayload.buffer);\n view.setUint16(0, pID, false);\n whisperPayload[2] = codec;\n whisperPayload[3] = 0;\n whisperPayload[4] = targetClientIds.length;\n for (let index = 0; index < targetClientIds.length; index++) {\n view.setUint16(WHISPER_HEADER_SIZE + index * 2, targetClientIds[index]!, false);\n }\n whisperPayload.set(data, WHISPER_HEADER_SIZE + targetClientIds.length * 2);\n\n const packet: Packet = {\n typeFlagged: PacketType.VoiceWhisper | PacketFlags.Unencrypted,\n id: pID,\n clientID: this.#clientID,\n generationID: pGen,\n data: whisperPayload,\n receivedAt: 0,\n };\n const header = buildC2SHeader(packet);\n const final = new Uint8Array(TAG_SIZE + HEADER_SIZE + payloadLen);\n final.set(this.#crypt.fakeSignature, 0);\n final.set(header, TAG_SIZE);\n final.set(whisperPayload, TAG_SIZE + HEADER_SIZE);\n this.#write(final);\n }\n\n close(): void {\n if (this.#closed) return;\n this.#closed = true;\n this.#stopPing?.();\n this.#stopResend?.();\n this.#conn?.close();\n }\n\n // ---- Private ---------------------------------------------------------------\n\n #sendSplitPacket(pType: PacketType, data: Uint8Array, flags: number, dummy: boolean): void {\n const maxSize = MAX_OUT_PACKET_SIZE - HEADER_SIZE - TAG_SIZE; // 487\n let pos = 0;\n let first = true;\n\n while (pos < data.length) {\n const blockSize = Math.min(data.length - pos, maxSize);\n const last = pos + blockSize === data.length;\n\n let pFlags = flags;\n if (first !== last) pFlags |= PacketFlags.Fragmented;\n\n this.#sendPacketRaw(pType, data.slice(pos, pos + blockSize), pFlags, dummy);\n pos += blockSize;\n first = false;\n }\n }\n\n #sendPacketRaw(\n pType: PacketType,\n data: Uint8Array,\n flags: number,\n dummy = !this.#crypt.cryptoInitComplete,\n ): void {\n flags = applyProtocolFlags(pType, flags);\n const [pID, pGen] = this.#nextPacketIdentity(pType);\n const p: Packet = {\n typeFlagged: pType | flags,\n id: pID,\n clientID: this.#clientID,\n generationID: pGen,\n data,\n receivedAt: 0,\n };\n\n const unencrypted = (flags & PacketFlags.Unencrypted) !== 0;\n const header = buildC2SHeader(p);\n const [ciphertext, tag] = this.#crypt.encrypt(\n pType,\n pID,\n pGen,\n header,\n data,\n dummy,\n unencrypted,\n );\n\n const final = new Uint8Array(TAG_SIZE + HEADER_SIZE + ciphertext.length);\n final.set(tag.slice(0, TAG_SIZE), 0);\n final.set(header, TAG_SIZE);\n final.set(ciphertext, TAG_SIZE + HEADER_SIZE);\n this.#write(final);\n\n const rp: ResendPacket = {\n packet: p,\n firstSend: Date.now(),\n lastSend: Date.now(),\n retryCount: 0,\n nextInterval: RESEND_BASE_INTERVAL_MS,\n };\n this.#trackResend(pType, p, rp);\n }\n\n #write(data: Uint8Array): void {\n if (!this.#conn) return;\n try {\n this.#conn.send(Buffer.from(data), (err) => {\n if (err) this.#logger.warn(\"udp send error\", err);\n });\n } catch (error: unknown) {\n const normalized = error instanceof Error ? error : new Error(String(error));\n // dgram can throw synchronously for an invalid family/address pair. A\n // malformed remote target must close this session, never escape from an\n // async socket callback and terminate the gateway process.\n this.#logger.error(\"udp send failed\", normalized);\n this.#triggerClose(normalized);\n }\n }\n\n #handleRawPacket(raw: Uint8Array): void {\n if (raw.length < 11) return;\n\n const tag = raw.slice(0, TAG_SIZE);\n const header = raw.slice(TAG_SIZE, TAG_SIZE + 3);\n const ciphertext = raw.slice(TAG_SIZE + 3);\n\n const parsed = parseS2CHeader(header);\n const p: Packet = {\n ...parsed,\n clientID: 0,\n generationID: this.#resolveGeneration(parsed.id, parsed.typeFlagged & 0x0f),\n data: new Uint8Array(0),\n receivedAt: Date.now(),\n };\n\n const decrypted = this.#decryptPacketData(p, header, ciphertext, tag);\n if (decrypted === null) return;\n p.data = decrypted.plaintext;\n\n const pType = packetType(p);\n\n if (pType === PacketType.Ping) {\n this.#sendPong(p.id, decrypted.dummyUsed);\n return;\n }\n\n if (!this.#handleCommandWindowAndAck(p, decrypted.dummyUsed)) return;\n\n this.#handlePacketQueue(p);\n this.#updatePostReceiveState(p);\n }\n\n #resolveGeneration(id: number, pType: number): number {\n switch (pType as PacketType) {\n case PacketType.Command:\n return this.#recvWindowCommand.getGeneration(id);\n case PacketType.CommandLow:\n return this.#recvWindowCommandLow.getGeneration(id);\n case PacketType.Ack:\n return this.#sendWindowCommand.getGeneration(id);\n case PacketType.AckLow:\n return this.#sendWindowCommandLow.getGeneration(id);\n default:\n return 0;\n }\n }\n\n #decryptPacketData(\n p: Packet,\n header: Uint8Array,\n ciphertext: Uint8Array,\n tag: Uint8Array,\n ): { plaintext: Uint8Array; dummyUsed: boolean } | null {\n const unencrypted = (packetFlags(p) & PacketFlags.Unencrypted) !== 0;\n const dummy = !this.#crypt.cryptoInitComplete;\n let dummyUsed = dummy;\n const pType = packetType(p);\n const gen = p.generationID;\n\n try {\n const plaintext = this.#crypt.decrypt(\n pType,\n p.id,\n gen,\n header,\n ciphertext,\n tag,\n dummy,\n unencrypted,\n );\n return { plaintext, dummyUsed };\n } catch {\n // Try adjacent generations\n for (const offset of [-1, 1]) {\n const guessGen = gen + offset;\n if (guessGen < 0) continue;\n try {\n const plaintext = this.#crypt.decrypt(\n pType,\n p.id,\n guessGen,\n header,\n ciphertext,\n tag,\n false,\n false,\n );\n return { plaintext, dummyUsed: false };\n } catch {\n // continue\n }\n }\n\n // Try dummy fallback for command/ack types\n if (\n pType === PacketType.Command ||\n pType === PacketType.CommandLow ||\n pType === PacketType.Ack\n ) {\n try {\n const plaintext = this.#crypt.decrypt(\n pType,\n p.id,\n gen,\n header,\n ciphertext,\n tag,\n true,\n unencrypted,\n );\n return { plaintext, dummyUsed: true };\n } catch {\n // fall through\n }\n }\n\n return null;\n }\n }\n\n #handleCommandWindowAndAck(p: Packet, dummyUsed: boolean): boolean {\n const pType = packetType(p);\n if (pType !== PacketType.Command && pType !== PacketType.CommandLow) return true;\n\n const win = pType === PacketType.Command ? this.#recvWindowCommand : this.#recvWindowCommandLow;\n const ackType = pType === PacketType.Command ? PacketType.Ack : PacketType.AckLow;\n\n if (!win.isInWindow(p.id)) {\n if (win.isOldPacket(p.id)) {\n this.#sendAck(p.id, ackType, dummyUsed);\n }\n return false;\n }\n this.#sendAck(p.id, ackType, dummyUsed);\n return true;\n }\n\n #sendAck(packetID: number, ackType: PacketType, dummyUsed: boolean): void {\n const ackData = new Uint8Array(2);\n new DataView(ackData.buffer).setUint16(0, packetID, false);\n this.#sendPacketRaw(ackType, ackData, 0, dummyUsed);\n }\n\n #sendPong(pID: number, dummyUsed: boolean): void {\n const pongData = new Uint8Array(2);\n new DataView(pongData.buffer).setUint16(0, pID, false);\n this.#sendPacketRaw(PacketType.Pong, pongData, PacketFlags.Unencrypted, dummyUsed);\n }\n\n #handlePacketQueue(p: Packet): void {\n const pType = packetType(p);\n if (pType !== PacketType.Command && pType !== PacketType.CommandLow) {\n this.onPacket?.(p);\n return;\n }\n\n const isCommand = pType === PacketType.Command;\n const queue = isCommand ? this.#commandQueue : this.#commandLowQueue;\n const win = isCommand ? this.#recvWindowCommand : this.#recvWindowCommandLow;\n\n queue.set(p.id, p);\n\n if (isCommand) {\n this.#fastForwardMissingPackets(queue, win, true);\n } else {\n this.#fastForwardMissingPackets(queue, win, false);\n }\n\n while (true) {\n const current = isCommand ? this.#_nextCommandID : this.#_nextCommandLowID;\n const packet = queue.get(current);\n if (packet === undefined) break;\n\n const result = this.#tryReassemble(packet, queue, current, win);\n if (!result) break;\n\n const [reassembled, newNext] = result;\n if (isCommand) this.#_nextCommandID = newNext;\n else this.#_nextCommandLowID = newNext;\n\n this.#tryDecompress(reassembled);\n this.onPacket?.(reassembled);\n }\n }\n\n #_nextCommandID = 0;\n #_nextCommandLowID = 0;\n\n #fastForwardMissingPackets(\n queue: Map<number, Packet>,\n win: GenerationWindow,\n isCommand: boolean,\n ): void {\n let nextID = isCommand ? this.#_nextCommandID : this.#_nextCommandLowID;\n while (!queue.has(nextID) && hasOldNewerPacket(queue, nextID)) {\n nextID = (nextID + 1) & 0xffff;\n win.advance(1);\n }\n if (isCommand) this.#_nextCommandID = nextID;\n else this.#_nextCommandLowID = nextID;\n }\n\n #tryReassemble(\n startPacket: Packet,\n queue: Map<number, Packet>,\n nextID: number,\n win: GenerationWindow,\n ): [reassembled: Packet, newNextID: number] | null {\n if ((packetFlags(startPacket) & PacketFlags.Fragmented) === 0) {\n queue.delete(nextID);\n win.advance(1);\n return [startPacket, (nextID + 1) & 0xffff];\n }\n\n const fragments: Packet[] = [];\n let totalSize = 0;\n let currID = nextID;\n let startSeen = false;\n let complete = false;\n\n while (true) {\n const frag = queue.get(currID);\n if (!frag) return null;\n fragments.push(frag);\n totalSize += frag.data.length;\n\n const fragmented = (packetFlags(frag) & PacketFlags.Fragmented) !== 0;\n if (!startSeen) {\n startSeen = true;\n if (!fragmented) {\n complete = true;\n break;\n }\n } else if (fragmented) {\n complete = true;\n break;\n }\n currID = (currID + 1) & 0xffff;\n }\n\n if (!complete) return null;\n\n const combined = new Uint8Array(totalSize);\n let pos = 0;\n for (const frag of fragments) {\n combined.set(frag.data, pos);\n pos += frag.data.length;\n queue.delete(nextID);\n win.advance(1);\n nextID = (nextID + 1) & 0xffff;\n }\n\n startPacket.data = combined;\n startPacket.typeFlagged &= ~PacketFlags.Fragmented;\n return [startPacket, nextID];\n }\n\n #tryDecompress(packet: Packet): void {\n if ((packetFlags(packet) & PacketFlags.Compressed) === 0) return;\n try {\n const qlz = new Qlz();\n packet.data = qlz.decompress(packet.data);\n packet.typeFlagged &= ~PacketFlags.Compressed;\n } catch (err) {\n this.#logger.debug(\"decompression failed\", { id: packet.id, err });\n }\n }\n\n #updatePostReceiveState(p: Packet): void {\n const pType = packetType(p);\n\n if (pType === PacketType.Init1) {\n this.#initPacketCheck = null;\n return;\n }\n\n if ((pType === PacketType.Ack || pType === PacketType.AckLow) && p.data.length >= 2) {\n const ackID = new DataView(p.data.buffer, p.data.byteOffset).getUint16(0, false);\n const targetType = pType === PacketType.Ack ? PacketType.Command : PacketType.CommandLow;\n const key = (targetType << 16) | ackID;\n this.#ackManager.delete(key);\n }\n }\n\n #nextPacketIdentity(pType: PacketType): [id: number, gen: number] {\n const pID = this.#packetCounter[pType]!;\n const pGen = this.#generationCounter[pType]!;\n\n if (pType === PacketType.Init1) return [pID, pGen];\n\n this.#packetCounter[pType] = (pID + 1) & 0xffff;\n if (this.#packetCounter[pType] === 0) {\n this.#generationCounter[pType] = (pGen + 1) >>> 0;\n }\n\n if (pType === PacketType.Command) {\n this.#sendWindowCommand.advanceToExcluded(pID);\n } else if (pType === PacketType.CommandLow) {\n this.#sendWindowCommandLow.advanceToExcluded(pID);\n }\n\n return [pID, pGen];\n }\n\n #trackResend(pType: PacketType, p: Packet, rp: ResendPacket): void {\n if (pType === PacketType.Init1) {\n this.#initPacketCheck = rp;\n return;\n }\n if (pType === PacketType.Command || pType === PacketType.CommandLow) {\n const key = (pType << 16) | p.id;\n this.#ackManager.set(key, rp);\n }\n }\n\n #checkResends(): void {\n const now = Date.now();\n\n if (now - this.#lastMessageReceived > PACKET_TIMEOUT_MS) {\n this.#logger.warn(\"idle timeout\");\n this.#triggerClose(new Error(\"idle timeout\"));\n return;\n }\n\n if (this.#initPacketCheck) {\n this.#doResend(this.#initPacketCheck, now);\n }\n\n for (const [key, rp] of this.#ackManager) {\n if (now - rp.firstSend > PACKET_TIMEOUT_MS) {\n this.#ackManager.delete(key);\n this.#triggerClose(new Error(\"packet ack timeout\"));\n return;\n }\n this.#doResend(rp, now);\n }\n }\n\n #doResend(rp: ResendPacket, now: number): void {\n if (now - rp.lastSend < rp.nextInterval) return;\n\n rp.lastSend = now;\n rp.retryCount++;\n rp.nextInterval = Math.min(rp.nextInterval * 2, MAX_RETRY_INTERVAL_MS);\n\n const dummy = !this.#crypt.cryptoInitComplete;\n const unencrypted = (packetFlags(rp.packet) & PacketFlags.Unencrypted) !== 0;\n const header = buildC2SHeader(rp.packet);\n const pType = packetType(rp.packet);\n\n const [ciphertext, tag] = this.#crypt.encrypt(\n pType,\n rp.packet.id,\n rp.packet.generationID,\n header,\n rp.packet.data,\n dummy,\n unencrypted,\n );\n\n const final = new Uint8Array(TAG_SIZE + HEADER_SIZE + ciphertext.length);\n final.set(tag.slice(0, TAG_SIZE), 0);\n final.set(header, TAG_SIZE);\n final.set(ciphertext, TAG_SIZE + HEADER_SIZE);\n this.#write(final);\n }\n\n #triggerClose(err: Error | null): void {\n if (this.#closed) return;\n this.close();\n this.onClosed?.(err);\n }\n}\n\n// ---- Helpers ----------------------------------------------------------------\n\nfunction applyProtocolFlags(pType: PacketType, flags: number): number {\n if (pType === PacketType.Command || pType === PacketType.CommandLow) {\n return flags | PacketFlags.NewProtocol;\n }\n return flags;\n}\n\nfunction hasOldNewerPacket(queue: Map<number, Packet>, nextID: number): boolean {\n const now = Date.now();\n for (const [id, pkg] of queue) {\n const diff = (id - nextID + 0x10000) & 0xffff;\n if (diff < 0x8000 && now - pkg.receivedAt > 5_000) {\n return true;\n }\n }\n return false;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"handler-DI_abe9c.js","names":["noopLogger","splitHostPort","PacketType","\"Voice\"","\"VoiceWhisper\"","\"Command\"","\"CommandLow\"","\"Ping\"","\"Pong\"","\"Ack\"","\"AckLow\"","\"Init1\"","PacketFlags","\"Fragmented\"","\"NewProtocol\"","\"Compressed\"","\"Unencrypted\"","packetType","p","packetFlags","isUnencrypted","buildC2SHeader","header","view","parseS2CHeader","raw","parseC2SHeader","GenerationWindow","#mod","mod","#receiveWindow","windowSize","#generation","amount","newBaseOffset","#mappedBaseOffset","genStep","moveDist","mappedValue","maxOffset","#isNextGen","TABLE_SIZE","Qlz","parseQlzHeader","data","dest","decompressedSize","flags","headerLen","#hashtable","state","#ensureControl","#processReference","#processLiteral","st","b1","b2","hash","matchlen","offset","i","end","#updateHashtable","threshold","v","RECEIVE_PACKET_WINDOW_SIZE","PING_INTERVAL_MS","PACKET_TIMEOUT_MS","MAX_RETRY_INTERVAL_MS","TAG_SIZE","VOICE_HEADER_SIZE","WHISPER_HEADER_SIZE","RESEND_BASE_INTERVAL_MS","RESEND_LOOP_INTERVAL_MS","PacketHandler","#crypt","crypt","#logger","logger","#clientID","id","addr","socket","createSocket","isIP","host","onError","reject","error","port","resolve","#conn","conn","#closed","#lastMessageReceived","#handleRawPacket","msg","err","#triggerClose","#packetCounter","init1Data","processInit1","#sendPacketRaw","pingTimer","#stopPing","resendTimer","#checkResends","#stopResend","#initPacketCheck","dummy","pType","#sendSplitPacket","pID","pGen","#generationCounter","payloadLen","voicePayload","codec","final","#write","targetClientIds","whisperPayload","index","pos","first","blockSize","last","pFlags","applyProtocolFlags","#nextPacketIdentity","unencrypted","ciphertext","tag","rp","#trackResend","normalized","parsed","#resolveGeneration","decrypted","#decryptPacketData","#sendPong","#handleCommandWindowAndAck","#handlePacketQueue","#updatePostReceiveState","#recvWindowCommand","#recvWindowCommandLow","#sendWindowCommand","#sendWindowCommandLow","dummyUsed","gen","guessGen","win","ackType","#sendAck","ackData","packetID","pongData","isCommand","queue","#commandQueue","#commandLowQueue","#fastForwardMissingPackets","current","#_nextCommandID","#_nextCommandLowID","packet","result","#tryReassemble","reassembled","newNext","#tryDecompress","nextID","hasOldNewerPacket","startPacket","fragments","totalSize","currID","startSeen","complete","frag","fragmented","combined","ackID","key","#ackManager","now","#doResend","pkg"],"sources":["../src/transport/packet.ts","../src/transport/generation-window.ts","../src/transport/quicklz.ts","../src/transport/handler.ts"],"sourcesContent":["export const enum PacketType {\n Voice = 0,\n VoiceWhisper = 1,\n Command = 2,\n CommandLow = 3,\n Ping = 4,\n Pong = 5,\n Ack = 6,\n AckLow = 7,\n Init1 = 8,\n}\n\nexport const enum PacketFlags {\n Fragmented = 0x10,\n NewProtocol = 0x20,\n Compressed = 0x40,\n Unencrypted = 0x80,\n}\n\nexport interface Packet {\n /** Type byte combined with flags (low nibble = type, high nibble = flags). */\n typeFlagged: number;\n id: number;\n clientID: number;\n generationID: number;\n data: Uint8Array;\n receivedAt: number; // Date.now()\n}\n\nexport function packetType(p: Packet): PacketType {\n return (p.typeFlagged & 0x0f) as PacketType;\n}\n\nexport function packetFlags(p: Packet): number {\n return p.typeFlagged & 0xf0;\n}\n\nexport function isUnencrypted(p: Packet): boolean {\n return (packetFlags(p) & PacketFlags.Unencrypted) !== 0;\n}\n\n/** Build the 5-byte client-to-server header: [packetID(2), clientID(2), typeFlagged(1)]. */\nexport function buildC2SHeader(p: Packet): Uint8Array {\n const header = new Uint8Array(5);\n const view = new DataView(header.buffer);\n view.setUint16(0, p.id, false);\n view.setUint16(2, p.clientID, false);\n header[4] = p.typeFlagged;\n return header;\n}\n\n/** Parse a 3-byte server-to-client header. */\nexport function parseS2CHeader(raw: Uint8Array): Pick<Packet, \"id\" | \"typeFlagged\"> {\n const view = new DataView(raw.buffer, raw.byteOffset, raw.byteLength);\n return {\n id: view.getUint16(0, false),\n typeFlagged: raw[2]!,\n };\n}\n\n/** Parse a 5-byte client-to-server header. */\nexport function parseC2SHeader(raw: Uint8Array): Pick<Packet, \"id\" | \"clientID\" | \"typeFlagged\"> {\n const view = new DataView(raw.buffer, raw.byteOffset, raw.byteLength);\n return {\n id: view.getUint16(0, false),\n clientID: view.getUint16(2, false),\n typeFlagged: raw[4]!,\n };\n}\n","export class GenerationWindow {\n #mappedBaseOffset = 0;\n #generation = 0;\n readonly #mod: number;\n readonly #receiveWindow: number;\n\n constructor(mod: number, windowSize: number) {\n this.#mod = mod;\n this.#receiveWindow = windowSize;\n }\n\n get generation(): number {\n return this.#generation;\n }\n\n advance(amount: number): void {\n if (amount <= 0) return;\n const newBaseOffset = this.#mappedBaseOffset + amount;\n const genStep = Math.floor(newBaseOffset / this.#mod);\n if (genStep > 0) {\n this.#generation = Math.min(this.#generation + genStep, 0xffff_ffff);\n }\n this.#mappedBaseOffset = newBaseOffset % this.#mod;\n }\n\n advanceToExcluded(mappedValue: number): void {\n let moveDist = mappedValue - this.#mappedBaseOffset;\n if (moveDist < 0) moveDist += this.#mod;\n this.advance(moveDist + 1);\n }\n\n syncTo(mappedValue: number): void {\n let moveDist = mappedValue - this.#mappedBaseOffset;\n if (moveDist < 0) moveDist += this.#mod;\n this.advance(moveDist);\n }\n\n isInWindow(mappedValue: number): boolean {\n const maxOffset = this.#mappedBaseOffset + this.#receiveWindow;\n if (maxOffset < this.#mod) {\n return mappedValue >= this.#mappedBaseOffset && mappedValue < maxOffset;\n }\n return mappedValue >= this.#mappedBaseOffset || mappedValue < maxOffset - this.#mod;\n }\n\n mappedToIndex(mappedValue: number): number {\n if (this.#isNextGen(mappedValue)) {\n return mappedValue + this.#mod - this.#mappedBaseOffset;\n }\n return mappedValue - this.#mappedBaseOffset;\n }\n\n isOldPacket(mappedValue: number): boolean {\n return this.mappedToIndex(mappedValue) < 0;\n }\n\n isFuturePacket(mappedValue: number): boolean {\n return this.mappedToIndex(mappedValue) >= this.#receiveWindow;\n }\n\n #isNextGen(mappedValue: number): boolean {\n return (\n this.#mappedBaseOffset > this.#mod - this.#receiveWindow &&\n mappedValue < this.#mappedBaseOffset + this.#receiveWindow - this.#mod\n );\n }\n\n getGeneration(mappedValue: number): number {\n if (this.#isNextGen(mappedValue)) return this.#generation + 1;\n return this.#generation;\n }\n\n reset(): void {\n this.#mappedBaseOffset = 0;\n this.#generation = 0;\n }\n}\n","const TABLE_SIZE = 4096;\n\ninterface QlzState {\n control: number;\n sourcePos: number;\n destPos: number;\n nextHashed: number;\n}\n\nexport class Qlz {\n #hashtable = new Int32Array(TABLE_SIZE);\n\n decompress(data: Uint8Array): Uint8Array {\n const { headerLen, decompressedSize, flags } = parseQlzHeader(data);\n\n const dest = new Uint8Array(decompressedSize);\n\n if ((flags & 0x01) === 0) {\n dest.set(data.slice(headerLen, headerLen + decompressedSize));\n return dest;\n }\n\n this.#hashtable.fill(0);\n\n const state: QlzState = {\n control: 1,\n sourcePos: headerLen,\n destPos: 0,\n nextHashed: 0,\n };\n\n while (this.#ensureControl(data, state)) {\n if ((state.control & 1) !== 0) {\n if (!this.#processReference(data, dest, state)) break;\n } else {\n if (this.#processLiteral(data, dest, decompressedSize, state)) break;\n }\n }\n\n return dest;\n }\n\n #ensureControl(data: Uint8Array, st: QlzState): boolean {\n if (st.control !== 1) return true;\n if (st.sourcePos + 4 > data.length) return false;\n st.control =\n (data[st.sourcePos]! |\n (data[st.sourcePos + 1]! << 8) |\n (data[st.sourcePos + 2]! << 16) |\n (data[st.sourcePos + 3]! << 24)) >>>\n 0;\n st.sourcePos += 4;\n return true;\n }\n\n #processReference(data: Uint8Array, dest: Uint8Array, st: QlzState): boolean {\n st.control = (st.control >>> 1) >>> 0;\n if (st.sourcePos + 2 > data.length) return false;\n\n const b1 = data[st.sourcePos]!;\n const b2 = data[st.sourcePos + 1]!;\n st.sourcePos += 2;\n\n const hash = (b1 >> 4) | (b2 << 4);\n let matchlen = b1 & 0x0f;\n if (matchlen !== 0) {\n matchlen += 2;\n } else {\n if (st.sourcePos >= data.length) return false;\n matchlen = data[st.sourcePos]!;\n st.sourcePos++;\n }\n\n const offset = this.#hashtable[hash]!;\n for (let i = 0; i < matchlen; i++) {\n if (st.destPos < dest.length && offset + i < st.destPos) {\n dest[st.destPos] = dest[offset + i]!;\n st.destPos++;\n }\n }\n\n const end = st.destPos + 1 - matchlen;\n this.#updateHashtable(dest, st, end);\n st.nextHashed = st.destPos;\n\n return true;\n }\n\n #processLiteral(\n data: Uint8Array,\n dest: Uint8Array,\n decompressedSize: number,\n st: QlzState,\n ): boolean {\n const threshold = Math.max(decompressedSize, 10) - 10;\n if (st.destPos >= threshold) {\n while (st.destPos < decompressedSize) {\n if (st.control === 1) {\n st.sourcePos += 4;\n if (st.sourcePos > data.length) break;\n st.control =\n (data[st.sourcePos - 4]! |\n (data[st.sourcePos - 3]! << 8) |\n (data[st.sourcePos - 2]! << 16) |\n (data[st.sourcePos - 1]! << 24)) >>>\n 0;\n }\n if (st.sourcePos >= data.length) break;\n dest[st.destPos++] = data[st.sourcePos++]!;\n st.control = (st.control >>> 1) >>> 0;\n }\n return true;\n }\n\n if (st.sourcePos >= data.length || st.destPos >= dest.length) return true;\n\n dest[st.destPos++] = data[st.sourcePos++]!;\n st.control = (st.control >>> 1) >>> 0;\n\n const end = Math.max(st.destPos - 2, 0);\n this.#updateHashtable(dest, st, end);\n if (st.nextHashed < end) st.nextHashed = end;\n\n return false;\n }\n\n #updateHashtable(dest: Uint8Array, st: QlzState, end: number): void {\n while (st.nextHashed < end) {\n if (st.nextHashed + 3 > dest.length) break;\n const v =\n (dest[st.nextHashed]! |\n (dest[st.nextHashed + 1]! << 8) |\n (dest[st.nextHashed + 2]! << 16)) >>>\n 0;\n const hash = ((v >> 12) ^ v) & 0xfff;\n this.#hashtable[hash] = st.nextHashed;\n st.nextHashed++;\n }\n }\n}\n\nfunction parseQlzHeader(data: Uint8Array): {\n headerLen: number;\n decompressedSize: number;\n flags: number;\n} {\n if (data.length < 3) throw new Error(\"QuickLZ: data too short\");\n\n const flags = data[0]!;\n const level = (flags >> 2) & 0x03;\n if (level !== 1) throw new Error(\"QuickLZ: only level 1 is supported\");\n\n const headerLen = (flags & 0x02) !== 0 ? 9 : 3;\n if (data.length < headerLen) throw new Error(\"QuickLZ: data too short for header\");\n\n let decompressedSize: number;\n if ((flags & 0x02) !== 0) {\n decompressedSize = (data[5]! | (data[6]! << 8) | (data[7]! << 16) | (data[8]! << 24)) >>> 0;\n } else {\n decompressedSize = data[2]!;\n }\n\n return { headerLen, decompressedSize, flags };\n}\n","import { createSocket, type Socket as UdpSocket } from \"node:dgram\";\nimport { isIP } from \"node:net\";\nimport { splitHostPort } from \"../address.js\";\nimport type { Crypt } from \"../crypto/crypt.js\";\nimport type { Logger } from \"../types.js\";\nimport { noopLogger } from \"../types.js\";\nimport {\n type Packet,\n PacketType,\n PacketFlags,\n packetType,\n packetFlags,\n buildC2SHeader,\n parseS2CHeader,\n} from \"./packet.js\";\nimport { GenerationWindow } from \"./generation-window.js\";\nimport { Qlz } from \"./quicklz.js\";\nimport { processInit1 } from \"../handshake/crypt-handshake.js\";\n\nconst MAX_OUT_PACKET_SIZE = 500;\nconst RECEIVE_PACKET_WINDOW_SIZE = 1024;\nconst PING_INTERVAL_MS = 5_000;\nconst PACKET_TIMEOUT_MS = 60_000;\nconst MAX_RETRY_INTERVAL_MS = 1_000;\nconst HEADER_SIZE = 5;\nconst TAG_SIZE = 8;\nconst VOICE_HEADER_SIZE = 3;\nconst WHISPER_HEADER_SIZE = 5;\nconst RESEND_BASE_INTERVAL_MS = 500;\nconst RESEND_LOOP_INTERVAL_MS = 100;\n\ninterface ResendPacket {\n packet: Packet;\n firstSend: number;\n lastSend: number;\n retryCount: number;\n nextInterval: number;\n}\n\nexport class PacketHandler {\n onPacket: ((p: Packet) => void) | null = null;\n onClosed: ((err: Error | null) => void) | null = null;\n\n readonly #crypt: Crypt;\n readonly #logger: Logger;\n\n #conn: UdpSocket | null = null;\n #clientID = 0;\n #closed = false;\n #stopPing: (() => void) | null = null;\n #stopResend: (() => void) | null = null;\n #lastMessageReceived = Date.now();\n\n #packetCounter = new Uint16Array(9);\n #generationCounter = new Uint32Array(9);\n\n #recvWindowCommand = new GenerationWindow(1 << 16, RECEIVE_PACKET_WINDOW_SIZE);\n #recvWindowCommandLow = new GenerationWindow(1 << 16, RECEIVE_PACKET_WINDOW_SIZE);\n #sendWindowCommand = new GenerationWindow(1 << 16, RECEIVE_PACKET_WINDOW_SIZE);\n #sendWindowCommandLow = new GenerationWindow(1 << 16, RECEIVE_PACKET_WINDOW_SIZE);\n\n #commandQueue = new Map<number, Packet>();\n #commandLowQueue = new Map<number, Packet>();\n #ackManager = new Map<number, ResendPacket>();\n #initPacketCheck: ResendPacket | null = null;\n\n constructor(crypt: Crypt, logger: Logger = noopLogger) {\n this.#crypt = crypt;\n this.#logger = logger;\n }\n\n setClientID(id: number): void {\n this.#clientID = id;\n }\n\n connect(addr: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const { host, port } = splitHostPort(addr);\n\n const socket = createSocket(isIP(host) === 6 ? \"udp6\" : \"udp4\");\n const onError = (error: Error) => {\n socket.close();\n reject(error);\n };\n socket.once(\"error\", onError);\n socket.connect(parseInt(port, 10), host, () => {\n socket.off(\"error\", onError);\n this.start(socket);\n resolve();\n });\n });\n }\n\n start(conn: UdpSocket): void {\n this.#conn = conn;\n this.#closed = false;\n this.#lastMessageReceived = Date.now();\n\n conn.on(\"message\", (msg) => {\n this.#lastMessageReceived = Date.now();\n this.#handleRawPacket(new Uint8Array(msg.buffer, msg.byteOffset, msg.byteLength));\n });\n conn.on(\"error\", (err) => {\n this.#logger.error(\"udp error\", err);\n this.#triggerClose(err);\n });\n conn.on(\"close\", () => this.#triggerClose(null));\n\n this.#packetCounter[PacketType.Command] = 1;\n this.#packetCounter[PacketType.Init1] = 101;\n\n const init1Data = processInit1(this.#crypt, null);\n if (init1Data) this.#sendPacketRaw(PacketType.Init1, init1Data, 0);\n\n // Ping loop\n const pingTimer = setInterval(() => {\n if (this.#crypt.cryptoInitComplete) {\n this.sendPacket(PacketType.Ping, new Uint8Array(0), PacketFlags.Unencrypted);\n }\n }, PING_INTERVAL_MS);\n this.#stopPing = () => clearInterval(pingTimer);\n\n // Resend loop\n const resendTimer = setInterval(() => this.#checkResends(), RESEND_LOOP_INTERVAL_MS);\n this.#stopResend = () => clearInterval(resendTimer);\n }\n\n receivedFinalInitAck(): void {\n this.#initPacketCheck = null;\n }\n\n sendPacket(pType: PacketType, data: Uint8Array, flags: number): void {\n const dummy = !this.#crypt.cryptoInitComplete;\n if (data.length > 487 && pType !== PacketType.Voice && pType !== PacketType.VoiceWhisper) {\n this.#sendSplitPacket(pType, data, flags, dummy);\n return;\n }\n this.#sendPacketRaw(pType, data, flags, dummy);\n }\n\n sendVoicePacket(data: Uint8Array, codec: number): void {\n const pID = this.#packetCounter[PacketType.Voice]!;\n const pGen = this.#generationCounter[PacketType.Voice]!;\n this.#packetCounter[PacketType.Voice] = (pID + 1) & 0xffff;\n if (this.#packetCounter[PacketType.Voice] === 0) {\n this.#generationCounter[PacketType.Voice]!++;\n }\n\n const payloadLen = VOICE_HEADER_SIZE + data.length;\n const voicePayload = new Uint8Array(payloadLen);\n new DataView(voicePayload.buffer).setUint16(0, pID, false);\n voicePayload[2] = codec;\n voicePayload.set(data, VOICE_HEADER_SIZE);\n\n const p: Packet = {\n typeFlagged: PacketType.Voice | PacketFlags.Unencrypted,\n id: pID,\n clientID: this.#clientID,\n generationID: pGen,\n data: voicePayload,\n receivedAt: 0,\n };\n\n const header = buildC2SHeader(p);\n const final = new Uint8Array(TAG_SIZE + HEADER_SIZE + payloadLen);\n final.set(this.#crypt.fakeSignature, 0);\n final.set(header, TAG_SIZE);\n final.set(voicePayload, TAG_SIZE + HEADER_SIZE);\n this.#write(final);\n }\n\n /** Send an unencrypted TeamSpeak voice whisper packet to up to 32 clients. */\n sendWhisperPacket(data: Uint8Array, targetClientIds: number[], codec: number): void {\n if (targetClientIds.length === 0) throw new Error(\"whisper requires at least one target\");\n if (targetClientIds.length > 32) throw new Error(\"too many whisper targets\");\n if (targetClientIds.some((id) => !Number.isInteger(id) || id < 0 || id > 0xffff)) {\n throw new RangeError(\"invalid whisper target\");\n }\n\n const pID = this.#packetCounter[PacketType.VoiceWhisper]!;\n const pGen = this.#generationCounter[PacketType.VoiceWhisper]!;\n this.#packetCounter[PacketType.VoiceWhisper] = (pID + 1) & 0xffff;\n if (this.#packetCounter[PacketType.VoiceWhisper] === 0) {\n this.#generationCounter[PacketType.VoiceWhisper] = (pGen + 1) >>> 0;\n }\n\n const payloadLen = WHISPER_HEADER_SIZE + targetClientIds.length * 2 + data.length;\n const whisperPayload = new Uint8Array(payloadLen);\n const view = new DataView(whisperPayload.buffer);\n view.setUint16(0, pID, false);\n whisperPayload[2] = codec;\n whisperPayload[3] = 0;\n whisperPayload[4] = targetClientIds.length;\n for (let index = 0; index < targetClientIds.length; index++) {\n view.setUint16(WHISPER_HEADER_SIZE + index * 2, targetClientIds[index]!, false);\n }\n whisperPayload.set(data, WHISPER_HEADER_SIZE + targetClientIds.length * 2);\n\n const packet: Packet = {\n typeFlagged: PacketType.VoiceWhisper | PacketFlags.Unencrypted,\n id: pID,\n clientID: this.#clientID,\n generationID: pGen,\n data: whisperPayload,\n receivedAt: 0,\n };\n const header = buildC2SHeader(packet);\n const final = new Uint8Array(TAG_SIZE + HEADER_SIZE + payloadLen);\n final.set(this.#crypt.fakeSignature, 0);\n final.set(header, TAG_SIZE);\n final.set(whisperPayload, TAG_SIZE + HEADER_SIZE);\n this.#write(final);\n }\n\n close(): void {\n if (this.#closed) return;\n this.#closed = true;\n this.#stopPing?.();\n this.#stopResend?.();\n this.#conn?.close();\n }\n\n // ---- Private ---------------------------------------------------------------\n\n #sendSplitPacket(pType: PacketType, data: Uint8Array, flags: number, dummy: boolean): void {\n const maxSize = MAX_OUT_PACKET_SIZE - HEADER_SIZE - TAG_SIZE; // 487\n let pos = 0;\n let first = true;\n\n while (pos < data.length) {\n const blockSize = Math.min(data.length - pos, maxSize);\n const last = pos + blockSize === data.length;\n\n let pFlags = flags;\n if (first !== last) pFlags |= PacketFlags.Fragmented;\n\n this.#sendPacketRaw(pType, data.slice(pos, pos + blockSize), pFlags, dummy);\n pos += blockSize;\n first = false;\n }\n }\n\n #sendPacketRaw(\n pType: PacketType,\n data: Uint8Array,\n flags: number,\n dummy = !this.#crypt.cryptoInitComplete,\n ): void {\n flags = applyProtocolFlags(pType, flags);\n const [pID, pGen] = this.#nextPacketIdentity(pType);\n const p: Packet = {\n typeFlagged: pType | flags,\n id: pID,\n clientID: this.#clientID,\n generationID: pGen,\n data,\n receivedAt: 0,\n };\n\n const unencrypted = (flags & PacketFlags.Unencrypted) !== 0;\n const header = buildC2SHeader(p);\n const [ciphertext, tag] = this.#crypt.encrypt(\n pType,\n pID,\n pGen,\n header,\n data,\n dummy,\n unencrypted,\n );\n\n const final = new Uint8Array(TAG_SIZE + HEADER_SIZE + ciphertext.length);\n final.set(tag.slice(0, TAG_SIZE), 0);\n final.set(header, TAG_SIZE);\n final.set(ciphertext, TAG_SIZE + HEADER_SIZE);\n this.#write(final);\n\n const rp: ResendPacket = {\n packet: p,\n firstSend: Date.now(),\n lastSend: Date.now(),\n retryCount: 0,\n nextInterval: RESEND_BASE_INTERVAL_MS,\n };\n this.#trackResend(pType, p, rp);\n }\n\n #write(data: Uint8Array): void {\n if (!this.#conn) return;\n try {\n this.#conn.send(Buffer.from(data), (err) => {\n if (err) this.#logger.warn(\"udp send error\", err);\n });\n } catch (error: unknown) {\n const normalized = error instanceof Error ? error : new Error(String(error));\n // dgram can throw synchronously for an invalid family/address pair. A\n // malformed remote target must close this session, never escape from an\n // async socket callback and terminate the gateway process.\n this.#logger.error(\"udp send failed\", normalized);\n this.#triggerClose(normalized);\n }\n }\n\n #handleRawPacket(raw: Uint8Array): void {\n if (raw.length < 11) return;\n\n const tag = raw.slice(0, TAG_SIZE);\n const header = raw.slice(TAG_SIZE, TAG_SIZE + 3);\n const ciphertext = raw.slice(TAG_SIZE + 3);\n\n const parsed = parseS2CHeader(header);\n const p: Packet = {\n ...parsed,\n clientID: 0,\n generationID: this.#resolveGeneration(parsed.id, parsed.typeFlagged & 0x0f),\n data: new Uint8Array(0),\n receivedAt: Date.now(),\n };\n\n const decrypted = this.#decryptPacketData(p, header, ciphertext, tag);\n if (decrypted === null) return;\n p.data = decrypted.plaintext;\n\n const pType = packetType(p);\n\n if (pType === PacketType.Ping) {\n this.#sendPong(p.id, decrypted.dummyUsed);\n return;\n }\n\n if (!this.#handleCommandWindowAndAck(p, decrypted.dummyUsed)) return;\n\n this.#handlePacketQueue(p);\n this.#updatePostReceiveState(p);\n }\n\n #resolveGeneration(id: number, pType: number): number {\n switch (pType as PacketType) {\n case PacketType.Command:\n return this.#recvWindowCommand.getGeneration(id);\n case PacketType.CommandLow:\n return this.#recvWindowCommandLow.getGeneration(id);\n case PacketType.Ack:\n return this.#sendWindowCommand.getGeneration(id);\n case PacketType.AckLow:\n return this.#sendWindowCommandLow.getGeneration(id);\n default:\n return 0;\n }\n }\n\n #decryptPacketData(\n p: Packet,\n header: Uint8Array,\n ciphertext: Uint8Array,\n tag: Uint8Array,\n ): { plaintext: Uint8Array; dummyUsed: boolean } | null {\n const unencrypted = (packetFlags(p) & PacketFlags.Unencrypted) !== 0;\n const dummy = !this.#crypt.cryptoInitComplete;\n let dummyUsed = dummy;\n const pType = packetType(p);\n const gen = p.generationID;\n\n try {\n const plaintext = this.#crypt.decrypt(\n pType,\n p.id,\n gen,\n header,\n ciphertext,\n tag,\n dummy,\n unencrypted,\n );\n return { plaintext, dummyUsed };\n } catch {\n // Try adjacent generations\n for (const offset of [-1, 1]) {\n const guessGen = gen + offset;\n if (guessGen < 0) continue;\n try {\n const plaintext = this.#crypt.decrypt(\n pType,\n p.id,\n guessGen,\n header,\n ciphertext,\n tag,\n false,\n false,\n );\n return { plaintext, dummyUsed: false };\n } catch {\n // continue\n }\n }\n\n // Try dummy fallback for command/ack types\n if (\n pType === PacketType.Command ||\n pType === PacketType.CommandLow ||\n pType === PacketType.Ack\n ) {\n try {\n const plaintext = this.#crypt.decrypt(\n pType,\n p.id,\n gen,\n header,\n ciphertext,\n tag,\n true,\n unencrypted,\n );\n return { plaintext, dummyUsed: true };\n } catch {\n // fall through\n }\n }\n\n return null;\n }\n }\n\n #handleCommandWindowAndAck(p: Packet, dummyUsed: boolean): boolean {\n const pType = packetType(p);\n if (pType !== PacketType.Command && pType !== PacketType.CommandLow) return true;\n\n const win = pType === PacketType.Command ? this.#recvWindowCommand : this.#recvWindowCommandLow;\n const ackType = pType === PacketType.Command ? PacketType.Ack : PacketType.AckLow;\n\n if (!win.isInWindow(p.id)) {\n if (win.isOldPacket(p.id)) {\n this.#sendAck(p.id, ackType, dummyUsed);\n }\n return false;\n }\n this.#sendAck(p.id, ackType, dummyUsed);\n return true;\n }\n\n #sendAck(packetID: number, ackType: PacketType, dummyUsed: boolean): void {\n const ackData = new Uint8Array(2);\n new DataView(ackData.buffer).setUint16(0, packetID, false);\n this.#sendPacketRaw(ackType, ackData, 0, dummyUsed);\n }\n\n #sendPong(pID: number, dummyUsed: boolean): void {\n const pongData = new Uint8Array(2);\n new DataView(pongData.buffer).setUint16(0, pID, false);\n this.#sendPacketRaw(PacketType.Pong, pongData, PacketFlags.Unencrypted, dummyUsed);\n }\n\n #handlePacketQueue(p: Packet): void {\n const pType = packetType(p);\n if (pType !== PacketType.Command && pType !== PacketType.CommandLow) {\n this.onPacket?.(p);\n return;\n }\n\n const isCommand = pType === PacketType.Command;\n const queue = isCommand ? this.#commandQueue : this.#commandLowQueue;\n const win = isCommand ? this.#recvWindowCommand : this.#recvWindowCommandLow;\n\n queue.set(p.id, p);\n\n if (isCommand) {\n this.#fastForwardMissingPackets(queue, win, true);\n } else {\n this.#fastForwardMissingPackets(queue, win, false);\n }\n\n while (true) {\n const current = isCommand ? this.#_nextCommandID : this.#_nextCommandLowID;\n const packet = queue.get(current);\n if (packet === undefined) break;\n\n const result = this.#tryReassemble(packet, queue, current, win);\n if (!result) break;\n\n const [reassembled, newNext] = result;\n if (isCommand) this.#_nextCommandID = newNext;\n else this.#_nextCommandLowID = newNext;\n\n this.#tryDecompress(reassembled);\n this.onPacket?.(reassembled);\n }\n }\n\n #_nextCommandID = 0;\n #_nextCommandLowID = 0;\n\n #fastForwardMissingPackets(\n queue: Map<number, Packet>,\n win: GenerationWindow,\n isCommand: boolean,\n ): void {\n let nextID = isCommand ? this.#_nextCommandID : this.#_nextCommandLowID;\n while (!queue.has(nextID) && hasOldNewerPacket(queue, nextID)) {\n nextID = (nextID + 1) & 0xffff;\n win.advance(1);\n }\n if (isCommand) this.#_nextCommandID = nextID;\n else this.#_nextCommandLowID = nextID;\n }\n\n #tryReassemble(\n startPacket: Packet,\n queue: Map<number, Packet>,\n nextID: number,\n win: GenerationWindow,\n ): [reassembled: Packet, newNextID: number] | null {\n if ((packetFlags(startPacket) & PacketFlags.Fragmented) === 0) {\n queue.delete(nextID);\n win.advance(1);\n return [startPacket, (nextID + 1) & 0xffff];\n }\n\n const fragments: Packet[] = [];\n let totalSize = 0;\n let currID = nextID;\n let startSeen = false;\n let complete = false;\n\n while (true) {\n const frag = queue.get(currID);\n if (!frag) return null;\n fragments.push(frag);\n totalSize += frag.data.length;\n\n const fragmented = (packetFlags(frag) & PacketFlags.Fragmented) !== 0;\n if (!startSeen) {\n startSeen = true;\n if (!fragmented) {\n complete = true;\n break;\n }\n } else if (fragmented) {\n complete = true;\n break;\n }\n currID = (currID + 1) & 0xffff;\n }\n\n if (!complete) return null;\n\n const combined = new Uint8Array(totalSize);\n let pos = 0;\n for (const frag of fragments) {\n combined.set(frag.data, pos);\n pos += frag.data.length;\n queue.delete(nextID);\n win.advance(1);\n nextID = (nextID + 1) & 0xffff;\n }\n\n startPacket.data = combined;\n startPacket.typeFlagged &= ~PacketFlags.Fragmented;\n return [startPacket, nextID];\n }\n\n #tryDecompress(packet: Packet): void {\n if ((packetFlags(packet) & PacketFlags.Compressed) === 0) return;\n try {\n const qlz = new Qlz();\n packet.data = qlz.decompress(packet.data);\n packet.typeFlagged &= ~PacketFlags.Compressed;\n } catch (err) {\n this.#logger.debug(\"decompression failed\", { id: packet.id, err });\n }\n }\n\n #updatePostReceiveState(p: Packet): void {\n const pType = packetType(p);\n\n if (pType === PacketType.Init1) {\n this.#initPacketCheck = null;\n return;\n }\n\n if ((pType === PacketType.Ack || pType === PacketType.AckLow) && p.data.length >= 2) {\n const ackID = new DataView(p.data.buffer, p.data.byteOffset).getUint16(0, false);\n const targetType = pType === PacketType.Ack ? PacketType.Command : PacketType.CommandLow;\n const key = (targetType << 16) | ackID;\n this.#ackManager.delete(key);\n }\n }\n\n #nextPacketIdentity(pType: PacketType): [id: number, gen: number] {\n const pID = this.#packetCounter[pType]!;\n const pGen = this.#generationCounter[pType]!;\n\n if (pType === PacketType.Init1) return [pID, pGen];\n\n this.#packetCounter[pType] = (pID + 1) & 0xffff;\n if (this.#packetCounter[pType] === 0) {\n this.#generationCounter[pType] = (pGen + 1) >>> 0;\n }\n\n if (pType === PacketType.Command) {\n this.#sendWindowCommand.advanceToExcluded(pID);\n } else if (pType === PacketType.CommandLow) {\n this.#sendWindowCommandLow.advanceToExcluded(pID);\n }\n\n return [pID, pGen];\n }\n\n #trackResend(pType: PacketType, p: Packet, rp: ResendPacket): void {\n if (pType === PacketType.Init1) {\n this.#initPacketCheck = rp;\n return;\n }\n if (pType === PacketType.Command || pType === PacketType.CommandLow) {\n const key = (pType << 16) | p.id;\n this.#ackManager.set(key, rp);\n }\n }\n\n #checkResends(): void {\n const now = Date.now();\n\n if (now - this.#lastMessageReceived > PACKET_TIMEOUT_MS) {\n this.#logger.warn(\"idle timeout\");\n this.#triggerClose(new Error(\"idle timeout\"));\n return;\n }\n\n if (this.#initPacketCheck) {\n this.#doResend(this.#initPacketCheck, now);\n }\n\n for (const [key, rp] of this.#ackManager) {\n if (now - rp.firstSend > PACKET_TIMEOUT_MS) {\n this.#ackManager.delete(key);\n this.#triggerClose(new Error(\"packet ack timeout\"));\n return;\n }\n this.#doResend(rp, now);\n }\n }\n\n #doResend(rp: ResendPacket, now: number): void {\n if (now - rp.lastSend < rp.nextInterval) return;\n\n rp.lastSend = now;\n rp.retryCount++;\n rp.nextInterval = Math.min(rp.nextInterval * 2, MAX_RETRY_INTERVAL_MS);\n\n const dummy = !this.#crypt.cryptoInitComplete;\n const unencrypted = (packetFlags(rp.packet) & PacketFlags.Unencrypted) !== 0;\n const header = buildC2SHeader(rp.packet);\n const pType = packetType(rp.packet);\n\n const [ciphertext, tag] = this.#crypt.encrypt(\n pType,\n rp.packet.id,\n rp.packet.generationID,\n header,\n rp.packet.data,\n dummy,\n unencrypted,\n );\n\n const final = new Uint8Array(TAG_SIZE + HEADER_SIZE + ciphertext.length);\n final.set(tag.slice(0, TAG_SIZE), 0);\n final.set(header, TAG_SIZE);\n final.set(ciphertext, TAG_SIZE + HEADER_SIZE);\n this.#write(final);\n }\n\n #triggerClose(err: Error | null): void {\n if (this.#closed) return;\n this.close();\n this.onClosed?.(err);\n }\n}\n\n// ---- Helpers ----------------------------------------------------------------\n\nfunction applyProtocolFlags(pType: PacketType, flags: number): number {\n if (pType === PacketType.Command || pType === PacketType.CommandLow) {\n return flags | PacketFlags.NewProtocol;\n }\n return flags;\n}\n\nfunction hasOldNewerPacket(queue: Map<number, Packet>, nextID: number): boolean {\n const now = Date.now();\n for (const [id, pkg] of queue) {\n const diff = (id - nextID + 0x10000) & 0xffff;\n if (diff < 0x8000 && now - pkg.receivedAt > 5_000) {\n return true;\n }\n }\n return false;\n}\n"],"mappings":";;;;;AAAA,IAAkBE,IAAX,yBAAA,GAAA;QACL,EAAA,EAAAC,QAAA,KAAA,SACA,EAAA,EAAAC,eAAA,KAAA,gBACA,EAAA,EAAAC,UAAA,KAAA,WACA,EAAA,EAAAC,aAAA,KAAA,cACA,EAAA,EAAAC,OAAA,KAAA,QACA,EAAA,EAAAC,OAAA,KAAA,QACA,EAAA,EAAAC,MAAA,KAAA,OACA,EAAA,EAAAC,SAAA,KAAA,UACA,EAAA,EAAAC,QAAA,KAAA;AACF,EAAA,CAAA,CAAA,GAEkBC,IAAX,yBAAA,GAAA;QACL,EAAA,EAAAC,aAAA,MAAA,cACA,EAAA,EAAAC,cAAA,MAAA,eACA,EAAA,EAAAC,aAAA,MAAA,cACA,EAAA,EAAAC,cAAA,OAAA;AACF,EAAA,CAAA,CAAA;AAYA,SAAgBC,EAAW,GAAuB;CAChD,OAAQC,EAAE,cAAc;AAC1B;AAEA,SAAgBC,EAAY,GAAmB;CAC7C,OAAOD,EAAE,cAAc;AACzB;AAEA,SAAgBE,EAAc,GAAoB;CAChD,OAAA,GAAQD,EAAYD,CAAC,IAAA;AACvB;AAGA,SAAgBG,EAAe,GAAuB;CACpD,IAAMC,oBAAS,IAAI,WAAW,CAAC,GACzBC,IAAO,IAAI,SAASD,EAAO,MAAM;CAIvC,OAHAC,EAAK,UAAU,GAAGL,EAAE,IAAI,EAAK,GAC7BK,EAAK,UAAU,GAAGL,EAAE,UAAU,EAAK,GACnC,EAAO,KAAKA,EAAE,aACPI;AACT;AAGA,SAAgBE,EAAe,GAAqD;CAElF,OAAO;EACL,IAAI,IAFW,SAASC,EAAI,QAAQA,EAAI,YAAYA,EAAI,UAEpD,CAAA,CAAK,UAAU,GAAG,EAAK;EAC3B,aAAaA,EAAI;CACnB;AACF;AAGA,SAAgBC,EAAe,GAAkE;CAC/F,IAAMH,IAAO,IAAI,SAASE,EAAI,QAAQA,EAAI,YAAYA,EAAI,UAAU;CACpE,OAAO;EACL,IAAIF,EAAK,UAAU,GAAG,EAAK;EAC3B,UAAUA,EAAK,UAAU,GAAG,EAAK;EACjC,aAAaE,EAAI;CACnB;AACF;;;ACpEA,IAAaE,IAAb,MAA8B;CAC5B,KAAoB;CACpB,KAAc;CACd;CACA;CAEA,YAAY,GAAa,GAAoB;EAE3C,AADA,KAAKC,KAAOC,GACZ,KAAKC,KAAiBC;CACxB;CAEA,IAAI,aAAqB;EACvB,OAAO,KAAKC;CACd;CAEA,QAAQ,GAAsB;EAC5B,IAAIC,KAAU,GAAG;EACjB,IAAMC,IAAgB,KAAKC,KAAoBF,GACzCG,IAAU,KAAK,MAAMF,IAAgB,KAAKN,EAAI;EAIpD,AAHIQ,IAAU,MACZ,KAAKJ,KAAc,KAAK,IAAI,KAAKA,KAAcI,GAAS,UAAW,IAErE,KAAKD,KAAoBD,IAAgB,KAAKN;CAChD;CAEA,kBAAkB,GAA2B;EAC3C,IAAIS,IAAWC,IAAc,KAAKH;EAElC,AADIE,IAAW,MAAG,KAAY,KAAKT,KACnC,KAAK,QAAQS,IAAW,CAAC;CAC3B;CAEA,OAAO,GAA2B;EAChC,IAAIA,IAAWC,IAAc,KAAKH;EAElC,AADIE,IAAW,MAAG,KAAY,KAAKT,KACnC,KAAK,QAAQS,CAAQ;CACvB;CAEA,WAAW,GAA8B;EACvC,IAAME,IAAY,KAAKJ,KAAoB,KAAKL;EAIhD,OAHIS,IAAY,KAAKX,KACZU,KAAe,KAAKH,MAAqBG,IAAcC,IAEzDD,KAAe,KAAKH,MAAqBG,IAAcC,IAAY,KAAKX;CACjF;CAEA,cAAc,GAA6B;EAIzC,OAHI,KAAKY,GAAWF,CAAW,IACtBA,IAAc,KAAKV,KAAO,KAAKO,KAEjCG,IAAc,KAAKH;CAC5B;CAEA,YAAY,GAA8B;EACxC,OAAO,KAAK,cAAcG,CAAW,IAAI;CAC3C;CAEA,eAAe,GAA8B;EAC3C,OAAO,KAAK,cAAcA,CAAW,KAAK,KAAKR;CACjD;CAEA,GAAW,GAA8B;EACvC,OACE,KAAKK,KAAoB,KAAKP,KAAO,KAAKE,MAC1CQ,IAAc,KAAKH,KAAoB,KAAKL,KAAiB,KAAKF;CAEtE;CAEA,cAAc,GAA6B;EAEzC,OADI,KAAKY,GAAWF,CAAW,IAAU,KAAKN,KAAc,IACrD,KAAKA;CACd;CAEA,QAAc;EAEZ,AADA,KAAKG,KAAoB,GACzB,KAAKH,KAAc;CACrB;AACF,GC5EMS,IAAa,MASNC,IAAb,MAAiB;CACf,KAAa,IAAI,WAAWD,CAAU;CAEtC,WAAW,GAA8B;EACvC,IAAM,EAAE,cAAW,qBAAkB,aAAUE,EAAeC,CAAI,GAE5DC,IAAO,IAAI,WAAWC,CAAgB;EAE5C,IAAA,EAAKC,IAAQ,IAEX,OADAF,EAAK,IAAID,EAAK,MAAMI,GAAWA,IAAYF,CAAgB,CAAC,GACrDD;EAGT,KAAKI,GAAW,KAAK,CAAC;EAEtB,IAAMC,IAAkB;GACtB,SAAS;GACT,WAAWF;GACX,SAAS;GACT,YAAY;EACd;EAEA,OAAO,KAAKG,GAAeP,GAAMM,CAAK,IACpC,IAAKA,EAAM,UAAU,GACf;OAAA,CAAC,KAAKE,GAAkBR,GAAMC,GAAMK,CAAK,GAAG;EAAA,OAEhD,IAAI,KAAKG,GAAgBT,GAAMC,GAAMC,GAAkBI,CAAK,GAAG;EAInE,OAAOL;CACT;CAEA,GAAe,GAAkB,GAAuB;EAUtD,OATIS,EAAG,YAAY,IACfA,EAAG,YAAY,IAAIV,EAAK,SAAe,MAC3C,EAAG,WACAA,EAAKU,EAAG,aACNV,EAAKU,EAAG,YAAY,MAAO,IAC3BV,EAAKU,EAAG,YAAY,MAAO,KAC3BV,EAAKU,EAAG,YAAY,MAAO,QAC9B,GACF,EAAG,aAAa,GACT,MATsB;CAU/B;CAEA,GAAkB,GAAkB,GAAkB,GAAuB;EAE3E,IADA,EAAG,UAAWA,EAAG,YAAY,MAAO,GAChCA,EAAG,YAAY,IAAIV,EAAK,QAAQ,OAAO;EAE3C,IAAMW,IAAKX,EAAKU,EAAG,YACbE,IAAKZ,EAAKU,EAAG,YAAY;EAC/B,EAAG,aAAa;EAEhB,IAAMG,IAAQF,KAAM,IAAMC,KAAM,GAC5BE,IAAWH,IAAK;EACpB,IAAIG,MAAa,GACf,KAAY;OACP;GACL,IAAIJ,EAAG,aAAaV,EAAK,QAAQ,OAAO;GAExC,AADA,IAAWA,EAAKU,EAAG,YACnB,EAAG;EACL;EAEA,IAAMK,IAAS,KAAKV,GAAWQ;EAC/B,KAAK,IAAIG,IAAI,GAAGA,IAAIF,GAAU,KAC5B,AAAIJ,EAAG,UAAUT,EAAK,UAAUc,IAASC,IAAIN,EAAG,YAC9C,EAAKA,EAAG,WAAWT,EAAKc,IAASC,IACjC,EAAG;EAIP,IAAMC,IAAMP,EAAG,UAAU,IAAII;EAI7B,OAHA,KAAKI,GAAiBjB,GAAMS,GAAIO,CAAG,GACnC,EAAG,aAAaP,EAAG,SAEZ;CACT;CAEA,GACE,GACA,GACA,GACA,GACS;EACT,IAAMS,IAAY,KAAK,IAAIjB,GAAkB,EAAE,IAAI;EACnD,IAAIQ,EAAG,WAAWS,GAAW;GAC3B,OAAOT,EAAG,UAAUR,IAAkB;IACpC,IAAIQ,EAAG,YAAY,GAAG;KAEpB,IADA,EAAG,aAAa,GACZA,EAAG,YAAYV,EAAK,QAAQ;KAChC,EAAG,WACAA,EAAKU,EAAG,YAAY,KAClBV,EAAKU,EAAG,YAAY,MAAO,IAC3BV,EAAKU,EAAG,YAAY,MAAO,KAC3BV,EAAKU,EAAG,YAAY,MAAO,QAC9B;IACJ;IACA,IAAIA,EAAG,aAAaV,EAAK,QAAQ;IAEjC,AADA,EAAK,EAAG,aAAaA,EAAK,EAAG,cAC7B,EAAG,UAAWU,EAAG,YAAY,MAAO;GACtC;GACA,OAAO;EACT;EAEA,IAAIA,EAAG,aAAaV,EAAK,UAAUU,EAAG,WAAWT,EAAK,QAAQ,OAAO;EAGrE,AADA,EAAK,EAAG,aAAaD,EAAK,EAAG,cAC7B,EAAG,UAAWU,EAAG,YAAY,MAAO;EAEpC,IAAMO,IAAM,KAAK,IAAIP,EAAG,UAAU,GAAG,CAAC;EAItC,OAHA,KAAKQ,GAAiBjB,GAAMS,GAAIO,CAAG,GAC/BP,EAAG,aAAaO,MAAK,EAAG,aAAaA,IAElC;CACT;CAEA,GAAiB,GAAkB,GAAc,GAAmB;EAClE,OAAOP,EAAG,aAAaO,KACjB,IAAG,aAAa,IAAIhB,EAAK,UADH;GAE1B,IAAMmB,KACHnB,EAAKS,EAAG,cACNT,EAAKS,EAAG,aAAa,MAAO,IAC5BT,EAAKS,EAAG,aAAa,MAAO,QAC/B,GACIG,KAASO,KAAK,KAAMA,KAAK;GAE/B,AADA,KAAKf,GAAWQ,KAAQH,EAAG,YAC3B,EAAG;EACL;CACF;AACF;AAEA,SAASX,EAAe,GAItB;CACA,IAAIC,EAAK,SAAS,GAAG,MAAU,MAAM,yBAAyB;CAE9D,IAAMG,IAAQH,EAAK;CAEnB,KADeG,KAAS,IAAK,MACf,GAAG,MAAU,MAAM,oCAAoC;CAErE,IAAMC,IAAaD,IAAQ,IAAc,IAAI;CAC7C,IAAIH,EAAK,SAASI,GAAW,MAAU,MAAM,oCAAoC;CAEjF,IAAIF;CAOJ,OANA,AAGEA,IAHGC,IAAQ,KACSH,EAAK,KAAOA,EAAK,MAAO,IAAMA,EAAK,MAAO,KAAOA,EAAK,MAAO,QAAS,IAEvEA,EAAK,IAGnB;EAAE;EAAW;EAAkB;CAAM;AAC9C;;;AC/IA,IAAMqB,IAA6B,MAC7BC,IAAmB,KACnBC,IAAoB,KACpBC,IAAwB,KAExBC,IAAW,GACXC,IAAoB,GACpBC,IAAsB,GACtBC,IAA0B,KAC1BC,IAA0B,KAUnBC,IAAb,MAA2B;CACzB,WAAyC;CACzC,WAAiD;CAEjD;CACA;CAEA,KAA0B;CAC1B,KAAY;CACZ,KAAU;CACV,KAAiC;CACjC,KAAmC;CACnC,KAAuB,KAAK,IAAI;CAEhC,qBAAiB,IAAI,YAAY,CAAC;CAClC,qBAAqB,IAAI,YAAY,CAAC;CAEtC,KAAqB,IAAI/C,EAAiB,OAASsC,CAA0B;CAC7E,KAAwB,IAAItC,EAAiB,OAASsC,CAA0B;CAChF,KAAqB,IAAItC,EAAiB,OAASsC,CAA0B;CAC7E,KAAwB,IAAItC,EAAiB,OAASsC,CAA0B;CAEhF,qBAAgB,IAAI,IAAoB;CACxC,qBAAmB,IAAI,IAAoB;CAC3C,qBAAc,IAAI,IAA0B;CAC5C,KAAwC;CAExC,YAAY,GAAc,IAAiBjE,GAAY;EAErD,AADA,KAAK2E,KAASC,GACd,KAAKC,KAAUC;CACjB;CAEA,YAAY,GAAkB;EAC5B,KAAKC,KAAYC;CACnB;CAEA,QAAQ,GAA6B;EACnC,OAAO,IAAI,SAAS,GAAS,MAAW;GACtC,IAAM,EAAE,SAAM,YAAS/E,EAAcgF,CAAI,GAEnCC,IAASC,EAAaC,EAAKC,CAAI,MAAM,IAAI,SAAS,MAAM,GACxDC,KAAW,MAAiB;IAEhC,AADAJ,EAAO,MAAM,GACbK,EAAOC,CAAK;GACd;GAEA,AADAN,EAAO,KAAK,SAASI,CAAO,GAC5BJ,EAAO,QAAQ,SAASO,GAAM,EAAE,GAAGJ,SAAY;IAG7C,AAFAH,EAAO,IAAI,SAASI,CAAO,GAC3B,KAAK,MAAMJ,CAAM,GACjBQ,EAAQ;GACV,CAAC;EACH,CAAC;CACH;CAEA,MAAM,GAAuB;EAgB3B,AAfA,KAAKC,KAAQC,GACb,KAAKC,KAAU,IACf,KAAKC,KAAuB,KAAK,IAAI,GAErCF,EAAK,GAAG,YAAY,MAAQ;GAE1B,AADA,KAAKE,KAAuB,KAAK,IAAI,GACrC,KAAKC,GAAiB,IAAI,WAAWC,EAAI,QAAQA,EAAI,YAAYA,EAAI,UAAU,CAAC;EAClF,CAAC,GACDJ,EAAK,GAAG,UAAU,MAAQ;GAExB,AADA,KAAKf,GAAQ,MAAM,aAAaoB,CAAG,GACnC,KAAKC,GAAcD,CAAG;EACxB,CAAC,GACDL,EAAK,GAAG,eAAe,KAAKM,GAAc,IAAI,CAAC,GAE/C,KAAKC,GAAejG,EAAW,WAAW,GAC1C,KAAKiG,GAAejG,EAAW,SAAS;EAExC,IAAMkG,IAAYC,EAAa,KAAK1B,IAAQ,IAAI;EAChD,AAAIyB,KAAW,KAAKE,GAAepG,EAAW,OAAOkG,GAAW,CAAC;EAGjE,IAAMG,IAAY,kBAAkB;GAClC,AAAI,KAAK5B,GAAO,sBACd,KAAK,WAAWzE,EAAW,sBAAM,IAAI,WAAY,GAAGU,EAAY,WAAW;EAE/E,GAAGsD,CAAgB;EACnB,KAAKsC,WAAkB,cAAcD,CAAS;EAG9C,IAAME,IAAc,kBAAkB,KAAKC,GAAc,GAAGjC,CAAuB;EACnF,KAAKkC,WAAoB,cAAcF,CAAW;CACpD;CAEA,uBAA6B;EAC3B,KAAKG,KAAmB;CAC1B;CAEA,WAAW,GAAmB,GAAkB,GAAqB;EACnE,IAAMC,IAAQ,CAAC,KAAKlC,GAAO;EAC3B,IAAI/B,EAAK,SAAS,OAAOkE,MAAU5G,EAAW,SAAS4G,MAAU5G,EAAW,cAAc;GACxF,KAAK6G,GAAiBD,GAAOlE,GAAMG,GAAO8D,CAAK;GAC/C;EACF;EACA,KAAKP,GAAeQ,GAAOlE,GAAMG,GAAO8D,CAAK;CAC/C;CAEA,gBAAgB,GAAkB,GAAqB;EACrD,IAAMG,IAAM,KAAKb,GAAejG,EAAW,QACrC+G,IAAO,KAAKC,GAAmBhH,EAAW;EAEhD,AADA,KAAKiG,GAAejG,EAAW,SAAU8G,IAAM,IAAK,OAChD,KAAKb,GAAejG,EAAW,WAAW,KAC5C,KAAKgH,GAAmBhH,EAAW,MAAM;EAG3C,IAAMiH,IAAa7C,IAAoB1B,EAAK,QACtCwE,IAAe,IAAI,WAAWD,CAAU;EAG9C,AAFA,IAAI,SAASC,EAAa,MAAM,CAAC,CAAC,UAAU,GAAGJ,GAAK,EAAK,GACzD,EAAa,KAAKK,GAClBD,EAAa,IAAIxE,GAAM0B,CAAiB;EAWxC,IAAMhD,IAASD,EAAe;GAR5B,aAAanB,EAAW,QAAQU,EAAY;GAC5C,IAAIoG;GACJ,UAAU,KAAKjC;GACf,cAAckC;GACd,MAAMG;GACN,YAAY;EAGgB,CAAC,GACzBE,IAAQ,IAAI,WAAW,KAAyBH,CAAU;EAIhE,AAHAG,EAAM,IAAI,KAAK3C,GAAO,eAAe,CAAC,GACtC2C,EAAM,IAAIhG,GAAQ+C,CAAQ,GAC1BiD,EAAM,IAAIF,GAAc,EAAsB,GAC9C,KAAKG,GAAOD,CAAK;CACnB;CAGA,kBAAkB,GAAkB,GAA2B,GAAqB;EAClF,IAAIE,EAAgB,WAAW,GAAG,MAAU,MAAM,sCAAsC;EACxF,IAAIA,EAAgB,SAAS,IAAI,MAAU,MAAM,0BAA0B;EAC3E,IAAIA,EAAgB,MAAM,MAAO,CAAC,OAAO,UAAUxC,CAAE,KAAKA,IAAK,KAAKA,IAAK,KAAM,GAC7E,MAAU,WAAW,wBAAwB;EAG/C,IAAMgC,IAAM,KAAKb,GAAejG,EAAW,eACrC+G,IAAO,KAAKC,GAAmBhH,EAAW;EAEhD,AADA,KAAKiG,GAAejG,EAAW,gBAAiB8G,IAAM,IAAK,OACvD,KAAKb,GAAejG,EAAW,kBAAkB,MACnD,KAAKgH,GAAmBhH,EAAW,gBAAiB+G,IAAO,MAAO;EAGpE,IAAME,IAAa5C,IAAsBiD,EAAgB,SAAS,IAAI5E,EAAK,QACrE6E,IAAiB,IAAI,WAAWN,CAAU,GAC1C5F,IAAO,IAAI,SAASkG,EAAe,MAAM;EAI/C,AAHAlG,EAAK,UAAU,GAAGyF,GAAK,EAAK,GAC5B,EAAe,KAAKK,GACpB,EAAe,KAAK,GACpB,EAAe,KAAKG,EAAgB;EACpC,KAAK,IAAIE,IAAQ,GAAGA,IAAQF,EAAgB,QAAQ,KAClD,EAAK,UAAUjD,IAAsBmD,IAAQ,GAAGF,EAAgBE,IAAS,EAAK;EAEhF,EAAe,IAAI9E,GAAM2B,IAAsBiD,EAAgB,SAAS,CAAC;EAUzE,IAAMlG,IAASD,EAAe;GAP5B,aAAanB,EAAW,eAAeU,EAAY;GACnD,IAAIoG;GACJ,UAAU,KAAKjC;GACf,cAAckC;GACd,MAAMQ;GACN,YAAY;EAEgB,CAAM,GAC9BH,IAAQ,IAAI,WAAW,KAAyBH,CAAU;EAIhE,AAHAG,EAAM,IAAI,KAAK3C,GAAO,eAAe,CAAC,GACtC2C,EAAM,IAAIhG,GAAQ+C,CAAQ,GAC1BiD,EAAM,IAAIG,GAAgB,EAAsB,GAChD,KAAKF,GAAOD,CAAK;CACnB;CAEA,QAAc;EACZ,AAAI,KAAKzB,OACT,KAAKA,KAAU,IACf,KAAKW,KAAY,GACjB,KAAKG,KAAc,GACnB,KAAKhB,IAAO,MAAM;CACpB;CAIA,GAAiB,GAAmB,GAAkB,GAAe,GAAsB;EACzF,IACIgC,IAAM,GACNC,IAAQ;EAEZ,OAAOD,IAAM/E,EAAK,SAAQ;GACxB,IAAMiF,IAAY,KAAK,IAAIjF,EAAK,SAAS+E,GAAK,GAAO,GAC/CG,IAAOH,IAAME,MAAcjF,EAAK,QAElCmF,IAAShF;GAKb,AAJI6E,MAAUE,MAAM,KAAUlH,EAAY,aAE1C,KAAK0F,GAAeQ,GAAOlE,EAAK,MAAM+E,GAAKA,IAAME,CAAS,GAAGE,GAAQlB,CAAK,GAC1E,KAAOgB,GACP,IAAQ;EACV;CACF;CAEA,GACE,GACA,GACA,GACA,IAAQ,CAAC,KAAKlD,GAAO,oBACf;EACN,IAAQqD,EAAmBlB,GAAO/D,CAAK;EACvC,IAAM,CAACiE,GAAKC,KAAQ,KAAKgB,GAAoBnB,CAAK,GAC5C5F,IAAY;GAChB,aAAa4F,IAAQ/D;GACrB,IAAIiE;GACJ,UAAU,KAAKjC;GACf,cAAckC;GACd;GACA,YAAY;EACd,GAEMiB,KAAenF,IAAQnC,EAAY,iBAAiB,GACpDU,IAASD,EAAeH,CAAC,GACzB,CAACiH,GAAYC,KAAO,KAAKzD,GAAO,QACpCmC,GACAE,GACAC,GACA3F,GACAsB,GACAiE,GACAqB,CACF,GAEMZ,IAAQ,IAAI,WAAW,KAAyBa,EAAW,MAAM;EAIvE,AAHAb,EAAM,IAAIc,EAAI,MAAM,GAAG/D,CAAQ,GAAG,CAAC,GACnCiD,EAAM,IAAIhG,GAAQ+C,CAAQ,GAC1BiD,EAAM,IAAIa,GAAY,EAAsB,GAC5C,KAAKZ,GAAOD,CAAK;EAEjB,IAAMe,IAAmB;GACvB,QAAQnH;GACR,WAAW,KAAK,IAAI;GACpB,UAAU,KAAK,IAAI;GACnB,YAAY;GACZ,cAAcsD;EAChB;EACA,KAAK8D,GAAaxB,GAAO5F,GAAGmH,CAAE;CAChC;CAEA,GAAO,GAAwB;EAC7B,IAAK,KAAK1C,IACV,IAAI;GACF,KAAKA,GAAM,KAAK,OAAO,KAAK/C,CAAI,IAAI,MAAQ;IAC1C,AAAIqD,KAAK,KAAKpB,GAAQ,KAAK,kBAAkBoB,CAAG;GAClD,CAAC;EACH,SAAST,GAAgB;GACvB,IAAM+C,IAAa/C,aAAiB,QAAQA,IAAY,MAAM,OAAOA,CAAK,CAAC;GAK3E,AADA,KAAKX,GAAQ,MAAM,mBAAmB0D,CAAU,GAChD,KAAKrC,GAAcqC,CAAU;EAC/B;CACF;CAEA,GAAiB,GAAuB;EACtC,IAAI9G,EAAI,SAAS,IAAI;EAErB,IAAM2G,IAAM3G,EAAI,MAAM,GAAG4C,CAAQ,GAC3B/C,IAASG,EAAI,MAAM4C,GAAU,EAAY,GACzC8D,IAAa1G,EAAI,MAAM,EAAY,GAEnC+G,IAAShH,EAAeF,CAAM,GAC9BJ,IAAY;GAChB,GAAGsH;GACH,UAAU;GACV,cAAc,KAAKC,GAAmBD,EAAO,IAAIA,EAAO,cAAc,EAAI;GAC1E,sBAAM,IAAI,WAAY;GACtB,YAAY,KAAK,IAAI;EACvB,GAEME,IAAY,KAAKC,GAAmBzH,GAAGI,GAAQ6G,GAAYC,CAAG;EACpE,IAAIM,MAAc,MAKlB;OAJA,EAAE,OAAOA,EAAU,WAELzH,EAAWC,CAErB,MAAUhB,EAAW,MAAM;IAC7B,KAAK0I,GAAU1H,EAAE,IAAIwH,EAAU,SAAS;IACxC;GACF;GAEA,AAAK,KAAKG,GAA2B3H,GAAGwH,EAAU,SAAS,MAE3D,KAAKI,GAAmB5H,CAAC,GACzB,KAAK6H,GAAwB7H,CAAC;EAL9B;CAMF;CAEA,GAAmB,GAAY,GAAuB;EACpD,QAAQ4F,GAAR;GACE,KAAK5G,EAAW,SACd,OAAO,KAAK8I,GAAmB,cAAchE,CAAE;GACjD,KAAK9E,EAAW,YACd,OAAO,KAAK+I,GAAsB,cAAcjE,CAAE;GACpD,KAAK9E,EAAW,KACd,OAAO,KAAKgJ,GAAmB,cAAclE,CAAE;GACjD,KAAK9E,EAAW,QACd,OAAO,KAAKiJ,GAAsB,cAAcnE,CAAE;GACpD,SACE,OAAO;EACX;CACF;CAEA,GACE,GACA,GACA,GACA,GACsD;EACtD,IAAMkD,KAAe/G,EAAYD,CAAC,IAAIN,EAAY,iBAAiB,GAC7DiG,IAAQ,CAAC,KAAKlC,GAAO,oBACvByE,IAAYvC,GACVC,IAAQ7F,EAAWC,CAAC,GACpBmI,IAAMnI,EAAE;EAEd,IAAI;GAWF,OAAO;IAAE,WAVS,KAAKyD,GAAO,QAC5BmC,GACA5F,EAAE,IACFmI,GACA/H,GACA6G,GACAC,GACAvB,GACAqB,CAEO;IAAW;GAAU;EAChC,QAAQ;GAEN,KAAK,IAAMvE,KAAU,CAAC,IAAI,CAAC,GAAG;IAC5B,IAAM2F,IAAWD,IAAM1F;IACvB,IAAI,MAAW,IACf,IAAI;KAWF,OAAO;MAAE,WAVS,KAAKgB,GAAO,QAC5BmC,GACA5F,EAAE,IACFoI,GACAhI,GACA6G,GACAC,GACA,IACA,EAEO;MAAW,WAAW;KAAM;IACvC,QAAQ,CAER;GACF;GAGA,IACEtB,MAAU5G,EAAW,WACrB4G,MAAU5G,EAAW,cACrB4G,MAAU5G,EAAW,KAErB,IAAI;IAWF,OAAO;KAAE,WAVS,KAAKyE,GAAO,QAC5BmC,GACA5F,EAAE,IACFmI,GACA/H,GACA6G,GACAC,GACA,IACAF,CAEO;KAAW,WAAW;IAAK;GACtC,QAAQ,CAER;GAGF,OAAO;EACT;CACF;CAEA,GAA2B,GAAW,GAA6B;EACjE,IAAMpB,IAAQ7F,EAAWC,CAAC;EAC1B,IAAI4F,MAAU5G,EAAW,WAAW4G,MAAU5G,EAAW,YAAY,OAAO;EAE5E,IAAMqJ,IAAMzC,MAAU5G,EAAW,UAAU,KAAK8I,KAAqB,KAAKC,IACpEO,IAAU1C,MAAU5G,EAAW,UAAUA,EAAW,MAAMA,EAAW;EAS3E,OAPKqJ,EAAI,WAAWrI,EAAE,EAAE,KAMxB,KAAKuI,GAASvI,EAAE,IAAIsI,GAASJ,CAAS,GAC/B,OANDG,EAAI,YAAYrI,EAAE,EAAE,KACtB,KAAKuI,GAASvI,EAAE,IAAIsI,GAASJ,CAAS,GAEjC;CAIX;CAEA,GAAS,GAAkB,GAAqB,GAA0B;EACxE,IAAMM,oBAAU,IAAI,WAAW,CAAC;EAEhC,AADA,IAAI,SAASA,EAAQ,MAAM,CAAC,CAAC,UAAU,GAAGC,GAAU,EAAK,GACzD,KAAKrD,GAAekD,GAASE,GAAS,GAAGN,CAAS;CACpD;CAEA,GAAU,GAAa,GAA0B;EAC/C,IAAMQ,oBAAW,IAAI,WAAW,CAAC;EAEjC,AADA,IAAI,SAASA,EAAS,MAAM,CAAC,CAAC,UAAU,GAAG5C,GAAK,EAAK,GACrD,KAAKV,GAAepG,EAAW,MAAM0J,GAAUhJ,EAAY,aAAawI,CAAS;CACnF;CAEA,GAAmB,GAAiB;EAClC,IAAMtC,IAAQ7F,EAAWC,CAAC;EAC1B,IAAI4F,MAAU5G,EAAW,WAAW4G,MAAU5G,EAAW,YAAY;GACnE,KAAK,WAAWgB,CAAC;GACjB;EACF;EAEA,IAAM2I,IAAY/C,MAAU5G,EAAW,SACjC4J,IAAQD,IAAY,KAAKE,KAAgB,KAAKC,IAC9CT,IAAMM,IAAY,KAAKb,KAAqB,KAAKC;EAUvD,KARAa,EAAM,IAAI5I,EAAE,IAAIA,CAAC,GAEb2I,IACF,KAAKI,GAA2BH,GAAOP,GAAK,EAAI,IAEhD,KAAKU,GAA2BH,GAAOP,GAAK,EAAK,KAGtC;GACX,IAAMW,IAAUL,IAAY,KAAKM,KAAkB,KAAKC,IAClDC,IAASP,EAAM,IAAII,CAAO;GAChC,IAAIG,MAAW,KAAA,GAAW;GAE1B,IAAMC,IAAS,KAAKC,GAAeF,GAAQP,GAAOI,GAASX,CAAG;GAC9D,IAAI,CAACe,GAAQ;GAEb,IAAM,CAACE,GAAaC,KAAWH;GAK/B,AAJIT,IAAW,KAAKM,KAAkBM,IACjC,KAAKL,KAAqBK,GAE/B,KAAKC,GAAeF,CAAW,GAC/B,KAAK,WAAWA,CAAW;EAC7B;CACF;CAEA,KAAkB;CAClB,KAAqB;CAErB,GACE,GACA,GACA,GACM;EACN,IAAIG,IAASd,IAAY,KAAKM,KAAkB,KAAKC;EACrD,OAAO,CAACN,EAAM,IAAIa,CAAM,KAAKC,EAAkBd,GAAOa,CAAM,IAE1D,AADA,IAAUA,IAAS,IAAK,OACxBpB,EAAI,QAAQ,CAAC;EAEf,AAAIM,IAAW,KAAKM,KAAkBQ,IACjC,KAAKP,KAAqBO;CACjC;CAEA,GACE,GACA,GACA,GACA,GACiD;EACjD,KAAKxJ,EAAY0J,CAAW,IAAIjK,EAAY,gBAAgB,GAG1D,OAFAkJ,EAAM,OAAOa,CAAM,GACnBpB,EAAI,QAAQ,CAAC,GACN,CAACsB,GAAcF,IAAS,IAAK,KAAM;EAG5C,IAAMG,IAAsB,CAAC,GACzBC,IAAY,GACZC,IAASL,GACTM,IAAY,IACZC,IAAW;EAEf,SAAa;GACX,IAAMC,IAAOrB,EAAM,IAAIkB,CAAM;GAC7B,IAAI,CAACG,GAAM,OAAO;GAElB,AADAL,EAAU,KAAKK,CAAI,GACnB,KAAaA,EAAK,KAAK;GAEvB,IAAMC,KAAcjK,EAAYgK,CAAI,IAAIvK,EAAY,gBAAgB;GACpE,IAAI,CAACqK,GAEH;QADA,IAAY,IACR,CAACG,GAAY;KACf,IAAW;KACX;IACF;UACK,IAAIA,GAAY;IACrB,IAAW;IACX;GACF;GACA,IAAUJ,IAAS,IAAK;EAC1B;EAEA,IAAI,CAACE,GAAU,OAAO;EAEtB,IAAMG,IAAW,IAAI,WAAWN,CAAS,GACrCpD,IAAM;EACV,KAAK,IAAMwD,KAAQL,GAKjB,AAJAO,EAAS,IAAIF,EAAK,MAAMxD,CAAG,GAC3B,KAAOwD,EAAK,KAAK,QACjBrB,EAAM,OAAOa,CAAM,GACnBpB,EAAI,QAAQ,CAAC,GACb,IAAUoB,IAAS,IAAK;EAK1B,OAFA,EAAY,OAAOU,GACnB,EAAY,eAAe,CAACzK,EAAY,YACjC,CAACiK,GAAaF,CAAM;CAC7B;CAEA,GAAe,GAAsB;EACnC,KAAKxJ,EAAYkJ,CAAM,IAAIzJ,EAAY,gBAAgB,GACvD,IAAI;GAGF,AADA,EAAO,OAAO,IADE8B,EACF,CAAA,CAAI,WAAW2H,EAAO,IAAI,GACxC,EAAO,eAAe,CAACzJ,EAAY;EACrC,SAASqF,GAAK;GACZ,KAAKpB,GAAQ,MAAM,wBAAwB;IAAE,IAAIwF,EAAO;IAAI;GAAI,CAAC;EACnE;CACF;CAEA,GAAwB,GAAiB;EACvC,IAAMvD,IAAQ7F,EAAWC,CAAC;EAE1B,IAAI4F,MAAU5G,EAAW,OAAO;GAC9B,KAAK0G,KAAmB;GACxB;EACF;EAEA,KAAKE,MAAU5G,EAAW,OAAO4G,MAAU5G,EAAW,WAAWgB,EAAE,KAAK,UAAU,GAAG;GACnF,IAAMoK,IAAQ,IAAI,SAASpK,EAAE,KAAK,QAAQA,EAAE,KAAK,UAAU,CAAC,CAAC,UAAU,GAAG,EAAK,GAEzEqK,KADazE,MAAU5G,EAAW,MAAMA,EAAW,UAAUA,EAAW,eACnD,KAAMoL;GACjC,KAAKE,GAAY,OAAOD,CAAG;EAC7B;CACF;CAEA,GAAoB,GAA8C;EAChE,IAAMvE,IAAM,KAAKb,GAAeW,IAC1BG,IAAO,KAAKC,GAAmBJ;EAerC,OAbIA,MAAU5G,EAAW,QAAc,CAAC8G,GAAKC,CAAI,KAEjD,KAAKd,GAAeW,KAAUE,IAAM,IAAK,OACrC,KAAKb,GAAeW,OAAW,MACjC,KAAKI,GAAmBJ,KAAUG,IAAO,MAAO,IAG9CH,MAAU5G,EAAW,UACvB,KAAKgJ,GAAmB,kBAAkBlC,CAAG,IACpCF,MAAU5G,EAAW,cAC9B,KAAKiJ,GAAsB,kBAAkBnC,CAAG,GAG3C,CAACA,GAAKC,CAAI;CACnB;CAEA,GAAa,GAAmB,GAAW,GAAwB;EACjE,IAAIH,MAAU5G,EAAW,OAAO;GAC9B,KAAK0G,KAAmByB;GACxB;EACF;EACA,IAAIvB,MAAU5G,EAAW,WAAW4G,MAAU5G,EAAW,YAAY;GACnE,IAAMqL,IAAOzE,KAAS,KAAM5F,EAAE;GAC9B,KAAKsK,GAAY,IAAID,GAAKlD,CAAE;EAC9B;CACF;CAEA,KAAsB;EACpB,IAAMoD,IAAM,KAAK,IAAI;EAErB,IAAIA,IAAM,KAAK3F,KAAuB3B,GAAmB;GAEvD,AADA,KAAKU,GAAQ,KAAK,cAAc,GAChC,KAAKqB,GAAc,gBAAI,MAAM,cAAc,CAAC;GAC5C;EACF;EAEA,AAAI,KAAKU,MACP,KAAK8E,GAAU,KAAK9E,IAAkB6E,CAAG;EAG3C,KAAK,IAAM,CAACF,GAAKlD,MAAO,KAAKmD,IAAa;GACxC,IAAIC,IAAMpD,EAAG,YAAYlE,GAAmB;IAE1C,AADA,KAAKqH,GAAY,OAAOD,CAAG,GAC3B,KAAKrF,GAAc,gBAAI,MAAM,oBAAoB,CAAC;IAClD;GACF;GACA,KAAKwF,GAAUrD,GAAIoD,CAAG;EACxB;CACF;CAEA,GAAU,GAAkB,GAAmB;EAC7C,IAAIA,IAAMpD,EAAG,WAAWA,EAAG,cAAc;EAIzC,AAFA,EAAG,WAAWoD,GACd,EAAG,cACH,EAAG,eAAe,KAAK,IAAIpD,EAAG,eAAe,GAAGjE,CAAqB;EAErE,IAAMyC,IAAQ,CAAC,KAAKlC,GAAO,oBACrBuD,KAAe/G,EAAYkH,EAAG,MAAM,IAAIzH,EAAY,iBAAiB,GACrEU,IAASD,EAAegH,EAAG,MAAM,GACjCvB,IAAQ7F,EAAWoH,EAAG,MAAM,GAE5B,CAACF,GAAYC,KAAO,KAAKzD,GAAO,QACpCmC,GACAuB,EAAG,OAAO,IACVA,EAAG,OAAO,cACV/G,GACA+G,EAAG,OAAO,MACVxB,GACAqB,CACF,GAEMZ,IAAQ,IAAI,WAAW,KAAyBa,EAAW,MAAM;EAIvE,AAHAb,EAAM,IAAIc,EAAI,MAAM,GAAG/D,CAAQ,GAAG,CAAC,GACnCiD,EAAM,IAAIhG,GAAQ+C,CAAQ,GAC1BiD,EAAM,IAAIa,GAAY,EAAsB,GAC5C,KAAKZ,GAAOD,CAAK;CACnB;CAEA,GAAc,GAAyB;EACrC,AAAI,KAAKzB,OACT,KAAK,MAAM,GACX,KAAK,WAAWI,CAAG;CACrB;AACF;AAIA,SAAS+B,EAAmB,GAAmB,GAAuB;CAIpE,OAHIlB,MAAU5G,EAAW,WAAW4G,MAAU5G,EAAW,aAChD6C,IAAQnC,EAAY,cAEtBmC;AACT;AAEA,SAAS6H,EAAkB,GAA4B,GAAyB;CAC9E,IAAMa,IAAM,KAAK,IAAI;CACrB,KAAK,IAAM,CAACzG,GAAI2G,MAAQ7B,GAEtB,KADc9E,IAAK2F,IAAS,QAAW,SAC5B,SAAUc,IAAME,EAAI,aAAa,KAC1C,OAAO;CAGX,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"handler-DuwoO5RC.cjs","names":[],"sources":["../src/transport/packet.ts","../src/transport/generation-window.ts","../src/transport/quicklz.ts","../src/transport/handler.ts"],"sourcesContent":["export const enum PacketType {\n Voice = 0,\n VoiceWhisper = 1,\n Command = 2,\n CommandLow = 3,\n Ping = 4,\n Pong = 5,\n Ack = 6,\n AckLow = 7,\n Init1 = 8,\n}\n\nexport const enum PacketFlags {\n Fragmented = 0x10,\n NewProtocol = 0x20,\n Compressed = 0x40,\n Unencrypted = 0x80,\n}\n\nexport interface Packet {\n /** Type byte combined with flags (low nibble = type, high nibble = flags). */\n typeFlagged: number;\n id: number;\n clientID: number;\n generationID: number;\n data: Uint8Array;\n receivedAt: number; // Date.now()\n}\n\nexport function packetType(p: Packet): PacketType {\n return (p.typeFlagged & 0x0f) as PacketType;\n}\n\nexport function packetFlags(p: Packet): number {\n return p.typeFlagged & 0xf0;\n}\n\nexport function isUnencrypted(p: Packet): boolean {\n return (packetFlags(p) & PacketFlags.Unencrypted) !== 0;\n}\n\n/** Build the 5-byte client-to-server header: [packetID(2), clientID(2), typeFlagged(1)]. */\nexport function buildC2SHeader(p: Packet): Uint8Array {\n const header = new Uint8Array(5);\n const view = new DataView(header.buffer);\n view.setUint16(0, p.id, false);\n view.setUint16(2, p.clientID, false);\n header[4] = p.typeFlagged;\n return header;\n}\n\n/** Parse a 3-byte server-to-client header. */\nexport function parseS2CHeader(raw: Uint8Array): Pick<Packet, \"id\" | \"typeFlagged\"> {\n const view = new DataView(raw.buffer, raw.byteOffset, raw.byteLength);\n return {\n id: view.getUint16(0, false),\n typeFlagged: raw[2]!,\n };\n}\n\n/** Parse a 5-byte client-to-server header. */\nexport function parseC2SHeader(raw: Uint8Array): Pick<Packet, \"id\" | \"clientID\" | \"typeFlagged\"> {\n const view = new DataView(raw.buffer, raw.byteOffset, raw.byteLength);\n return {\n id: view.getUint16(0, false),\n clientID: view.getUint16(2, false),\n typeFlagged: raw[4]!,\n };\n}\n","export class GenerationWindow {\n #mappedBaseOffset = 0;\n #generation = 0;\n readonly #mod: number;\n readonly #receiveWindow: number;\n\n constructor(mod: number, windowSize: number) {\n this.#mod = mod;\n this.#receiveWindow = windowSize;\n }\n\n get generation(): number {\n return this.#generation;\n }\n\n advance(amount: number): void {\n if (amount <= 0) return;\n const newBaseOffset = this.#mappedBaseOffset + amount;\n const genStep = Math.floor(newBaseOffset / this.#mod);\n if (genStep > 0) {\n this.#generation = Math.min(this.#generation + genStep, 0xffff_ffff);\n }\n this.#mappedBaseOffset = newBaseOffset % this.#mod;\n }\n\n advanceToExcluded(mappedValue: number): void {\n let moveDist = mappedValue - this.#mappedBaseOffset;\n if (moveDist < 0) moveDist += this.#mod;\n this.advance(moveDist + 1);\n }\n\n syncTo(mappedValue: number): void {\n let moveDist = mappedValue - this.#mappedBaseOffset;\n if (moveDist < 0) moveDist += this.#mod;\n this.advance(moveDist);\n }\n\n isInWindow(mappedValue: number): boolean {\n const maxOffset = this.#mappedBaseOffset + this.#receiveWindow;\n if (maxOffset < this.#mod) {\n return mappedValue >= this.#mappedBaseOffset && mappedValue < maxOffset;\n }\n return mappedValue >= this.#mappedBaseOffset || mappedValue < maxOffset - this.#mod;\n }\n\n mappedToIndex(mappedValue: number): number {\n if (this.#isNextGen(mappedValue)) {\n return mappedValue + this.#mod - this.#mappedBaseOffset;\n }\n return mappedValue - this.#mappedBaseOffset;\n }\n\n isOldPacket(mappedValue: number): boolean {\n return this.mappedToIndex(mappedValue) < 0;\n }\n\n isFuturePacket(mappedValue: number): boolean {\n return this.mappedToIndex(mappedValue) >= this.#receiveWindow;\n }\n\n #isNextGen(mappedValue: number): boolean {\n return (\n this.#mappedBaseOffset > this.#mod - this.#receiveWindow &&\n mappedValue < this.#mappedBaseOffset + this.#receiveWindow - this.#mod\n );\n }\n\n getGeneration(mappedValue: number): number {\n if (this.#isNextGen(mappedValue)) return this.#generation + 1;\n return this.#generation;\n }\n\n reset(): void {\n this.#mappedBaseOffset = 0;\n this.#generation = 0;\n }\n}\n","const TABLE_SIZE = 4096;\n\ninterface QlzState {\n control: number;\n sourcePos: number;\n destPos: number;\n nextHashed: number;\n}\n\nexport class Qlz {\n #hashtable = new Int32Array(TABLE_SIZE);\n\n decompress(data: Uint8Array): Uint8Array {\n const { headerLen, decompressedSize, flags } = parseQlzHeader(data);\n\n const dest = new Uint8Array(decompressedSize);\n\n if ((flags & 0x01) === 0) {\n dest.set(data.slice(headerLen, headerLen + decompressedSize));\n return dest;\n }\n\n this.#hashtable.fill(0);\n\n const state: QlzState = {\n control: 1,\n sourcePos: headerLen,\n destPos: 0,\n nextHashed: 0,\n };\n\n while (this.#ensureControl(data, state)) {\n if ((state.control & 1) !== 0) {\n if (!this.#processReference(data, dest, state)) break;\n } else {\n if (this.#processLiteral(data, dest, decompressedSize, state)) break;\n }\n }\n\n return dest;\n }\n\n #ensureControl(data: Uint8Array, st: QlzState): boolean {\n if (st.control !== 1) return true;\n if (st.sourcePos + 4 > data.length) return false;\n st.control =\n (data[st.sourcePos]! |\n (data[st.sourcePos + 1]! << 8) |\n (data[st.sourcePos + 2]! << 16) |\n (data[st.sourcePos + 3]! << 24)) >>>\n 0;\n st.sourcePos += 4;\n return true;\n }\n\n #processReference(data: Uint8Array, dest: Uint8Array, st: QlzState): boolean {\n st.control = (st.control >>> 1) >>> 0;\n if (st.sourcePos + 2 > data.length) return false;\n\n const b1 = data[st.sourcePos]!;\n const b2 = data[st.sourcePos + 1]!;\n st.sourcePos += 2;\n\n const hash = (b1 >> 4) | (b2 << 4);\n let matchlen = b1 & 0x0f;\n if (matchlen !== 0) {\n matchlen += 2;\n } else {\n if (st.sourcePos >= data.length) return false;\n matchlen = data[st.sourcePos]!;\n st.sourcePos++;\n }\n\n const offset = this.#hashtable[hash]!;\n for (let i = 0; i < matchlen; i++) {\n if (st.destPos < dest.length && offset + i < st.destPos) {\n dest[st.destPos] = dest[offset + i]!;\n st.destPos++;\n }\n }\n\n const end = st.destPos + 1 - matchlen;\n this.#updateHashtable(dest, st, end);\n st.nextHashed = st.destPos;\n\n return true;\n }\n\n #processLiteral(\n data: Uint8Array,\n dest: Uint8Array,\n decompressedSize: number,\n st: QlzState,\n ): boolean {\n const threshold = Math.max(decompressedSize, 10) - 10;\n if (st.destPos >= threshold) {\n while (st.destPos < decompressedSize) {\n if (st.control === 1) {\n st.sourcePos += 4;\n if (st.sourcePos > data.length) break;\n st.control =\n (data[st.sourcePos - 4]! |\n (data[st.sourcePos - 3]! << 8) |\n (data[st.sourcePos - 2]! << 16) |\n (data[st.sourcePos - 1]! << 24)) >>>\n 0;\n }\n if (st.sourcePos >= data.length) break;\n dest[st.destPos++] = data[st.sourcePos++]!;\n st.control = (st.control >>> 1) >>> 0;\n }\n return true;\n }\n\n if (st.sourcePos >= data.length || st.destPos >= dest.length) return true;\n\n dest[st.destPos++] = data[st.sourcePos++]!;\n st.control = (st.control >>> 1) >>> 0;\n\n const end = Math.max(st.destPos - 2, 0);\n this.#updateHashtable(dest, st, end);\n if (st.nextHashed < end) st.nextHashed = end;\n\n return false;\n }\n\n #updateHashtable(dest: Uint8Array, st: QlzState, end: number): void {\n while (st.nextHashed < end) {\n if (st.nextHashed + 3 > dest.length) break;\n const v =\n (dest[st.nextHashed]! |\n (dest[st.nextHashed + 1]! << 8) |\n (dest[st.nextHashed + 2]! << 16)) >>>\n 0;\n const hash = ((v >> 12) ^ v) & 0xfff;\n this.#hashtable[hash] = st.nextHashed;\n st.nextHashed++;\n }\n }\n}\n\nfunction parseQlzHeader(data: Uint8Array): {\n headerLen: number;\n decompressedSize: number;\n flags: number;\n} {\n if (data.length < 3) throw new Error(\"QuickLZ: data too short\");\n\n const flags = data[0]!;\n const level = (flags >> 2) & 0x03;\n if (level !== 1) throw new Error(\"QuickLZ: only level 1 is supported\");\n\n const headerLen = (flags & 0x02) !== 0 ? 9 : 3;\n if (data.length < headerLen) throw new Error(\"QuickLZ: data too short for header\");\n\n let decompressedSize: number;\n if ((flags & 0x02) !== 0) {\n decompressedSize = (data[5]! | (data[6]! << 8) | (data[7]! << 16) | (data[8]! << 24)) >>> 0;\n } else {\n decompressedSize = data[2]!;\n }\n\n return { headerLen, decompressedSize, flags };\n}\n","import { createSocket, type Socket as UdpSocket } from \"node:dgram\";\nimport { isIP } from \"node:net\";\nimport { splitHostPort } from \"../address.js\";\nimport type { Crypt } from \"../crypto/crypt.js\";\nimport type { Logger } from \"../types.js\";\nimport { noopLogger } from \"../types.js\";\nimport {\n type Packet,\n PacketType,\n PacketFlags,\n packetType,\n packetFlags,\n buildC2SHeader,\n parseS2CHeader,\n} from \"./packet.js\";\nimport { GenerationWindow } from \"./generation-window.js\";\nimport { Qlz } from \"./quicklz.js\";\nimport { processInit1 } from \"../handshake/crypt-handshake.js\";\n\nconst MAX_OUT_PACKET_SIZE = 500;\nconst RECEIVE_PACKET_WINDOW_SIZE = 1024;\nconst PING_INTERVAL_MS = 5_000;\nconst PACKET_TIMEOUT_MS = 60_000;\nconst MAX_RETRY_INTERVAL_MS = 1_000;\nconst HEADER_SIZE = 5;\nconst TAG_SIZE = 8;\nconst VOICE_HEADER_SIZE = 3;\nconst WHISPER_HEADER_SIZE = 5;\nconst RESEND_BASE_INTERVAL_MS = 500;\nconst RESEND_LOOP_INTERVAL_MS = 100;\n\ninterface ResendPacket {\n packet: Packet;\n firstSend: number;\n lastSend: number;\n retryCount: number;\n nextInterval: number;\n}\n\nexport class PacketHandler {\n onPacket: ((p: Packet) => void) | null = null;\n onClosed: ((err: Error | null) => void) | null = null;\n\n readonly #crypt: Crypt;\n readonly #logger: Logger;\n\n #conn: UdpSocket | null = null;\n #clientID = 0;\n #closed = false;\n #stopPing: (() => void) | null = null;\n #stopResend: (() => void) | null = null;\n #lastMessageReceived = Date.now();\n\n #packetCounter = new Uint16Array(9);\n #generationCounter = new Uint32Array(9);\n\n #recvWindowCommand = new GenerationWindow(1 << 16, RECEIVE_PACKET_WINDOW_SIZE);\n #recvWindowCommandLow = new GenerationWindow(1 << 16, RECEIVE_PACKET_WINDOW_SIZE);\n #sendWindowCommand = new GenerationWindow(1 << 16, RECEIVE_PACKET_WINDOW_SIZE);\n #sendWindowCommandLow = new GenerationWindow(1 << 16, RECEIVE_PACKET_WINDOW_SIZE);\n\n #commandQueue = new Map<number, Packet>();\n #commandLowQueue = new Map<number, Packet>();\n #ackManager = new Map<number, ResendPacket>();\n #initPacketCheck: ResendPacket | null = null;\n\n constructor(crypt: Crypt, logger: Logger = noopLogger) {\n this.#crypt = crypt;\n this.#logger = logger;\n }\n\n setClientID(id: number): void {\n this.#clientID = id;\n }\n\n connect(addr: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const { host, port } = splitHostPort(addr);\n\n const socket = createSocket(isIP(host) === 6 ? \"udp6\" : \"udp4\");\n const onError = (error: Error) => {\n socket.close();\n reject(error);\n };\n socket.once(\"error\", onError);\n socket.connect(parseInt(port, 10), host, () => {\n socket.off(\"error\", onError);\n this.start(socket);\n resolve();\n });\n });\n }\n\n start(conn: UdpSocket): void {\n this.#conn = conn;\n this.#closed = false;\n this.#lastMessageReceived = Date.now();\n\n conn.on(\"message\", (msg) => {\n this.#lastMessageReceived = Date.now();\n this.#handleRawPacket(new Uint8Array(msg.buffer, msg.byteOffset, msg.byteLength));\n });\n conn.on(\"error\", (err) => {\n this.#logger.error(\"udp error\", err);\n this.#triggerClose(err);\n });\n conn.on(\"close\", () => this.#triggerClose(null));\n\n this.#packetCounter[PacketType.Command] = 1;\n this.#packetCounter[PacketType.Init1] = 101;\n\n const init1Data = processInit1(this.#crypt, null);\n if (init1Data) this.#sendPacketRaw(PacketType.Init1, init1Data, 0);\n\n // Ping loop\n const pingTimer = setInterval(() => {\n if (this.#crypt.cryptoInitComplete) {\n this.sendPacket(PacketType.Ping, new Uint8Array(0), PacketFlags.Unencrypted);\n }\n }, PING_INTERVAL_MS);\n this.#stopPing = () => clearInterval(pingTimer);\n\n // Resend loop\n const resendTimer = setInterval(() => this.#checkResends(), RESEND_LOOP_INTERVAL_MS);\n this.#stopResend = () => clearInterval(resendTimer);\n }\n\n receivedFinalInitAck(): void {\n this.#initPacketCheck = null;\n }\n\n sendPacket(pType: PacketType, data: Uint8Array, flags: number): void {\n const dummy = !this.#crypt.cryptoInitComplete;\n if (data.length > 487 && pType !== PacketType.Voice && pType !== PacketType.VoiceWhisper) {\n this.#sendSplitPacket(pType, data, flags, dummy);\n return;\n }\n this.#sendPacketRaw(pType, data, flags, dummy);\n }\n\n sendVoicePacket(data: Uint8Array, codec: number): void {\n const pID = this.#packetCounter[PacketType.Voice]!;\n const pGen = this.#generationCounter[PacketType.Voice]!;\n this.#packetCounter[PacketType.Voice] = (pID + 1) & 0xffff;\n if (this.#packetCounter[PacketType.Voice] === 0) {\n this.#generationCounter[PacketType.Voice]!++;\n }\n\n const payloadLen = VOICE_HEADER_SIZE + data.length;\n const voicePayload = new Uint8Array(payloadLen);\n new DataView(voicePayload.buffer).setUint16(0, pID, false);\n voicePayload[2] = codec;\n voicePayload.set(data, VOICE_HEADER_SIZE);\n\n const p: Packet = {\n typeFlagged: PacketType.Voice | PacketFlags.Unencrypted,\n id: pID,\n clientID: this.#clientID,\n generationID: pGen,\n data: voicePayload,\n receivedAt: 0,\n };\n\n const header = buildC2SHeader(p);\n const final = new Uint8Array(TAG_SIZE + HEADER_SIZE + payloadLen);\n final.set(this.#crypt.fakeSignature, 0);\n final.set(header, TAG_SIZE);\n final.set(voicePayload, TAG_SIZE + HEADER_SIZE);\n this.#write(final);\n }\n\n /** Send an unencrypted TeamSpeak voice whisper packet to up to 32 clients. */\n sendWhisperPacket(data: Uint8Array, targetClientIds: number[], codec: number): void {\n if (targetClientIds.length === 0) throw new Error(\"whisper requires at least one target\");\n if (targetClientIds.length > 32) throw new Error(\"too many whisper targets\");\n if (targetClientIds.some((id) => !Number.isInteger(id) || id < 0 || id > 0xffff)) {\n throw new RangeError(\"invalid whisper target\");\n }\n\n const pID = this.#packetCounter[PacketType.VoiceWhisper]!;\n const pGen = this.#generationCounter[PacketType.VoiceWhisper]!;\n this.#packetCounter[PacketType.VoiceWhisper] = (pID + 1) & 0xffff;\n if (this.#packetCounter[PacketType.VoiceWhisper] === 0) {\n this.#generationCounter[PacketType.VoiceWhisper] = (pGen + 1) >>> 0;\n }\n\n const payloadLen = WHISPER_HEADER_SIZE + targetClientIds.length * 2 + data.length;\n const whisperPayload = new Uint8Array(payloadLen);\n const view = new DataView(whisperPayload.buffer);\n view.setUint16(0, pID, false);\n whisperPayload[2] = codec;\n whisperPayload[3] = 0;\n whisperPayload[4] = targetClientIds.length;\n for (let index = 0; index < targetClientIds.length; index++) {\n view.setUint16(WHISPER_HEADER_SIZE + index * 2, targetClientIds[index]!, false);\n }\n whisperPayload.set(data, WHISPER_HEADER_SIZE + targetClientIds.length * 2);\n\n const packet: Packet = {\n typeFlagged: PacketType.VoiceWhisper | PacketFlags.Unencrypted,\n id: pID,\n clientID: this.#clientID,\n generationID: pGen,\n data: whisperPayload,\n receivedAt: 0,\n };\n const header = buildC2SHeader(packet);\n const final = new Uint8Array(TAG_SIZE + HEADER_SIZE + payloadLen);\n final.set(this.#crypt.fakeSignature, 0);\n final.set(header, TAG_SIZE);\n final.set(whisperPayload, TAG_SIZE + HEADER_SIZE);\n this.#write(final);\n }\n\n close(): void {\n if (this.#closed) return;\n this.#closed = true;\n this.#stopPing?.();\n this.#stopResend?.();\n this.#conn?.close();\n }\n\n // ---- Private ---------------------------------------------------------------\n\n #sendSplitPacket(pType: PacketType, data: Uint8Array, flags: number, dummy: boolean): void {\n const maxSize = MAX_OUT_PACKET_SIZE - HEADER_SIZE - TAG_SIZE; // 487\n let pos = 0;\n let first = true;\n\n while (pos < data.length) {\n const blockSize = Math.min(data.length - pos, maxSize);\n const last = pos + blockSize === data.length;\n\n let pFlags = flags;\n if (first !== last) pFlags |= PacketFlags.Fragmented;\n\n this.#sendPacketRaw(pType, data.slice(pos, pos + blockSize), pFlags, dummy);\n pos += blockSize;\n first = false;\n }\n }\n\n #sendPacketRaw(\n pType: PacketType,\n data: Uint8Array,\n flags: number,\n dummy = !this.#crypt.cryptoInitComplete,\n ): void {\n flags = applyProtocolFlags(pType, flags);\n const [pID, pGen] = this.#nextPacketIdentity(pType);\n const p: Packet = {\n typeFlagged: pType | flags,\n id: pID,\n clientID: this.#clientID,\n generationID: pGen,\n data,\n receivedAt: 0,\n };\n\n const unencrypted = (flags & PacketFlags.Unencrypted) !== 0;\n const header = buildC2SHeader(p);\n const [ciphertext, tag] = this.#crypt.encrypt(\n pType,\n pID,\n pGen,\n header,\n data,\n dummy,\n unencrypted,\n );\n\n const final = new Uint8Array(TAG_SIZE + HEADER_SIZE + ciphertext.length);\n final.set(tag.slice(0, TAG_SIZE), 0);\n final.set(header, TAG_SIZE);\n final.set(ciphertext, TAG_SIZE + HEADER_SIZE);\n this.#write(final);\n\n const rp: ResendPacket = {\n packet: p,\n firstSend: Date.now(),\n lastSend: Date.now(),\n retryCount: 0,\n nextInterval: RESEND_BASE_INTERVAL_MS,\n };\n this.#trackResend(pType, p, rp);\n }\n\n #write(data: Uint8Array): void {\n if (!this.#conn) return;\n try {\n this.#conn.send(Buffer.from(data), (err) => {\n if (err) this.#logger.warn(\"udp send error\", err);\n });\n } catch (error: unknown) {\n const normalized = error instanceof Error ? error : new Error(String(error));\n // dgram can throw synchronously for an invalid family/address pair. A\n // malformed remote target must close this session, never escape from an\n // async socket callback and terminate the gateway process.\n this.#logger.error(\"udp send failed\", normalized);\n this.#triggerClose(normalized);\n }\n }\n\n #handleRawPacket(raw: Uint8Array): void {\n if (raw.length < 11) return;\n\n const tag = raw.slice(0, TAG_SIZE);\n const header = raw.slice(TAG_SIZE, TAG_SIZE + 3);\n const ciphertext = raw.slice(TAG_SIZE + 3);\n\n const parsed = parseS2CHeader(header);\n const p: Packet = {\n ...parsed,\n clientID: 0,\n generationID: this.#resolveGeneration(parsed.id, parsed.typeFlagged & 0x0f),\n data: new Uint8Array(0),\n receivedAt: Date.now(),\n };\n\n const decrypted = this.#decryptPacketData(p, header, ciphertext, tag);\n if (decrypted === null) return;\n p.data = decrypted.plaintext;\n\n const pType = packetType(p);\n\n if (pType === PacketType.Ping) {\n this.#sendPong(p.id, decrypted.dummyUsed);\n return;\n }\n\n if (!this.#handleCommandWindowAndAck(p, decrypted.dummyUsed)) return;\n\n this.#handlePacketQueue(p);\n this.#updatePostReceiveState(p);\n }\n\n #resolveGeneration(id: number, pType: number): number {\n switch (pType as PacketType) {\n case PacketType.Command:\n return this.#recvWindowCommand.getGeneration(id);\n case PacketType.CommandLow:\n return this.#recvWindowCommandLow.getGeneration(id);\n case PacketType.Ack:\n return this.#sendWindowCommand.getGeneration(id);\n case PacketType.AckLow:\n return this.#sendWindowCommandLow.getGeneration(id);\n default:\n return 0;\n }\n }\n\n #decryptPacketData(\n p: Packet,\n header: Uint8Array,\n ciphertext: Uint8Array,\n tag: Uint8Array,\n ): { plaintext: Uint8Array; dummyUsed: boolean } | null {\n const unencrypted = (packetFlags(p) & PacketFlags.Unencrypted) !== 0;\n const dummy = !this.#crypt.cryptoInitComplete;\n let dummyUsed = dummy;\n const pType = packetType(p);\n const gen = p.generationID;\n\n try {\n const plaintext = this.#crypt.decrypt(\n pType,\n p.id,\n gen,\n header,\n ciphertext,\n tag,\n dummy,\n unencrypted,\n );\n return { plaintext, dummyUsed };\n } catch {\n // Try adjacent generations\n for (const offset of [-1, 1]) {\n const guessGen = gen + offset;\n if (guessGen < 0) continue;\n try {\n const plaintext = this.#crypt.decrypt(\n pType,\n p.id,\n guessGen,\n header,\n ciphertext,\n tag,\n false,\n false,\n );\n return { plaintext, dummyUsed: false };\n } catch {\n // continue\n }\n }\n\n // Try dummy fallback for command/ack types\n if (\n pType === PacketType.Command ||\n pType === PacketType.CommandLow ||\n pType === PacketType.Ack\n ) {\n try {\n const plaintext = this.#crypt.decrypt(\n pType,\n p.id,\n gen,\n header,\n ciphertext,\n tag,\n true,\n unencrypted,\n );\n return { plaintext, dummyUsed: true };\n } catch {\n // fall through\n }\n }\n\n return null;\n }\n }\n\n #handleCommandWindowAndAck(p: Packet, dummyUsed: boolean): boolean {\n const pType = packetType(p);\n if (pType !== PacketType.Command && pType !== PacketType.CommandLow) return true;\n\n const win = pType === PacketType.Command ? this.#recvWindowCommand : this.#recvWindowCommandLow;\n const ackType = pType === PacketType.Command ? PacketType.Ack : PacketType.AckLow;\n\n if (!win.isInWindow(p.id)) {\n if (win.isOldPacket(p.id)) {\n this.#sendAck(p.id, ackType, dummyUsed);\n }\n return false;\n }\n this.#sendAck(p.id, ackType, dummyUsed);\n return true;\n }\n\n #sendAck(packetID: number, ackType: PacketType, dummyUsed: boolean): void {\n const ackData = new Uint8Array(2);\n new DataView(ackData.buffer).setUint16(0, packetID, false);\n this.#sendPacketRaw(ackType, ackData, 0, dummyUsed);\n }\n\n #sendPong(pID: number, dummyUsed: boolean): void {\n const pongData = new Uint8Array(2);\n new DataView(pongData.buffer).setUint16(0, pID, false);\n this.#sendPacketRaw(PacketType.Pong, pongData, PacketFlags.Unencrypted, dummyUsed);\n }\n\n #handlePacketQueue(p: Packet): void {\n const pType = packetType(p);\n if (pType !== PacketType.Command && pType !== PacketType.CommandLow) {\n this.onPacket?.(p);\n return;\n }\n\n const isCommand = pType === PacketType.Command;\n const queue = isCommand ? this.#commandQueue : this.#commandLowQueue;\n const win = isCommand ? this.#recvWindowCommand : this.#recvWindowCommandLow;\n\n queue.set(p.id, p);\n\n if (isCommand) {\n this.#fastForwardMissingPackets(queue, win, true);\n } else {\n this.#fastForwardMissingPackets(queue, win, false);\n }\n\n while (true) {\n const current = isCommand ? this.#_nextCommandID : this.#_nextCommandLowID;\n const packet = queue.get(current);\n if (packet === undefined) break;\n\n const result = this.#tryReassemble(packet, queue, current, win);\n if (!result) break;\n\n const [reassembled, newNext] = result;\n if (isCommand) this.#_nextCommandID = newNext;\n else this.#_nextCommandLowID = newNext;\n\n this.#tryDecompress(reassembled);\n this.onPacket?.(reassembled);\n }\n }\n\n #_nextCommandID = 0;\n #_nextCommandLowID = 0;\n\n #fastForwardMissingPackets(\n queue: Map<number, Packet>,\n win: GenerationWindow,\n isCommand: boolean,\n ): void {\n let nextID = isCommand ? this.#_nextCommandID : this.#_nextCommandLowID;\n while (!queue.has(nextID) && hasOldNewerPacket(queue, nextID)) {\n nextID = (nextID + 1) & 0xffff;\n win.advance(1);\n }\n if (isCommand) this.#_nextCommandID = nextID;\n else this.#_nextCommandLowID = nextID;\n }\n\n #tryReassemble(\n startPacket: Packet,\n queue: Map<number, Packet>,\n nextID: number,\n win: GenerationWindow,\n ): [reassembled: Packet, newNextID: number] | null {\n if ((packetFlags(startPacket) & PacketFlags.Fragmented) === 0) {\n queue.delete(nextID);\n win.advance(1);\n return [startPacket, (nextID + 1) & 0xffff];\n }\n\n const fragments: Packet[] = [];\n let totalSize = 0;\n let currID = nextID;\n let startSeen = false;\n let complete = false;\n\n while (true) {\n const frag = queue.get(currID);\n if (!frag) return null;\n fragments.push(frag);\n totalSize += frag.data.length;\n\n const fragmented = (packetFlags(frag) & PacketFlags.Fragmented) !== 0;\n if (!startSeen) {\n startSeen = true;\n if (!fragmented) {\n complete = true;\n break;\n }\n } else if (fragmented) {\n complete = true;\n break;\n }\n currID = (currID + 1) & 0xffff;\n }\n\n if (!complete) return null;\n\n const combined = new Uint8Array(totalSize);\n let pos = 0;\n for (const frag of fragments) {\n combined.set(frag.data, pos);\n pos += frag.data.length;\n queue.delete(nextID);\n win.advance(1);\n nextID = (nextID + 1) & 0xffff;\n }\n\n startPacket.data = combined;\n startPacket.typeFlagged &= ~PacketFlags.Fragmented;\n return [startPacket, nextID];\n }\n\n #tryDecompress(packet: Packet): void {\n if ((packetFlags(packet) & PacketFlags.Compressed) === 0) return;\n try {\n const qlz = new Qlz();\n packet.data = qlz.decompress(packet.data);\n packet.typeFlagged &= ~PacketFlags.Compressed;\n } catch (err) {\n this.#logger.debug(\"decompression failed\", { id: packet.id, err });\n }\n }\n\n #updatePostReceiveState(p: Packet): void {\n const pType = packetType(p);\n\n if (pType === PacketType.Init1) {\n this.#initPacketCheck = null;\n return;\n }\n\n if ((pType === PacketType.Ack || pType === PacketType.AckLow) && p.data.length >= 2) {\n const ackID = new DataView(p.data.buffer, p.data.byteOffset).getUint16(0, false);\n const targetType = pType === PacketType.Ack ? PacketType.Command : PacketType.CommandLow;\n const key = (targetType << 16) | ackID;\n this.#ackManager.delete(key);\n }\n }\n\n #nextPacketIdentity(pType: PacketType): [id: number, gen: number] {\n const pID = this.#packetCounter[pType]!;\n const pGen = this.#generationCounter[pType]!;\n\n if (pType === PacketType.Init1) return [pID, pGen];\n\n this.#packetCounter[pType] = (pID + 1) & 0xffff;\n if (this.#packetCounter[pType] === 0) {\n this.#generationCounter[pType] = (pGen + 1) >>> 0;\n }\n\n if (pType === PacketType.Command) {\n this.#sendWindowCommand.advanceToExcluded(pID);\n } else if (pType === PacketType.CommandLow) {\n this.#sendWindowCommandLow.advanceToExcluded(pID);\n }\n\n return [pID, pGen];\n }\n\n #trackResend(pType: PacketType, p: Packet, rp: ResendPacket): void {\n if (pType === PacketType.Init1) {\n this.#initPacketCheck = rp;\n return;\n }\n if (pType === PacketType.Command || pType === PacketType.CommandLow) {\n const key = (pType << 16) | p.id;\n this.#ackManager.set(key, rp);\n }\n }\n\n #checkResends(): void {\n const now = Date.now();\n\n if (now - this.#lastMessageReceived > PACKET_TIMEOUT_MS) {\n this.#logger.warn(\"idle timeout\");\n this.#triggerClose(new Error(\"idle timeout\"));\n return;\n }\n\n if (this.#initPacketCheck) {\n this.#doResend(this.#initPacketCheck, now);\n }\n\n for (const [key, rp] of this.#ackManager) {\n if (now - rp.firstSend > PACKET_TIMEOUT_MS) {\n this.#ackManager.delete(key);\n this.#triggerClose(new Error(\"packet ack timeout\"));\n return;\n }\n this.#doResend(rp, now);\n }\n }\n\n #doResend(rp: ResendPacket, now: number): void {\n if (now - rp.lastSend < rp.nextInterval) return;\n\n rp.lastSend = now;\n rp.retryCount++;\n rp.nextInterval = Math.min(rp.nextInterval * 2, MAX_RETRY_INTERVAL_MS);\n\n const dummy = !this.#crypt.cryptoInitComplete;\n const unencrypted = (packetFlags(rp.packet) & PacketFlags.Unencrypted) !== 0;\n const header = buildC2SHeader(rp.packet);\n const pType = packetType(rp.packet);\n\n const [ciphertext, tag] = this.#crypt.encrypt(\n pType,\n rp.packet.id,\n rp.packet.generationID,\n header,\n rp.packet.data,\n dummy,\n unencrypted,\n );\n\n const final = new Uint8Array(TAG_SIZE + HEADER_SIZE + ciphertext.length);\n final.set(tag.slice(0, TAG_SIZE), 0);\n final.set(header, TAG_SIZE);\n final.set(ciphertext, TAG_SIZE + HEADER_SIZE);\n this.#write(final);\n }\n\n #triggerClose(err: Error | null): void {\n if (this.#closed) return;\n this.close();\n this.onClosed?.(err);\n }\n}\n\n// ---- Helpers ----------------------------------------------------------------\n\nfunction applyProtocolFlags(pType: PacketType, flags: number): number {\n if (pType === PacketType.Command || pType === PacketType.CommandLow) {\n return flags | PacketFlags.NewProtocol;\n }\n return flags;\n}\n\nfunction hasOldNewerPacket(queue: Map<number, Packet>, nextID: number): boolean {\n const now = Date.now();\n for (const [id, pkg] of queue) {\n const diff = (id - nextID + 0x10000) & 0xffff;\n if (diff < 0x8000 && now - pkg.receivedAt > 5_000) {\n return true;\n }\n }\n return false;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"handler-DuwoO5RC.cjs","names":["noopLogger","splitHostPort","createSocket","isIP","processInit1","require_address","require_crypt_handshake","node_net","node_dgram","PacketType","\"Voice\"","\"VoiceWhisper\"","\"Command\"","\"CommandLow\"","\"Ping\"","\"Pong\"","\"Ack\"","\"AckLow\"","\"Init1\"","PacketFlags","\"Fragmented\"","\"NewProtocol\"","\"Compressed\"","\"Unencrypted\"","packetType","p","packetFlags","isUnencrypted","buildC2SHeader","header","view","parseS2CHeader","raw","parseC2SHeader","GenerationWindow","#mod","mod","#receiveWindow","windowSize","#generation","amount","newBaseOffset","#mappedBaseOffset","genStep","moveDist","mappedValue","maxOffset","#isNextGen","TABLE_SIZE","Qlz","parseQlzHeader","data","dest","decompressedSize","flags","headerLen","#hashtable","state","#ensureControl","#processReference","#processLiteral","st","b1","b2","hash","matchlen","offset","i","end","#updateHashtable","threshold","v","RECEIVE_PACKET_WINDOW_SIZE","PING_INTERVAL_MS","PACKET_TIMEOUT_MS","MAX_RETRY_INTERVAL_MS","TAG_SIZE","VOICE_HEADER_SIZE","WHISPER_HEADER_SIZE","RESEND_BASE_INTERVAL_MS","RESEND_LOOP_INTERVAL_MS","PacketHandler","#crypt","crypt","#logger","logger","#clientID","id","addr","socket","host","onError","reject","error","port","resolve","#conn","conn","#closed","#lastMessageReceived","#handleRawPacket","msg","err","#triggerClose","#packetCounter","init1Data","#sendPacketRaw","pingTimer","#stopPing","resendTimer","#checkResends","#stopResend","#initPacketCheck","dummy","pType","#sendSplitPacket","pID","pGen","#generationCounter","payloadLen","voicePayload","codec","final","#write","targetClientIds","whisperPayload","index","pos","first","blockSize","last","pFlags","applyProtocolFlags","#nextPacketIdentity","unencrypted","ciphertext","tag","rp","#trackResend","normalized","parsed","#resolveGeneration","decrypted","#decryptPacketData","#sendPong","#handleCommandWindowAndAck","#handlePacketQueue","#updatePostReceiveState","#recvWindowCommand","#recvWindowCommandLow","#sendWindowCommand","#sendWindowCommandLow","dummyUsed","gen","guessGen","win","ackType","#sendAck","ackData","packetID","pongData","isCommand","queue","#commandQueue","#commandLowQueue","#fastForwardMissingPackets","current","#_nextCommandID","#_nextCommandLowID","packet","result","#tryReassemble","reassembled","newNext","#tryDecompress","nextID","hasOldNewerPacket","startPacket","fragments","totalSize","currID","startSeen","complete","frag","fragmented","combined","ackID","key","#ackManager","now","#doResend","pkg"],"sources":["../src/transport/packet.ts","../src/transport/generation-window.ts","../src/transport/quicklz.ts","../src/transport/handler.ts"],"sourcesContent":["export const enum PacketType {\n Voice = 0,\n VoiceWhisper = 1,\n Command = 2,\n CommandLow = 3,\n Ping = 4,\n Pong = 5,\n Ack = 6,\n AckLow = 7,\n Init1 = 8,\n}\n\nexport const enum PacketFlags {\n Fragmented = 0x10,\n NewProtocol = 0x20,\n Compressed = 0x40,\n Unencrypted = 0x80,\n}\n\nexport interface Packet {\n /** Type byte combined with flags (low nibble = type, high nibble = flags). */\n typeFlagged: number;\n id: number;\n clientID: number;\n generationID: number;\n data: Uint8Array;\n receivedAt: number; // Date.now()\n}\n\nexport function packetType(p: Packet): PacketType {\n return (p.typeFlagged & 0x0f) as PacketType;\n}\n\nexport function packetFlags(p: Packet): number {\n return p.typeFlagged & 0xf0;\n}\n\nexport function isUnencrypted(p: Packet): boolean {\n return (packetFlags(p) & PacketFlags.Unencrypted) !== 0;\n}\n\n/** Build the 5-byte client-to-server header: [packetID(2), clientID(2), typeFlagged(1)]. */\nexport function buildC2SHeader(p: Packet): Uint8Array {\n const header = new Uint8Array(5);\n const view = new DataView(header.buffer);\n view.setUint16(0, p.id, false);\n view.setUint16(2, p.clientID, false);\n header[4] = p.typeFlagged;\n return header;\n}\n\n/** Parse a 3-byte server-to-client header. */\nexport function parseS2CHeader(raw: Uint8Array): Pick<Packet, \"id\" | \"typeFlagged\"> {\n const view = new DataView(raw.buffer, raw.byteOffset, raw.byteLength);\n return {\n id: view.getUint16(0, false),\n typeFlagged: raw[2]!,\n };\n}\n\n/** Parse a 5-byte client-to-server header. */\nexport function parseC2SHeader(raw: Uint8Array): Pick<Packet, \"id\" | \"clientID\" | \"typeFlagged\"> {\n const view = new DataView(raw.buffer, raw.byteOffset, raw.byteLength);\n return {\n id: view.getUint16(0, false),\n clientID: view.getUint16(2, false),\n typeFlagged: raw[4]!,\n };\n}\n","export class GenerationWindow {\n #mappedBaseOffset = 0;\n #generation = 0;\n readonly #mod: number;\n readonly #receiveWindow: number;\n\n constructor(mod: number, windowSize: number) {\n this.#mod = mod;\n this.#receiveWindow = windowSize;\n }\n\n get generation(): number {\n return this.#generation;\n }\n\n advance(amount: number): void {\n if (amount <= 0) return;\n const newBaseOffset = this.#mappedBaseOffset + amount;\n const genStep = Math.floor(newBaseOffset / this.#mod);\n if (genStep > 0) {\n this.#generation = Math.min(this.#generation + genStep, 0xffff_ffff);\n }\n this.#mappedBaseOffset = newBaseOffset % this.#mod;\n }\n\n advanceToExcluded(mappedValue: number): void {\n let moveDist = mappedValue - this.#mappedBaseOffset;\n if (moveDist < 0) moveDist += this.#mod;\n this.advance(moveDist + 1);\n }\n\n syncTo(mappedValue: number): void {\n let moveDist = mappedValue - this.#mappedBaseOffset;\n if (moveDist < 0) moveDist += this.#mod;\n this.advance(moveDist);\n }\n\n isInWindow(mappedValue: number): boolean {\n const maxOffset = this.#mappedBaseOffset + this.#receiveWindow;\n if (maxOffset < this.#mod) {\n return mappedValue >= this.#mappedBaseOffset && mappedValue < maxOffset;\n }\n return mappedValue >= this.#mappedBaseOffset || mappedValue < maxOffset - this.#mod;\n }\n\n mappedToIndex(mappedValue: number): number {\n if (this.#isNextGen(mappedValue)) {\n return mappedValue + this.#mod - this.#mappedBaseOffset;\n }\n return mappedValue - this.#mappedBaseOffset;\n }\n\n isOldPacket(mappedValue: number): boolean {\n return this.mappedToIndex(mappedValue) < 0;\n }\n\n isFuturePacket(mappedValue: number): boolean {\n return this.mappedToIndex(mappedValue) >= this.#receiveWindow;\n }\n\n #isNextGen(mappedValue: number): boolean {\n return (\n this.#mappedBaseOffset > this.#mod - this.#receiveWindow &&\n mappedValue < this.#mappedBaseOffset + this.#receiveWindow - this.#mod\n );\n }\n\n getGeneration(mappedValue: number): number {\n if (this.#isNextGen(mappedValue)) return this.#generation + 1;\n return this.#generation;\n }\n\n reset(): void {\n this.#mappedBaseOffset = 0;\n this.#generation = 0;\n }\n}\n","const TABLE_SIZE = 4096;\n\ninterface QlzState {\n control: number;\n sourcePos: number;\n destPos: number;\n nextHashed: number;\n}\n\nexport class Qlz {\n #hashtable = new Int32Array(TABLE_SIZE);\n\n decompress(data: Uint8Array): Uint8Array {\n const { headerLen, decompressedSize, flags } = parseQlzHeader(data);\n\n const dest = new Uint8Array(decompressedSize);\n\n if ((flags & 0x01) === 0) {\n dest.set(data.slice(headerLen, headerLen + decompressedSize));\n return dest;\n }\n\n this.#hashtable.fill(0);\n\n const state: QlzState = {\n control: 1,\n sourcePos: headerLen,\n destPos: 0,\n nextHashed: 0,\n };\n\n while (this.#ensureControl(data, state)) {\n if ((state.control & 1) !== 0) {\n if (!this.#processReference(data, dest, state)) break;\n } else {\n if (this.#processLiteral(data, dest, decompressedSize, state)) break;\n }\n }\n\n return dest;\n }\n\n #ensureControl(data: Uint8Array, st: QlzState): boolean {\n if (st.control !== 1) return true;\n if (st.sourcePos + 4 > data.length) return false;\n st.control =\n (data[st.sourcePos]! |\n (data[st.sourcePos + 1]! << 8) |\n (data[st.sourcePos + 2]! << 16) |\n (data[st.sourcePos + 3]! << 24)) >>>\n 0;\n st.sourcePos += 4;\n return true;\n }\n\n #processReference(data: Uint8Array, dest: Uint8Array, st: QlzState): boolean {\n st.control = (st.control >>> 1) >>> 0;\n if (st.sourcePos + 2 > data.length) return false;\n\n const b1 = data[st.sourcePos]!;\n const b2 = data[st.sourcePos + 1]!;\n st.sourcePos += 2;\n\n const hash = (b1 >> 4) | (b2 << 4);\n let matchlen = b1 & 0x0f;\n if (matchlen !== 0) {\n matchlen += 2;\n } else {\n if (st.sourcePos >= data.length) return false;\n matchlen = data[st.sourcePos]!;\n st.sourcePos++;\n }\n\n const offset = this.#hashtable[hash]!;\n for (let i = 0; i < matchlen; i++) {\n if (st.destPos < dest.length && offset + i < st.destPos) {\n dest[st.destPos] = dest[offset + i]!;\n st.destPos++;\n }\n }\n\n const end = st.destPos + 1 - matchlen;\n this.#updateHashtable(dest, st, end);\n st.nextHashed = st.destPos;\n\n return true;\n }\n\n #processLiteral(\n data: Uint8Array,\n dest: Uint8Array,\n decompressedSize: number,\n st: QlzState,\n ): boolean {\n const threshold = Math.max(decompressedSize, 10) - 10;\n if (st.destPos >= threshold) {\n while (st.destPos < decompressedSize) {\n if (st.control === 1) {\n st.sourcePos += 4;\n if (st.sourcePos > data.length) break;\n st.control =\n (data[st.sourcePos - 4]! |\n (data[st.sourcePos - 3]! << 8) |\n (data[st.sourcePos - 2]! << 16) |\n (data[st.sourcePos - 1]! << 24)) >>>\n 0;\n }\n if (st.sourcePos >= data.length) break;\n dest[st.destPos++] = data[st.sourcePos++]!;\n st.control = (st.control >>> 1) >>> 0;\n }\n return true;\n }\n\n if (st.sourcePos >= data.length || st.destPos >= dest.length) return true;\n\n dest[st.destPos++] = data[st.sourcePos++]!;\n st.control = (st.control >>> 1) >>> 0;\n\n const end = Math.max(st.destPos - 2, 0);\n this.#updateHashtable(dest, st, end);\n if (st.nextHashed < end) st.nextHashed = end;\n\n return false;\n }\n\n #updateHashtable(dest: Uint8Array, st: QlzState, end: number): void {\n while (st.nextHashed < end) {\n if (st.nextHashed + 3 > dest.length) break;\n const v =\n (dest[st.nextHashed]! |\n (dest[st.nextHashed + 1]! << 8) |\n (dest[st.nextHashed + 2]! << 16)) >>>\n 0;\n const hash = ((v >> 12) ^ v) & 0xfff;\n this.#hashtable[hash] = st.nextHashed;\n st.nextHashed++;\n }\n }\n}\n\nfunction parseQlzHeader(data: Uint8Array): {\n headerLen: number;\n decompressedSize: number;\n flags: number;\n} {\n if (data.length < 3) throw new Error(\"QuickLZ: data too short\");\n\n const flags = data[0]!;\n const level = (flags >> 2) & 0x03;\n if (level !== 1) throw new Error(\"QuickLZ: only level 1 is supported\");\n\n const headerLen = (flags & 0x02) !== 0 ? 9 : 3;\n if (data.length < headerLen) throw new Error(\"QuickLZ: data too short for header\");\n\n let decompressedSize: number;\n if ((flags & 0x02) !== 0) {\n decompressedSize = (data[5]! | (data[6]! << 8) | (data[7]! << 16) | (data[8]! << 24)) >>> 0;\n } else {\n decompressedSize = data[2]!;\n }\n\n return { headerLen, decompressedSize, flags };\n}\n","import { createSocket, type Socket as UdpSocket } from \"node:dgram\";\nimport { isIP } from \"node:net\";\nimport { splitHostPort } from \"../address.js\";\nimport type { Crypt } from \"../crypto/crypt.js\";\nimport type { Logger } from \"../types.js\";\nimport { noopLogger } from \"../types.js\";\nimport {\n type Packet,\n PacketType,\n PacketFlags,\n packetType,\n packetFlags,\n buildC2SHeader,\n parseS2CHeader,\n} from \"./packet.js\";\nimport { GenerationWindow } from \"./generation-window.js\";\nimport { Qlz } from \"./quicklz.js\";\nimport { processInit1 } from \"../handshake/crypt-handshake.js\";\n\nconst MAX_OUT_PACKET_SIZE = 500;\nconst RECEIVE_PACKET_WINDOW_SIZE = 1024;\nconst PING_INTERVAL_MS = 5_000;\nconst PACKET_TIMEOUT_MS = 60_000;\nconst MAX_RETRY_INTERVAL_MS = 1_000;\nconst HEADER_SIZE = 5;\nconst TAG_SIZE = 8;\nconst VOICE_HEADER_SIZE = 3;\nconst WHISPER_HEADER_SIZE = 5;\nconst RESEND_BASE_INTERVAL_MS = 500;\nconst RESEND_LOOP_INTERVAL_MS = 100;\n\ninterface ResendPacket {\n packet: Packet;\n firstSend: number;\n lastSend: number;\n retryCount: number;\n nextInterval: number;\n}\n\nexport class PacketHandler {\n onPacket: ((p: Packet) => void) | null = null;\n onClosed: ((err: Error | null) => void) | null = null;\n\n readonly #crypt: Crypt;\n readonly #logger: Logger;\n\n #conn: UdpSocket | null = null;\n #clientID = 0;\n #closed = false;\n #stopPing: (() => void) | null = null;\n #stopResend: (() => void) | null = null;\n #lastMessageReceived = Date.now();\n\n #packetCounter = new Uint16Array(9);\n #generationCounter = new Uint32Array(9);\n\n #recvWindowCommand = new GenerationWindow(1 << 16, RECEIVE_PACKET_WINDOW_SIZE);\n #recvWindowCommandLow = new GenerationWindow(1 << 16, RECEIVE_PACKET_WINDOW_SIZE);\n #sendWindowCommand = new GenerationWindow(1 << 16, RECEIVE_PACKET_WINDOW_SIZE);\n #sendWindowCommandLow = new GenerationWindow(1 << 16, RECEIVE_PACKET_WINDOW_SIZE);\n\n #commandQueue = new Map<number, Packet>();\n #commandLowQueue = new Map<number, Packet>();\n #ackManager = new Map<number, ResendPacket>();\n #initPacketCheck: ResendPacket | null = null;\n\n constructor(crypt: Crypt, logger: Logger = noopLogger) {\n this.#crypt = crypt;\n this.#logger = logger;\n }\n\n setClientID(id: number): void {\n this.#clientID = id;\n }\n\n connect(addr: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const { host, port } = splitHostPort(addr);\n\n const socket = createSocket(isIP(host) === 6 ? \"udp6\" : \"udp4\");\n const onError = (error: Error) => {\n socket.close();\n reject(error);\n };\n socket.once(\"error\", onError);\n socket.connect(parseInt(port, 10), host, () => {\n socket.off(\"error\", onError);\n this.start(socket);\n resolve();\n });\n });\n }\n\n start(conn: UdpSocket): void {\n this.#conn = conn;\n this.#closed = false;\n this.#lastMessageReceived = Date.now();\n\n conn.on(\"message\", (msg) => {\n this.#lastMessageReceived = Date.now();\n this.#handleRawPacket(new Uint8Array(msg.buffer, msg.byteOffset, msg.byteLength));\n });\n conn.on(\"error\", (err) => {\n this.#logger.error(\"udp error\", err);\n this.#triggerClose(err);\n });\n conn.on(\"close\", () => this.#triggerClose(null));\n\n this.#packetCounter[PacketType.Command] = 1;\n this.#packetCounter[PacketType.Init1] = 101;\n\n const init1Data = processInit1(this.#crypt, null);\n if (init1Data) this.#sendPacketRaw(PacketType.Init1, init1Data, 0);\n\n // Ping loop\n const pingTimer = setInterval(() => {\n if (this.#crypt.cryptoInitComplete) {\n this.sendPacket(PacketType.Ping, new Uint8Array(0), PacketFlags.Unencrypted);\n }\n }, PING_INTERVAL_MS);\n this.#stopPing = () => clearInterval(pingTimer);\n\n // Resend loop\n const resendTimer = setInterval(() => this.#checkResends(), RESEND_LOOP_INTERVAL_MS);\n this.#stopResend = () => clearInterval(resendTimer);\n }\n\n receivedFinalInitAck(): void {\n this.#initPacketCheck = null;\n }\n\n sendPacket(pType: PacketType, data: Uint8Array, flags: number): void {\n const dummy = !this.#crypt.cryptoInitComplete;\n if (data.length > 487 && pType !== PacketType.Voice && pType !== PacketType.VoiceWhisper) {\n this.#sendSplitPacket(pType, data, flags, dummy);\n return;\n }\n this.#sendPacketRaw(pType, data, flags, dummy);\n }\n\n sendVoicePacket(data: Uint8Array, codec: number): void {\n const pID = this.#packetCounter[PacketType.Voice]!;\n const pGen = this.#generationCounter[PacketType.Voice]!;\n this.#packetCounter[PacketType.Voice] = (pID + 1) & 0xffff;\n if (this.#packetCounter[PacketType.Voice] === 0) {\n this.#generationCounter[PacketType.Voice]!++;\n }\n\n const payloadLen = VOICE_HEADER_SIZE + data.length;\n const voicePayload = new Uint8Array(payloadLen);\n new DataView(voicePayload.buffer).setUint16(0, pID, false);\n voicePayload[2] = codec;\n voicePayload.set(data, VOICE_HEADER_SIZE);\n\n const p: Packet = {\n typeFlagged: PacketType.Voice | PacketFlags.Unencrypted,\n id: pID,\n clientID: this.#clientID,\n generationID: pGen,\n data: voicePayload,\n receivedAt: 0,\n };\n\n const header = buildC2SHeader(p);\n const final = new Uint8Array(TAG_SIZE + HEADER_SIZE + payloadLen);\n final.set(this.#crypt.fakeSignature, 0);\n final.set(header, TAG_SIZE);\n final.set(voicePayload, TAG_SIZE + HEADER_SIZE);\n this.#write(final);\n }\n\n /** Send an unencrypted TeamSpeak voice whisper packet to up to 32 clients. */\n sendWhisperPacket(data: Uint8Array, targetClientIds: number[], codec: number): void {\n if (targetClientIds.length === 0) throw new Error(\"whisper requires at least one target\");\n if (targetClientIds.length > 32) throw new Error(\"too many whisper targets\");\n if (targetClientIds.some((id) => !Number.isInteger(id) || id < 0 || id > 0xffff)) {\n throw new RangeError(\"invalid whisper target\");\n }\n\n const pID = this.#packetCounter[PacketType.VoiceWhisper]!;\n const pGen = this.#generationCounter[PacketType.VoiceWhisper]!;\n this.#packetCounter[PacketType.VoiceWhisper] = (pID + 1) & 0xffff;\n if (this.#packetCounter[PacketType.VoiceWhisper] === 0) {\n this.#generationCounter[PacketType.VoiceWhisper] = (pGen + 1) >>> 0;\n }\n\n const payloadLen = WHISPER_HEADER_SIZE + targetClientIds.length * 2 + data.length;\n const whisperPayload = new Uint8Array(payloadLen);\n const view = new DataView(whisperPayload.buffer);\n view.setUint16(0, pID, false);\n whisperPayload[2] = codec;\n whisperPayload[3] = 0;\n whisperPayload[4] = targetClientIds.length;\n for (let index = 0; index < targetClientIds.length; index++) {\n view.setUint16(WHISPER_HEADER_SIZE + index * 2, targetClientIds[index]!, false);\n }\n whisperPayload.set(data, WHISPER_HEADER_SIZE + targetClientIds.length * 2);\n\n const packet: Packet = {\n typeFlagged: PacketType.VoiceWhisper | PacketFlags.Unencrypted,\n id: pID,\n clientID: this.#clientID,\n generationID: pGen,\n data: whisperPayload,\n receivedAt: 0,\n };\n const header = buildC2SHeader(packet);\n const final = new Uint8Array(TAG_SIZE + HEADER_SIZE + payloadLen);\n final.set(this.#crypt.fakeSignature, 0);\n final.set(header, TAG_SIZE);\n final.set(whisperPayload, TAG_SIZE + HEADER_SIZE);\n this.#write(final);\n }\n\n close(): void {\n if (this.#closed) return;\n this.#closed = true;\n this.#stopPing?.();\n this.#stopResend?.();\n this.#conn?.close();\n }\n\n // ---- Private ---------------------------------------------------------------\n\n #sendSplitPacket(pType: PacketType, data: Uint8Array, flags: number, dummy: boolean): void {\n const maxSize = MAX_OUT_PACKET_SIZE - HEADER_SIZE - TAG_SIZE; // 487\n let pos = 0;\n let first = true;\n\n while (pos < data.length) {\n const blockSize = Math.min(data.length - pos, maxSize);\n const last = pos + blockSize === data.length;\n\n let pFlags = flags;\n if (first !== last) pFlags |= PacketFlags.Fragmented;\n\n this.#sendPacketRaw(pType, data.slice(pos, pos + blockSize), pFlags, dummy);\n pos += blockSize;\n first = false;\n }\n }\n\n #sendPacketRaw(\n pType: PacketType,\n data: Uint8Array,\n flags: number,\n dummy = !this.#crypt.cryptoInitComplete,\n ): void {\n flags = applyProtocolFlags(pType, flags);\n const [pID, pGen] = this.#nextPacketIdentity(pType);\n const p: Packet = {\n typeFlagged: pType | flags,\n id: pID,\n clientID: this.#clientID,\n generationID: pGen,\n data,\n receivedAt: 0,\n };\n\n const unencrypted = (flags & PacketFlags.Unencrypted) !== 0;\n const header = buildC2SHeader(p);\n const [ciphertext, tag] = this.#crypt.encrypt(\n pType,\n pID,\n pGen,\n header,\n data,\n dummy,\n unencrypted,\n );\n\n const final = new Uint8Array(TAG_SIZE + HEADER_SIZE + ciphertext.length);\n final.set(tag.slice(0, TAG_SIZE), 0);\n final.set(header, TAG_SIZE);\n final.set(ciphertext, TAG_SIZE + HEADER_SIZE);\n this.#write(final);\n\n const rp: ResendPacket = {\n packet: p,\n firstSend: Date.now(),\n lastSend: Date.now(),\n retryCount: 0,\n nextInterval: RESEND_BASE_INTERVAL_MS,\n };\n this.#trackResend(pType, p, rp);\n }\n\n #write(data: Uint8Array): void {\n if (!this.#conn) return;\n try {\n this.#conn.send(Buffer.from(data), (err) => {\n if (err) this.#logger.warn(\"udp send error\", err);\n });\n } catch (error: unknown) {\n const normalized = error instanceof Error ? error : new Error(String(error));\n // dgram can throw synchronously for an invalid family/address pair. A\n // malformed remote target must close this session, never escape from an\n // async socket callback and terminate the gateway process.\n this.#logger.error(\"udp send failed\", normalized);\n this.#triggerClose(normalized);\n }\n }\n\n #handleRawPacket(raw: Uint8Array): void {\n if (raw.length < 11) return;\n\n const tag = raw.slice(0, TAG_SIZE);\n const header = raw.slice(TAG_SIZE, TAG_SIZE + 3);\n const ciphertext = raw.slice(TAG_SIZE + 3);\n\n const parsed = parseS2CHeader(header);\n const p: Packet = {\n ...parsed,\n clientID: 0,\n generationID: this.#resolveGeneration(parsed.id, parsed.typeFlagged & 0x0f),\n data: new Uint8Array(0),\n receivedAt: Date.now(),\n };\n\n const decrypted = this.#decryptPacketData(p, header, ciphertext, tag);\n if (decrypted === null) return;\n p.data = decrypted.plaintext;\n\n const pType = packetType(p);\n\n if (pType === PacketType.Ping) {\n this.#sendPong(p.id, decrypted.dummyUsed);\n return;\n }\n\n if (!this.#handleCommandWindowAndAck(p, decrypted.dummyUsed)) return;\n\n this.#handlePacketQueue(p);\n this.#updatePostReceiveState(p);\n }\n\n #resolveGeneration(id: number, pType: number): number {\n switch (pType as PacketType) {\n case PacketType.Command:\n return this.#recvWindowCommand.getGeneration(id);\n case PacketType.CommandLow:\n return this.#recvWindowCommandLow.getGeneration(id);\n case PacketType.Ack:\n return this.#sendWindowCommand.getGeneration(id);\n case PacketType.AckLow:\n return this.#sendWindowCommandLow.getGeneration(id);\n default:\n return 0;\n }\n }\n\n #decryptPacketData(\n p: Packet,\n header: Uint8Array,\n ciphertext: Uint8Array,\n tag: Uint8Array,\n ): { plaintext: Uint8Array; dummyUsed: boolean } | null {\n const unencrypted = (packetFlags(p) & PacketFlags.Unencrypted) !== 0;\n const dummy = !this.#crypt.cryptoInitComplete;\n let dummyUsed = dummy;\n const pType = packetType(p);\n const gen = p.generationID;\n\n try {\n const plaintext = this.#crypt.decrypt(\n pType,\n p.id,\n gen,\n header,\n ciphertext,\n tag,\n dummy,\n unencrypted,\n );\n return { plaintext, dummyUsed };\n } catch {\n // Try adjacent generations\n for (const offset of [-1, 1]) {\n const guessGen = gen + offset;\n if (guessGen < 0) continue;\n try {\n const plaintext = this.#crypt.decrypt(\n pType,\n p.id,\n guessGen,\n header,\n ciphertext,\n tag,\n false,\n false,\n );\n return { plaintext, dummyUsed: false };\n } catch {\n // continue\n }\n }\n\n // Try dummy fallback for command/ack types\n if (\n pType === PacketType.Command ||\n pType === PacketType.CommandLow ||\n pType === PacketType.Ack\n ) {\n try {\n const plaintext = this.#crypt.decrypt(\n pType,\n p.id,\n gen,\n header,\n ciphertext,\n tag,\n true,\n unencrypted,\n );\n return { plaintext, dummyUsed: true };\n } catch {\n // fall through\n }\n }\n\n return null;\n }\n }\n\n #handleCommandWindowAndAck(p: Packet, dummyUsed: boolean): boolean {\n const pType = packetType(p);\n if (pType !== PacketType.Command && pType !== PacketType.CommandLow) return true;\n\n const win = pType === PacketType.Command ? this.#recvWindowCommand : this.#recvWindowCommandLow;\n const ackType = pType === PacketType.Command ? PacketType.Ack : PacketType.AckLow;\n\n if (!win.isInWindow(p.id)) {\n if (win.isOldPacket(p.id)) {\n this.#sendAck(p.id, ackType, dummyUsed);\n }\n return false;\n }\n this.#sendAck(p.id, ackType, dummyUsed);\n return true;\n }\n\n #sendAck(packetID: number, ackType: PacketType, dummyUsed: boolean): void {\n const ackData = new Uint8Array(2);\n new DataView(ackData.buffer).setUint16(0, packetID, false);\n this.#sendPacketRaw(ackType, ackData, 0, dummyUsed);\n }\n\n #sendPong(pID: number, dummyUsed: boolean): void {\n const pongData = new Uint8Array(2);\n new DataView(pongData.buffer).setUint16(0, pID, false);\n this.#sendPacketRaw(PacketType.Pong, pongData, PacketFlags.Unencrypted, dummyUsed);\n }\n\n #handlePacketQueue(p: Packet): void {\n const pType = packetType(p);\n if (pType !== PacketType.Command && pType !== PacketType.CommandLow) {\n this.onPacket?.(p);\n return;\n }\n\n const isCommand = pType === PacketType.Command;\n const queue = isCommand ? this.#commandQueue : this.#commandLowQueue;\n const win = isCommand ? this.#recvWindowCommand : this.#recvWindowCommandLow;\n\n queue.set(p.id, p);\n\n if (isCommand) {\n this.#fastForwardMissingPackets(queue, win, true);\n } else {\n this.#fastForwardMissingPackets(queue, win, false);\n }\n\n while (true) {\n const current = isCommand ? this.#_nextCommandID : this.#_nextCommandLowID;\n const packet = queue.get(current);\n if (packet === undefined) break;\n\n const result = this.#tryReassemble(packet, queue, current, win);\n if (!result) break;\n\n const [reassembled, newNext] = result;\n if (isCommand) this.#_nextCommandID = newNext;\n else this.#_nextCommandLowID = newNext;\n\n this.#tryDecompress(reassembled);\n this.onPacket?.(reassembled);\n }\n }\n\n #_nextCommandID = 0;\n #_nextCommandLowID = 0;\n\n #fastForwardMissingPackets(\n queue: Map<number, Packet>,\n win: GenerationWindow,\n isCommand: boolean,\n ): void {\n let nextID = isCommand ? this.#_nextCommandID : this.#_nextCommandLowID;\n while (!queue.has(nextID) && hasOldNewerPacket(queue, nextID)) {\n nextID = (nextID + 1) & 0xffff;\n win.advance(1);\n }\n if (isCommand) this.#_nextCommandID = nextID;\n else this.#_nextCommandLowID = nextID;\n }\n\n #tryReassemble(\n startPacket: Packet,\n queue: Map<number, Packet>,\n nextID: number,\n win: GenerationWindow,\n ): [reassembled: Packet, newNextID: number] | null {\n if ((packetFlags(startPacket) & PacketFlags.Fragmented) === 0) {\n queue.delete(nextID);\n win.advance(1);\n return [startPacket, (nextID + 1) & 0xffff];\n }\n\n const fragments: Packet[] = [];\n let totalSize = 0;\n let currID = nextID;\n let startSeen = false;\n let complete = false;\n\n while (true) {\n const frag = queue.get(currID);\n if (!frag) return null;\n fragments.push(frag);\n totalSize += frag.data.length;\n\n const fragmented = (packetFlags(frag) & PacketFlags.Fragmented) !== 0;\n if (!startSeen) {\n startSeen = true;\n if (!fragmented) {\n complete = true;\n break;\n }\n } else if (fragmented) {\n complete = true;\n break;\n }\n currID = (currID + 1) & 0xffff;\n }\n\n if (!complete) return null;\n\n const combined = new Uint8Array(totalSize);\n let pos = 0;\n for (const frag of fragments) {\n combined.set(frag.data, pos);\n pos += frag.data.length;\n queue.delete(nextID);\n win.advance(1);\n nextID = (nextID + 1) & 0xffff;\n }\n\n startPacket.data = combined;\n startPacket.typeFlagged &= ~PacketFlags.Fragmented;\n return [startPacket, nextID];\n }\n\n #tryDecompress(packet: Packet): void {\n if ((packetFlags(packet) & PacketFlags.Compressed) === 0) return;\n try {\n const qlz = new Qlz();\n packet.data = qlz.decompress(packet.data);\n packet.typeFlagged &= ~PacketFlags.Compressed;\n } catch (err) {\n this.#logger.debug(\"decompression failed\", { id: packet.id, err });\n }\n }\n\n #updatePostReceiveState(p: Packet): void {\n const pType = packetType(p);\n\n if (pType === PacketType.Init1) {\n this.#initPacketCheck = null;\n return;\n }\n\n if ((pType === PacketType.Ack || pType === PacketType.AckLow) && p.data.length >= 2) {\n const ackID = new DataView(p.data.buffer, p.data.byteOffset).getUint16(0, false);\n const targetType = pType === PacketType.Ack ? PacketType.Command : PacketType.CommandLow;\n const key = (targetType << 16) | ackID;\n this.#ackManager.delete(key);\n }\n }\n\n #nextPacketIdentity(pType: PacketType): [id: number, gen: number] {\n const pID = this.#packetCounter[pType]!;\n const pGen = this.#generationCounter[pType]!;\n\n if (pType === PacketType.Init1) return [pID, pGen];\n\n this.#packetCounter[pType] = (pID + 1) & 0xffff;\n if (this.#packetCounter[pType] === 0) {\n this.#generationCounter[pType] = (pGen + 1) >>> 0;\n }\n\n if (pType === PacketType.Command) {\n this.#sendWindowCommand.advanceToExcluded(pID);\n } else if (pType === PacketType.CommandLow) {\n this.#sendWindowCommandLow.advanceToExcluded(pID);\n }\n\n return [pID, pGen];\n }\n\n #trackResend(pType: PacketType, p: Packet, rp: ResendPacket): void {\n if (pType === PacketType.Init1) {\n this.#initPacketCheck = rp;\n return;\n }\n if (pType === PacketType.Command || pType === PacketType.CommandLow) {\n const key = (pType << 16) | p.id;\n this.#ackManager.set(key, rp);\n }\n }\n\n #checkResends(): void {\n const now = Date.now();\n\n if (now - this.#lastMessageReceived > PACKET_TIMEOUT_MS) {\n this.#logger.warn(\"idle timeout\");\n this.#triggerClose(new Error(\"idle timeout\"));\n return;\n }\n\n if (this.#initPacketCheck) {\n this.#doResend(this.#initPacketCheck, now);\n }\n\n for (const [key, rp] of this.#ackManager) {\n if (now - rp.firstSend > PACKET_TIMEOUT_MS) {\n this.#ackManager.delete(key);\n this.#triggerClose(new Error(\"packet ack timeout\"));\n return;\n }\n this.#doResend(rp, now);\n }\n }\n\n #doResend(rp: ResendPacket, now: number): void {\n if (now - rp.lastSend < rp.nextInterval) return;\n\n rp.lastSend = now;\n rp.retryCount++;\n rp.nextInterval = Math.min(rp.nextInterval * 2, MAX_RETRY_INTERVAL_MS);\n\n const dummy = !this.#crypt.cryptoInitComplete;\n const unencrypted = (packetFlags(rp.packet) & PacketFlags.Unencrypted) !== 0;\n const header = buildC2SHeader(rp.packet);\n const pType = packetType(rp.packet);\n\n const [ciphertext, tag] = this.#crypt.encrypt(\n pType,\n rp.packet.id,\n rp.packet.generationID,\n header,\n rp.packet.data,\n dummy,\n unencrypted,\n );\n\n const final = new Uint8Array(TAG_SIZE + HEADER_SIZE + ciphertext.length);\n final.set(tag.slice(0, TAG_SIZE), 0);\n final.set(header, TAG_SIZE);\n final.set(ciphertext, TAG_SIZE + HEADER_SIZE);\n this.#write(final);\n }\n\n #triggerClose(err: Error | null): void {\n if (this.#closed) return;\n this.close();\n this.onClosed?.(err);\n }\n}\n\n// ---- Helpers ----------------------------------------------------------------\n\nfunction applyProtocolFlags(pType: PacketType, flags: number): number {\n if (pType === PacketType.Command || pType === PacketType.CommandLow) {\n return flags | PacketFlags.NewProtocol;\n }\n return flags;\n}\n\nfunction hasOldNewerPacket(queue: Map<number, Packet>, nextID: number): boolean {\n const now = Date.now();\n for (const [id, pkg] of queue) {\n const diff = (id - nextID + 0x10000) & 0xffff;\n if (diff < 0x8000 && now - pkg.receivedAt > 5_000) {\n return true;\n }\n }\n return false;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"license.d.ts","sourceRoot":"","sources":["../../src/handshake/license.ts"],"names":[],"mappings":"AAUA,mBAAW,gBAAgB;IACzB,YAAY,IAAI;IAChB,MAAM,IAAI;IACV,SAAS,IAAI;IACb,SAAS,KAAK;CACf;AAED,UAAU,YAAY;IACpB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,UAAU,EAAE,UAAU,EAAE,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,IAAI,CAAC;IACrB,aAAa,EAAE,IAAI,CAAC;IACpB,SAAS,EAAE,gBAAgB,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,YAAY;IACvB,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC;gBAEpB,MAAM,EAAE,YAAY,EAAE;IAIlC;;;OAGG;IACH,SAAS,IAAI,UAAU,CAAC,WAAW,CAAC;CAOrC;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,YAAY,CAc5D"}
1
+ {"version":3,"file":"license.d.ts","sourceRoot":"","sources":["../../src/handshake/license.ts"],"names":[],"mappings":"AAUA,mBAAW,gBAAgB;IACzB,YAAY,IAAI;IAChB,MAAM,IAAI;IACV,SAAS,IAAI;IACb,SAAS,KAAK;CACf;AAED,UAAU,YAAY;IACpB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,UAAU,EAAE,UAAU,EAAE,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,IAAI,CAAC;IACrB,aAAa,EAAE,IAAI,CAAC;IACpB,SAAS,EAAE,gBAAgB,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,YAAY;IACvB,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC;IAEhC,YAAY,MAAM,EAAE,YAAY,EAAE,EAEjC;IAED;;;OAGG;IACH,SAAS,IAAI,UAAU,CAAC,WAAW,CAAC,CAMnC;CACF;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,YAAY,CAc5D"}