@push.rocks/smartproxy 3.37.0 → 3.37.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.
@@ -0,0 +1,45 @@
1
+ import { Buffer } from 'buffer';
2
+ /**
3
+ * SNI (Server Name Indication) handler for TLS connections.
4
+ * Provides robust extraction of SNI values from TLS ClientHello messages.
5
+ */
6
+ export declare class SniHandler {
7
+ private static readonly TLS_HANDSHAKE_RECORD_TYPE;
8
+ private static readonly TLS_CLIENT_HELLO_HANDSHAKE_TYPE;
9
+ private static readonly TLS_SNI_EXTENSION_TYPE;
10
+ private static readonly TLS_SESSION_TICKET_EXTENSION_TYPE;
11
+ private static readonly TLS_SNI_HOST_NAME_TYPE;
12
+ /**
13
+ * Checks if a buffer contains a TLS handshake message (record type 22)
14
+ * @param buffer - The buffer to check
15
+ * @returns true if the buffer starts with a TLS handshake record type
16
+ */
17
+ static isTlsHandshake(buffer: Buffer): boolean;
18
+ /**
19
+ * Checks if a buffer contains a TLS ClientHello message
20
+ * @param buffer - The buffer to check
21
+ * @returns true if the buffer appears to be a ClientHello message
22
+ */
23
+ static isClientHello(buffer: Buffer): boolean;
24
+ /**
25
+ * Extracts the SNI (Server Name Indication) from a TLS ClientHello message.
26
+ * Implements robust parsing with support for session resumption edge cases.
27
+ *
28
+ * @param buffer - The buffer containing the TLS ClientHello message
29
+ * @param enableLogging - Whether to enable detailed debug logging
30
+ * @returns The extracted server name or undefined if not found
31
+ */
32
+ static extractSNI(buffer: Buffer, enableLogging?: boolean): string | undefined;
33
+ /**
34
+ * Attempts to extract SNI from an initial ClientHello packet and handles
35
+ * session resumption edge cases more robustly than the standard extraction.
36
+ *
37
+ * This method is specifically designed for Chrome and other browsers that
38
+ * may send different ClientHello formats during session resumption.
39
+ *
40
+ * @param buffer - The buffer containing the TLS ClientHello message
41
+ * @param enableLogging - Whether to enable detailed debug logging
42
+ * @returns The extracted server name or undefined if not found
43
+ */
44
+ static extractSNIWithResumptionSupport(buffer: Buffer, enableLogging?: boolean): string | undefined;
45
+ }
@@ -0,0 +1,274 @@
1
+ import { Buffer } from 'buffer';
2
+ /**
3
+ * SNI (Server Name Indication) handler for TLS connections.
4
+ * Provides robust extraction of SNI values from TLS ClientHello messages.
5
+ */
6
+ export class SniHandler {
7
+ // TLS record types and constants
8
+ static { this.TLS_HANDSHAKE_RECORD_TYPE = 22; }
9
+ static { this.TLS_CLIENT_HELLO_HANDSHAKE_TYPE = 1; }
10
+ static { this.TLS_SNI_EXTENSION_TYPE = 0x0000; }
11
+ static { this.TLS_SESSION_TICKET_EXTENSION_TYPE = 0x0023; }
12
+ static { this.TLS_SNI_HOST_NAME_TYPE = 0; }
13
+ /**
14
+ * Checks if a buffer contains a TLS handshake message (record type 22)
15
+ * @param buffer - The buffer to check
16
+ * @returns true if the buffer starts with a TLS handshake record type
17
+ */
18
+ static isTlsHandshake(buffer) {
19
+ return buffer.length > 0 && buffer[0] === this.TLS_HANDSHAKE_RECORD_TYPE;
20
+ }
21
+ /**
22
+ * Checks if a buffer contains a TLS ClientHello message
23
+ * @param buffer - The buffer to check
24
+ * @returns true if the buffer appears to be a ClientHello message
25
+ */
26
+ static isClientHello(buffer) {
27
+ // Minimum ClientHello size (TLS record header + handshake header)
28
+ if (buffer.length < 9) {
29
+ return false;
30
+ }
31
+ // Check record type (must be TLS_HANDSHAKE_RECORD_TYPE)
32
+ if (buffer[0] !== this.TLS_HANDSHAKE_RECORD_TYPE) {
33
+ return false;
34
+ }
35
+ // Skip version and length in TLS record header (5 bytes total)
36
+ // Check handshake type at byte 5 (must be CLIENT_HELLO)
37
+ return buffer[5] === this.TLS_CLIENT_HELLO_HANDSHAKE_TYPE;
38
+ }
39
+ /**
40
+ * Extracts the SNI (Server Name Indication) from a TLS ClientHello message.
41
+ * Implements robust parsing with support for session resumption edge cases.
42
+ *
43
+ * @param buffer - The buffer containing the TLS ClientHello message
44
+ * @param enableLogging - Whether to enable detailed debug logging
45
+ * @returns The extracted server name or undefined if not found
46
+ */
47
+ static extractSNI(buffer, enableLogging = false) {
48
+ // Logging helper
49
+ const log = (message) => {
50
+ if (enableLogging) {
51
+ console.log(`[SNI Extraction] ${message}`);
52
+ }
53
+ };
54
+ try {
55
+ // Buffer must be at least 5 bytes (TLS record header)
56
+ if (buffer.length < 5) {
57
+ log('Buffer too small for TLS record header');
58
+ return undefined;
59
+ }
60
+ // Check record type (must be TLS_HANDSHAKE_RECORD_TYPE = 22)
61
+ if (buffer[0] !== this.TLS_HANDSHAKE_RECORD_TYPE) {
62
+ log(`Not a TLS handshake record: ${buffer[0]}`);
63
+ return undefined;
64
+ }
65
+ // Check TLS version
66
+ const majorVersion = buffer[1];
67
+ const minorVersion = buffer[2];
68
+ log(`TLS version: ${majorVersion}.${minorVersion}`);
69
+ // Parse record length (bytes 3-4, big-endian)
70
+ const recordLength = (buffer[3] << 8) + buffer[4];
71
+ log(`Record length: ${recordLength}`);
72
+ // Validate record length against buffer size
73
+ if (buffer.length < recordLength + 5) {
74
+ log('Buffer smaller than expected record length');
75
+ return undefined;
76
+ }
77
+ // Start of handshake message in the buffer
78
+ let pos = 5;
79
+ // Check handshake type (must be CLIENT_HELLO = 1)
80
+ if (buffer[pos] !== this.TLS_CLIENT_HELLO_HANDSHAKE_TYPE) {
81
+ log(`Not a ClientHello message: ${buffer[pos]}`);
82
+ return undefined;
83
+ }
84
+ // Skip handshake type (1 byte)
85
+ pos += 1;
86
+ // Parse handshake length (3 bytes, big-endian)
87
+ const handshakeLength = (buffer[pos] << 16) + (buffer[pos + 1] << 8) + buffer[pos + 2];
88
+ log(`Handshake length: ${handshakeLength}`);
89
+ // Skip handshake length (3 bytes)
90
+ pos += 3;
91
+ // Check client version (2 bytes)
92
+ const clientMajorVersion = buffer[pos];
93
+ const clientMinorVersion = buffer[pos + 1];
94
+ log(`Client version: ${clientMajorVersion}.${clientMinorVersion}`);
95
+ // Skip client version (2 bytes)
96
+ pos += 2;
97
+ // Skip client random (32 bytes)
98
+ pos += 32;
99
+ // Parse session ID
100
+ if (pos + 1 > buffer.length) {
101
+ log('Buffer too small for session ID length');
102
+ return undefined;
103
+ }
104
+ const sessionIdLength = buffer[pos];
105
+ log(`Session ID length: ${sessionIdLength}`);
106
+ // Skip session ID length (1 byte) and session ID
107
+ pos += 1 + sessionIdLength;
108
+ // Check if we have enough bytes left
109
+ if (pos + 2 > buffer.length) {
110
+ log('Buffer too small for cipher suites length');
111
+ return undefined;
112
+ }
113
+ // Parse cipher suites length (2 bytes, big-endian)
114
+ const cipherSuitesLength = (buffer[pos] << 8) + buffer[pos + 1];
115
+ log(`Cipher suites length: ${cipherSuitesLength}`);
116
+ // Skip cipher suites length (2 bytes) and cipher suites
117
+ pos += 2 + cipherSuitesLength;
118
+ // Check if we have enough bytes left
119
+ if (pos + 1 > buffer.length) {
120
+ log('Buffer too small for compression methods length');
121
+ return undefined;
122
+ }
123
+ // Parse compression methods length (1 byte)
124
+ const compressionMethodsLength = buffer[pos];
125
+ log(`Compression methods length: ${compressionMethodsLength}`);
126
+ // Skip compression methods length (1 byte) and compression methods
127
+ pos += 1 + compressionMethodsLength;
128
+ // Check if we have enough bytes for extensions length
129
+ if (pos + 2 > buffer.length) {
130
+ log('No extensions present or buffer too small');
131
+ return undefined;
132
+ }
133
+ // Parse extensions length (2 bytes, big-endian)
134
+ const extensionsLength = (buffer[pos] << 8) + buffer[pos + 1];
135
+ log(`Extensions length: ${extensionsLength}`);
136
+ // Skip extensions length (2 bytes)
137
+ pos += 2;
138
+ // Extensions end position
139
+ const extensionsEnd = pos + extensionsLength;
140
+ // Check if extensions length is valid
141
+ if (extensionsEnd > buffer.length) {
142
+ log('Extensions length exceeds buffer size');
143
+ return undefined;
144
+ }
145
+ // Track if we found session tickets (for improved resumption handling)
146
+ let hasSessionTicket = false;
147
+ // Iterate through extensions
148
+ while (pos + 4 <= extensionsEnd) {
149
+ // Parse extension type (2 bytes, big-endian)
150
+ const extensionType = (buffer[pos] << 8) + buffer[pos + 1];
151
+ log(`Extension type: 0x${extensionType.toString(16).padStart(4, '0')}`);
152
+ // Skip extension type (2 bytes)
153
+ pos += 2;
154
+ // Parse extension length (2 bytes, big-endian)
155
+ const extensionLength = (buffer[pos] << 8) + buffer[pos + 1];
156
+ log(`Extension length: ${extensionLength}`);
157
+ // Skip extension length (2 bytes)
158
+ pos += 2;
159
+ // Check if this is the SNI extension
160
+ if (extensionType === this.TLS_SNI_EXTENSION_TYPE) {
161
+ log('Found SNI extension');
162
+ // Ensure we have enough bytes for the server name list
163
+ if (pos + 2 > extensionsEnd) {
164
+ log('Extension too small for server name list length');
165
+ pos += extensionLength; // Skip this extension
166
+ continue;
167
+ }
168
+ // Parse server name list length (2 bytes, big-endian)
169
+ const serverNameListLength = (buffer[pos] << 8) + buffer[pos + 1];
170
+ log(`Server name list length: ${serverNameListLength}`);
171
+ // Skip server name list length (2 bytes)
172
+ pos += 2;
173
+ // Ensure server name list length is valid
174
+ if (pos + serverNameListLength > extensionsEnd) {
175
+ log('Server name list length exceeds extension size');
176
+ break; // Exit the loop, extension parsing is broken
177
+ }
178
+ // End position of server name list
179
+ const serverNameListEnd = pos + serverNameListLength;
180
+ // Iterate through server names
181
+ while (pos + 3 <= serverNameListEnd) {
182
+ // Check name type (must be HOST_NAME_TYPE = 0 for hostname)
183
+ const nameType = buffer[pos];
184
+ log(`Name type: ${nameType}`);
185
+ if (nameType !== this.TLS_SNI_HOST_NAME_TYPE) {
186
+ log(`Unsupported name type: ${nameType}`);
187
+ pos += 1; // Skip name type (1 byte)
188
+ // Skip name length (2 bytes) and name data
189
+ if (pos + 2 <= serverNameListEnd) {
190
+ const nameLength = (buffer[pos] << 8) + buffer[pos + 1];
191
+ pos += 2 + nameLength;
192
+ }
193
+ else {
194
+ log('Invalid server name entry');
195
+ break;
196
+ }
197
+ continue;
198
+ }
199
+ // Skip name type (1 byte)
200
+ pos += 1;
201
+ // Ensure we have enough bytes for name length
202
+ if (pos + 2 > serverNameListEnd) {
203
+ log('Server name entry too small for name length');
204
+ break;
205
+ }
206
+ // Parse name length (2 bytes, big-endian)
207
+ const nameLength = (buffer[pos] << 8) + buffer[pos + 1];
208
+ log(`Name length: ${nameLength}`);
209
+ // Skip name length (2 bytes)
210
+ pos += 2;
211
+ // Ensure we have enough bytes for the name
212
+ if (pos + nameLength > serverNameListEnd) {
213
+ log('Name length exceeds server name list size');
214
+ break;
215
+ }
216
+ // Extract server name (hostname)
217
+ const serverName = buffer.slice(pos, pos + nameLength).toString('utf8');
218
+ log(`Extracted server name: ${serverName}`);
219
+ return serverName;
220
+ }
221
+ }
222
+ else if (extensionType === this.TLS_SESSION_TICKET_EXTENSION_TYPE) {
223
+ // If we encounter a session ticket extension, mark it for later
224
+ log('Found session ticket extension');
225
+ hasSessionTicket = true;
226
+ pos += extensionLength; // Skip this extension
227
+ }
228
+ else {
229
+ // Skip this extension
230
+ pos += extensionLength;
231
+ }
232
+ }
233
+ // Log if we found a session ticket but no SNI
234
+ if (hasSessionTicket) {
235
+ log('Session ticket present but no SNI found - possible resumption scenario');
236
+ }
237
+ log('No SNI extension found in ClientHello');
238
+ return undefined;
239
+ }
240
+ catch (error) {
241
+ log(`Error parsing SNI: ${error instanceof Error ? error.message : String(error)}`);
242
+ return undefined;
243
+ }
244
+ }
245
+ /**
246
+ * Attempts to extract SNI from an initial ClientHello packet and handles
247
+ * session resumption edge cases more robustly than the standard extraction.
248
+ *
249
+ * This method is specifically designed for Chrome and other browsers that
250
+ * may send different ClientHello formats during session resumption.
251
+ *
252
+ * @param buffer - The buffer containing the TLS ClientHello message
253
+ * @param enableLogging - Whether to enable detailed debug logging
254
+ * @returns The extracted server name or undefined if not found
255
+ */
256
+ static extractSNIWithResumptionSupport(buffer, enableLogging = false) {
257
+ // First try the standard SNI extraction
258
+ const standardSni = this.extractSNI(buffer, enableLogging);
259
+ if (standardSni) {
260
+ return standardSni;
261
+ }
262
+ // If standard extraction failed and we have a valid ClientHello,
263
+ // this might be a session resumption with non-standard format
264
+ if (this.isClientHello(buffer)) {
265
+ if (enableLogging) {
266
+ console.log('[SNI Extraction] Detected ClientHello without standard SNI, possible session resumption');
267
+ }
268
+ // Additional handling could be implemented here for specific browser behaviors
269
+ // For now, this is a placeholder for future improvements
270
+ }
271
+ return undefined;
272
+ }
273
+ }
274
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5zbmloYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2xhc3Nlcy5zbmloYW5kbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFFaEM7OztHQUdHO0FBQ0gsTUFBTSxPQUFPLFVBQVU7SUFDckIsaUNBQWlDO2FBQ1QsOEJBQXlCLEdBQUcsRUFBRSxDQUFDO2FBQy9CLG9DQUErQixHQUFHLENBQUMsQ0FBQzthQUNwQywyQkFBc0IsR0FBRyxNQUFNLENBQUM7YUFDaEMsc0NBQWlDLEdBQUcsTUFBTSxDQUFDO2FBQzNDLDJCQUFzQixHQUFHLENBQUMsQ0FBQztJQUVuRDs7OztPQUlHO0lBQ0ksTUFBTSxDQUFDLGNBQWMsQ0FBQyxNQUFjO1FBQ3pDLE9BQU8sTUFBTSxDQUFDLE1BQU0sR0FBRyxDQUFDLElBQUksTUFBTSxDQUFDLENBQUMsQ0FBQyxLQUFLLElBQUksQ0FBQyx5QkFBeUIsQ0FBQztJQUMzRSxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLE1BQU0sQ0FBQyxhQUFhLENBQUMsTUFBYztRQUN4QyxrRUFBa0U7UUFDbEUsSUFBSSxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ3RCLE9BQU8sS0FBSyxDQUFDO1FBQ2YsQ0FBQztRQUVELHdEQUF3RDtRQUN4RCxJQUFJLE1BQU0sQ0FBQyxDQUFDLENBQUMsS0FBSyxJQUFJLENBQUMseUJBQXlCLEVBQUUsQ0FBQztZQUNqRCxPQUFPLEtBQUssQ0FBQztRQUNmLENBQUM7UUFFRCwrREFBK0Q7UUFDL0Qsd0RBQXdEO1FBQ3hELE9BQU8sTUFBTSxDQUFDLENBQUMsQ0FBQyxLQUFLLElBQUksQ0FBQywrQkFBK0IsQ0FBQztJQUM1RCxDQUFDO0lBRUQ7Ozs7Ozs7T0FPRztJQUNJLE1BQU0sQ0FBQyxVQUFVLENBQUMsTUFBYyxFQUFFLGdCQUF5QixLQUFLO1FBQ3JFLGlCQUFp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
@@ -3,3 +3,4 @@ export * from './classes.networkproxy.js';
3
3
  export * from './classes.portproxy.js';
