@push.rocks/smartproxy 12.0.0 → 13.1.2

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 (258) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/certificate/acme/acme-factory.d.ts +17 -0
  3. package/dist_ts/certificate/acme/acme-factory.js +40 -0
  4. package/dist_ts/certificate/acme/challenge-handler.d.ts +44 -0
  5. package/dist_ts/certificate/acme/challenge-handler.js +92 -0
  6. package/dist_ts/certificate/acme/index.d.ts +4 -0
  7. package/dist_ts/certificate/acme/index.js +5 -0
  8. package/dist_ts/certificate/events/certificate-events.d.ts +33 -0
  9. package/dist_ts/certificate/events/certificate-events.js +38 -0
  10. package/dist_ts/certificate/index.d.ts +24 -0
  11. package/dist_ts/certificate/index.js +39 -0
  12. package/dist_ts/certificate/models/certificate-types.d.ts +77 -0
  13. package/dist_ts/certificate/models/certificate-types.js +2 -0
  14. package/dist_ts/certificate/providers/cert-provisioner.d.ts +93 -0
  15. package/dist_ts/certificate/providers/cert-provisioner.js +262 -0
  16. package/dist_ts/certificate/providers/index.d.ts +4 -0
  17. package/dist_ts/certificate/providers/index.js +5 -0
  18. package/dist_ts/certificate/storage/file-storage.d.ts +66 -0
  19. package/dist_ts/certificate/storage/file-storage.js +194 -0
  20. package/dist_ts/certificate/storage/index.d.ts +4 -0
  21. package/dist_ts/certificate/storage/index.js +5 -0
  22. package/dist_ts/certificate/utils/certificate-helpers.d.ts +17 -0
  23. package/dist_ts/certificate/utils/certificate-helpers.js +45 -0
  24. package/dist_ts/common/eventUtils.d.ts +1 -1
  25. package/dist_ts/common/port80-adapter.d.ts +1 -1
  26. package/dist_ts/core/events/index.d.ts +4 -0
  27. package/dist_ts/core/events/index.js +5 -0
  28. package/dist_ts/core/index.d.ts +6 -0
  29. package/dist_ts/core/index.js +8 -0
  30. package/dist_ts/core/models/common-types.d.ts +82 -0
  31. package/dist_ts/core/models/common-types.js +15 -0
  32. package/dist_ts/core/models/index.d.ts +4 -0
  33. package/dist_ts/core/models/index.js +5 -0
  34. package/dist_ts/core/utils/event-utils.d.ts +15 -0
  35. package/dist_ts/core/utils/event-utils.js +19 -0
  36. package/dist_ts/core/utils/index.d.ts +6 -0
  37. package/dist_ts/core/utils/index.js +7 -0
  38. package/dist_ts/core/utils/ip-utils.d.ts +53 -0
  39. package/dist_ts/core/utils/ip-utils.js +153 -0
  40. package/dist_ts/core/utils/validation-utils.d.ts +61 -0
  41. package/dist_ts/core/utils/validation-utils.js +149 -0
  42. package/dist_ts/forwarding/config/domain-config.d.ts +12 -0
  43. package/dist_ts/forwarding/config/domain-config.js +12 -0
  44. package/dist_ts/forwarding/config/domain-manager.d.ts +86 -0
  45. package/dist_ts/forwarding/config/domain-manager.js +242 -0
  46. package/dist_ts/forwarding/config/forwarding-types.d.ts +104 -0
  47. package/dist_ts/forwarding/config/forwarding-types.js +50 -0
  48. package/dist_ts/forwarding/config/index.d.ts +6 -0
  49. package/dist_ts/forwarding/config/index.js +7 -0
  50. package/dist_ts/forwarding/factory/forwarding-factory.d.ts +25 -0
  51. package/dist_ts/forwarding/factory/forwarding-factory.js +138 -0
  52. package/dist_ts/forwarding/factory/index.d.ts +4 -0
  53. package/dist_ts/forwarding/factory/index.js +5 -0
  54. package/dist_ts/forwarding/handlers/base-handler.d.ts +55 -0
  55. package/dist_ts/forwarding/handlers/base-handler.js +94 -0
  56. package/dist_ts/forwarding/handlers/http-handler.d.ts +30 -0
  57. package/dist_ts/forwarding/handlers/http-handler.js +131 -0
  58. package/dist_ts/forwarding/handlers/https-passthrough-handler.d.ts +29 -0
  59. package/dist_ts/forwarding/handlers/https-passthrough-handler.js +162 -0
  60. package/dist_ts/forwarding/handlers/https-terminate-to-http-handler.d.ts +36 -0
  61. package/dist_ts/forwarding/handlers/https-terminate-to-http-handler.js +229 -0
  62. package/dist_ts/forwarding/handlers/https-terminate-to-https-handler.d.ts +35 -0
  63. package/dist_ts/forwarding/handlers/https-terminate-to-https-handler.js +254 -0
  64. package/dist_ts/forwarding/handlers/index.d.ts +8 -0
  65. package/dist_ts/forwarding/handlers/index.js +9 -0
  66. package/dist_ts/forwarding/index.d.ts +19 -0
  67. package/dist_ts/forwarding/index.js +25 -0
  68. package/dist_ts/http/index.d.ts +15 -0
  69. package/dist_ts/http/index.js +20 -0
  70. package/dist_ts/http/models/http-types.d.ts +81 -0
  71. package/dist_ts/http/models/http-types.js +62 -0
  72. package/dist_ts/http/port80/acme-interfaces.d.ts +78 -0
  73. package/dist_ts/http/port80/acme-interfaces.js +6 -0
  74. package/dist_ts/http/port80/challenge-responder.d.ts +53 -0
  75. package/dist_ts/http/port80/challenge-responder.js +203 -0
  76. package/dist_ts/http/port80/index.d.ts +6 -0
  77. package/dist_ts/http/port80/index.js +9 -0
  78. package/dist_ts/http/port80/port80-handler.d.ts +121 -0
  79. package/dist_ts/http/port80/port80-handler.js +554 -0
  80. package/dist_ts/http/redirects/index.d.ts +4 -0
  81. package/dist_ts/http/redirects/index.js +5 -0
  82. package/dist_ts/http/router/index.d.ts +4 -0
  83. package/dist_ts/http/router/index.js +5 -0
  84. package/dist_ts/http/router/proxy-router.d.ts +115 -0
  85. package/dist_ts/http/router/proxy-router.js +325 -0
  86. package/dist_ts/index.d.ts +15 -8
  87. package/dist_ts/index.js +26 -10
  88. package/dist_ts/networkproxy/classes.np.certificatemanager.js +2 -2
  89. package/dist_ts/networkproxy/index.d.ts +1 -6
  90. package/dist_ts/networkproxy/index.js +4 -8
  91. package/dist_ts/plugins.d.ts +2 -1
  92. package/dist_ts/plugins.js +3 -2
  93. package/dist_ts/port80handler/classes.port80handler.d.ts +8 -136
  94. package/dist_ts/port80handler/classes.port80handler.js +14 -567
  95. package/dist_ts/proxies/index.d.ts +6 -0
  96. package/dist_ts/proxies/index.js +8 -0
  97. package/dist_ts/proxies/network-proxy/certificate-manager.d.ts +77 -0
  98. package/dist_ts/proxies/network-proxy/certificate-manager.js +373 -0
  99. package/dist_ts/proxies/network-proxy/connection-pool.d.ts +47 -0
  100. package/dist_ts/proxies/network-proxy/connection-pool.js +210 -0
  101. package/dist_ts/proxies/network-proxy/index.d.ts +10 -0
  102. package/dist_ts/proxies/network-proxy/index.js +12 -0
  103. package/dist_ts/proxies/network-proxy/models/index.d.ts +4 -0
  104. package/dist_ts/proxies/network-proxy/models/index.js +5 -0
  105. package/dist_ts/proxies/network-proxy/models/types.d.ts +80 -0
  106. package/dist_ts/proxies/network-proxy/models/types.js +35 -0
  107. package/dist_ts/proxies/network-proxy/network-proxy.d.ts +118 -0
  108. package/dist_ts/proxies/network-proxy/network-proxy.js +387 -0
  109. package/dist_ts/proxies/network-proxy/request-handler.d.ts +57 -0
  110. package/dist_ts/proxies/network-proxy/request-handler.js +394 -0
  111. package/dist_ts/proxies/network-proxy/websocket-handler.d.ts +38 -0
  112. package/dist_ts/proxies/network-proxy/websocket-handler.js +188 -0
  113. package/dist_ts/proxies/nftables-proxy/index.d.ts +5 -0
  114. package/dist_ts/proxies/nftables-proxy/index.js +6 -0
  115. package/dist_ts/proxies/nftables-proxy/models/errors.d.ts +15 -0
  116. package/dist_ts/proxies/nftables-proxy/models/errors.js +28 -0
  117. package/dist_ts/proxies/nftables-proxy/models/index.d.ts +5 -0
  118. package/dist_ts/proxies/nftables-proxy/models/index.js +6 -0
  119. package/dist_ts/proxies/nftables-proxy/models/interfaces.d.ts +75 -0
  120. package/dist_ts/proxies/nftables-proxy/models/interfaces.js +5 -0
  121. package/dist_ts/proxies/nftables-proxy/nftables-proxy.d.ts +136 -0
  122. package/dist_ts/proxies/nftables-proxy/nftables-proxy.js +1516 -0
  123. package/dist_ts/proxies/smart-proxy/connection-handler.d.ts +39 -0
  124. package/dist_ts/proxies/smart-proxy/connection-handler.js +894 -0
  125. package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +78 -0
  126. package/dist_ts/proxies/smart-proxy/connection-manager.js +378 -0
  127. package/dist_ts/proxies/smart-proxy/domain-config-manager.d.ts +95 -0
  128. package/dist_ts/proxies/smart-proxy/domain-config-manager.js +255 -0
  129. package/dist_ts/proxies/smart-proxy/index.d.ts +13 -0
  130. package/dist_ts/proxies/smart-proxy/index.js +17 -0
  131. package/dist_ts/proxies/smart-proxy/models/index.d.ts +4 -0
  132. package/dist_ts/proxies/smart-proxy/models/index.js +5 -0
  133. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +107 -0
  134. package/dist_ts/proxies/smart-proxy/models/interfaces.js +2 -0
  135. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.d.ts +62 -0
  136. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.js +316 -0
  137. package/dist_ts/proxies/smart-proxy/port-range-manager.d.ts +56 -0
  138. package/dist_ts/proxies/smart-proxy/port-range-manager.js +176 -0
  139. package/dist_ts/proxies/smart-proxy/security-manager.d.ts +64 -0
  140. package/dist_ts/proxies/smart-proxy/security-manager.js +149 -0
  141. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +63 -0
  142. package/dist_ts/proxies/smart-proxy/smart-proxy.js +523 -0
  143. package/dist_ts/proxies/smart-proxy/timeout-manager.d.ts +47 -0
  144. package/dist_ts/proxies/smart-proxy/timeout-manager.js +154 -0
  145. package/dist_ts/proxies/smart-proxy/tls-manager.d.ts +57 -0
  146. package/dist_ts/proxies/smart-proxy/tls-manager.js +132 -0
  147. package/dist_ts/smartproxy/classes.pp.networkproxybridge.d.ts +2 -2
  148. package/dist_ts/smartproxy/classes.pp.networkproxybridge.js +1 -1
  149. package/dist_ts/smartproxy/classes.pp.tlsmanager.js +2 -2
  150. package/dist_ts/smartproxy/classes.smartproxy.js +3 -3
  151. package/dist_ts/tls/alerts/index.d.ts +4 -0
  152. package/dist_ts/tls/alerts/index.js +5 -0
  153. package/dist_ts/tls/alerts/tls-alert.d.ts +150 -0
  154. package/dist_ts/tls/alerts/tls-alert.js +226 -0
  155. package/dist_ts/tls/index.d.ts +18 -0
  156. package/dist_ts/tls/index.js +27 -0
  157. package/dist_ts/tls/sni/client-hello-parser.d.ts +100 -0
  158. package/dist_ts/tls/sni/client-hello-parser.js +463 -0
  159. package/dist_ts/tls/sni/index.d.ts +4 -0
  160. package/dist_ts/tls/sni/index.js +5 -0
  161. package/dist_ts/tls/sni/sni-extraction.d.ts +58 -0
  162. package/dist_ts/tls/sni/sni-extraction.js +275 -0
  163. package/dist_ts/tls/sni/sni-handler.d.ts +154 -0
  164. package/dist_ts/tls/sni/sni-handler.js +191 -0
  165. package/dist_ts/tls/utils/index.d.ts +4 -0
  166. package/dist_ts/tls/utils/index.js +5 -0
  167. package/dist_ts/tls/utils/tls-utils.d.ts +158 -0
  168. package/dist_ts/tls/utils/tls-utils.js +187 -0
  169. package/package.json +1 -1
  170. package/readme.md +89 -21
  171. package/readme.plan.md +253 -469
  172. package/ts/00_commitinfo_data.ts +1 -1
  173. package/ts/certificate/acme/acme-factory.ts +48 -0
  174. package/ts/certificate/acme/challenge-handler.ts +110 -0
  175. package/ts/certificate/acme/index.ts +3 -0
  176. package/ts/certificate/events/certificate-events.ts +36 -0
  177. package/ts/certificate/index.ts +67 -0
  178. package/ts/certificate/models/certificate-types.ts +88 -0
  179. package/ts/certificate/providers/cert-provisioner.ts +326 -0
  180. package/ts/certificate/providers/index.ts +3 -0
  181. package/ts/certificate/storage/file-storage.ts +234 -0
  182. package/ts/certificate/storage/index.ts +3 -0
  183. package/ts/certificate/utils/certificate-helpers.ts +50 -0
  184. package/ts/common/eventUtils.ts +1 -1
  185. package/ts/common/port80-adapter.ts +1 -1
  186. package/ts/core/events/index.ts +3 -0
  187. package/ts/core/index.ts +8 -0
  188. package/ts/core/models/common-types.ts +91 -0
  189. package/ts/core/models/index.ts +5 -0
  190. package/ts/core/utils/event-utils.ts +34 -0
  191. package/ts/core/utils/index.ts +7 -0
  192. package/ts/core/utils/ip-utils.ts +175 -0
  193. package/ts/core/utils/validation-utils.ts +177 -0
  194. package/ts/{smartproxy/forwarding → forwarding/config}/domain-config.ts +1 -1
  195. package/ts/{smartproxy/forwarding → forwarding/config}/domain-manager.ts +8 -8
  196. package/ts/{smartproxy/types/forwarding.types.ts → forwarding/config/forwarding-types.ts} +6 -6
  197. package/ts/forwarding/config/index.ts +7 -0
  198. package/ts/{smartproxy/forwarding/forwarding.factory.ts → forwarding/factory/forwarding-factory.ts} +12 -11
  199. package/ts/forwarding/factory/index.ts +5 -0
  200. package/ts/{smartproxy/forwarding/forwarding.handler.ts → forwarding/handlers/base-handler.ts} +2 -2
  201. package/ts/{smartproxy/forwarding/http.handler.ts → forwarding/handlers/http-handler.ts} +13 -4
  202. package/ts/{smartproxy/forwarding/https-passthrough.handler.ts → forwarding/handlers/https-passthrough-handler.ts} +13 -4
  203. package/ts/{smartproxy/forwarding/https-terminate-to-http.handler.ts → forwarding/handlers/https-terminate-to-http-handler.ts} +3 -3
  204. package/ts/{smartproxy/forwarding/https-terminate-to-https.handler.ts → forwarding/handlers/https-terminate-to-https-handler.ts} +3 -3
  205. package/ts/forwarding/handlers/index.ts +9 -0
  206. package/ts/forwarding/index.ts +34 -0
  207. package/ts/http/index.ts +23 -0
  208. package/ts/http/models/http-types.ts +105 -0
  209. package/ts/http/port80/acme-interfaces.ts +85 -0
  210. package/ts/http/port80/challenge-responder.ts +246 -0
  211. package/ts/http/port80/index.ts +13 -0
  212. package/ts/{port80handler/classes.port80handler.ts → http/port80/port80-handler.ts} +164 -161
  213. package/ts/http/redirects/index.ts +3 -0
  214. package/ts/http/router/index.ts +5 -0
  215. package/ts/{classes.router.ts → http/router/proxy-router.ts} +27 -20
  216. package/ts/index.ts +32 -9
  217. package/ts/plugins.ts +2 -1
  218. package/ts/proxies/index.ts +8 -0
  219. package/ts/{networkproxy/classes.np.certificatemanager.ts → proxies/network-proxy/certificate-manager.ts} +17 -16
  220. package/ts/{networkproxy/classes.np.connectionpool.ts → proxies/network-proxy/connection-pool.ts} +3 -3
  221. package/ts/proxies/network-proxy/index.ts +13 -0
  222. package/ts/proxies/network-proxy/models/index.ts +4 -0
  223. package/ts/{networkproxy/classes.np.types.ts → proxies/network-proxy/models/types.ts} +7 -11
  224. package/ts/{networkproxy/classes.np.networkproxy.ts → proxies/network-proxy/network-proxy.ts} +31 -24
  225. package/ts/{networkproxy/classes.np.requesthandler.ts → proxies/network-proxy/request-handler.ts} +12 -7
  226. package/ts/{networkproxy/classes.np.websockethandler.ts → proxies/network-proxy/websocket-handler.ts} +6 -6
  227. package/ts/proxies/nftables-proxy/index.ts +5 -0
  228. package/ts/proxies/nftables-proxy/models/errors.ts +30 -0
  229. package/ts/proxies/nftables-proxy/models/index.ts +5 -0
  230. package/ts/proxies/nftables-proxy/models/interfaces.ts +94 -0
  231. package/ts/{nfttablesproxy/classes.nftablesproxy.ts → proxies/nftables-proxy/nftables-proxy.ts} +24 -126
  232. package/ts/{smartproxy/classes.pp.connectionhandler.ts → proxies/smart-proxy/connection-handler.ts} +12 -12
  233. package/ts/{smartproxy/classes.pp.connectionmanager.ts → proxies/smart-proxy/connection-manager.ts} +8 -8
  234. package/ts/{smartproxy/classes.pp.domainconfigmanager.ts → proxies/smart-proxy/domain-config-manager.ts} +15 -14
  235. package/ts/proxies/smart-proxy/index.ts +18 -0
  236. package/ts/proxies/smart-proxy/models/index.ts +4 -0
  237. package/ts/{smartproxy/classes.pp.interfaces.ts → proxies/smart-proxy/models/interfaces.ts} +12 -8
  238. package/ts/{smartproxy/classes.pp.networkproxybridge.ts → proxies/smart-proxy/network-proxy-bridge.ts} +14 -14
  239. package/ts/{smartproxy/classes.pp.portrangemanager.ts → proxies/smart-proxy/port-range-manager.ts} +1 -1
  240. package/ts/{smartproxy/classes.pp.securitymanager.ts → proxies/smart-proxy/security-manager.ts} +3 -3
  241. package/ts/{smartproxy/classes.smartproxy.ts → proxies/smart-proxy/smart-proxy.ts} +29 -24
  242. package/ts/{smartproxy/classes.pp.timeoutmanager.ts → proxies/smart-proxy/timeout-manager.ts} +3 -3
  243. package/ts/{smartproxy/classes.pp.tlsmanager.ts → proxies/smart-proxy/tls-manager.ts} +3 -3
  244. package/ts/tls/alerts/index.ts +3 -0
  245. package/ts/{smartproxy/classes.pp.tlsalert.ts → tls/alerts/tls-alert.ts} +44 -43
  246. package/ts/tls/index.ts +33 -0
  247. package/ts/tls/sni/client-hello-parser.ts +629 -0
  248. package/ts/tls/sni/index.ts +3 -0
  249. package/ts/tls/sni/sni-extraction.ts +353 -0
  250. package/ts/tls/sni/sni-handler.ts +264 -0
  251. package/ts/tls/utils/index.ts +3 -0
  252. package/ts/tls/utils/tls-utils.ts +201 -0
  253. package/ts/common/acmeFactory.ts +0 -23
  254. package/ts/helpers.certificates.ts +0 -30
  255. package/ts/networkproxy/index.ts +0 -7
  256. package/ts/smartproxy/classes.pp.certprovisioner.ts +0 -200
  257. package/ts/smartproxy/classes.pp.snihandler.ts +0 -1281
  258. package/ts/smartproxy/forwarding/index.ts +0 -52
