@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.
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/certificate/acme/acme-factory.d.ts +17 -0
- package/dist_ts/certificate/acme/acme-factory.js +40 -0
- package/dist_ts/certificate/acme/challenge-handler.d.ts +44 -0
- package/dist_ts/certificate/acme/challenge-handler.js +92 -0
- package/dist_ts/certificate/acme/index.d.ts +4 -0
- package/dist_ts/certificate/acme/index.js +5 -0
- package/dist_ts/certificate/events/certificate-events.d.ts +33 -0
- package/dist_ts/certificate/events/certificate-events.js +38 -0
- package/dist_ts/certificate/index.d.ts +24 -0
- package/dist_ts/certificate/index.js +39 -0
- package/dist_ts/certificate/models/certificate-types.d.ts +77 -0
- package/dist_ts/certificate/models/certificate-types.js +2 -0
- package/dist_ts/certificate/providers/cert-provisioner.d.ts +93 -0
- package/dist_ts/certificate/providers/cert-provisioner.js +262 -0
- package/dist_ts/certificate/providers/index.d.ts +4 -0
- package/dist_ts/certificate/providers/index.js +5 -0
- package/dist_ts/certificate/storage/file-storage.d.ts +66 -0
- package/dist_ts/certificate/storage/file-storage.js +194 -0
- package/dist_ts/certificate/storage/index.d.ts +4 -0
- package/dist_ts/certificate/storage/index.js +5 -0
- package/dist_ts/certificate/utils/certificate-helpers.d.ts +17 -0
- package/dist_ts/certificate/utils/certificate-helpers.js +45 -0
- package/dist_ts/common/eventUtils.d.ts +1 -1
- package/dist_ts/common/port80-adapter.d.ts +1 -1
- package/dist_ts/core/events/index.d.ts +4 -0
- package/dist_ts/core/events/index.js +5 -0
- package/dist_ts/core/index.d.ts +6 -0
- package/dist_ts/core/index.js +8 -0
- package/dist_ts/core/models/common-types.d.ts +82 -0
- package/dist_ts/core/models/common-types.js +15 -0
- package/dist_ts/core/models/index.d.ts +4 -0
- package/dist_ts/core/models/index.js +5 -0
- package/dist_ts/core/utils/event-utils.d.ts +15 -0
- package/dist_ts/core/utils/event-utils.js +19 -0
- package/dist_ts/core/utils/index.d.ts +6 -0
- package/dist_ts/core/utils/index.js +7 -0
- package/dist_ts/core/utils/ip-utils.d.ts +53 -0
- package/dist_ts/core/utils/ip-utils.js +153 -0
- package/dist_ts/core/utils/validation-utils.d.ts +61 -0
- package/dist_ts/core/utils/validation-utils.js +149 -0
- package/dist_ts/forwarding/config/domain-config.d.ts +12 -0
- package/dist_ts/forwarding/config/domain-config.js +12 -0
- package/dist_ts/forwarding/config/domain-manager.d.ts +86 -0
- package/dist_ts/forwarding/config/domain-manager.js +242 -0
- package/dist_ts/forwarding/config/forwarding-types.d.ts +104 -0
- package/dist_ts/forwarding/config/forwarding-types.js +50 -0
- package/dist_ts/forwarding/config/index.d.ts +6 -0
- package/dist_ts/forwarding/config/index.js +7 -0
- package/dist_ts/forwarding/factory/forwarding-factory.d.ts +25 -0
- package/dist_ts/forwarding/factory/forwarding-factory.js +138 -0
- package/dist_ts/forwarding/factory/index.d.ts +4 -0
- package/dist_ts/forwarding/factory/index.js +5 -0
- package/dist_ts/forwarding/handlers/base-handler.d.ts +55 -0
- package/dist_ts/forwarding/handlers/base-handler.js +94 -0
- package/dist_ts/forwarding/handlers/http-handler.d.ts +30 -0
- package/dist_ts/forwarding/handlers/http-handler.js +131 -0
- package/dist_ts/forwarding/handlers/https-passthrough-handler.d.ts +29 -0
- package/dist_ts/forwarding/handlers/https-passthrough-handler.js +162 -0
- package/dist_ts/forwarding/handlers/https-terminate-to-http-handler.d.ts +36 -0
- package/dist_ts/forwarding/handlers/https-terminate-to-http-handler.js +229 -0
- package/dist_ts/forwarding/handlers/https-terminate-to-https-handler.d.ts +35 -0
- package/dist_ts/forwarding/handlers/https-terminate-to-https-handler.js +254 -0
- package/dist_ts/forwarding/handlers/index.d.ts +8 -0
- package/dist_ts/forwarding/handlers/index.js +9 -0
- package/dist_ts/forwarding/index.d.ts +19 -0
- package/dist_ts/forwarding/index.js +25 -0
- package/dist_ts/http/index.d.ts +15 -0
- package/dist_ts/http/index.js +20 -0
- package/dist_ts/http/models/http-types.d.ts +81 -0
- package/dist_ts/http/models/http-types.js +62 -0
- package/dist_ts/http/port80/acme-interfaces.d.ts +78 -0
- package/dist_ts/http/port80/acme-interfaces.js +6 -0
- package/dist_ts/http/port80/challenge-responder.d.ts +53 -0
- package/dist_ts/http/port80/challenge-responder.js +203 -0
- package/dist_ts/http/port80/index.d.ts +6 -0
- package/dist_ts/http/port80/index.js +9 -0
- package/dist_ts/http/port80/port80-handler.d.ts +121 -0
- package/dist_ts/http/port80/port80-handler.js +554 -0
- package/dist_ts/http/redirects/index.d.ts +4 -0
- package/dist_ts/http/redirects/index.js +5 -0
- package/dist_ts/http/router/index.d.ts +4 -0
- package/dist_ts/http/router/index.js +5 -0
- package/dist_ts/http/router/proxy-router.d.ts +115 -0
- package/dist_ts/http/router/proxy-router.js +325 -0
- package/dist_ts/index.d.ts +15 -8
- package/dist_ts/index.js +26 -10
- package/dist_ts/networkproxy/classes.np.certificatemanager.js +2 -2
- package/dist_ts/networkproxy/index.d.ts +1 -6
- package/dist_ts/networkproxy/index.js +4 -8
- package/dist_ts/plugins.d.ts +2 -1
- package/dist_ts/plugins.js +3 -2
- package/dist_ts/port80handler/classes.port80handler.d.ts +8 -136
- package/dist_ts/port80handler/classes.port80handler.js +14 -567
- package/dist_ts/proxies/index.d.ts +6 -0
- package/dist_ts/proxies/index.js +8 -0
- package/dist_ts/proxies/network-proxy/certificate-manager.d.ts +77 -0
- package/dist_ts/proxies/network-proxy/certificate-manager.js +373 -0
- package/dist_ts/proxies/network-proxy/connection-pool.d.ts +47 -0
- package/dist_ts/proxies/network-proxy/connection-pool.js +210 -0
- package/dist_ts/proxies/network-proxy/index.d.ts +10 -0
- package/dist_ts/proxies/network-proxy/index.js +12 -0
- package/dist_ts/proxies/network-proxy/models/index.d.ts +4 -0
- package/dist_ts/proxies/network-proxy/models/index.js +5 -0
- package/dist_ts/proxies/network-proxy/models/types.d.ts +80 -0
- package/dist_ts/proxies/network-proxy/models/types.js +35 -0
- package/dist_ts/proxies/network-proxy/network-proxy.d.ts +118 -0
- package/dist_ts/proxies/network-proxy/network-proxy.js +387 -0
- package/dist_ts/proxies/network-proxy/request-handler.d.ts +57 -0
- package/dist_ts/proxies/network-proxy/request-handler.js +394 -0
- package/dist_ts/proxies/network-proxy/websocket-handler.d.ts +38 -0
- package/dist_ts/proxies/network-proxy/websocket-handler.js +188 -0
- package/dist_ts/proxies/nftables-proxy/index.d.ts +5 -0
- package/dist_ts/proxies/nftables-proxy/index.js +6 -0
- package/dist_ts/proxies/nftables-proxy/models/errors.d.ts +15 -0
- package/dist_ts/proxies/nftables-proxy/models/errors.js +28 -0
- package/dist_ts/proxies/nftables-proxy/models/index.d.ts +5 -0
- package/dist_ts/proxies/nftables-proxy/models/index.js +6 -0
- package/dist_ts/proxies/nftables-proxy/models/interfaces.d.ts +75 -0
- package/dist_ts/proxies/nftables-proxy/models/interfaces.js +5 -0
- package/dist_ts/proxies/nftables-proxy/nftables-proxy.d.ts +136 -0
- package/dist_ts/proxies/nftables-proxy/nftables-proxy.js +1516 -0
- package/dist_ts/proxies/smart-proxy/connection-handler.d.ts +39 -0
- package/dist_ts/proxies/smart-proxy/connection-handler.js +894 -0
- package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +78 -0
- package/dist_ts/proxies/smart-proxy/connection-manager.js +378 -0
- package/dist_ts/proxies/smart-proxy/domain-config-manager.d.ts +95 -0
- package/dist_ts/proxies/smart-proxy/domain-config-manager.js +255 -0
- package/dist_ts/proxies/smart-proxy/index.d.ts +13 -0
- package/dist_ts/proxies/smart-proxy/index.js +17 -0
- package/dist_ts/proxies/smart-proxy/models/index.d.ts +4 -0
- package/dist_ts/proxies/smart-proxy/models/index.js +5 -0
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +107 -0
- package/dist_ts/proxies/smart-proxy/models/interfaces.js +2 -0
- package/dist_ts/proxies/smart-proxy/network-proxy-bridge.d.ts +62 -0
- package/dist_ts/proxies/smart-proxy/network-proxy-bridge.js +316 -0
- package/dist_ts/proxies/smart-proxy/port-range-manager.d.ts +56 -0
- package/dist_ts/proxies/smart-proxy/port-range-manager.js +176 -0
- package/dist_ts/proxies/smart-proxy/security-manager.d.ts +64 -0
- package/dist_ts/proxies/smart-proxy/security-manager.js +149 -0
- package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +63 -0
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +523 -0
- package/dist_ts/proxies/smart-proxy/timeout-manager.d.ts +47 -0
- package/dist_ts/proxies/smart-proxy/timeout-manager.js +154 -0
- package/dist_ts/proxies/smart-proxy/tls-manager.d.ts +57 -0
- package/dist_ts/proxies/smart-proxy/tls-manager.js +132 -0
- package/dist_ts/smartproxy/classes.pp.networkproxybridge.d.ts +2 -2
- package/dist_ts/smartproxy/classes.pp.networkproxybridge.js +1 -1
- package/dist_ts/smartproxy/classes.pp.tlsmanager.js +2 -2
- package/dist_ts/smartproxy/classes.smartproxy.js +3 -3
- package/dist_ts/tls/alerts/index.d.ts +4 -0
- package/dist_ts/tls/alerts/index.js +5 -0
- package/dist_ts/tls/alerts/tls-alert.d.ts +150 -0
- package/dist_ts/tls/alerts/tls-alert.js +226 -0
- package/dist_ts/tls/index.d.ts +18 -0
- package/dist_ts/tls/index.js +27 -0
- package/dist_ts/tls/sni/client-hello-parser.d.ts +100 -0
- package/dist_ts/tls/sni/client-hello-parser.js +463 -0
- package/dist_ts/tls/sni/index.d.ts +4 -0
- package/dist_ts/tls/sni/index.js +5 -0
- package/dist_ts/tls/sni/sni-extraction.d.ts +58 -0
- package/dist_ts/tls/sni/sni-extraction.js +275 -0
- package/dist_ts/tls/sni/sni-handler.d.ts +154 -0
- package/dist_ts/tls/sni/sni-handler.js +191 -0
- package/dist_ts/tls/utils/index.d.ts +4 -0
- package/dist_ts/tls/utils/index.js +5 -0
- package/dist_ts/tls/utils/tls-utils.d.ts +158 -0
- package/dist_ts/tls/utils/tls-utils.js +187 -0
- package/package.json +1 -1
- package/readme.md +89 -21
- package/readme.plan.md +253 -469
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/certificate/acme/acme-factory.ts +48 -0
- package/ts/certificate/acme/challenge-handler.ts +110 -0
- package/ts/certificate/acme/index.ts +3 -0
- package/ts/certificate/events/certificate-events.ts +36 -0
- package/ts/certificate/index.ts +67 -0
- package/ts/certificate/models/certificate-types.ts +88 -0
- package/ts/certificate/providers/cert-provisioner.ts +326 -0
- package/ts/certificate/providers/index.ts +3 -0
- package/ts/certificate/storage/file-storage.ts +234 -0
- package/ts/certificate/storage/index.ts +3 -0
- package/ts/certificate/utils/certificate-helpers.ts +50 -0
- package/ts/common/eventUtils.ts +1 -1
- package/ts/common/port80-adapter.ts +1 -1
- package/ts/core/events/index.ts +3 -0
- package/ts/core/index.ts +8 -0
- package/ts/core/models/common-types.ts +91 -0
- package/ts/core/models/index.ts +5 -0
- package/ts/core/utils/event-utils.ts +34 -0
- package/ts/core/utils/index.ts +7 -0
- package/ts/core/utils/ip-utils.ts +175 -0
- package/ts/core/utils/validation-utils.ts +177 -0
- package/ts/{smartproxy/forwarding → forwarding/config}/domain-config.ts +1 -1
- package/ts/{smartproxy/forwarding → forwarding/config}/domain-manager.ts +8 -8
- package/ts/{smartproxy/types/forwarding.types.ts → forwarding/config/forwarding-types.ts} +6 -6
- package/ts/forwarding/config/index.ts +7 -0
- package/ts/{smartproxy/forwarding/forwarding.factory.ts → forwarding/factory/forwarding-factory.ts} +12 -11
- package/ts/forwarding/factory/index.ts +5 -0
- package/ts/{smartproxy/forwarding/forwarding.handler.ts → forwarding/handlers/base-handler.ts} +2 -2
- package/ts/{smartproxy/forwarding/http.handler.ts → forwarding/handlers/http-handler.ts} +13 -4
- package/ts/{smartproxy/forwarding/https-passthrough.handler.ts → forwarding/handlers/https-passthrough-handler.ts} +13 -4
- package/ts/{smartproxy/forwarding/https-terminate-to-http.handler.ts → forwarding/handlers/https-terminate-to-http-handler.ts} +3 -3
- package/ts/{smartproxy/forwarding/https-terminate-to-https.handler.ts → forwarding/handlers/https-terminate-to-https-handler.ts} +3 -3
- package/ts/forwarding/handlers/index.ts +9 -0
- package/ts/forwarding/index.ts +34 -0
- package/ts/http/index.ts +23 -0
- package/ts/http/models/http-types.ts +105 -0
- package/ts/http/port80/acme-interfaces.ts +85 -0
- package/ts/http/port80/challenge-responder.ts +246 -0
- package/ts/http/port80/index.ts +13 -0
- package/ts/{port80handler/classes.port80handler.ts → http/port80/port80-handler.ts} +164 -161
- package/ts/http/redirects/index.ts +3 -0
- package/ts/http/router/index.ts +5 -0
- package/ts/{classes.router.ts → http/router/proxy-router.ts} +27 -20
- package/ts/index.ts +32 -9
- package/ts/plugins.ts +2 -1
- package/ts/proxies/index.ts +8 -0
- package/ts/{networkproxy/classes.np.certificatemanager.ts → proxies/network-proxy/certificate-manager.ts} +17 -16
- package/ts/{networkproxy/classes.np.connectionpool.ts → proxies/network-proxy/connection-pool.ts} +3 -3
- package/ts/proxies/network-proxy/index.ts +13 -0
- package/ts/proxies/network-proxy/models/index.ts +4 -0
- package/ts/{networkproxy/classes.np.types.ts → proxies/network-proxy/models/types.ts} +7 -11
- package/ts/{networkproxy/classes.np.networkproxy.ts → proxies/network-proxy/network-proxy.ts} +31 -24
- package/ts/{networkproxy/classes.np.requesthandler.ts → proxies/network-proxy/request-handler.ts} +12 -7
- package/ts/{networkproxy/classes.np.websockethandler.ts → proxies/network-proxy/websocket-handler.ts} +6 -6
- package/ts/proxies/nftables-proxy/index.ts +5 -0
- package/ts/proxies/nftables-proxy/models/errors.ts +30 -0
- package/ts/proxies/nftables-proxy/models/index.ts +5 -0
- package/ts/proxies/nftables-proxy/models/interfaces.ts +94 -0
- package/ts/{nfttablesproxy/classes.nftablesproxy.ts → proxies/nftables-proxy/nftables-proxy.ts} +24 -126
- package/ts/{smartproxy/classes.pp.connectionhandler.ts → proxies/smart-proxy/connection-handler.ts} +12 -12
- package/ts/{smartproxy/classes.pp.connectionmanager.ts → proxies/smart-proxy/connection-manager.ts} +8 -8
- package/ts/{smartproxy/classes.pp.domainconfigmanager.ts → proxies/smart-proxy/domain-config-manager.ts} +15 -14
- package/ts/proxies/smart-proxy/index.ts +18 -0
- package/ts/proxies/smart-proxy/models/index.ts +4 -0
- package/ts/{smartproxy/classes.pp.interfaces.ts → proxies/smart-proxy/models/interfaces.ts} +12 -8
- package/ts/{smartproxy/classes.pp.networkproxybridge.ts → proxies/smart-proxy/network-proxy-bridge.ts} +14 -14
- package/ts/{smartproxy/classes.pp.portrangemanager.ts → proxies/smart-proxy/port-range-manager.ts} +1 -1
- package/ts/{smartproxy/classes.pp.securitymanager.ts → proxies/smart-proxy/security-manager.ts} +3 -3
- package/ts/{smartproxy/classes.smartproxy.ts → proxies/smart-proxy/smart-proxy.ts} +29 -24
- package/ts/{smartproxy/classes.pp.timeoutmanager.ts → proxies/smart-proxy/timeout-manager.ts} +3 -3
- package/ts/{smartproxy/classes.pp.tlsmanager.ts → proxies/smart-proxy/tls-manager.ts} +3 -3
- package/ts/tls/alerts/index.ts +3 -0
- package/ts/{smartproxy/classes.pp.tlsalert.ts → tls/alerts/tls-alert.ts} +44 -43
- package/ts/tls/index.ts +33 -0
- package/ts/tls/sni/client-hello-parser.ts +629 -0
- package/ts/tls/sni/index.ts +3 -0
- package/ts/tls/sni/sni-extraction.ts +353 -0
- package/ts/tls/sni/sni-handler.ts +264 -0
- package/ts/tls/utils/index.ts +3 -0
- package/ts/tls/utils/tls-utils.ts +201 -0
- package/ts/common/acmeFactory.ts +0 -23
- package/ts/helpers.certificates.ts +0 -30
- package/ts/networkproxy/index.ts +0 -7
- package/ts/smartproxy/classes.pp.certprovisioner.ts +0 -200
- package/ts/smartproxy/classes.pp.snihandler.ts +0 -1281
- package/ts/smartproxy/forwarding/index.ts +0 -52
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
2
|
+
import { TlsExtensionType, TlsUtils } from '../utils/tls-utils.js';
|
|
3
|
+
import {
|
|
4
|
+
ClientHelloParser,
|
|
5
|
+
type LoggerFunction
|
|
6
|
+
} from './client-hello-parser.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Connection tracking information
|
|
10
|
+
*/
|
|
11
|
+
export interface ConnectionInfo {
|
|
12
|
+
sourceIp: string;
|
|
13
|
+
sourcePort: number;
|
|
14
|
+
destIp: string;
|
|
15
|
+
destPort: number;
|
|
16
|
+
timestamp?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Utilities for extracting SNI information from TLS handshakes
|
|
21
|
+
*/
|
|
22
|
+
export class SniExtraction {
|
|
23
|
+
/**
|
|
24
|
+
* Extracts the SNI (Server Name Indication) from a TLS ClientHello message.
|
|
25
|
+
*
|
|
26
|
+
* @param buffer The buffer containing the TLS ClientHello message
|
|
27
|
+
* @param logger Optional logging function
|
|
28
|
+
* @returns The extracted server name or undefined if not found
|
|
29
|
+
*/
|
|
30
|
+
public static extractSNI(buffer: Buffer, logger?: LoggerFunction): string | undefined {
|
|
31
|
+
const log = logger || (() => {});
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
// Parse the ClientHello
|
|
35
|
+
const parseResult = ClientHelloParser.parseClientHello(buffer, logger);
|
|
36
|
+
if (!parseResult.isValid) {
|
|
37
|
+
log(`Failed to parse ClientHello: ${parseResult.error}`);
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Check if ServerName extension was found
|
|
42
|
+
if (parseResult.serverNameList && parseResult.serverNameList.length > 0) {
|
|
43
|
+
// Use the first hostname (most common case)
|
|
44
|
+
const serverName = parseResult.serverNameList[0];
|
|
45
|
+
log(`Found SNI: ${serverName}`);
|
|
46
|
+
return serverName;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
log('No SNI extension found in ClientHello');
|
|
50
|
+
return undefined;
|
|
51
|
+
} catch (error) {
|
|
52
|
+
log(`Error extracting SNI: ${error instanceof Error ? error.message : String(error)}`);
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Attempts to extract SNI from the PSK extension in a TLS 1.3 ClientHello.
|
|
59
|
+
*
|
|
60
|
+
* In TLS 1.3, when a client attempts to resume a session, it may include
|
|
61
|
+
* the server name in the PSK identity hint rather than in the SNI extension.
|
|
62
|
+
*
|
|
63
|
+
* @param buffer The buffer containing the TLS ClientHello message
|
|
64
|
+
* @param logger Optional logging function
|
|
65
|
+
* @returns The extracted server name or undefined if not found
|
|
66
|
+
*/
|
|
67
|
+
public static extractSNIFromPSKExtension(
|
|
68
|
+
buffer: Buffer,
|
|
69
|
+
logger?: LoggerFunction
|
|
70
|
+
): string | undefined {
|
|
71
|
+
const log = logger || (() => {});
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
// Ensure this is a ClientHello
|
|
75
|
+
if (!TlsUtils.isClientHello(buffer)) {
|
|
76
|
+
log('Not a ClientHello message');
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Parse the ClientHello to find PSK extension
|
|
81
|
+
const parseResult = ClientHelloParser.parseClientHello(buffer, logger);
|
|
82
|
+
if (!parseResult.isValid || !parseResult.extensions) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Find the PSK extension
|
|
87
|
+
const pskExtension = parseResult.extensions.find(ext =>
|
|
88
|
+
ext.type === TlsExtensionType.PRE_SHARED_KEY);
|
|
89
|
+
|
|
90
|
+
if (!pskExtension) {
|
|
91
|
+
log('No PSK extension found');
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Parse the PSK extension data
|
|
96
|
+
const data = pskExtension.data;
|
|
97
|
+
|
|
98
|
+
// PSK extension structure:
|
|
99
|
+
// 2 bytes: identities list length
|
|
100
|
+
if (data.length < 2) return undefined;
|
|
101
|
+
|
|
102
|
+
const identitiesLength = (data[0] << 8) + data[1];
|
|
103
|
+
let pos = 2;
|
|
104
|
+
|
|
105
|
+
// End of identities list
|
|
106
|
+
const identitiesEnd = pos + identitiesLength;
|
|
107
|
+
if (identitiesEnd > data.length) return undefined;
|
|
108
|
+
|
|
109
|
+
// Process each PSK identity
|
|
110
|
+
while (pos + 2 <= identitiesEnd) {
|
|
111
|
+
// Identity length (2 bytes)
|
|
112
|
+
if (pos + 2 > identitiesEnd) break;
|
|
113
|
+
|
|
114
|
+
const identityLength = (data[pos] << 8) + data[pos + 1];
|
|
115
|
+
pos += 2;
|
|
116
|
+
|
|
117
|
+
if (pos + identityLength > identitiesEnd) break;
|
|
118
|
+
|
|
119
|
+
// Try to extract hostname from identity
|
|
120
|
+
// Chrome often embeds the hostname in the PSK identity
|
|
121
|
+
// This is a heuristic as there's no standard format
|
|
122
|
+
if (identityLength > 0) {
|
|
123
|
+
const identity = data.slice(pos, pos + identityLength);
|
|
124
|
+
|
|
125
|
+
// Skip identity bytes
|
|
126
|
+
pos += identityLength;
|
|
127
|
+
|
|
128
|
+
// Skip obfuscated ticket age (4 bytes)
|
|
129
|
+
if (pos + 4 <= identitiesEnd) {
|
|
130
|
+
pos += 4;
|
|
131
|
+
} else {
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Try to parse the identity as UTF-8
|
|
136
|
+
try {
|
|
137
|
+
const identityStr = identity.toString('utf8');
|
|
138
|
+
log(`PSK identity: ${identityStr}`);
|
|
139
|
+
|
|
140
|
+
// Check if the identity contains hostname hints
|
|
141
|
+
// Chrome often embeds the hostname in a known format
|
|
142
|
+
// Try to extract using common patterns
|
|
143
|
+
|
|
144
|
+
// Pattern 1: Look for domain name pattern
|
|
145
|
+
const domainPattern =
|
|
146
|
+
/([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?/i;
|
|
147
|
+
const domainMatch = identityStr.match(domainPattern);
|
|
148
|
+
if (domainMatch && domainMatch[0]) {
|
|
149
|
+
log(`Found domain in PSK identity: ${domainMatch[0]}`);
|
|
150
|
+
return domainMatch[0];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Pattern 2: Chrome sometimes uses a specific format with delimiters
|
|
154
|
+
// This is a heuristic approach since the format isn't standardized
|
|
155
|
+
const parts = identityStr.split('|');
|
|
156
|
+
if (parts.length > 1) {
|
|
157
|
+
for (const part of parts) {
|
|
158
|
+
if (part.includes('.') && !part.includes('/')) {
|
|
159
|
+
const possibleDomain = part.trim();
|
|
160
|
+
if (/^[a-z0-9.-]+$/i.test(possibleDomain)) {
|
|
161
|
+
log(`Found possible domain in PSK delimiter format: ${possibleDomain}`);
|
|
162
|
+
return possibleDomain;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
} catch (e) {
|
|
168
|
+
log('Failed to parse PSK identity as UTF-8');
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
log('No hostname found in PSK extension');
|
|
174
|
+
return undefined;
|
|
175
|
+
} catch (error) {
|
|
176
|
+
log(`Error parsing PSK: ${error instanceof Error ? error.message : String(error)}`);
|
|
177
|
+
return undefined;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Main entry point for SNI extraction with support for fragmented messages
|
|
183
|
+
* and session resumption edge cases.
|
|
184
|
+
*
|
|
185
|
+
* @param buffer The buffer containing TLS data
|
|
186
|
+
* @param connectionInfo Connection tracking information
|
|
187
|
+
* @param logger Optional logging function
|
|
188
|
+
* @param cachedSni Optional previously cached SNI value
|
|
189
|
+
* @returns The extracted server name or undefined
|
|
190
|
+
*/
|
|
191
|
+
public static extractSNIWithResumptionSupport(
|
|
192
|
+
buffer: Buffer,
|
|
193
|
+
connectionInfo?: ConnectionInfo,
|
|
194
|
+
logger?: LoggerFunction,
|
|
195
|
+
cachedSni?: string
|
|
196
|
+
): string | undefined {
|
|
197
|
+
const log = logger || (() => {});
|
|
198
|
+
|
|
199
|
+
// Log buffer details for debugging
|
|
200
|
+
if (logger) {
|
|
201
|
+
log(`Buffer size: ${buffer.length} bytes`);
|
|
202
|
+
log(`Buffer starts with: ${buffer.slice(0, Math.min(10, buffer.length)).toString('hex')}`);
|
|
203
|
+
|
|
204
|
+
if (buffer.length >= 5) {
|
|
205
|
+
const recordType = buffer[0];
|
|
206
|
+
const majorVersion = buffer[1];
|
|
207
|
+
const minorVersion = buffer[2];
|
|
208
|
+
const recordLength = (buffer[3] << 8) + buffer[4];
|
|
209
|
+
|
|
210
|
+
log(
|
|
211
|
+
`TLS Record: type=${recordType}, version=${majorVersion}.${minorVersion}, length=${recordLength}`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Check if we need to handle fragmented packets
|
|
217
|
+
let processBuffer = buffer;
|
|
218
|
+
if (connectionInfo) {
|
|
219
|
+
const connectionId = TlsUtils.createConnectionId(connectionInfo);
|
|
220
|
+
const reassembledBuffer = ClientHelloParser.handleFragmentedClientHello(
|
|
221
|
+
buffer,
|
|
222
|
+
connectionId,
|
|
223
|
+
logger
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
if (!reassembledBuffer) {
|
|
227
|
+
log(`Waiting for more fragments on connection ${connectionId}`);
|
|
228
|
+
return undefined; // Need more fragments to complete ClientHello
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
processBuffer = reassembledBuffer;
|
|
232
|
+
log(`Using reassembled buffer of length ${processBuffer.length}`);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// First try the standard SNI extraction
|
|
236
|
+
const standardSni = this.extractSNI(processBuffer, logger);
|
|
237
|
+
if (standardSni) {
|
|
238
|
+
log(`Found standard SNI: ${standardSni}`);
|
|
239
|
+
return standardSni;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Check for session resumption when standard SNI extraction fails
|
|
243
|
+
if (TlsUtils.isClientHello(processBuffer)) {
|
|
244
|
+
const resumptionInfo = ClientHelloParser.hasSessionResumption(processBuffer, logger);
|
|
245
|
+
|
|
246
|
+
if (resumptionInfo.isResumption) {
|
|
247
|
+
log(`Detected session resumption in ClientHello without standard SNI`);
|
|
248
|
+
|
|
249
|
+
// Try to extract SNI from PSK extension
|
|
250
|
+
const pskSni = this.extractSNIFromPSKExtension(processBuffer, logger);
|
|
251
|
+
if (pskSni) {
|
|
252
|
+
log(`Extracted SNI from PSK extension: ${pskSni}`);
|
|
253
|
+
return pskSni;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// If cached SNI was provided, use it for application data packets
|
|
259
|
+
if (cachedSni && TlsUtils.isTlsApplicationData(buffer)) {
|
|
260
|
+
log(`Using provided cached SNI for application data: ${cachedSni}`);
|
|
261
|
+
return cachedSni;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return undefined;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Unified method for processing a TLS packet and extracting SNI.
|
|
269
|
+
* Main entry point for SNI extraction that handles all edge cases.
|
|
270
|
+
*
|
|
271
|
+
* @param buffer The buffer containing TLS data
|
|
272
|
+
* @param connectionInfo Connection tracking information
|
|
273
|
+
* @param logger Optional logging function
|
|
274
|
+
* @param cachedSni Optional previously cached SNI value
|
|
275
|
+
* @returns The extracted server name or undefined
|
|
276
|
+
*/
|
|
277
|
+
public static processTlsPacket(
|
|
278
|
+
buffer: Buffer,
|
|
279
|
+
connectionInfo: ConnectionInfo,
|
|
280
|
+
logger?: LoggerFunction,
|
|
281
|
+
cachedSni?: string
|
|
282
|
+
): string | undefined {
|
|
283
|
+
const log = logger || (() => {});
|
|
284
|
+
|
|
285
|
+
// Add timestamp if not provided
|
|
286
|
+
if (!connectionInfo.timestamp) {
|
|
287
|
+
connectionInfo.timestamp = Date.now();
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Check if this is a TLS handshake or application data
|
|
291
|
+
if (!TlsUtils.isTlsHandshake(buffer) && !TlsUtils.isTlsApplicationData(buffer)) {
|
|
292
|
+
log('Not a TLS handshake or application data packet');
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Create connection ID for tracking
|
|
297
|
+
const connectionId = TlsUtils.createConnectionId(connectionInfo);
|
|
298
|
+
log(`Processing TLS packet for connection ${connectionId}, buffer length: ${buffer.length}`);
|
|
299
|
+
|
|
300
|
+
// Handle application data with cached SNI (for connection racing)
|
|
301
|
+
if (TlsUtils.isTlsApplicationData(buffer)) {
|
|
302
|
+
// If explicit cachedSni was provided, use it
|
|
303
|
+
if (cachedSni) {
|
|
304
|
+
log(`Using provided cached SNI for application data: ${cachedSni}`);
|
|
305
|
+
return cachedSni;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
log('Application data packet without cached SNI, cannot determine hostname');
|
|
309
|
+
return undefined;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Enhanced session resumption detection
|
|
313
|
+
if (TlsUtils.isClientHello(buffer)) {
|
|
314
|
+
const resumptionInfo = ClientHelloParser.hasSessionResumption(buffer, logger);
|
|
315
|
+
|
|
316
|
+
if (resumptionInfo.isResumption) {
|
|
317
|
+
log(`Session resumption detected in TLS packet`);
|
|
318
|
+
|
|
319
|
+
// Always try standard SNI extraction first
|
|
320
|
+
const standardSni = this.extractSNI(buffer, logger);
|
|
321
|
+
if (standardSni) {
|
|
322
|
+
log(`Found standard SNI in session resumption: ${standardSni}`);
|
|
323
|
+
return standardSni;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Enhanced session resumption SNI extraction
|
|
327
|
+
// Try extracting from PSK identity
|
|
328
|
+
const pskSni = this.extractSNIFromPSKExtension(buffer, logger);
|
|
329
|
+
if (pskSni) {
|
|
330
|
+
log(`Extracted SNI from PSK extension: ${pskSni}`);
|
|
331
|
+
return pskSni;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
log(`Session resumption without extractable SNI`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// For handshake messages, try the full extraction process
|
|
339
|
+
const sni = this.extractSNIWithResumptionSupport(buffer, connectionInfo, logger);
|
|
340
|
+
|
|
341
|
+
if (sni) {
|
|
342
|
+
log(`Successfully extracted SNI: ${sni}`);
|
|
343
|
+
return sni;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// If we couldn't extract an SNI, check if this is a valid ClientHello
|
|
347
|
+
if (TlsUtils.isClientHello(buffer)) {
|
|
348
|
+
log('Valid ClientHello detected, but no SNI extracted - might need more data');
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return undefined;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
2
|
+
import {
|
|
3
|
+
TlsRecordType,
|
|
4
|
+
TlsHandshakeType,
|
|
5
|
+
TlsExtensionType,
|
|
6
|
+
TlsUtils
|
|
7
|
+
} from '../utils/tls-utils.js';
|
|
8
|
+
import {
|
|
9
|
+
ClientHelloParser,
|
|
10
|
+
type LoggerFunction
|
|
11
|
+
} from './client-hello-parser.js';
|
|
12
|
+
import {
|
|
13
|
+
SniExtraction,
|
|
14
|
+
type ConnectionInfo
|
|
15
|
+
} from './sni-extraction.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* SNI (Server Name Indication) handler for TLS connections.
|
|
19
|
+
* Provides robust extraction of SNI values from TLS ClientHello messages
|
|
20
|
+
* with support for fragmented packets, TLS 1.3 resumption, Chrome-specific
|
|
21
|
+
* connection behaviors, and tab hibernation/reactivation scenarios.
|
|
22
|
+
*
|
|
23
|
+
* This class retains the original API but leverages the new modular implementation
|
|
24
|
+
* for better maintainability and testability.
|
|
25
|
+
*/
|
|
26
|
+
export class SniHandler {
|
|
27
|
+
// Re-export constants for backward compatibility
|
|
28
|
+
private static readonly TLS_HANDSHAKE_RECORD_TYPE = TlsRecordType.HANDSHAKE;
|
|
29
|
+
private static readonly TLS_APPLICATION_DATA_TYPE = TlsRecordType.APPLICATION_DATA;
|
|
30
|
+
private static readonly TLS_CLIENT_HELLO_HANDSHAKE_TYPE = TlsHandshakeType.CLIENT_HELLO;
|
|
31
|
+
private static readonly TLS_SNI_EXTENSION_TYPE = TlsExtensionType.SERVER_NAME;
|
|
32
|
+
private static readonly TLS_SESSION_TICKET_EXTENSION_TYPE = TlsExtensionType.SESSION_TICKET;
|
|
33
|
+
private static readonly TLS_SNI_HOST_NAME_TYPE = 0; // NameType.HOST_NAME in RFC 6066
|
|
34
|
+
private static readonly TLS_PSK_EXTENSION_TYPE = TlsExtensionType.PRE_SHARED_KEY;
|
|
35
|
+
private static readonly TLS_PSK_KE_MODES_EXTENSION_TYPE = TlsExtensionType.PSK_KEY_EXCHANGE_MODES;
|
|
36
|
+
private static readonly TLS_EARLY_DATA_EXTENSION_TYPE = TlsExtensionType.EARLY_DATA;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Checks if a buffer contains a TLS handshake message (record type 22)
|
|
40
|
+
* @param buffer - The buffer to check
|
|
41
|
+
* @returns true if the buffer starts with a TLS handshake record type
|
|
42
|
+
*/
|
|
43
|
+
public static isTlsHandshake(buffer: Buffer): boolean {
|
|
44
|
+
return TlsUtils.isTlsHandshake(buffer);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Checks if a buffer contains TLS application data (record type 23)
|
|
49
|
+
* @param buffer - The buffer to check
|
|
50
|
+
* @returns true if the buffer starts with a TLS application data record type
|
|
51
|
+
*/
|
|
52
|
+
public static isTlsApplicationData(buffer: Buffer): boolean {
|
|
53
|
+
return TlsUtils.isTlsApplicationData(buffer);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Creates a connection ID based on source/destination information
|
|
58
|
+
* Used to track fragmented ClientHello messages across multiple packets
|
|
59
|
+
*
|
|
60
|
+
* @param connectionInfo - Object containing connection identifiers (IP/port)
|
|
61
|
+
* @returns A string ID for the connection
|
|
62
|
+
*/
|
|
63
|
+
public static createConnectionId(connectionInfo: {
|
|
64
|
+
sourceIp?: string;
|
|
65
|
+
sourcePort?: number;
|
|
66
|
+
destIp?: string;
|
|
67
|
+
destPort?: number;
|
|
68
|
+
}): string {
|
|
69
|
+
return TlsUtils.createConnectionId(connectionInfo);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Handles potential fragmented ClientHello messages by buffering and reassembling
|
|
74
|
+
* TLS record fragments that might span multiple TCP packets.
|
|
75
|
+
*
|
|
76
|
+
* @param buffer - The current buffer fragment
|
|
77
|
+
* @param connectionId - Unique identifier for the connection
|
|
78
|
+
* @param enableLogging - Whether to enable logging
|
|
79
|
+
* @returns A complete buffer if reassembly is successful, or undefined if more fragments are needed
|
|
80
|
+
*/
|
|
81
|
+
public static handleFragmentedClientHello(
|
|
82
|
+
buffer: Buffer,
|
|
83
|
+
connectionId: string,
|
|
84
|
+
enableLogging: boolean = false
|
|
85
|
+
): Buffer | undefined {
|
|
86
|
+
const logger = enableLogging ?
|
|
87
|
+
(message: string) => console.log(`[SNI Fragment] ${message}`) :
|
|
88
|
+
undefined;
|
|
89
|
+
|
|
90
|
+
return ClientHelloParser.handleFragmentedClientHello(buffer, connectionId, logger);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Checks if a buffer contains a TLS ClientHello message
|
|
95
|
+
* @param buffer - The buffer to check
|
|
96
|
+
* @returns true if the buffer appears to be a ClientHello message
|
|
97
|
+
*/
|
|
98
|
+
public static isClientHello(buffer: Buffer): boolean {
|
|
99
|
+
return TlsUtils.isClientHello(buffer);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Checks if a ClientHello message contains session resumption indicators
|
|
104
|
+
* such as session tickets or PSK (Pre-Shared Key) extensions.
|
|
105
|
+
*
|
|
106
|
+
* @param buffer - The buffer containing a ClientHello message
|
|
107
|
+
* @param enableLogging - Whether to enable logging
|
|
108
|
+
* @returns Object containing details about session resumption and SNI presence
|
|
109
|
+
*/
|
|
110
|
+
public static hasSessionResumption(
|
|
111
|
+
buffer: Buffer,
|
|
112
|
+
enableLogging: boolean = false
|
|
113
|
+
): { isResumption: boolean; hasSNI: boolean } {
|
|
114
|
+
const logger = enableLogging ?
|
|
115
|
+
(message: string) => console.log(`[Session Resumption] ${message}`) :
|
|
116
|
+
undefined;
|
|
117
|
+
|
|
118
|
+
return ClientHelloParser.hasSessionResumption(buffer, logger);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Detects characteristics of a tab reactivation TLS handshake
|
|
123
|
+
* These often have specific patterns in Chrome and other browsers
|
|
124
|
+
*
|
|
125
|
+
* @param buffer - The buffer containing a ClientHello message
|
|
126
|
+
* @param enableLogging - Whether to enable logging
|
|
127
|
+
* @returns true if this appears to be a tab reactivation handshake
|
|
128
|
+
*/
|
|
129
|
+
public static isTabReactivationHandshake(
|
|
130
|
+
buffer: Buffer,
|
|
131
|
+
enableLogging: boolean = false
|
|
132
|
+
): boolean {
|
|
133
|
+
const logger = enableLogging ?
|
|
134
|
+
(message: string) => console.log(`[Tab Reactivation] ${message}`) :
|
|
135
|
+
undefined;
|
|
136
|
+
|
|
137
|
+
return ClientHelloParser.isTabReactivationHandshake(buffer, logger);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Extracts the SNI (Server Name Indication) from a TLS ClientHello message.
|
|
142
|
+
* Implements robust parsing with support for session resumption edge cases.
|
|
143
|
+
*
|
|
144
|
+
* @param buffer - The buffer containing the TLS ClientHello message
|
|
145
|
+
* @param enableLogging - Whether to enable detailed debug logging
|
|
146
|
+
* @returns The extracted server name or undefined if not found
|
|
147
|
+
*/
|
|
148
|
+
public static extractSNI(buffer: Buffer, enableLogging: boolean = false): string | undefined {
|
|
149
|
+
const logger = enableLogging ?
|
|
150
|
+
(message: string) => console.log(`[SNI Extraction] ${message}`) :
|
|
151
|
+
undefined;
|
|
152
|
+
|
|
153
|
+
return SniExtraction.extractSNI(buffer, logger);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Attempts to extract SNI from the PSK extension in a TLS 1.3 ClientHello.
|
|
158
|
+
*
|
|
159
|
+
* In TLS 1.3, when a client attempts to resume a session, it may include
|
|
160
|
+
* the server name in the PSK identity hint rather than in the SNI extension.
|
|
161
|
+
*
|
|
162
|
+
* @param buffer - The buffer containing the TLS ClientHello message
|
|
163
|
+
* @param enableLogging - Whether to enable detailed debug logging
|
|
164
|
+
* @returns The extracted server name or undefined if not found
|
|
165
|
+
*/
|
|
166
|
+
public static extractSNIFromPSKExtension(
|
|
167
|
+
buffer: Buffer,
|
|
168
|
+
enableLogging: boolean = false
|
|
169
|
+
): string | undefined {
|
|
170
|
+
const logger = enableLogging ?
|
|
171
|
+
(message: string) => console.log(`[PSK-SNI Extraction] ${message}`) :
|
|
172
|
+
undefined;
|
|
173
|
+
|
|
174
|
+
return SniExtraction.extractSNIFromPSKExtension(buffer, logger);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Checks if the buffer contains TLS 1.3 early data (0-RTT)
|
|
179
|
+
* @param buffer - The buffer to check
|
|
180
|
+
* @param enableLogging - Whether to enable logging
|
|
181
|
+
* @returns true if early data is detected
|
|
182
|
+
*/
|
|
183
|
+
public static hasEarlyData(buffer: Buffer, enableLogging: boolean = false): boolean {
|
|
184
|
+
// This functionality has been moved to ClientHelloParser
|
|
185
|
+
// We can implement it in terms of the parse result if needed
|
|
186
|
+
const logger = enableLogging ?
|
|
187
|
+
(message: string) => console.log(`[Early Data] ${message}`) :
|
|
188
|
+
undefined;
|
|
189
|
+
|
|
190
|
+
const parseResult = ClientHelloParser.parseClientHello(buffer, logger);
|
|
191
|
+
return parseResult.isValid && parseResult.hasEarlyData;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Attempts to extract SNI from an initial ClientHello packet and handles
|
|
196
|
+
* session resumption edge cases more robustly than the standard extraction.
|
|
197
|
+
*
|
|
198
|
+
* This method handles:
|
|
199
|
+
* 1. Standard SNI extraction
|
|
200
|
+
* 2. TLS 1.3 PSK-based resumption (Chrome, Firefox, etc.)
|
|
201
|
+
* 3. Session ticket-based resumption
|
|
202
|
+
* 4. Fragmented ClientHello messages
|
|
203
|
+
* 5. TLS 1.3 Early Data (0-RTT)
|
|
204
|
+
* 6. Chrome's connection racing behaviors
|
|
205
|
+
*
|
|
206
|
+
* @param buffer - The buffer containing the TLS ClientHello message
|
|
207
|
+
* @param connectionInfo - Optional connection information for fragment handling
|
|
208
|
+
* @param enableLogging - Whether to enable detailed debug logging
|
|
209
|
+
* @returns The extracted server name or undefined if not found or more data needed
|
|
210
|
+
*/
|
|
211
|
+
public static extractSNIWithResumptionSupport(
|
|
212
|
+
buffer: Buffer,
|
|
213
|
+
connectionInfo?: {
|
|
214
|
+
sourceIp?: string;
|
|
215
|
+
sourcePort?: number;
|
|
216
|
+
destIp?: string;
|
|
217
|
+
destPort?: number;
|
|
218
|
+
},
|
|
219
|
+
enableLogging: boolean = false
|
|
220
|
+
): string | undefined {
|
|
221
|
+
const logger = enableLogging ?
|
|
222
|
+
(message: string) => console.log(`[SNI Extraction] ${message}`) :
|
|
223
|
+
undefined;
|
|
224
|
+
|
|
225
|
+
return SniExtraction.extractSNIWithResumptionSupport(
|
|
226
|
+
buffer,
|
|
227
|
+
connectionInfo as ConnectionInfo,
|
|
228
|
+
logger
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Main entry point for SNI extraction that handles all edge cases.
|
|
234
|
+
* This should be called for each TLS packet received from a client.
|
|
235
|
+
*
|
|
236
|
+
* The method uses connection tracking to handle fragmented ClientHello
|
|
237
|
+
* messages and various TLS 1.3 behaviors, including Chrome's connection
|
|
238
|
+
* racing patterns and tab reactivation behaviors.
|
|
239
|
+
*
|
|
240
|
+
* @param buffer - The buffer containing TLS data
|
|
241
|
+
* @param connectionInfo - Connection metadata (IPs and ports)
|
|
242
|
+
* @param enableLogging - Whether to enable detailed debug logging
|
|
243
|
+
* @param cachedSni - Optional cached SNI from previous connections (for racing detection)
|
|
244
|
+
* @returns The extracted server name or undefined if not found or more data needed
|
|
245
|
+
*/
|
|
246
|
+
public static processTlsPacket(
|
|
247
|
+
buffer: Buffer,
|
|
248
|
+
connectionInfo: {
|
|
249
|
+
sourceIp: string;
|
|
250
|
+
sourcePort: number;
|
|
251
|
+
destIp: string;
|
|
252
|
+
destPort: number;
|
|
253
|
+
timestamp?: number;
|
|
254
|
+
},
|
|
255
|
+
enableLogging: boolean = false,
|
|
256
|
+
cachedSni?: string
|
|
257
|
+
): string | undefined {
|
|
258
|
+
const logger = enableLogging ?
|
|
259
|
+
(message: string) => console.log(`[TLS Packet] ${message}`) :
|
|
260
|
+
undefined;
|
|
261
|
+
|
|
262
|
+
return SniExtraction.processTlsPacket(buffer, connectionInfo, logger, cachedSni);
|
|
263
|
+
}
|
|
264
|
+
}
|