4
4
  export * from './classes.port80handler.js';
5
5
  export * from './classes.sslredirect.js';
6
+ export * from './classes.snihandler.js';
package/dist_ts/index.js CHANGED
@@ -3,4 +3,5 @@ export * from './classes.networkproxy.js';
3
3
  export * from './classes.portproxy.js';
4
4
  export * from './classes.port80handler.js';
5
5
  export * from './classes.sslredirect.js';
6
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLDRCQUE0QixDQUFDO0FBQzNDLGNBQWMsMkJBQTJCLENBQUM7QUFDMUMsY0FBYyx3QkFBd0IsQ0FBQztBQUN2QyxjQUFjLDRCQUE0QixDQUFDO0FBQzNDLGNBQWMsMEJBQTBCLENBQUMifQ==
6
+ export * from './classes.snihandler.js';
7
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLDRCQUE0QixDQUFDO0FBQzNDLGNBQWMsMkJBQTJCLENBQUM7QUFDMUMsY0FBYyx3QkFBd0IsQ0FBQztBQUN2QyxjQUFjLDRCQUE0QixDQUFDO0FBQzNDLGNBQWMsMEJBQTBCLENBQUM7QUFDekMsY0FBYyx5QkFBeUIsQ0FBQyJ9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartproxy",
3
- "version": "3.37.0",
3
+ "version": "3.37.1",
4
4
  "private": false,
