@push.rocks/smartproxy 21.0.0 → 21.1.0
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/changelog.md +9 -0
- package/dist_ts/core/utils/proxy-protocol.d.ts +5 -17
- package/dist_ts/core/utils/proxy-protocol.js +13 -97
- package/dist_ts/core/utils/websocket-utils.d.ts +6 -7
- package/dist_ts/core/utils/websocket-utils.js +10 -66
- package/dist_ts/detection/detectors/http-detector-v2.d.ts +33 -0
- package/dist_ts/detection/detectors/http-detector-v2.js +87 -0
- package/dist_ts/detection/detectors/http-detector.d.ts +33 -0
- package/dist_ts/detection/detectors/http-detector.js +89 -0
- package/dist_ts/detection/detectors/quick-detector.d.ts +28 -0
- package/dist_ts/detection/detectors/quick-detector.js +131 -0
- package/dist_ts/detection/detectors/routing-extractor.d.ts +28 -0
- package/dist_ts/detection/detectors/routing-extractor.js +122 -0
- package/dist_ts/detection/detectors/tls-detector-v2.d.ts +33 -0
- package/dist_ts/detection/detectors/tls-detector-v2.js +80 -0
- package/dist_ts/detection/detectors/tls-detector.d.ts +33 -0
- package/dist_ts/detection/detectors/tls-detector.js +106 -0
- package/dist_ts/detection/index.d.ts +17 -0
- package/dist_ts/detection/index.js +22 -0
- package/dist_ts/detection/models/detection-types.d.ts +87 -0
- package/dist_ts/detection/models/detection-types.js +5 -0
- package/dist_ts/detection/models/interfaces.d.ts +97 -0
- package/dist_ts/detection/models/interfaces.js +5 -0
- package/dist_ts/detection/protocol-detector-v2.d.ts +46 -0
- package/dist_ts/detection/protocol-detector-v2.js +116 -0
- package/dist_ts/detection/protocol-detector.d.ts +74 -0
- package/dist_ts/detection/protocol-detector.js +173 -0
- package/dist_ts/detection/utils/buffer-utils.d.ts +61 -0
- package/dist_ts/detection/utils/buffer-utils.js +127 -0
- package/dist_ts/detection/utils/fragment-manager.d.ts +31 -0
- package/dist_ts/detection/utils/fragment-manager.js +53 -0
- package/dist_ts/detection/utils/parser-utils.d.ts +42 -0
- package/dist_ts/detection/utils/parser-utils.js +63 -0
- package/dist_ts/index.d.ts +2 -0
- package/dist_ts/index.js +3 -1
- package/dist_ts/protocols/common/fragment-handler.d.ts +73 -0
- package/dist_ts/protocols/common/fragment-handler.js +117 -0
- package/dist_ts/protocols/common/index.d.ts +7 -0
- package/dist_ts/protocols/common/index.js +8 -0
- package/dist_ts/protocols/common/types.d.ts +68 -0
- package/dist_ts/protocols/common/types.js +7 -0
- package/dist_ts/protocols/http/constants.d.ts +119 -0
- package/dist_ts/protocols/http/constants.js +200 -0
- package/dist_ts/protocols/http/index.d.ts +7 -0
- package/dist_ts/protocols/http/index.js +8 -0
- package/dist_ts/protocols/http/parser.d.ts +58 -0
- package/dist_ts/protocols/http/parser.js +184 -0
- package/dist_ts/protocols/http/types.d.ts +62 -0
- package/dist_ts/protocols/http/types.js +5 -0
- package/dist_ts/protocols/index.d.ts +11 -0
- package/dist_ts/protocols/index.js +12 -0
- package/dist_ts/protocols/proxy/index.d.ts +6 -0
- package/dist_ts/protocols/proxy/index.js +7 -0
- package/dist_ts/protocols/proxy/parser.d.ts +44 -0
- package/dist_ts/protocols/proxy/parser.js +153 -0
- package/dist_ts/protocols/proxy/types.d.ts +47 -0
- package/dist_ts/protocols/proxy/types.js +6 -0
- package/dist_ts/protocols/tls/alerts/index.d.ts +4 -0
- package/dist_ts/protocols/tls/alerts/index.js +5 -0
- package/dist_ts/protocols/tls/alerts/tls-alert.d.ts +150 -0
- package/dist_ts/protocols/tls/alerts/tls-alert.js +226 -0
- package/dist_ts/protocols/tls/constants.d.ts +122 -0
- package/dist_ts/protocols/tls/constants.js +135 -0
- package/dist_ts/protocols/tls/index.d.ts +12 -0
- package/dist_ts/protocols/tls/index.js +27 -0
- package/dist_ts/protocols/tls/parser.d.ts +53 -0
- package/dist_ts/protocols/tls/parser.js +294 -0
- package/dist_ts/protocols/tls/sni/client-hello-parser.d.ts +100 -0
- package/dist_ts/protocols/tls/sni/client-hello-parser.js +463 -0
- package/dist_ts/protocols/tls/sni/index.d.ts +5 -0
- package/dist_ts/protocols/tls/sni/index.js +6 -0
- package/dist_ts/protocols/tls/sni/sni-extraction.d.ts +58 -0
- package/dist_ts/protocols/tls/sni/sni-extraction.js +275 -0
- package/dist_ts/protocols/tls/types.d.ts +65 -0
- package/dist_ts/protocols/tls/types.js +5 -0
- package/dist_ts/protocols/tls/utils/index.d.ts +4 -0
- package/dist_ts/protocols/tls/utils/index.js +5 -0
- package/dist_ts/protocols/tls/utils/tls-utils.d.ts +158 -0
- package/dist_ts/protocols/tls/utils/tls-utils.js +187 -0
- package/dist_ts/protocols/websocket/constants.d.ts +55 -0
- package/dist_ts/protocols/websocket/constants.js +58 -0
- package/dist_ts/protocols/websocket/index.d.ts +7 -0
- package/dist_ts/protocols/websocket/index.js +8 -0
- package/dist_ts/protocols/websocket/types.d.ts +47 -0
- package/dist_ts/protocols/websocket/types.js +5 -0
- package/dist_ts/protocols/websocket/utils.d.ts +25 -0
- package/dist_ts/protocols/websocket/utils.js +103 -0
- package/dist_ts/proxies/http-proxy/models/http-types.d.ts +25 -27
- package/dist_ts/proxies/http-proxy/models/http-types.js +24 -44
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +5 -0
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +81 -61
- package/dist_ts/proxies/smart-proxy/tls-manager.js +2 -1
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +2 -0
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +61 -52
- package/dist_ts/tls/index.d.ts +5 -7
- package/dist_ts/tls/index.js +8 -11
- package/dist_ts/tls/sni/client-hello-parser.js +3 -2
- package/dist_ts/tls/sni/sni-handler.js +4 -4
- package/dist_ts/tls/utils/tls-utils.d.ts +1 -110
- package/dist_ts/tls/utils/tls-utils.js +4 -116
- package/package.json +17 -8
- package/readme.plan.md +0 -0
- package/ts/core/utils/proxy-protocol.ts +14 -131
- package/ts/core/utils/websocket-utils.ts +12 -60
- package/ts/detection/detectors/http-detector.ts +114 -0
- package/ts/detection/detectors/quick-detector.ts +148 -0
- package/ts/detection/detectors/routing-extractor.ts +147 -0
- package/ts/detection/detectors/tls-detector.ts +120 -0
- package/ts/detection/index.ts +25 -0
- package/ts/detection/models/detection-types.ts +102 -0
- package/ts/detection/models/interfaces.ts +115 -0
- package/ts/detection/protocol-detector.ts +230 -0
- package/ts/detection/utils/buffer-utils.ts +141 -0
- package/ts/detection/utils/fragment-manager.ts +64 -0
- package/ts/detection/utils/parser-utils.ts +77 -0
- package/ts/index.ts +3 -1
- package/ts/protocols/common/fragment-handler.ts +163 -0
- package/ts/protocols/common/index.ts +8 -0
- package/ts/protocols/common/types.ts +76 -0
- package/ts/protocols/http/constants.ts +219 -0
- package/ts/protocols/http/index.ts +8 -0
- package/ts/protocols/http/parser.ts +219 -0
- package/ts/protocols/http/types.ts +70 -0
- package/ts/protocols/index.ts +12 -0
- package/ts/protocols/proxy/index.ts +7 -0
- package/ts/protocols/proxy/parser.ts +183 -0
- package/ts/protocols/proxy/types.ts +53 -0
- package/ts/{tls → protocols/tls}/alerts/tls-alert.ts +1 -1
- package/ts/protocols/tls/index.ts +37 -0
- package/ts/protocols/tls/sni/index.ts +6 -0
- package/ts/{tls → protocols/tls}/utils/tls-utils.ts +1 -1
- package/ts/protocols/websocket/constants.ts +60 -0
- package/ts/protocols/websocket/index.ts +8 -0
- package/ts/protocols/websocket/types.ts +53 -0
- package/ts/protocols/websocket/utils.ts +98 -0
- package/ts/proxies/http-proxy/models/http-types.ts +29 -46
- package/ts/proxies/smart-proxy/models/interfaces.ts +7 -0
- package/ts/proxies/smart-proxy/route-connection-handler.ts +91 -68
- package/ts/proxies/smart-proxy/tls-manager.ts +1 -0
- package/ts/proxies/smart-proxy/utils/route-helpers.ts +72 -56
- package/ts/tls/index.ts +8 -12
- package/ts/tls/sni/sni-handler.ts +3 -3
- /package/ts/{tls → protocols/tls}/alerts/index.ts +0 -0
- /package/ts/{tls → protocols/tls}/sni/client-hello-parser.ts +0 -0
- /package/ts/{tls → protocols/tls}/sni/sni-extraction.ts +0 -0
- /package/ts/{tls → protocols/tls}/utils/index.ts +0 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import * as plugins from '../../../plugins.js';
|
|
2
|
+
// Import from protocols for consistent status codes
|
|
3
|
+
import { HttpStatus as ProtocolHttpStatus, getStatusText as getProtocolStatusText } from '../../../protocols/http/index.js';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* HTTP-specific event types
|
|
@@ -10,34 +12,33 @@ export enum HttpEvents {
|
|
|
10
12
|
REQUEST_ERROR = 'request-error',
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
15
|
+
|
|
16
|
+
// Re-export for backward compatibility with subset of commonly used codes
|
|
17
|
+
export const HttpStatus = {
|
|
18
|
+
OK: ProtocolHttpStatus.OK,
|
|
19
|
+
MOVED_PERMANENTLY: ProtocolHttpStatus.MOVED_PERMANENTLY,
|
|
20
|
+
FOUND: ProtocolHttpStatus.FOUND,
|
|
21
|
+
TEMPORARY_REDIRECT: ProtocolHttpStatus.TEMPORARY_REDIRECT,
|
|
22
|
+
PERMANENT_REDIRECT: ProtocolHttpStatus.PERMANENT_REDIRECT,
|
|
23
|
+
BAD_REQUEST: ProtocolHttpStatus.BAD_REQUEST,
|
|
24
|
+
UNAUTHORIZED: ProtocolHttpStatus.UNAUTHORIZED,
|
|
25
|
+
FORBIDDEN: ProtocolHttpStatus.FORBIDDEN,
|
|
26
|
+
NOT_FOUND: ProtocolHttpStatus.NOT_FOUND,
|
|
27
|
+
METHOD_NOT_ALLOWED: ProtocolHttpStatus.METHOD_NOT_ALLOWED,
|
|
28
|
+
REQUEST_TIMEOUT: ProtocolHttpStatus.REQUEST_TIMEOUT,
|
|
29
|
+
TOO_MANY_REQUESTS: ProtocolHttpStatus.TOO_MANY_REQUESTS,
|
|
30
|
+
INTERNAL_SERVER_ERROR: ProtocolHttpStatus.INTERNAL_SERVER_ERROR,
|
|
31
|
+
NOT_IMPLEMENTED: ProtocolHttpStatus.NOT_IMPLEMENTED,
|
|
32
|
+
BAD_GATEWAY: ProtocolHttpStatus.BAD_GATEWAY,
|
|
33
|
+
SERVICE_UNAVAILABLE: ProtocolHttpStatus.SERVICE_UNAVAILABLE,
|
|
34
|
+
GATEWAY_TIMEOUT: ProtocolHttpStatus.GATEWAY_TIMEOUT,
|
|
35
|
+
} as const;
|
|
35
36
|
|
|
36
37
|
/**
|
|
37
38
|
* Base error class for HTTP-related errors
|
|
38
39
|
*/
|
|
39
40
|
export class HttpError extends Error {
|
|
40
|
-
constructor(message: string, public readonly statusCode:
|
|
41
|
+
constructor(message: string, public readonly statusCode: number = HttpStatus.INTERNAL_SERVER_ERROR) {
|
|
41
42
|
super(message);
|
|
42
43
|
this.name = 'HttpError';
|
|
43
44
|
}
|
|
@@ -61,7 +62,7 @@ export class CertificateError extends HttpError {
|
|
|
61
62
|
* Error related to server operations
|
|
62
63
|
*/
|
|
63
64
|
export class ServerError extends HttpError {
|
|
64
|
-
constructor(message: string, public readonly code?: string, statusCode:
|
|
65
|
+
constructor(message: string, public readonly code?: string, statusCode: number = HttpStatus.INTERNAL_SERVER_ERROR) {
|
|
65
66
|
super(message, statusCode);
|
|
66
67
|
this.name = 'ServerError';
|
|
67
68
|
}
|
|
@@ -93,7 +94,7 @@ export class NotFoundError extends HttpError {
|
|
|
93
94
|
export interface IRedirectConfig {
|
|
94
95
|
source: string; // Source path or pattern
|
|
95
96
|
destination: string; // Destination URL
|
|
96
|
-
type:
|
|
97
|
+
type: number; // Redirect status code
|
|
97
98
|
preserveQuery?: boolean; // Whether to preserve query parameters
|
|
98
99
|
}
|
|
99
100
|
|
|
@@ -115,30 +116,12 @@ export interface IRouterConfig {
|
|
|
115
116
|
*/
|
|
116
117
|
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
|
|
117
118
|
|
|
119
|
+
|
|
118
120
|
/**
|
|
119
121
|
* Helper function to get HTTP status text
|
|
120
122
|
*/
|
|
121
|
-
export function getStatusText(status:
|
|
122
|
-
|
|
123
|
-
[HttpStatus.OK]: 'OK',
|
|
124
|
-
[HttpStatus.MOVED_PERMANENTLY]: 'Moved Permanently',
|
|
125
|
-
[HttpStatus.FOUND]: 'Found',
|
|
126
|
-
[HttpStatus.TEMPORARY_REDIRECT]: 'Temporary Redirect',
|
|
127
|
-
[HttpStatus.PERMANENT_REDIRECT]: 'Permanent Redirect',
|
|
128
|
-
[HttpStatus.BAD_REQUEST]: 'Bad Request',
|
|
129
|
-
[HttpStatus.UNAUTHORIZED]: 'Unauthorized',
|
|
130
|
-
[HttpStatus.FORBIDDEN]: 'Forbidden',
|
|
131
|
-
[HttpStatus.NOT_FOUND]: 'Not Found',
|
|
132
|
-
[HttpStatus.METHOD_NOT_ALLOWED]: 'Method Not Allowed',
|
|
133
|
-
[HttpStatus.REQUEST_TIMEOUT]: 'Request Timeout',
|
|
134
|
-
[HttpStatus.TOO_MANY_REQUESTS]: 'Too Many Requests',
|
|
135
|
-
[HttpStatus.INTERNAL_SERVER_ERROR]: 'Internal Server Error',
|
|
136
|
-
[HttpStatus.NOT_IMPLEMENTED]: 'Not Implemented',
|
|
137
|
-
[HttpStatus.BAD_GATEWAY]: 'Bad Gateway',
|
|
138
|
-
[HttpStatus.SERVICE_UNAVAILABLE]: 'Service Unavailable',
|
|
139
|
-
[HttpStatus.GATEWAY_TIMEOUT]: 'Gateway Timeout',
|
|
140
|
-
};
|
|
141
|
-
return statusTexts[status] || 'Unknown';
|
|
123
|
+
export function getStatusText(status: number): string {
|
|
124
|
+
return getProtocolStatusText(status as ProtocolHttpStatus);
|
|
142
125
|
}
|
|
143
126
|
|
|
144
127
|
// Legacy interfaces for backward compatibility
|
|
@@ -195,4 +195,11 @@ export interface IConnectionRecord {
|
|
|
195
195
|
|
|
196
196
|
// NFTables tracking
|
|
197
197
|
nftablesHandled?: boolean; // Whether this connection is being handled by NFTables at kernel level
|
|
198
|
+
|
|
199
|
+
// HTTP-specific information (extracted from protocol detection)
|
|
200
|
+
httpInfo?: {
|
|
201
|
+
method?: string;
|
|
202
|
+
path?: string;
|
|
203
|
+
headers?: Record<string, string>;
|
|
204
|
+
};
|
|
198
205
|
}
|
|
@@ -10,6 +10,7 @@ import { WrappedSocket } from '../../core/models/wrapped-socket.js';
|
|
|
10
10
|
import { getUnderlyingSocket } from '../../core/models/socket-types.js';
|
|
11
11
|
import { ProxyProtocolParser } from '../../core/utils/proxy-protocol.js';
|
|
12
12
|
import type { SmartProxy } from './smart-proxy.js';
|
|
13
|
+
import { ProtocolDetector } from '../../detection/index.js';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Handles new connection processing and setup logic with support for route-based configuration
|
|
@@ -301,11 +302,27 @@ export class RouteConnectionHandler {
|
|
|
301
302
|
});
|
|
302
303
|
|
|
303
304
|
// Handler for processing initial data (after potential PROXY protocol)
|
|
304
|
-
const processInitialData = (chunk: Buffer) => {
|
|
305
|
+
const processInitialData = async (chunk: Buffer) => {
|
|
306
|
+
// Use ProtocolDetector to identify protocol
|
|
307
|
+
const connectionId = ProtocolDetector.createConnectionId({
|
|
308
|
+
sourceIp: record.remoteIP,
|
|
309
|
+
sourcePort: socket.remotePort,
|
|
310
|
+
destIp: socket.localAddress,
|
|
311
|
+
destPort: socket.localPort,
|
|
312
|
+
socketId: record.id
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
const detectionResult = await ProtocolDetector.detectWithConnectionTracking(
|
|
316
|
+
chunk,
|
|
317
|
+
connectionId,
|
|
318
|
+
{ extractFullHeaders: false } // Only extract essential info for routing
|
|
319
|
+
);
|
|
320
|
+
|
|
305
321
|
// Block non-TLS connections on port 443
|
|
306
|
-
if (
|
|
307
|
-
logger.log('warn', `Non-TLS connection ${
|
|
308
|
-
connectionId,
|
|
322
|
+
if (localPort === 443 && detectionResult.protocol !== 'tls') {
|
|
323
|
+
logger.log('warn', `Non-TLS connection ${record.id} detected on port 443. Terminating connection - only TLS traffic is allowed on standard HTTPS port.`, {
|
|
324
|
+
connectionId: record.id,
|
|
325
|
+
detectedProtocol: detectionResult.protocol,
|
|
309
326
|
message: 'Terminating connection - only TLS traffic is allowed on standard HTTPS port.',
|
|
310
327
|
component: 'route-handler'
|
|
311
328
|
});
|
|
@@ -318,71 +335,78 @@ export class RouteConnectionHandler {
|
|
|
318
335
|
return;
|
|
319
336
|
}
|
|
320
337
|
|
|
321
|
-
//
|
|
338
|
+
// Extract domain and protocol info
|
|
322
339
|
let serverName = '';
|
|
323
|
-
if (
|
|
340
|
+
if (detectionResult.protocol === 'tls') {
|
|
324
341
|
record.isTLS = true;
|
|
342
|
+
serverName = detectionResult.connectionInfo.domain || '';
|
|
343
|
+
|
|
344
|
+
// Lock the connection to the negotiated SNI
|
|
345
|
+
record.lockedDomain = serverName;
|
|
325
346
|
|
|
326
|
-
// Check
|
|
327
|
-
if (this.smartProxy.
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
connectionId,
|
|
346
|
-
component: 'route-handler'
|
|
347
|
-
});
|
|
348
|
-
if (record.incomingTerminationReason === null) {
|
|
349
|
-
record.incomingTerminationReason = 'session_ticket_blocked_no_sni';
|
|
350
|
-
this.smartProxy.connectionManager.incrementTerminationStat(
|
|
351
|
-
'incoming',
|
|
352
|
-
'session_ticket_blocked_no_sni'
|
|
353
|
-
);
|
|
354
|
-
}
|
|
355
|
-
const alert = Buffer.from([0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x70]);
|
|
356
|
-
try {
|
|
357
|
-
// Count the alert bytes being sent
|
|
358
|
-
record.bytesSent += alert.length;
|
|
359
|
-
if (this.smartProxy.metricsCollector) {
|
|
360
|
-
this.smartProxy.metricsCollector.recordBytes(record.id, 0, alert.length);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
socket.cork();
|
|
364
|
-
socket.write(alert);
|
|
365
|
-
socket.uncork();
|
|
366
|
-
socket.end();
|
|
367
|
-
} catch {
|
|
368
|
-
socket.end();
|
|
347
|
+
// Check if we should reject connections without SNI
|
|
348
|
+
if (!serverName && this.smartProxy.settings.allowSessionTicket === false) {
|
|
349
|
+
logger.log('warn', `No SNI detected in TLS ClientHello for connection ${record.id}; sending TLS alert`, {
|
|
350
|
+
connectionId: record.id,
|
|
351
|
+
component: 'route-handler'
|
|
352
|
+
});
|
|
353
|
+
if (record.incomingTerminationReason === null) {
|
|
354
|
+
record.incomingTerminationReason = 'session_ticket_blocked_no_sni';
|
|
355
|
+
this.smartProxy.connectionManager.incrementTerminationStat(
|
|
356
|
+
'incoming',
|
|
357
|
+
'session_ticket_blocked_no_sni'
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
const alert = Buffer.from([0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x70]);
|
|
361
|
+
try {
|
|
362
|
+
// Count the alert bytes being sent
|
|
363
|
+
record.bytesSent += alert.length;
|
|
364
|
+
if (this.smartProxy.metricsCollector) {
|
|
365
|
+
this.smartProxy.metricsCollector.recordBytes(record.id, 0, alert.length);
|
|
369
366
|
}
|
|
370
|
-
|
|
371
|
-
|
|
367
|
+
|
|
368
|
+
socket.cork();
|
|
369
|
+
socket.write(alert);
|
|
370
|
+
socket.uncork();
|
|
371
|
+
socket.end();
|
|
372
|
+
} catch {
|
|
373
|
+
socket.end();
|
|
372
374
|
}
|
|
375
|
+
this.smartProxy.connectionManager.cleanupConnection(record, 'session_ticket_blocked_no_sni');
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
373
378
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
379
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
380
|
+
logger.log('info', `TLS connection with SNI`, {
|
|
381
|
+
connectionId: record.id,
|
|
382
|
+
serverName: serverName || '(empty)',
|
|
383
|
+
component: 'route-handler'
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
} else if (detectionResult.protocol === 'http') {
|
|
387
|
+
// For HTTP, extract domain from Host header
|
|
388
|
+
serverName = detectionResult.connectionInfo.domain || '';
|
|
389
|
+
|
|
390
|
+
// Store HTTP-specific info for later use
|
|
391
|
+
record.httpInfo = {
|
|
392
|
+
method: detectionResult.connectionInfo.method,
|
|
393
|
+
path: detectionResult.connectionInfo.path,
|
|
394
|
+
headers: detectionResult.connectionInfo.headers
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
398
|
+
logger.log('info', `HTTP connection detected`, {
|
|
399
|
+
connectionId: record.id,
|
|
400
|
+
domain: serverName || '(no host header)',
|
|
401
|
+
method: detectionResult.connectionInfo.method,
|
|
402
|
+
path: detectionResult.connectionInfo.path,
|
|
403
|
+
component: 'route-handler'
|
|
404
|
+
});
|
|
381
405
|
}
|
|
382
406
|
}
|
|
383
407
|
|
|
384
408
|
// Find the appropriate route for this connection
|
|
385
|
-
this.routeConnection(socket, record, serverName, chunk);
|
|
409
|
+
this.routeConnection(socket, record, serverName, chunk, detectionResult);
|
|
386
410
|
};
|
|
387
411
|
|
|
388
412
|
// First data handler to capture initial TLS handshake or PROXY protocol
|
|
@@ -454,7 +478,8 @@ export class RouteConnectionHandler {
|
|
|
454
478
|
socket: plugins.net.Socket | WrappedSocket,
|
|
455
479
|
record: IConnectionRecord,
|
|
456
480
|
serverName: string,
|
|
457
|
-
initialChunk?: Buffer
|
|
481
|
+
initialChunk?: Buffer,
|
|
482
|
+
detectionResult?: any // Using any temporarily to avoid circular dependency issues
|
|
458
483
|
): void {
|
|
459
484
|
const connectionId = record.id;
|
|
460
485
|
const localPort = record.localPort;
|
|
@@ -635,7 +660,7 @@ export class RouteConnectionHandler {
|
|
|
635
660
|
// Handle the route based on its action type
|
|
636
661
|
switch (route.action.type) {
|
|
637
662
|
case 'forward':
|
|
638
|
-
return this.handleForwardAction(socket, record, route, initialChunk);
|
|
663
|
+
return this.handleForwardAction(socket, record, route, initialChunk, detectionResult);
|
|
639
664
|
|
|
640
665
|
case 'socket-handler':
|
|
641
666
|
logger.log('info', `Handling socket-handler action for route ${route.name}`, {
|
|
@@ -738,7 +763,8 @@ export class RouteConnectionHandler {
|
|
|
738
763
|
socket: plugins.net.Socket | WrappedSocket,
|
|
739
764
|
record: IConnectionRecord,
|
|
740
765
|
route: IRouteConfig,
|
|
741
|
-
initialChunk?: Buffer
|
|
766
|
+
initialChunk?: Buffer,
|
|
767
|
+
detectionResult?: any // Using any temporarily to avoid circular dependency issues
|
|
742
768
|
): void {
|
|
743
769
|
const connectionId = record.id;
|
|
744
770
|
const action = route.action as IRouteAction;
|
|
@@ -819,14 +845,11 @@ export class RouteConnectionHandler {
|
|
|
819
845
|
// Create context for target selection
|
|
820
846
|
const targetSelectionContext = {
|
|
821
847
|
port: record.localPort,
|
|
822
|
-
path:
|
|
823
|
-
headers:
|
|
824
|
-
method:
|
|
848
|
+
path: record.httpInfo?.path,
|
|
849
|
+
headers: record.httpInfo?.headers,
|
|
850
|
+
method: record.httpInfo?.method
|
|
825
851
|
};
|
|
826
852
|
|
|
827
|
-
// TODO: Extract path, headers, and method from initialChunk if it's HTTP
|
|
828
|
-
// For now, we'll select based on port only
|
|
829
|
-
|
|
830
853
|
const selectedTarget = this.selectTarget(action.targets, targetSelectionContext);
|
|
831
854
|
if (!selectedTarget) {
|
|
832
855
|
logger.log('error', `No matching target found for connection ${connectionId}`, {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
import * as plugins from '../../../plugins.js';
|
|
22
22
|
import type { IRouteConfig, IRouteMatch, IRouteAction, IRouteTarget, TPortRange, IRouteContext } from '../models/route-types.js';
|
|
23
23
|
import { mergeRouteConfigs } from './route-utils.js';
|
|
24
|
+
import { ProtocolDetector, HttpDetector } from '../../../detection/index.js';
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* Create an HTTP-only route configuration
|
|
@@ -956,83 +957,91 @@ export const SocketHandlers = {
|
|
|
956
957
|
|
|
957
958
|
/**
|
|
958
959
|
* HTTP redirect handler
|
|
960
|
+
* Now uses the centralized detection module for HTTP parsing
|
|
959
961
|
*/
|
|
960
962
|
httpRedirect: (locationTemplate: string, statusCode: number = 301) => (socket: plugins.net.Socket, context: IRouteContext) => {
|
|
961
|
-
|
|
963
|
+
const connectionId = ProtocolDetector.createConnectionId({
|
|
964
|
+
socketId: context.connectionId || `${Date.now()}-${Math.random()}`
|
|
965
|
+
});
|
|
962
966
|
|
|
963
|
-
socket.once('data', (data) => {
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
const domain = context.domain || 'localhost';
|
|
971
|
-
const port = context.port;
|
|
972
|
-
|
|
973
|
-
let finalLocation = locationTemplate
|
|
974
|
-
.replace('{domain}', domain)
|
|
975
|
-
.replace('{port}', String(port))
|
|
976
|
-
.replace('{path}', path)
|
|
977
|
-
.replace('{clientIp}', context.clientIp);
|
|
967
|
+
socket.once('data', async (data) => {
|
|
968
|
+
// Use detection module for parsing
|
|
969
|
+
const detectionResult = await ProtocolDetector.detectWithConnectionTracking(
|
|
970
|
+
data,
|
|
971
|
+
connectionId,
|
|
972
|
+
{ extractFullHeaders: false } // We only need method and path
|
|
973
|
+
);
|
|
978
974
|
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
975
|
+
if (detectionResult.protocol === 'http' && detectionResult.connectionInfo.path) {
|
|
976
|
+
const method = detectionResult.connectionInfo.method || 'GET';
|
|
977
|
+
const path = detectionResult.connectionInfo.path || '/';
|
|
978
|
+
|
|
979
|
+
const domain = context.domain || 'localhost';
|
|
980
|
+
const port = context.port;
|
|
981
|
+
|
|
982
|
+
let finalLocation = locationTemplate
|
|
983
|
+
.replace('{domain}', domain)
|
|
984
|
+
.replace('{port}', String(port))
|
|
985
|
+
.replace('{path}', path)
|
|
986
|
+
.replace('{clientIp}', context.clientIp);
|
|
987
|
+
|
|
988
|
+
const message = `Redirecting to ${finalLocation}`;
|
|
989
|
+
const response = [
|
|
990
|
+
`HTTP/1.1 ${statusCode} ${statusCode === 301 ? 'Moved Permanently' : 'Found'}`,
|
|
991
|
+
`Location: ${finalLocation}`,
|
|
992
|
+
'Content-Type: text/plain',
|
|
993
|
+
`Content-Length: ${message.length}`,
|
|
994
|
+
'Connection: close',
|
|
995
|
+
'',
|
|
996
|
+
message
|
|
997
|
+
].join('\r\n');
|
|
998
|
+
|
|
999
|
+
socket.write(response);
|
|
1000
|
+
} else {
|
|
1001
|
+
// Not a valid HTTP request, close connection
|
|
1002
|
+
socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
|
|
1003
|
+
}
|
|
989
1004
|
|
|
990
|
-
socket.write(response);
|
|
991
1005
|
socket.end();
|
|
1006
|
+
// Clean up detection state
|
|
1007
|
+
ProtocolDetector.cleanupConnections();
|
|
992
1008
|
});
|
|
993
1009
|
},
|
|
994
1010
|
|
|
995
1011
|
/**
|
|
996
1012
|
* HTTP server handler for ACME challenges and other HTTP needs
|
|
1013
|
+
* Now uses the centralized detection module for HTTP parsing
|
|
997
1014
|
*/
|
|
998
1015
|
httpServer: (handler: (req: { method: string; url: string; headers: Record<string, string>; body?: string }, res: { status: (code: number) => void; header: (name: string, value: string) => void; send: (data: string) => void; end: () => void }) => void) => (socket: plugins.net.Socket, context: IRouteContext) => {
|
|
999
|
-
let buffer = '';
|
|
1000
1016
|
let requestParsed = false;
|
|
1017
|
+
const connectionId = ProtocolDetector.createConnectionId({
|
|
1018
|
+
socketId: context.connectionId || `${Date.now()}-${Math.random()}`
|
|
1019
|
+
});
|
|
1001
1020
|
|
|
1002
|
-
|
|
1021
|
+
const processData = async (data: Buffer) => {
|
|
1003
1022
|
if (requestParsed) return; // Only handle the first request
|
|
1004
1023
|
|
|
1005
|
-
|
|
1024
|
+
// Use HttpDetector for parsing
|
|
1025
|
+
const detectionResult = await ProtocolDetector.detectWithConnectionTracking(
|
|
1026
|
+
data,
|
|
1027
|
+
connectionId,
|
|
1028
|
+
{ extractFullHeaders: true }
|
|
1029
|
+
);
|
|
1006
1030
|
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1031
|
+
if (detectionResult.protocol !== 'http' || !detectionResult.isComplete) {
|
|
1032
|
+
// Not a complete HTTP request yet
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1010
1035
|
|
|
1011
1036
|
requestParsed = true;
|
|
1037
|
+
const connInfo = detectionResult.connectionInfo;
|
|
1012
1038
|
|
|
1013
|
-
//
|
|
1014
|
-
const headerPart = buffer.substring(0, headerEndIndex);
|
|
1015
|
-
const bodyPart = buffer.substring(headerEndIndex + 4);
|
|
1016
|
-
|
|
1017
|
-
const lines = headerPart.split('\r\n');
|
|
1018
|
-
const [method, url] = lines[0].split(' ');
|
|
1019
|
-
|
|
1020
|
-
const headers: Record<string, string> = {};
|
|
1021
|
-
for (let i = 1; i < lines.length; i++) {
|
|
1022
|
-
const colonIndex = lines[i].indexOf(':');
|
|
1023
|
-
if (colonIndex > 0) {
|
|
1024
|
-
const name = lines[i].substring(0, colonIndex).trim().toLowerCase();
|
|
1025
|
-
const value = lines[i].substring(colonIndex + 1).trim();
|
|
1026
|
-
headers[name] = value;
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
// Create request object
|
|
1039
|
+
// Create request object from detection result
|
|
1031
1040
|
const req = {
|
|
1032
|
-
method: method || 'GET',
|
|
1033
|
-
url:
|
|
1034
|
-
headers,
|
|
1035
|
-
body:
|
|
1041
|
+
method: connInfo.method || 'GET',
|
|
1042
|
+
url: connInfo.path || '/',
|
|
1043
|
+
headers: connInfo.headers || {},
|
|
1044
|
+
body: detectionResult.remainingBuffer?.toString() || ''
|
|
1036
1045
|
};
|
|
1037
1046
|
|
|
1038
1047
|
// Create response object
|
|
@@ -1093,13 +1102,20 @@ export const SocketHandlers = {
|
|
|
1093
1102
|
res.send('Internal Server Error');
|
|
1094
1103
|
}
|
|
1095
1104
|
}
|
|
1096
|
-
}
|
|
1105
|
+
};
|
|
1106
|
+
|
|
1107
|
+
socket.on('data', processData);
|
|
1097
1108
|
|
|
1098
1109
|
socket.on('error', () => {
|
|
1099
1110
|
if (!requestParsed) {
|
|
1100
1111
|
socket.end();
|
|
1101
1112
|
}
|
|
1102
1113
|
});
|
|
1114
|
+
|
|
1115
|
+
socket.on('close', () => {
|
|
1116
|
+
// Clean up detection state
|
|
1117
|
+
ProtocolDetector.cleanupConnections();
|
|
1118
|
+
});
|
|
1103
1119
|
}
|
|
1104
1120
|
};
|
|
1105
1121
|
|
package/ts/tls/index.ts
CHANGED
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* TLS module
|
|
2
|
+
* TLS module for smartproxy
|
|
3
|
+
* Re-exports protocol components and provides smartproxy-specific functionality
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
|
-
//
|
|
6
|
-
export * from '
|
|
6
|
+
// Re-export all protocol components from protocols/tls
|
|
7
|
+
export * from '../protocols/tls/index.js';
|
|
7
8
|
|
|
8
|
-
// Export SNI
|
|
9
|
+
// Export smartproxy-specific SNI handler
|
|
9
10
|
export * from './sni/sni-handler.js';
|
|
10
|
-
export * from './sni/sni-extraction.js';
|
|
11
|
-
export * from './sni/client-hello-parser.js';
|
|
12
|
-
|
|
13
|
-
// Export TLS utilities
|
|
14
|
-
export * from './utils/tls-utils.js';
|
|
15
11
|
|
|
16
12
|
// Create a namespace for SNI utilities
|
|
17
13
|
import { SniHandler } from './sni/sni-handler.js';
|
|
18
|
-
import { SniExtraction } from '
|
|
19
|
-
import { ClientHelloParser } from '
|
|
14
|
+
import { SniExtraction } from '../protocols/tls/sni/sni-extraction.js';
|
|
15
|
+
import { ClientHelloParser } from '../protocols/tls/sni/client-hello-parser.js';
|
|
20
16
|
|
|
21
17
|
// Export utility objects for convenience
|
|
22
18
|
export const SNI = {
|
|
@@ -30,4 +26,4 @@ export const SNI = {
|
|
|
30
26
|
// Convenience functions
|
|
31
27
|
extractSNI: SniHandler.extractSNI,
|
|
32
28
|
processTlsPacket: SniHandler.processTlsPacket,
|
|
33
|
-
};
|
|
29
|
+
};
|
|
@@ -4,15 +4,15 @@ import {
|
|
|
4
4
|
TlsHandshakeType,
|
|
5
5
|
TlsExtensionType,
|
|
6
6
|
TlsUtils
|
|
7
|
-
} from '
|
|
7
|
+
} from '../../protocols/tls/utils/tls-utils.js';
|
|
8
8
|
import {
|
|
9
9
|
ClientHelloParser,
|
|
10
10
|
type LoggerFunction
|
|
11
|
-
} from '
|
|
11
|
+
} from '../../protocols/tls/sni/client-hello-parser.js';
|
|
12
12
|
import {
|
|
13
13
|
SniExtraction,
|
|
14
14
|
type ConnectionInfo
|
|
15
|
-
} from '
|
|
15
|
+
} from '../../protocols/tls/sni/sni-extraction.js';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* SNI (Server Name Indication) handler for TLS connections.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|