@push.rocks/smartproxy 3.37.2 → 3.37.3
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.
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartproxy',
|
|
6
|
-
version: '3.37.
|
|
6
|
+
version: '3.37.3',
|
|
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
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLG1PQUFtTztDQUNqUCxDQUFBIn0=
|
|
@@ -9,6 +9,8 @@ export declare class SniHandler {
|
|
|
9
9
|
private static readonly TLS_SNI_EXTENSION_TYPE;
|
|
10
10
|
private static readonly TLS_SESSION_TICKET_EXTENSION_TYPE;
|
|
11
11
|
private static readonly TLS_SNI_HOST_NAME_TYPE;
|
|
12
|
+
private static readonly TLS_PSK_EXTENSION_TYPE;
|
|
13
|
+
private static readonly TLS_PSK_KE_MODES_EXTENSION_TYPE;
|
|
12
14
|
/**
|
|
13
15
|
* Checks if a buffer contains a TLS handshake message (record type 22)
|
|
14
16
|
* @param buffer - The buffer to check
|
|
@@ -30,12 +32,25 @@ export declare class SniHandler {
|
|
|
30
32
|
* @returns The extracted server name or undefined if not found
|
|
31
33
|
*/
|
|
32
34
|
static extractSNI(buffer: Buffer, enableLogging?: boolean): string | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Attempts to extract SNI from the PSK extension in a TLS 1.3 ClientHello.
|
|
37
|
+
*
|
|
38
|
+
* In TLS 1.3, when a client attempts to resume a session, it may include
|
|
39
|
+
* the server name in the PSK identity hint rather than in the SNI extension.
|
|
40
|
+
*
|
|
41
|
+
* @param buffer - The buffer containing the TLS ClientHello message
|
|
42
|
+
* @param enableLogging - Whether to enable detailed debug logging
|
|
43
|
+
* @returns The extracted server name or undefined if not found
|
|
44
|
+
*/
|
|
45
|
+
static extractSNIFromPSKExtension(buffer: Buffer, enableLogging?: boolean): string | undefined;
|
|
33
46
|
/**
|
|
34
47
|
* Attempts to extract SNI from an initial ClientHello packet and handles
|
|
35
48
|
* session resumption edge cases more robustly than the standard extraction.
|
|
36
49
|
*
|
|
37
|
-
* This method
|
|
38
|
-
*
|
|
50
|
+
* This method handles:
|
|
51
|
+
* 1. Standard SNI extraction
|
|
52
|
+
* 2. TLS 1.3 PSK-based resumption (Chrome, Firefox, etc.)
|
|
53
|
+
* 3. Session ticket-based resumption
|
|
39
54
|
*
|
|
40
55
|
* @param buffer - The buffer containing the TLS ClientHello message
|
|
41
56
|
* @param enableLogging - Whether to enable detailed debug logging
|
|
@@ -10,6 +10,8 @@ export class SniHandler {
|
|
|
10
10
|
static { this.TLS_SNI_EXTENSION_TYPE = 0x0000; }
|
|
11
11
|
static { this.TLS_SESSION_TICKET_EXTENSION_TYPE = 0x0023; }
|
|
12
12
|
static { this.TLS_SNI_HOST_NAME_TYPE = 0; }
|
|
13
|
+
static { this.TLS_PSK_EXTENSION_TYPE = 0x0029; } // Pre-Shared Key extension type for TLS 1.3
|
|
14
|
+
static { this.TLS_PSK_KE_MODES_EXTENSION_TYPE = 0x002D; } // PSK Key Exchange Modes
|
|
13
15
|
/**
|
|
14
16
|
* Checks if a buffer contains a TLS handshake message (record type 22)
|
|
15
17
|
* @param buffer - The buffer to check
|
|
@@ -144,6 +146,7 @@ export class SniHandler {
|
|
|
144
146
|
}
|
|
145
147
|
// Track if we found session tickets (for improved resumption handling)
|
|
146
148
|
let hasSessionTicket = false;
|
|
149
|
+
let hasPskExtension = false;
|
|
147
150
|
// Iterate through extensions
|
|
148
151
|
while (pos + 4 <= extensionsEnd) {
|
|
149
152
|
// Parse extension type (2 bytes, big-endian)
|
|
@@ -225,14 +228,21 @@ export class SniHandler {
|
|
|
225
228
|
hasSessionTicket = true;
|
|
226
229
|
pos += extensionLength; // Skip this extension
|
|
227
230
|
}
|
|
231
|
+
else if (extensionType === this.TLS_PSK_EXTENSION_TYPE) {
|
|
232
|
+
// TLS 1.3 PSK extension - mark for resumption support
|
|
233
|
+
log('Found PSK extension (TLS 1.3 resumption indicator)');
|
|
234
|
+
hasPskExtension = true;
|
|
235
|
+
// We'll skip the extension here and process it separately if needed
|
|
236
|
+
pos += extensionLength;
|
|
237
|
+
}
|
|
228
238
|
else {
|
|
229
239
|
// Skip this extension
|
|
230
240
|
pos += extensionLength;
|
|
231
241
|
}
|
|
232
242
|
}
|
|
233
|
-
// Log if we found
|
|
234
|
-
if (hasSessionTicket) {
|
|
235
|
-
log('Session
|
|
243
|
+
// Log if we found session resumption indicators but no SNI
|
|
244
|
+
if (hasSessionTicket || hasPskExtension) {
|
|
245
|
+
log('Session resumption indicators present but no SNI found');
|
|
236
246
|
}
|
|
237
247
|
log('No SNI extension found in ClientHello');
|
|
238
248
|
return undefined;
|
|
@@ -242,12 +252,157 @@ export class SniHandler {
|
|
|
242
252
|
return undefined;
|
|
243
253
|
}
|
|
244
254
|
}
|
|
255
|
+
/**
|
|
256
|
+
* Attempts to extract SNI from the PSK extension in a TLS 1.3 ClientHello.
|
|
257
|
+
*
|
|
258
|
+
* In TLS 1.3, when a client attempts to resume a session, it may include
|
|
259
|
+
* the server name in the PSK identity hint rather than in the SNI extension.
|
|
260
|
+
*
|
|
261
|
+
* @param buffer - The buffer containing the TLS ClientHello message
|
|
262
|
+
* @param enableLogging - Whether to enable detailed debug logging
|
|
263
|
+
* @returns The extracted server name or undefined if not found
|
|
264
|
+
*/
|
|
265
|
+
static extractSNIFromPSKExtension(buffer, enableLogging = false) {
|
|
266
|
+
const log = (message) => {
|
|
267
|
+
if (enableLogging) {
|
|
268
|
+
console.log(`[PSK-SNI Extraction] ${message}`);
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
try {
|
|
272
|
+
// Ensure this is a ClientHello
|
|
273
|
+
if (!this.isClientHello(buffer)) {
|
|
274
|
+
log('Not a ClientHello message');
|
|
275
|
+
return undefined;
|
|
276
|
+
}
|
|
277
|
+
// Find the start position of extensions
|
|
278
|
+
let pos = 5; // Start after record header
|
|
279
|
+
// Skip handshake type (1 byte)
|
|
280
|
+
pos += 1;
|
|
281
|
+
// Skip handshake length (3 bytes)
|
|
282
|
+
pos += 3;
|
|
283
|
+
// Skip client version (2 bytes)
|
|
284
|
+
pos += 2;
|
|
285
|
+
// Skip client random (32 bytes)
|
|
286
|
+
pos += 32;
|
|
287
|
+
// Skip session ID
|
|
288
|
+
if (pos + 1 > buffer.length)
|
|
289
|
+
return undefined;
|
|
290
|
+
const sessionIdLength = buffer[pos];
|
|
291
|
+
pos += 1 + sessionIdLength;
|
|
292
|
+
// Skip cipher suites
|
|
293
|
+
if (pos + 2 > buffer.length)
|
|
294
|
+
return undefined;
|
|
295
|
+
const cipherSuitesLength = (buffer[pos] << 8) + buffer[pos + 1];
|
|
296
|
+
pos += 2 + cipherSuitesLength;
|
|
297
|
+
// Skip compression methods
|
|
298
|
+
if (pos + 1 > buffer.length)
|
|
299
|
+
return undefined;
|
|
300
|
+
const compressionMethodsLength = buffer[pos];
|
|
301
|
+
pos += 1 + compressionMethodsLength;
|
|
302
|
+
// Check if we have extensions
|
|
303
|
+
if (pos + 2 > buffer.length) {
|
|
304
|
+
log('No extensions present');
|
|
305
|
+
return undefined;
|
|
306
|
+
}
|
|
307
|
+
// Get extensions length
|
|
308
|
+
const extensionsLength = (buffer[pos] << 8) + buffer[pos + 1];
|
|
309
|
+
pos += 2;
|
|
310
|
+
// Extensions end position
|
|
311
|
+
const extensionsEnd = pos + extensionsLength;
|
|
312
|
+
if (extensionsEnd > buffer.length)
|
|
313
|
+
return undefined;
|
|
314
|
+
// Look for PSK extension
|
|
315
|
+
while (pos + 4 <= extensionsEnd) {
|
|
316
|
+
const extensionType = (buffer[pos] << 8) + buffer[pos + 1];
|
|
317
|
+
pos += 2;
|
|
318
|
+
const extensionLength = (buffer[pos] << 8) + buffer[pos + 1];
|
|
319
|
+
pos += 2;
|
|
320
|
+
if (extensionType === this.TLS_PSK_EXTENSION_TYPE) {
|
|
321
|
+
log('Found PSK extension');
|
|
322
|
+
// PSK extension structure:
|
|
323
|
+
// 2 bytes: identities list length
|
|
324
|
+
if (pos + 2 > extensionsEnd)
|
|
325
|
+
break;
|
|
326
|
+
const identitiesLength = (buffer[pos] << 8) + buffer[pos + 1];
|
|
327
|
+
pos += 2;
|
|
328
|
+
// End of identities list
|
|
329
|
+
const identitiesEnd = pos + identitiesLength;
|
|
330
|
+
if (identitiesEnd > extensionsEnd)
|
|
331
|
+
break;
|
|
332
|
+
// Process each PSK identity
|
|
333
|
+
while (pos + 2 <= identitiesEnd) {
|
|
334
|
+
// Identity length (2 bytes)
|
|
335
|
+
if (pos + 2 > identitiesEnd)
|
|
336
|
+
break;
|
|
337
|
+
const identityLength = (buffer[pos] << 8) + buffer[pos + 1];
|
|
338
|
+
pos += 2;
|
|
339
|
+
if (pos + identityLength > identitiesEnd)
|
|
340
|
+
break;
|
|
341
|
+
// Try to extract hostname from identity
|
|
342
|
+
// Chrome often embeds the hostname in the PSK identity
|
|
343
|
+
// This is a heuristic as there's no standard format
|
|
344
|
+
if (identityLength > 0) {
|
|
345
|
+
const identity = buffer.slice(pos, pos + identityLength);
|
|
346
|
+
// Skip identity bytes
|
|
347
|
+
pos += identityLength;
|
|
348
|
+
// Skip obfuscated ticket age (4 bytes)
|
|
349
|
+
pos += 4;
|
|
350
|
+
// Try to parse the identity as UTF-8
|
|
351
|
+
try {
|
|
352
|
+
const identityStr = identity.toString('utf8');
|
|
353
|
+
log(`PSK identity: ${identityStr}`);
|
|
354
|
+
// Check if the identity contains hostname hints
|
|
355
|
+
// Chrome often embeds the hostname in a known format
|
|
356
|
+
// Try to extract using common patterns
|
|
357
|
+
// Pattern 1: Look for domain name pattern
|
|
358
|
+
const domainPattern = /([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?/i;
|
|
359
|
+
const domainMatch = identityStr.match(domainPattern);
|
|
360
|
+
if (domainMatch && domainMatch[0]) {
|
|
361
|
+
log(`Found domain in PSK identity: ${domainMatch[0]}`);
|
|
362
|
+
return domainMatch[0];
|
|
363
|
+
}
|
|
364
|
+
// Pattern 2: Chrome sometimes uses a specific format with delimiters
|
|
365
|
+
// This is a heuristic approach since the format isn't standardized
|
|
366
|
+
const parts = identityStr.split('|');
|
|
367
|
+
if (parts.length > 1) {
|
|
368
|
+
for (const part of parts) {
|
|
369
|
+
if (part.includes('.') && !part.includes('/')) {
|
|
370
|
+
const possibleDomain = part.trim();
|
|
371
|
+
if (/^[a-z0-9.-]+$/i.test(possibleDomain)) {
|
|
372
|
+
log(`Found possible domain in PSK delimiter format: ${possibleDomain}`);
|
|
373
|
+
return possibleDomain;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
catch (e) {
|
|
380
|
+
log('Failed to parse PSK identity as UTF-8');
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
// Skip this extension
|
|
387
|
+
pos += extensionLength;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
log('No hostname found in PSK extension');
|
|
391
|
+
return undefined;
|
|
392
|
+
}
|
|
393
|
+
catch (error) {
|
|
394
|
+
log(`Error parsing PSK: ${error instanceof Error ? error.message : String(error)}`);
|
|
395
|
+
return undefined;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
245
398
|
/**
|
|
246
399
|
* Attempts to extract SNI from an initial ClientHello packet and handles
|
|
247
400
|
* session resumption edge cases more robustly than the standard extraction.
|
|
248
401
|
*
|
|
249
|
-
* This method
|
|
250
|
-
*
|
|
402
|
+
* This method handles:
|
|
403
|
+
* 1. Standard SNI extraction
|
|
404
|
+
* 2. TLS 1.3 PSK-based resumption (Chrome, Firefox, etc.)
|
|
405
|
+
* 3. Session ticket-based resumption
|
|
251
406
|
*
|
|
252
407
|
* @param buffer - The buffer containing the TLS ClientHello message
|
|
253
408
|
* @param enableLogging - Whether to enable detailed debug logging
|
|
@@ -257,6 +412,9 @@ export class SniHandler {
|
|
|
257
412
|
// First try the standard SNI extraction
|
|
258
413
|
const standardSni = this.extractSNI(buffer, enableLogging);
|
|
259
414
|
if (standardSni) {
|
|
415
|
+
if (enableLogging) {
|
|
416
|
+
console.log(`[SNI Extraction] Found standard SNI: ${standardSni}`);
|
|
417
|
+
}
|
|
260
418
|
return standardSni;
|
|
261
419
|
}
|
|
262
420
|
// If standard extraction failed and we have a valid ClientHello,
|
|
@@ -265,10 +423,20 @@ export class SniHandler {
|
|
|
265
423
|
if (enableLogging) {
|
|
266
424
|
console.log('[SNI Extraction] Detected ClientHello without standard SNI, possible session resumption');
|
|
267
425
|
}
|
|
268
|
-
//
|
|
269
|
-
|
|
426
|
+
// Try to extract from PSK extension (TLS 1.3 resumption)
|
|
427
|
+
const pskSni = this.extractSNIFromPSKExtension(buffer, enableLogging);
|
|
428
|
+
if (pskSni) {
|
|
429
|
+
if (enableLogging) {
|
|
430
|
+
console.log(`[SNI Extraction] Extracted SNI from PSK extension: ${pskSni}`);
|
|
431
|
+
}
|
|
432
|
+
return pskSni;
|
|
433
|
+
}
|
|
434
|
+
// Could add more browser-specific heuristics here if needed
|
|
435
|
+
if (enableLogging) {
|
|
436
|
+
console.log('[SNI Extraction] Failed to extract SNI from resumption mechanisms');
|
|
437
|
+
}
|
|
270
438
|
}
|
|
271
439
|
return undefined;
|
|
272
440
|
}
|
|
273
441
|
}
|
|
274
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5zbmloYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2xhc3Nlcy5zbmloYW5kbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFFaEM7OztHQUdHO0FBQ0gsTUFBTSxPQUFPLFVBQVU7SUFDckIsaUNBQWlDO2FBQ1QsOEJBQXlCLEdBQUcsRUFBRSxDQUFDO2FBQy9CLG9DQUErQixHQUFHLENBQUMsQ0FBQzthQUNwQywyQkFBc0IsR0FBRyxNQUFNLENBQUM7YUFDaEMsc0NBQWlDLEdBQUcsTUFBTSxDQUFDO2FBQzNDLDJCQUFzQixHQUFHLENBQUMsQ0FBQztJQUVuRDs7OztPQUlHO0lBQ0ksTUFBTSxDQUFDLGNBQWMsQ0FBQyxNQUFjO1FBQ3pDLE9BQU8sTUFBTSxDQUFDLE1BQU0sR0FBRyxDQUFDLElBQUksTUFBTSxDQUFDLENBQUMsQ0FBQyxLQUFLLElBQUksQ0FBQyx5QkFBeUIsQ0FBQztJQUMzRSxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLE1BQU0sQ0FBQyxhQUFhLENBQUMsTUFBYztRQUN4QyxrRUFBa0U7UUFDbEUsSUFBSSxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ3RCLE9BQU8sS0FBSyxDQUFDO1FBQ2YsQ0FBQztRQUVELHdEQUF3RDtRQUN4RCxJQUFJLE1BQU0sQ0FBQyxDQUFDLENBQUMsS0FBSyxJQUFJLENBQUMseUJBQXlCLEVBQUUsQ0FBQztZQUNqRCxPQUFPLEtBQUssQ0FBQztRQUNmLENBQUM7UUFFRCwrREFBK0Q7UUFDL0Qsd0RBQXdEO1FBQ3hELE9BQU8sTUFBTSxDQUFDLENBQUMsQ0FBQyxLQUFLLElBQUksQ0FBQywrQkFBK0IsQ0FBQztJQUM1RCxDQUFDO0lBRUQ7Ozs7Ozs7T0FPRztJQUNJLE1BQU0sQ0FBQyxVQUFVLENBQUMsTUFBYyxFQUFFLGdCQUF5QixLQUFLO1FBQ3JFLGlCQUFp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
|
|
442
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5zbmloYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2xhc3Nlcy5zbmloYW5kbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFFaEM7OztHQUdHO0FBQ0gsTUFBTSxPQUFPLFVBQVU7SUFDckIsaUNBQWlDO2FBQ1QsOEJBQXlCLEdBQUcsRUFBRSxDQUFDO2FBQy9CLG9DQUErQixHQUFHLENBQUMsQ0FBQzthQUNwQywyQkFBc0IsR0FBRyxNQUFNLENBQUM7YUFDaEMsc0NBQWlDLEdBQUcsTUFBTSxDQUFDO2FBQzNDLDJCQUFzQixHQUFHLENBQUMsQ0FBQzthQUMzQiwyQkFBc0IsR0FBRyxNQUFNLENBQUMsR0FBQyw0Q0FBNEM7YUFDN0Usb0NBQStCLEdBQUcsTUFBTSxDQUFDLEdBQUMseUJBQXlCO0lBRTNGOzs7O09BSUc7SUFDSSxNQUFNLENBQUMsY0FBYyxDQUFDLE1BQWM7UUFDekMsT0FBTyxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUMsSUFBSSxNQUFNLENBQUMsQ0FBQyxDQUFDLEtBQUssSUFBSSxDQUFDLHlCQUF5QixDQUFDO0lBQzNFLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksTUFBTSxDQUFDLGFBQWEsQ0FBQyxNQUFjO1FBQ3hDLGtFQUFrRTtRQUNsRSxJQUFJLE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDdEIsT0FBTyxLQUFLLENBQUM7UUFDZixDQUFDO1FBRUQsd0RBQXdEO1FBQ3hELElBQUksTUFBTSxDQUFDLENBQUMsQ0FBQyxLQUFLLElBQUksQ0FBQyx5QkFBeUIsRUFBRSxDQUFDO1lBQ2pELE9BQU8sS0FBSyxDQUFDO1FBQ2YsQ0FBQztRQUVELCtEQUErRDtRQUMvRCx3REFBd0Q7UUFDeEQsT0FBTyxNQUFNLENBQUMsQ0FBQyxDQUFDLEtBQUssSUFBSSxDQUFDLCtCQUErQixDQUFDO0lBQzVE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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@push.rocks/smartproxy",
|
|
3
|
-
"version": "3.37.
|
|
3
|
+
"version": "3.37.3",
|
|
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",
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartproxy',
|
|
6
|
-
version: '3.37.
|
|
6
|
+
version: '3.37.3',
|
|
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
|
}
|
package/ts/classes.snihandler.ts
CHANGED
|
@@ -11,6 +11,8 @@ export class SniHandler {
|
|
|
11
11
|
private static readonly TLS_SNI_EXTENSION_TYPE = 0x0000;
|
|
12
12
|
private static readonly TLS_SESSION_TICKET_EXTENSION_TYPE = 0x0023;
|
|
13
13
|
private static readonly TLS_SNI_HOST_NAME_TYPE = 0;
|
|
14
|
+
private static readonly TLS_PSK_EXTENSION_TYPE = 0x0029; // Pre-Shared Key extension type for TLS 1.3
|
|
15
|
+
private static readonly TLS_PSK_KE_MODES_EXTENSION_TYPE = 0x002D; // PSK Key Exchange Modes
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* Checks if a buffer contains a TLS handshake message (record type 22)
|
|
@@ -178,6 +180,7 @@ export class SniHandler {
|
|
|
178
180
|
|
|
179
181
|
// Track if we found session tickets (for improved resumption handling)
|
|
180
182
|
let hasSessionTicket = false;
|
|
183
|
+
let hasPskExtension = false;
|
|
181
184
|
|
|
182
185
|
// Iterate through extensions
|
|
183
186
|
while (pos + 4 <= extensionsEnd) {
|
|
@@ -275,15 +278,21 @@ export class SniHandler {
|
|
|
275
278
|
log('Found session ticket extension');
|
|
276
279
|
hasSessionTicket = true;
|
|
277
280
|
pos += extensionLength; // Skip this extension
|
|
281
|
+
} else if (extensionType === this.TLS_PSK_EXTENSION_TYPE) {
|
|
282
|
+
// TLS 1.3 PSK extension - mark for resumption support
|
|
283
|
+
log('Found PSK extension (TLS 1.3 resumption indicator)');
|
|
284
|
+
hasPskExtension = true;
|
|
285
|
+
// We'll skip the extension here and process it separately if needed
|
|
286
|
+
pos += extensionLength;
|
|
278
287
|
} else {
|
|
279
288
|
// Skip this extension
|
|
280
289
|
pos += extensionLength;
|
|
281
290
|
}
|
|
282
291
|
}
|
|
283
292
|
|
|
284
|
-
// Log if we found
|
|
285
|
-
if (hasSessionTicket) {
|
|
286
|
-
log('Session
|
|
293
|
+
// Log if we found session resumption indicators but no SNI
|
|
294
|
+
if (hasSessionTicket || hasPskExtension) {
|
|
295
|
+
log('Session resumption indicators present but no SNI found');
|
|
287
296
|
}
|
|
288
297
|
|
|
289
298
|
log('No SNI extension found in ClientHello');
|
|
@@ -294,12 +303,177 @@ export class SniHandler {
|
|
|
294
303
|
}
|
|
295
304
|
}
|
|
296
305
|
|
|
306
|
+
/**
|
|
307
|
+
* Attempts to extract SNI from the PSK extension in a TLS 1.3 ClientHello.
|
|
308
|
+
*
|
|
309
|
+
* In TLS 1.3, when a client attempts to resume a session, it may include
|
|
310
|
+
* the server name in the PSK identity hint rather than in the SNI extension.
|
|
311
|
+
*
|
|
312
|
+
* @param buffer - The buffer containing the TLS ClientHello message
|
|
313
|
+
* @param enableLogging - Whether to enable detailed debug logging
|
|
314
|
+
* @returns The extracted server name or undefined if not found
|
|
315
|
+
*/
|
|
316
|
+
public static extractSNIFromPSKExtension(
|
|
317
|
+
buffer: Buffer,
|
|
318
|
+
enableLogging: boolean = false
|
|
319
|
+
): string | undefined {
|
|
320
|
+
const log = (message: string) => {
|
|
321
|
+
if (enableLogging) {
|
|
322
|
+
console.log(`[PSK-SNI Extraction] ${message}`);
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
try {
|
|
327
|
+
// Ensure this is a ClientHello
|
|
328
|
+
if (!this.isClientHello(buffer)) {
|
|
329
|
+
log('Not a ClientHello message');
|
|
330
|
+
return undefined;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Find the start position of extensions
|
|
334
|
+
let pos = 5; // Start after record header
|
|
335
|
+
|
|
336
|
+
// Skip handshake type (1 byte)
|
|
337
|
+
pos += 1;
|
|
338
|
+
|
|
339
|
+
// Skip handshake length (3 bytes)
|
|
340
|
+
pos += 3;
|
|
341
|
+
|
|
342
|
+
// Skip client version (2 bytes)
|
|
343
|
+
pos += 2;
|
|
344
|
+
|
|
345
|
+
// Skip client random (32 bytes)
|
|
346
|
+
pos += 32;
|
|
347
|
+
|
|
348
|
+
// Skip session ID
|
|
349
|
+
if (pos + 1 > buffer.length) return undefined;
|
|
350
|
+
const sessionIdLength = buffer[pos];
|
|
351
|
+
pos += 1 + sessionIdLength;
|
|
352
|
+
|
|
353
|
+
// Skip cipher suites
|
|
354
|
+
if (pos + 2 > buffer.length) return undefined;
|
|
355
|
+
const cipherSuitesLength = (buffer[pos] << 8) + buffer[pos + 1];
|
|
356
|
+
pos += 2 + cipherSuitesLength;
|
|
357
|
+
|
|
358
|
+
// Skip compression methods
|
|
359
|
+
if (pos + 1 > buffer.length) return undefined;
|
|
360
|
+
const compressionMethodsLength = buffer[pos];
|
|
361
|
+
pos += 1 + compressionMethodsLength;
|
|
362
|
+
|
|
363
|
+
// Check if we have extensions
|
|
364
|
+
if (pos + 2 > buffer.length) {
|
|
365
|
+
log('No extensions present');
|
|
366
|
+
return undefined;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Get extensions length
|
|
370
|
+
const extensionsLength = (buffer[pos] << 8) + buffer[pos + 1];
|
|
371
|
+
pos += 2;
|
|
372
|
+
|
|
373
|
+
// Extensions end position
|
|
374
|
+
const extensionsEnd = pos + extensionsLength;
|
|
375
|
+
if (extensionsEnd > buffer.length) return undefined;
|
|
376
|
+
|
|
377
|
+
// Look for PSK extension
|
|
378
|
+
while (pos + 4 <= extensionsEnd) {
|
|
379
|
+
const extensionType = (buffer[pos] << 8) + buffer[pos + 1];
|
|
380
|
+
pos += 2;
|
|
381
|
+
|
|
382
|
+
const extensionLength = (buffer[pos] << 8) + buffer[pos + 1];
|
|
383
|
+
pos += 2;
|
|
384
|
+
|
|
385
|
+
if (extensionType === this.TLS_PSK_EXTENSION_TYPE) {
|
|
386
|
+
log('Found PSK extension');
|
|
387
|
+
|
|
388
|
+
// PSK extension structure:
|
|
389
|
+
// 2 bytes: identities list length
|
|
390
|
+
if (pos + 2 > extensionsEnd) break;
|
|
391
|
+
const identitiesLength = (buffer[pos] << 8) + buffer[pos + 1];
|
|
392
|
+
pos += 2;
|
|
393
|
+
|
|
394
|
+
// End of identities list
|
|
395
|
+
const identitiesEnd = pos + identitiesLength;
|
|
396
|
+
if (identitiesEnd > extensionsEnd) break;
|
|
397
|
+
|
|
398
|
+
// Process each PSK identity
|
|
399
|
+
while (pos + 2 <= identitiesEnd) {
|
|
400
|
+
// Identity length (2 bytes)
|
|
401
|
+
if (pos + 2 > identitiesEnd) break;
|
|
402
|
+
const identityLength = (buffer[pos] << 8) + buffer[pos + 1];
|
|
403
|
+
pos += 2;
|
|
404
|
+
|
|
405
|
+
if (pos + identityLength > identitiesEnd) break;
|
|
406
|
+
|
|
407
|
+
// Try to extract hostname from identity
|
|
408
|
+
// Chrome often embeds the hostname in the PSK identity
|
|
409
|
+
// This is a heuristic as there's no standard format
|
|
410
|
+
if (identityLength > 0) {
|
|
411
|
+
const identity = buffer.slice(pos, pos + identityLength);
|
|
412
|
+
|
|
413
|
+
// Skip identity bytes
|
|
414
|
+
pos += identityLength;
|
|
415
|
+
|
|
416
|
+
// Skip obfuscated ticket age (4 bytes)
|
|
417
|
+
pos += 4;
|
|
418
|
+
|
|
419
|
+
// Try to parse the identity as UTF-8
|
|
420
|
+
try {
|
|
421
|
+
const identityStr = identity.toString('utf8');
|
|
422
|
+
log(`PSK identity: ${identityStr}`);
|
|
423
|
+
|
|
424
|
+
// Check if the identity contains hostname hints
|
|
425
|
+
// Chrome often embeds the hostname in a known format
|
|
426
|
+
// Try to extract using common patterns
|
|
427
|
+
|
|
428
|
+
// Pattern 1: Look for domain name pattern
|
|
429
|
+
const domainPattern = /([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?/i;
|
|
430
|
+
const domainMatch = identityStr.match(domainPattern);
|
|
431
|
+
if (domainMatch && domainMatch[0]) {
|
|
432
|
+
log(`Found domain in PSK identity: ${domainMatch[0]}`);
|
|
433
|
+
return domainMatch[0];
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Pattern 2: Chrome sometimes uses a specific format with delimiters
|
|
437
|
+
// This is a heuristic approach since the format isn't standardized
|
|
438
|
+
const parts = identityStr.split('|');
|
|
439
|
+
if (parts.length > 1) {
|
|
440
|
+
for (const part of parts) {
|
|
441
|
+
if (part.includes('.') && !part.includes('/')) {
|
|
442
|
+
const possibleDomain = part.trim();
|
|
443
|
+
if (/^[a-z0-9.-]+$/i.test(possibleDomain)) {
|
|
444
|
+
log(`Found possible domain in PSK delimiter format: ${possibleDomain}`);
|
|
445
|
+
return possibleDomain;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
} catch (e) {
|
|
451
|
+
log('Failed to parse PSK identity as UTF-8');
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
} else {
|
|
456
|
+
// Skip this extension
|
|
457
|
+
pos += extensionLength;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
log('No hostname found in PSK extension');
|
|
462
|
+
return undefined;
|
|
463
|
+
} catch (error) {
|
|
464
|
+
log(`Error parsing PSK: ${error instanceof Error ? error.message : String(error)}`);
|
|
465
|
+
return undefined;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
297
469
|
/**
|
|
298
470
|
* Attempts to extract SNI from an initial ClientHello packet and handles
|
|
299
471
|
* session resumption edge cases more robustly than the standard extraction.
|
|
300
472
|
*
|
|
301
|
-
* This method
|
|
302
|
-
*
|
|
473
|
+
* This method handles:
|
|
474
|
+
* 1. Standard SNI extraction
|
|
475
|
+
* 2. TLS 1.3 PSK-based resumption (Chrome, Firefox, etc.)
|
|
476
|
+
* 3. Session ticket-based resumption
|
|
303
477
|
*
|
|
304
478
|
* @param buffer - The buffer containing the TLS ClientHello message
|
|
305
479
|
* @param enableLogging - Whether to enable detailed debug logging
|
|
@@ -312,6 +486,9 @@ export class SniHandler {
|
|
|
312
486
|
// First try the standard SNI extraction
|
|
313
487
|
const standardSni = this.extractSNI(buffer, enableLogging);
|
|
314
488
|
if (standardSni) {
|
|
489
|
+
if (enableLogging) {
|
|
490
|
+
console.log(`[SNI Extraction] Found standard SNI: ${standardSni}`);
|
|
491
|
+
}
|
|
315
492
|
return standardSni;
|
|
316
493
|
}
|
|
317
494
|
|
|
@@ -322,8 +499,19 @@ export class SniHandler {
|
|
|
322
499
|
console.log('[SNI Extraction] Detected ClientHello without standard SNI, possible session resumption');
|
|
323
500
|
}
|
|
324
501
|
|
|
325
|
-
//
|
|
326
|
-
|
|
502
|
+
// Try to extract from PSK extension (TLS 1.3 resumption)
|
|
503
|
+
const pskSni = this.extractSNIFromPSKExtension(buffer, enableLogging);
|
|
504
|
+
if (pskSni) {
|
|
505
|
+
if (enableLogging) {
|
|
506
|
+
console.log(`[SNI Extraction] Extracted SNI from PSK extension: ${pskSni}`);
|
|
507
|
+
}
|
|
508
|
+
return pskSni;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Could add more browser-specific heuristics here if needed
|
|
512
|
+
if (enableLogging) {
|
|
513
|
+
console.log('[SNI Extraction] Failed to extract SNI from resumption mechanisms');
|
|
514
|
+
}
|
|
327
515
|
}
|
|
328
516
|
|
|
329
517
|
return undefined;
|