5
5
  "description": "A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, dynamic routing with authentication options, and automatic ACME certificate management.",
6
6
  "main": "dist_ts/index.js",
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartproxy',
6
- version: '3.37.0',
6
+ version: '3.37.1',
7
7
  description: 'A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, dynamic routing with authentication options, and automatic ACME certificate management.'
8
8
  }
@@ -1,5 +1,6 @@
1
1
  import * as plugins from './plugins.js';
2
2
  import { NetworkProxy } from './classes.networkproxy.js';
3
+ import { SniHandler } from './classes.snihandler.js';
3
4
 
4
5
  /** Domain configuration with per-domain allowed port ranges */
5
6
  export interface IDomainConfig {
@@ -117,192 +118,8 @@ interface IConnectionRecord {
117
118
  domainSwitches?: number; // Number of times the domain has been switched on this connection
118
119
  }
119
120
 
120
- /**
121
- * Extracts the SNI (Server Name Indication) from a TLS ClientHello packet.
122
- * Enhanced for robustness and detailed logging.
123
- * @param buffer - Buffer containing the TLS ClientHello.
124
- * @param enableLogging - Whether to enable detailed logging.
125
- * @returns The server name if found, otherwise undefined.
126
- */
127
- function extractSNI(buffer: Buffer, enableLogging: boolean = false): string | undefined {
128
- try {
129
- // Check if buffer is too small for TLS
130
- if (buffer.length < 5) {
131
- if (enableLogging) console.log('Buffer too small for TLS header');
132
- return undefined;
133
- }
134
-
135
- // Check record type (has to be handshake - 22)
136
- const recordType = buffer.readUInt8(0);
137
- if (recordType !== 22) {
138
- if (enableLogging) console.log(`Not a TLS handshake. Record type: ${recordType}`);
139
- return undefined;
140
- }
141
-
142
- // Check TLS version (has to be 3.1 or higher)
143
- const majorVersion = buffer.readUInt8(1);
144
- const minorVersion = buffer.readUInt8(2);
145
- if (enableLogging) console.log(`TLS Version: ${majorVersion}.${minorVersion}`);
146
-
147
- // Check record length
148
- const recordLength = buffer.readUInt16BE(3);
149
- if (buffer.length < 5 + recordLength) {
150
- if (enableLogging)
151
- console.log(
152
- `Buffer too small for TLS record. Expected: ${5 + recordLength}, Got: ${buffer.length}`
153
- );
154
- return undefined;
155
- }
156
-
157
- let offset = 5;
158
- const handshakeType = buffer.readUInt8(offset);
159
- if (handshakeType !== 1) {
160
- if (enableLogging) console.log(`Not a ClientHello. Handshake type: ${handshakeType}`);
161
- return undefined;
162
- }
163
-
164
- offset += 4; // Skip handshake header (type + length)
165
-
166
- // Client version
167
- const clientMajorVersion = buffer.readUInt8(offset);
168
- const clientMinorVersion = buffer.readUInt8(offset + 1);
169
- if (enableLogging) console.log(`Client Version: ${clientMajorVersion}.${clientMinorVersion}`);
170
-
171
- offset += 2 + 32; // Skip client version and random
172
-
173
- // Session ID
174
- const sessionIDLength = buffer.readUInt8(offset);
175
- if (enableLogging) console.log(`Session ID Length: ${sessionIDLength}`);
176
- offset += 1 + sessionIDLength; // Skip session ID
177
-
178
- // Cipher suites
179
- if (offset + 2 > buffer.length) {
180
- if (enableLogging) console.log('Buffer too small for cipher suites length');
181
- return undefined;
182
- }
183
- const cipherSuitesLength = buffer.readUInt16BE(offset);
184
- if (enableLogging) console.log(`Cipher Suites Length: ${cipherSuitesLength}`);
185
- offset += 2 + cipherSuitesLength; // Skip cipher suites
186
-
187
- // Compression methods
188
- if (offset + 1 > buffer.length) {
189
- if (enableLogging) console.log('Buffer too small for compression methods length');
190
- return undefined;
191
- }
192
- const compressionMethodsLength = buffer.readUInt8(offset);
193
- if (enableLogging) console.log(`Compression Methods Length: ${compressionMethodsLength}`);
194
- offset += 1 + compressionMethodsLength; // Skip compression methods
195
-
196
- // Extensions
197
- if (offset + 2 > buffer.length) {
198
- if (enableLogging) console.log('Buffer too small for extensions length');
199
- return undefined;
200
- }
201
- const extensionsLength = buffer.readUInt16BE(offset);
202
- if (enableLogging) console.log(`Extensions Length: ${extensionsLength}`);
203
- offset += 2;
204
- const extensionsEnd = offset + extensionsLength;
205
-
206
- if (extensionsEnd > buffer.length) {
207
- if (enableLogging)
208
- console.log(
209
- `Buffer too small for extensions. Expected end: ${extensionsEnd}, Buffer length: ${buffer.length}`
210
- );
211
- return undefined;
212
- }
213
-
214
- // Parse extensions
215
- while (offset + 4 <= extensionsEnd) {
216
- const extensionType = buffer.readUInt16BE(offset);
217
- const extensionLength = buffer.readUInt16BE(offset + 2);
218
-
219
- if (enableLogging)
220
- console.log(`Extension Type: 0x${extensionType.toString(16)}, Length: ${extensionLength}`);
221
-
222
- offset += 4;
223
-
224
- if (extensionType === 0x0000) {
225
- // SNI extension
226
- if (offset + 2 > buffer.length) {
227
- if (enableLogging) console.log('Buffer too small for SNI list length');
228
- return undefined;
229
- }
230
-
231
- const sniListLength = buffer.readUInt16BE(offset);
232
- if (enableLogging) console.log(`SNI List Length: ${sniListLength}`);
233
- offset += 2;
234
- const sniListEnd = offset + sniListLength;
235
-
236
- if (sniListEnd > buffer.length) {
237
- if (enableLogging)
238
- console.log(
239
- `Buffer too small for SNI list. Expected end: ${sniListEnd}, Buffer length: ${buffer.length}`
240
- );
241
- return undefined;
242
- }
243
-
244
- while (offset + 3 < sniListEnd) {
245
- const nameType = buffer.readUInt8(offset++);
246
- const nameLen = buffer.readUInt16BE(offset);
247
- offset += 2;
248
-
249
- if (enableLogging) console.log(`Name Type: ${nameType}, Name Length: ${nameLen}`);
250
-
251
- if (nameType === 0) {
252
- // host_name
253
- if (offset + nameLen > buffer.length) {
254
- if (enableLogging)
255
- console.log(
256
- `Buffer too small for hostname. Expected: ${offset + nameLen}, Got: ${
257
- buffer.length
258
- }`
259
- );
260
- return undefined;
261
- }
262
-
263
- const serverName = buffer.toString('utf8', offset, offset + nameLen);
264
- if (enableLogging) console.log(`Extracted SNI: ${serverName}`);
265
- return serverName;
266
- }
267
-
268
- offset += nameLen;
269
- }
270
- break;
271
- } else {
272
- offset += extensionLength;
273
- }
274
- }
275
-
276
- if (enableLogging) console.log('No SNI extension found');
277
- return undefined;
278
- } catch (err) {
279
- console.log(`Error extracting SNI: ${err}`);
280
- return undefined;
281
- }
282
- }
283
-
284
- /**
285
- * Checks if a TLS record is a proper ClientHello message (more accurate than just checking record type)
286
- * @param buffer - Buffer containing the TLS record
287
- * @returns true if the buffer contains a proper ClientHello message
288
- */
289
- function isClientHello(buffer: Buffer): boolean {
290
- try {
291
- if (buffer.length < 9) return false; // Too small for a proper ClientHello
292
-
293
- // Check record type (has to be handshake - 22)
294
- if (buffer.readUInt8(0) !== 22) return false;
295
-
296
- // After the TLS record header (5 bytes), check the handshake type (1 for ClientHello)
297
- if (buffer.readUInt8(5) !== 1) return false;
298
-
299
- // Basic checks passed, this appears to be a ClientHello
300
- return true;
301
- } catch (err) {
302
- console.log(`Error checking for ClientHello: ${err}`);
303
- return false;
304
- }
305
- }
121
+ // SNI functions are now imported from SniHandler class
122
+ // No need for wrapper functions
306
123
 
307
124
  // Helper: Check if a port falls within any of the given port ranges
308
125
  const isPortInRanges = (port: number, ranges: Array<{ from: number; to: number }>): boolean => {
@@ -346,10 +163,7 @@ const generateConnectionId = (): string => {
346
163
  return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
347
164
  };
348
165
 
349
- // Helper: Check if a buffer contains a TLS handshake
350
- const isTlsHandshake = (buffer: Buffer): boolean => {
351
- return buffer.length > 0 && buffer[0] === 22; // ContentType.handshake
352
- };
166
+ // SNI functions are now imported from SniHandler class
353
167
 
354
168
  // Helper: Ensure timeout values don't exceed Node.js max safe integer
355
169
  const ensureSafeTimeout = (timeout: number): number => {
@@ -761,7 +575,7 @@ export class PortProxy {
761
575
  record.bytesReceived += chunk.length;
762
576
 
763
577
  // Check for TLS handshake
764
- if (!record.isTLS && isTlsHandshake(chunk)) {
578
+ if (!record.isTLS && SniHandler.isTlsHandshake(chunk)) {
765
579
  record.isTLS = true;
766
580
 
767
581
  if (this.settings.enableTlsDebugLogging) {
@@ -1049,10 +863,10 @@ export class PortProxy {
1049
863
  // Define a handler for checking renegotiation with improved detection
1050
864
  const renegotiationHandler = (renegChunk: Buffer) => {
1051
865
  // Only process if this looks like a TLS ClientHello
1052
- if (isClientHello(renegChunk)) {
866
+ if (SniHandler.isClientHello(renegChunk)) {
1053
867
  try {
1054
868
  // Extract SNI from ClientHello
1055
- const newSNI = extractSNI(renegChunk, this.settings.enableTlsDebugLogging);
869
+ const newSNI = SniHandler.extractSNIWithResumptionSupport(renegChunk, this.settings.enableTlsDebugLogging);
1056
870
 
1057
871
  // Skip if no SNI was found
1058
872
  if (!newSNI) return;
@@ -1644,7 +1458,7 @@ export class PortProxy {
1644
1458
  connectionRecord.hasReceivedInitialData = true;
1645
1459
 
1646
1460
  // Check if this looks like a TLS handshake
1647
- if (isTlsHandshake(chunk)) {
1461
+ if (SniHandler.isTlsHandshake(chunk)) {
1648
1462
  connectionRecord.isTLS = true;
1649
1463
 
1650
1464
  // Forward directly to NetworkProxy without SNI processing
@@ -1706,7 +1520,7 @@ export class PortProxy {
1706
1520
  this.updateActivity(connectionRecord);
1707
1521
 
1708
1522
  // Check for TLS handshake if this is the first chunk
1709
- if (!connectionRecord.isTLS && isTlsHandshake(chunk)) {
1523
+ if (!connectionRecord.isTLS && SniHandler.isTlsHandshake(chunk)) {
1710
1524
  connectionRecord.isTLS = true;
1711
1525
 
1712
1526
  if (this.settings.enableTlsDebugLogging) {
@@ -1714,7 +1528,7 @@ export class PortProxy {
1714
1528
  `[${connectionId}] TLS handshake detected from ${remoteIP}, ${chunk.length} bytes`
1715
1529
  );
1716
1530
  // Try to extract SNI and log detailed debug info
1717
- extractSNI(chunk, true);
1531
+ SniHandler.extractSNIWithResumptionSupport(chunk, true);
1718
1532
  }
1719
1533
  }
1720
1534
  });
@@ -1743,7 +1557,7 @@ export class PortProxy {
1743
1557
  connectionRecord.hasReceivedInitialData = true;
1744
1558
 
1745
1559
  // Check if this looks like a TLS handshake
1746
- const isTlsHandshakeDetected = initialChunk && isTlsHandshake(initialChunk);
1560
+ const isTlsHandshakeDetected = initialChunk && SniHandler.isTlsHandshake(initialChunk);
1747
1561
  if (isTlsHandshakeDetected) {
1748
1562
  connectionRecord.isTLS = true;
1749
1563
 
@@ -1912,7 +1726,7 @@ export class PortProxy {
1912
1726
  // Try to extract SNI
1913
1727
  let serverName = '';
1914
1728
 
1915
- if (isTlsHandshake(chunk)) {
1729
+ if (SniHandler.isTlsHandshake(chunk)) {
1916
1730
  connectionRecord.isTLS = true;
1917
1731
 
1918
1732
  if (this.settings.enableTlsDebugLogging) {
@@ -1921,7 +1735,7 @@ export class PortProxy {
1921
1735
  );
1922
1736
  }
1923
1737
 
1924
- serverName = extractSNI(chunk, this.settings.enableTlsDebugLogging) || '';
1738
+ serverName = SniHandler.extractSNIWithResumptionSupport(chunk, this.settings.enableTlsDebugLogging) || '';
1925
1739
  }
1926
1740
 
1927
1741
  // Lock the connection to the negotiated SNI.