@@ -0,0 +1,629 @@
1
+ import { Buffer } from 'buffer';
2
+ import {
3
+ TlsRecordType,
4
+ TlsHandshakeType,
5
+ TlsExtensionType,
6
+ TlsUtils
7
+ } from '../utils/tls-utils.js';
8
+
9
+ /**
10
+ * Interface for logging functions used by the parser
11
+ */
12
+ export type LoggerFunction = (message: string) => void;
13
+
14
+ /**
15
+ * Result of a session resumption check
16
+ */
17
+ export interface SessionResumptionResult {
18
+ isResumption: boolean;
19
+ hasSNI: boolean;
20
+ }
21
+
22
+ /**
23
+ * Information about parsed TLS extensions
24
+ */
25
+ export interface ExtensionInfo {
26
+ type: number;
27
+ length: number;
28
+ data: Buffer;
29
+ }
30
+
31
+ /**
32
+ * Result of a ClientHello parse operation
33
+ */
34
+ export interface ClientHelloParseResult {
35
+ isValid: boolean;
36
+ version?: [number, number];
37
+ random?: Buffer;
38
+ sessionId?: Buffer;
39
+ hasSessionId: boolean;
40
+ cipherSuites?: Buffer;
41
+ compressionMethods?: Buffer;
42
+ extensions: ExtensionInfo[];
43
+ serverNameList?: string[];
44
+ hasSessionTicket: boolean;
45
+ hasPsk: boolean;
46
+ hasEarlyData: boolean;
47
+ error?: string;
48
+ }
49
+
50
+ /**
51
+ * Fragment tracking information
52
+ */
53
+ export interface FragmentTrackingInfo {
54
+ buffer: Buffer;
55
+ timestamp: number;
56
+ connectionId: string;
57
+ }
58
+
59
+ /**
60
+ * Class for parsing TLS ClientHello messages
61
+ */
62
+ export class ClientHelloParser {
63
+ // Buffer for handling fragmented ClientHello messages
64
+ private static fragmentedBuffers: Map<string, FragmentTrackingInfo> = new Map();
65
+ private static fragmentTimeout: number = 1000; // ms to wait for fragments before cleanup
66
+
67
+ /**
68
+ * Clean up expired fragments
69
+ */
70
+ private static cleanupExpiredFragments(): void {
71
+ const now = Date.now();
72
+ for (const [connectionId, info] of this.fragmentedBuffers.entries()) {
73
+ if (now - info.timestamp > this.fragmentTimeout) {
74
+ this.fragmentedBuffers.delete(connectionId);
75
+ }
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Handles potential fragmented ClientHello messages by buffering and reassembling
81
+ * TLS record fragments that might span multiple TCP packets.
82
+ *
83
+ * @param buffer The current buffer fragment
84
+ * @param connectionId Unique identifier for the connection
85
+ * @param logger Optional logging function
86
+ * @returns A complete buffer if reassembly is successful, or undefined if more fragments are needed
87
+ */
88
+ public static handleFragmentedClientHello(
89
+ buffer: Buffer,
90
+ connectionId: string,
91
+ logger?: LoggerFunction
92
+ ): Buffer | undefined {
93
+ const log = logger || (() => {});
94
+
95
+ // Periodically clean up expired fragments
96
+ this.cleanupExpiredFragments();
97
+
98
+ // Check if we've seen this connection before
99
+ if (!this.fragmentedBuffers.has(connectionId)) {
100
+ // New connection, start with this buffer
101
+ this.fragmentedBuffers.set(connectionId, {
102
+ buffer,
103
+ timestamp: Date.now(),
104
+ connectionId
105
+ });
106
+
107
+ // Evaluate if this buffer already contains a complete ClientHello
108
+ try {
109
+ if (buffer.length >= 5) {
110
+ // Get the record length from TLS header
111
+ const recordLength = (buffer[3] << 8) + buffer[4] + 5; // +5 for the TLS record header itself
112
+ log(`Initial buffer size: ${buffer.length}, expected record length: ${recordLength}`);
113
+
114
+ // Check if this buffer already contains a complete TLS record
115
+ if (buffer.length >= recordLength) {
116
+ log(`Initial buffer contains complete ClientHello, length: ${buffer.length}`);
117
+ return buffer;
118
+ }
119
+ } else {
120
+ log(
121
+ `Initial buffer too small (${buffer.length} bytes), needs at least 5 bytes for TLS header`
122
+ );
123
+ }
124
+ } catch (e) {
125
+ log(`Error checking initial buffer completeness: ${e}`);
126
+ }
127
+
128
+ log(`Started buffering connection ${connectionId}, initial size: ${buffer.length}`);
129
+ return undefined; // Need more fragments
130
+ } else {
131
+ // Existing connection, append this buffer
132
+ const existingInfo = this.fragmentedBuffers.get(connectionId)!;
133
+ const newBuffer = Buffer.concat([existingInfo.buffer, buffer]);
134
+
135
+ // Update the buffer and timestamp
136
+ this.fragmentedBuffers.set(connectionId, {
137
+ ...existingInfo,
138
+ buffer: newBuffer,
139
+ timestamp: Date.now()
140
+ });
141
+
142
+ log(`Appended to buffer for ${connectionId}, new size: ${newBuffer.length}`);
143
+
144
+ // Check if we now have a complete ClientHello
145
+ try {
146
+ if (newBuffer.length >= 5) {
147
+ // Get the record length from TLS header
148
+ const recordLength = (newBuffer[3] << 8) + newBuffer[4] + 5; // +5 for the TLS record header itself
149
+ log(
150
+ `Reassembled buffer size: ${newBuffer.length}, expected record length: ${recordLength}`
151
+ );
152
+
153
+ // Check if we have a complete TLS record now
154
+ if (newBuffer.length >= recordLength) {
155
+ log(
156
+ `Assembled complete ClientHello, length: ${newBuffer.length}, needed: ${recordLength}`
157
+ );
158
+
159
+ // Extract the complete TLS record (might be followed by more data)
160
+ const completeRecord = newBuffer.slice(0, recordLength);
161
+
162
+ // Check if this record is indeed a ClientHello (type 1) at position 5
163
+ if (
164
+ completeRecord.length > 5 &&
165
+ completeRecord[5] === TlsHandshakeType.CLIENT_HELLO
166
+ ) {
167
+ log(`Verified record is a ClientHello handshake message`);
168
+
169
+ // Complete message received, remove from tracking
170
+ this.fragmentedBuffers.delete(connectionId);
171
+ return completeRecord;
172
+ } else {
173
+ log(`Record is complete but not a ClientHello handshake, continuing to buffer`);
174
+ // This might be another TLS record type preceding the ClientHello
175
+
176
+ // Try checking for a ClientHello starting at the end of this record
177
+ if (newBuffer.length > recordLength + 5) {
178
+ const nextRecordType = newBuffer[recordLength];
179
+ log(
180
+ `Next record type: ${nextRecordType} (looking for ${TlsRecordType.HANDSHAKE})`
181
+ );
182
+
183
+ if (nextRecordType === TlsRecordType.HANDSHAKE) {
184
+ const handshakeType = newBuffer[recordLength + 5];
185
+ log(
186
+ `Next handshake type: ${handshakeType} (looking for ${TlsHandshakeType.CLIENT_HELLO})`
187
+ );
188
+
189
+ if (handshakeType === TlsHandshakeType.CLIENT_HELLO) {
190
+ // Found a ClientHello in the next record, return the entire buffer
191
+ log(`Found ClientHello in subsequent record, returning full buffer`);
192
+ this.fragmentedBuffers.delete(connectionId);
193
+ return newBuffer;
194
+ }
195
+ }
196
+ }
197
+ }
198
+ }
199
+ }
200
+ } catch (e) {
201
+ log(`Error checking reassembled buffer completeness: ${e}`);
202
+ }
203
+
204
+ return undefined; // Still need more fragments
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Parses a TLS ClientHello message and extracts all components
210
+ *
211
+ * @param buffer The buffer containing the ClientHello message
212
+ * @param logger Optional logging function
213
+ * @returns Parsed ClientHello or undefined if parsing failed
214
+ */
215
+ public static parseClientHello(
216
+ buffer: Buffer,
217
+ logger?: LoggerFunction
218
+ ): ClientHelloParseResult {
219
+ const log = logger || (() => {});
220
+ const result: ClientHelloParseResult = {
221
+ isValid: false,
222
+ hasSessionId: false,
223
+ extensions: [],
224
+ hasSessionTicket: false,
225
+ hasPsk: false,
226
+ hasEarlyData: false
227
+ };
228
+
229
+ try {
230
+ // Check basic validity
231
+ if (buffer.length < 5) {
232
+ result.error = 'Buffer too small for TLS record header';
233
+ return result;
234
+ }
235
+
236
+ // Check record type (must be HANDSHAKE)
237
+ if (buffer[0] !== TlsRecordType.HANDSHAKE) {
238
+ result.error = `Not a TLS handshake record: ${buffer[0]}`;
239
+ return result;
240
+ }
241
+
242
+ // Get TLS version from record header
243
+ const majorVersion = buffer[1];
244
+ const minorVersion = buffer[2];
245
+ result.version = [majorVersion, minorVersion];
246
+ log(`TLS record version: ${majorVersion}.${minorVersion}`);
247
+
248
+ // Parse record length (bytes 3-4, big-endian)
249
+ const recordLength = (buffer[3] << 8) + buffer[4];
250
+ log(`Record length: ${recordLength}`);
251
+
252
+ // Validate record length against buffer size
253
+ if (buffer.length < recordLength + 5) {
254
+ result.error = 'Buffer smaller than expected record length';
255
+ return result;
256
+ }
257
+
258
+ // Start of handshake message in the buffer
259
+ let pos = 5;
260
+
261
+ // Check handshake type (must be CLIENT_HELLO)
262
+ if (buffer[pos] !== TlsHandshakeType.CLIENT_HELLO) {
263
+ result.error = `Not a ClientHello message: ${buffer[pos]}`;
264
+ return result;
265
+ }
266
+
267
+ // Skip handshake type (1 byte)
268
+ pos += 1;
269
+
270
+ // Parse handshake length (3 bytes, big-endian)
271
+ const handshakeLength = (buffer[pos] << 16) + (buffer[pos + 1] << 8) + buffer[pos + 2];
272
+ log(`Handshake length: ${handshakeLength}`);
273
+
274
+ // Skip handshake length (3 bytes)
275
+ pos += 3;
276
+
277
+ // Check client version (2 bytes)
278
+ const clientMajorVersion = buffer[pos];
279
+ const clientMinorVersion = buffer[pos + 1];
280
+ log(`Client version: ${clientMajorVersion}.${clientMinorVersion}`);
281
+
282
+ // Skip client version (2 bytes)
283
+ pos += 2;
284
+
285
+ // Extract client random (32 bytes)
286
+ if (pos + 32 > buffer.length) {
287
+ result.error = 'Buffer too small for client random';
288
+ return result;
289
+ }
290
+
291
+ result.random = buffer.slice(pos, pos + 32);
292
+ log(`Client random: ${result.random.toString('hex')}`);
293
+
294
+ // Skip client random (32 bytes)
295
+ pos += 32;
296
+
297
+ // Parse session ID
298
+ if (pos + 1 > buffer.length) {
299
+ result.error = 'Buffer too small for session ID length';
300
+ return result;
301
+ }
302
+
303
+ const sessionIdLength = buffer[pos];
304
+ log(`Session ID length: ${sessionIdLength}`);
305
+ pos += 1;
306
+
307
+ result.hasSessionId = sessionIdLength > 0;
308
+
309
+ if (sessionIdLength > 0) {
310
+ if (pos + sessionIdLength > buffer.length) {
311
+ result.error = 'Buffer too small for session ID';
312
+ return result;
313
+ }
314
+
315
+ result.sessionId = buffer.slice(pos, pos + sessionIdLength);
316
+ log(`Session ID: ${result.sessionId.toString('hex')}`);
317
+ }
318
+
319
+ // Skip session ID
320
+ pos += sessionIdLength;
321
+
322
+ // Check if we have enough bytes left for cipher suites
323
+ if (pos + 2 > buffer.length) {
324
+ result.error = 'Buffer too small for cipher suites length';
325
+ return result;
326
+ }
327
+
328
+ // Parse cipher suites length (2 bytes, big-endian)
329
+ const cipherSuitesLength = (buffer[pos] << 8) + buffer[pos + 1];
330
+ log(`Cipher suites length: ${cipherSuitesLength}`);
331
+ pos += 2;
332
+
333
+ // Extract cipher suites
334
+ if (pos + cipherSuitesLength > buffer.length) {
335
+ result.error = 'Buffer too small for cipher suites';
336
+ return result;
337
+ }
338
+
339
+ result.cipherSuites = buffer.slice(pos, pos + cipherSuitesLength);
340
+
341
+ // Skip cipher suites
342
+ pos += cipherSuitesLength;
343
+
344
+ // Check if we have enough bytes left for compression methods
345
+ if (pos + 1 > buffer.length) {
346
+ result.error = 'Buffer too small for compression methods length';
347
+ return result;
348
+ }
349
+
350
+ // Parse compression methods length (1 byte)
351
+ const compressionMethodsLength = buffer[pos];
352
+ log(`Compression methods length: ${compressionMethodsLength}`);
353
+ pos += 1;
354
+
355
+ // Extract compression methods
356
+ if (pos + compressionMethodsLength > buffer.length) {
357
+ result.error = 'Buffer too small for compression methods';
358
+ return result;
359
+ }
360
+
361
+ result.compressionMethods = buffer.slice(pos, pos + compressionMethodsLength);
362
+
363
+ // Skip compression methods
364
+ pos += compressionMethodsLength;
365
+
366
+ // Check if we have enough bytes for extensions length
367
+ if (pos + 2 > buffer.length) {
368
+ // No extensions present - this is valid for older TLS versions
369
+ result.isValid = true;
370
+ return result;
371
+ }
372
+
373
+ // Parse extensions length (2 bytes, big-endian)
374
+ const extensionsLength = (buffer[pos] << 8) + buffer[pos + 1];
375
+ log(`Extensions length: ${extensionsLength}`);
376
+ pos += 2;
377
+
378
+ // Extensions end position
379
+ const extensionsEnd = pos + extensionsLength;
380
+
381
+ // Check if extensions length is valid
382
+ if (extensionsEnd > buffer.length) {
383
+ result.error = 'Extensions length exceeds buffer size';
384
+ return result;
385
+ }
386
+
387
+ // Iterate through extensions
388
+ const serverNames: string[] = [];
389
+
390
+ while (pos + 4 <= extensionsEnd) {
391
+ // Parse extension type (2 bytes, big-endian)
392
+ const extensionType = (buffer[pos] << 8) + buffer[pos + 1];
393
+ log(`Extension type: 0x${extensionType.toString(16).padStart(4, '0')}`);
394
+ pos += 2;
395
+
396
+ // Parse extension length (2 bytes, big-endian)
397
+ const extensionLength = (buffer[pos] << 8) + buffer[pos + 1];
398
+ log(`Extension length: ${extensionLength}`);
399
+ pos += 2;
400
+
401
+ // Extract extension data
402
+ if (pos + extensionLength > extensionsEnd) {
403
+ result.error = `Extension ${extensionType} data exceeds bounds`;
404
+ return result;
405
+ }
406
+
407
+ const extensionData = buffer.slice(pos, pos + extensionLength);
408
+
409
+ // Record all extensions
410
+ result.extensions.push({
411
+ type: extensionType,
412
+ length: extensionLength,
413
+ data: extensionData
414
+ });
415
+
416
+ // Track specific extension types
417
+ if (extensionType === TlsExtensionType.SERVER_NAME) {
418
+ // Server Name Indication (SNI)
419
+ this.parseServerNameExtension(extensionData, serverNames, logger);
420
+ } else if (extensionType === TlsExtensionType.SESSION_TICKET) {
421
+ // Session ticket
422
+ result.hasSessionTicket = true;
423
+ } else if (extensionType === TlsExtensionType.PRE_SHARED_KEY) {
424
+ // TLS 1.3 PSK
425
+ result.hasPsk = true;
426
+ } else if (extensionType === TlsExtensionType.EARLY_DATA) {
427
+ // TLS 1.3 Early Data (0-RTT)
428
+ result.hasEarlyData = true;
429
+ }
430
+
431
+ // Move to next extension
432
+ pos += extensionLength;
433
+ }
434
+
435
+ // Store any server names found
436
+ if (serverNames.length > 0) {
437
+ result.serverNameList = serverNames;
438
+ }
439
+
440
+ // Mark as valid if we get here
441
+ result.isValid = true;
442
+ return result;
443
+ } catch (error) {
444
+ const errorMessage = error instanceof Error ? error.message : String(error);
445
+ log(`Error parsing ClientHello: ${errorMessage}`);
446
+ result.error = errorMessage;
447
+ return result;
448
+ }
449
+ }
450
+
451
+ /**
452
+ * Parses the server name extension data and extracts hostnames
453
+ *
454
+ * @param data Extension data buffer
455
+ * @param serverNames Array to populate with found server names
456
+ * @param logger Optional logging function
457
+ * @returns true if parsing succeeded
458
+ */
459
+ private static parseServerNameExtension(
460
+ data: Buffer,
461
+ serverNames: string[],
462
+ logger?: LoggerFunction
463
+ ): boolean {
464
+ const log = logger || (() => {});
465
+
466
+ try {
467
+ // Need at least 2 bytes for server name list length
468
+ if (data.length < 2) {
469
+ log('SNI extension too small for server name list length');
470
+ return false;
471
+ }
472
+
473
+ // Parse server name list length (2 bytes)
474
+ const listLength = (data[0] << 8) + data[1];
475
+
476
+ // Skip to first name entry
477
+ let pos = 2;
478
+
479
+ // End of list
480
+ const listEnd = pos + listLength;
481
+
482
+ // Validate length
483
+ if (listEnd > data.length) {
484
+ log('SNI server name list exceeds extension data');
485
+ return false;
486
+ }
487
+
488
+ // Process all name entries
489
+ while (pos + 3 <= listEnd) {
490
+ // Name type (1 byte)
491
+ const nameType = data[pos];
492
+ pos += 1;
493
+
494
+ // For hostname, type must be 0
495
+ if (nameType !== 0) {
496
+ // Skip this entry
497
+ if (pos + 2 <= listEnd) {
498
+ const nameLength = (data[pos] << 8) + data[pos + 1];
499
+ pos += 2 + nameLength;
500
+ continue;
501
+ } else {
502
+ log('Malformed SNI entry');
503
+ return false;
504
+ }
505
+ }
506
+
507
+ // Parse hostname length (2 bytes)
508
+ if (pos + 2 > listEnd) {
509
+ log('SNI extension truncated');
510
+ return false;
511
+ }
512
+
513
+ const nameLength = (data[pos] << 8) + data[pos + 1];
514
+ pos += 2;
515
+
516
+ // Extract hostname
517
+ if (pos + nameLength > listEnd) {
518
+ log('SNI hostname truncated');
519
+ return false;
520
+ }
521
+
522
+ // Extract the hostname as UTF-8
523
+ try {
524
+ const hostname = data.slice(pos, pos + nameLength).toString('utf8');
525
+ log(`Found SNI hostname: ${hostname}`);
526
+ serverNames.push(hostname);
527
+ } catch (err) {
528
+ log(`Error extracting hostname: ${err}`);
529
+ }
530
+
531
+ // Move to next entry
532
+ pos += nameLength;
533
+ }
534
+
535
+ return serverNames.length > 0;
536
+ } catch (error) {
537
+ log(`Error parsing SNI extension: ${error}`);
538
+ return false;
539
+ }
540
+ }
541
+
542
+ /**
543
+ * Determines if a ClientHello contains session resumption indicators
544
+ *
545
+ * @param buffer The ClientHello buffer
546
+ * @param logger Optional logging function
547
+ * @returns Session resumption result
548
+ */
549
+ public static hasSessionResumption(
550
+ buffer: Buffer,
551
+ logger?: LoggerFunction
552
+ ): SessionResumptionResult {
553
+ const log = logger || (() => {});
554
+
555
+ if (!TlsUtils.isClientHello(buffer)) {
556
+ return { isResumption: false, hasSNI: false };
557
+ }
558
+
559
+ const parseResult = this.parseClientHello(buffer, logger);
560
+ if (!parseResult.isValid) {
561
+ log(`ClientHello parse failed: ${parseResult.error}`);
562
+ return { isResumption: false, hasSNI: false };
563
+ }
564
+
565
+ // Check resumption indicators
566
+ const hasSessionId = parseResult.hasSessionId;
567
+ const hasSessionTicket = parseResult.hasSessionTicket;
568
+ const hasPsk = parseResult.hasPsk;
569
+ const hasEarlyData = parseResult.hasEarlyData;
570
+
571
+ // Check for SNI
572
+ const hasSNI = !!parseResult.serverNameList && parseResult.serverNameList.length > 0;
573
+
574
+ // Consider it a resumption if any resumption mechanism is present
575
+ const isResumption = hasSessionTicket || hasPsk || hasEarlyData ||
576
+ (hasSessionId && !hasPsk); // Legacy resumption
577
+
578
+ // Log details
579
+ if (isResumption) {
580
+ log(
581
+ 'Session resumption detected: ' +
582
+ (hasSessionTicket ? 'session ticket, ' : '') +
583
+ (hasPsk ? 'PSK, ' : '') +
584
+ (hasEarlyData ? 'early data, ' : '') +
585
+ (hasSessionId ? 'session ID' : '') +
586
+ (hasSNI ? ', with SNI' : ', without SNI')
587
+ );
588
+ }
589
+
590
+ return { isResumption, hasSNI };
591
+ }
592
+
593
+ /**
594
+ * Checks if a ClientHello appears to be from a tab reactivation
595
+ *
596
+ * @param buffer The ClientHello buffer
597
+ * @param logger Optional logging function
598
+ * @returns true if it appears to be a tab reactivation
599
+ */
600
+ public static isTabReactivationHandshake(
601
+ buffer: Buffer,
602
+ logger?: LoggerFunction
603
+ ): boolean {
604
+ const log = logger || (() => {});
605
+
606
+ if (!TlsUtils.isClientHello(buffer)) {
607
+ return false;
608
+ }
609
+
610
+ // Parse the ClientHello
611
+ const parseResult = this.parseClientHello(buffer, logger);
612
+ if (!parseResult.isValid) {
613
+ return false;
614
+ }
615
+
616
+ // Tab reactivation pattern: session identifier + (ticket or PSK) but no SNI
617
+ const hasSessionId = parseResult.hasSessionId;
618
+ const hasSessionTicket = parseResult.hasSessionTicket;
619
+ const hasPsk = parseResult.hasPsk;
620
+ const hasSNI = !!parseResult.serverNameList && parseResult.serverNameList.length > 0;
621
+
622
+ if ((hasSessionId && (hasSessionTicket || hasPsk)) && !hasSNI) {
623
+ log('Detected tab reactivation pattern: session resumption without SNI');
624
+ return true;
625
+ }
626
+
627
+ return false;
628
+ }
629
+ }
@@ -0,0 +1,3 @@
1
+ /**
2
+ * SNI handling
3
+ */