@push.rocks/smartproxy 19.5.20 → 19.5.21
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/core/utils/index.d.ts +1 -0
- package/dist_ts/core/utils/index.js +2 -1
- package/dist_ts/core/utils/proxy-protocol.d.ts +45 -0
- package/dist_ts/core/utils/proxy-protocol.js +201 -0
- package/dist_ts/proxies/smart-proxy/connection-manager.js +3 -1
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +1 -0
- package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +1 -0
- package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +108 -11
- package/package.json +1 -1
- package/readme.hints.md +8 -1
- package/readme.plan.md +23 -19
- package/readme.proxy-chain-summary.md +112 -0
- package/readme.proxy-protocol-example.md +462 -0
- package/readme.proxy-protocol.md +415 -0
- package/ts/core/utils/index.ts +1 -0
- package/ts/core/utils/proxy-protocol.ts +246 -0
- package/ts/proxies/smart-proxy/connection-manager.ts +2 -0
- package/ts/proxies/smart-proxy/models/interfaces.ts +1 -0
- package/ts/proxies/smart-proxy/models/route-types.ts +3 -0
- package/ts/proxies/smart-proxy/route-connection-handler.ts +115 -12
|
@@ -13,6 +13,7 @@ import { SharedRouteManager as RouteManager } from '../../core/routing/route-man
|
|
|
13
13
|
import { cleanupSocket, createIndependentSocketHandlers, setupSocketHandlers, createSocketWithErrorHandler, setupBidirectionalForwarding } from '../../core/utils/socket-utils.js';
|
|
14
14
|
import { WrappedSocket } from '../../core/models/wrapped-socket.js';
|
|
15
15
|
import { getUnderlyingSocket } from '../../core/models/socket-types.js';
|
|
16
|
+
import { ProxyProtocolParser } from '../../core/utils/proxy-protocol.js';
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Handles new connection processing and setup logic with support for route-based configuration
|
|
@@ -295,17 +296,8 @@ export class RouteConnectionHandler {
|
|
|
295
296
|
}
|
|
296
297
|
});
|
|
297
298
|
|
|
298
|
-
//
|
|
299
|
-
|
|
300
|
-
// Clear the initial timeout since we've received data
|
|
301
|
-
if (initialTimeout) {
|
|
302
|
-
clearTimeout(initialTimeout);
|
|
303
|
-
initialTimeout = null;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
initialDataReceived = true;
|
|
307
|
-
record.hasReceivedInitialData = true;
|
|
308
|
-
|
|
299
|
+
// Handler for processing initial data (after potential PROXY protocol)
|
|
300
|
+
const processInitialData = (chunk: Buffer) => {
|
|
309
301
|
// Block non-TLS connections on port 443
|
|
310
302
|
if (!this.tlsManager.isTlsHandshake(chunk) && localPort === 443) {
|
|
311
303
|
logger.log('warn', `Non-TLS connection ${connectionId} detected on port 443. Terminating connection - only TLS traffic is allowed on standard HTTPS port.`, {
|
|
@@ -381,6 +373,67 @@ export class RouteConnectionHandler {
|
|
|
381
373
|
|
|
382
374
|
// Find the appropriate route for this connection
|
|
383
375
|
this.routeConnection(socket, record, serverName, chunk);
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
// First data handler to capture initial TLS handshake or PROXY protocol
|
|
379
|
+
socket.once('data', async (chunk: Buffer) => {
|
|
380
|
+
// Clear the initial timeout since we've received data
|
|
381
|
+
if (initialTimeout) {
|
|
382
|
+
clearTimeout(initialTimeout);
|
|
383
|
+
initialTimeout = null;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
initialDataReceived = true;
|
|
387
|
+
record.hasReceivedInitialData = true;
|
|
388
|
+
|
|
389
|
+
// Check if this is from a trusted proxy and might have PROXY protocol
|
|
390
|
+
if (this.settings.proxyIPs?.includes(socket.remoteAddress || '') && this.settings.acceptProxyProtocol !== false) {
|
|
391
|
+
// Check if this starts with PROXY protocol
|
|
392
|
+
if (chunk.toString('ascii', 0, Math.min(6, chunk.length)).startsWith('PROXY ')) {
|
|
393
|
+
try {
|
|
394
|
+
const parseResult = ProxyProtocolParser.parse(chunk);
|
|
395
|
+
|
|
396
|
+
if (parseResult.proxyInfo) {
|
|
397
|
+
// Update the wrapped socket with real client info (if it's a WrappedSocket)
|
|
398
|
+
if (socket instanceof WrappedSocket) {
|
|
399
|
+
socket.setProxyInfo(parseResult.proxyInfo.sourceIP, parseResult.proxyInfo.sourcePort);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Update connection record with real client info
|
|
403
|
+
record.remoteIP = parseResult.proxyInfo.sourceIP;
|
|
404
|
+
record.remotePort = parseResult.proxyInfo.sourcePort;
|
|
405
|
+
|
|
406
|
+
logger.log('info', `PROXY protocol parsed successfully`, {
|
|
407
|
+
connectionId,
|
|
408
|
+
realClientIP: parseResult.proxyInfo.sourceIP,
|
|
409
|
+
realClientPort: parseResult.proxyInfo.sourcePort,
|
|
410
|
+
proxyIP: socket.remoteAddress,
|
|
411
|
+
component: 'route-handler'
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
// Process remaining data if any
|
|
415
|
+
if (parseResult.remainingData.length > 0) {
|
|
416
|
+
processInitialData(parseResult.remainingData);
|
|
417
|
+
} else {
|
|
418
|
+
// Wait for more data
|
|
419
|
+
socket.once('data', processInitialData);
|
|
420
|
+
}
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
} catch (error) {
|
|
424
|
+
logger.log('error', `Failed to parse PROXY protocol from trusted proxy`, {
|
|
425
|
+
connectionId,
|
|
426
|
+
error: error.message,
|
|
427
|
+
proxyIP: socket.remoteAddress,
|
|
428
|
+
component: 'route-handler'
|
|
429
|
+
});
|
|
430
|
+
// Continue processing as normal data
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Process as normal data (no PROXY protocol)
|
|
436
|
+
processInitialData(chunk);
|
|
384
437
|
});
|
|
385
438
|
}
|
|
386
439
|
|
|
@@ -1119,7 +1172,7 @@ export class RouteConnectionHandler {
|
|
|
1119
1172
|
// Clean up the connection record - this is critical!
|
|
1120
1173
|
this.connectionManager.cleanupConnection(record, `connection_failed_${(error as any).code || 'unknown'}`);
|
|
1121
1174
|
},
|
|
1122
|
-
onConnect: () => {
|
|
1175
|
+
onConnect: async () => {
|
|
1123
1176
|
if (this.settings.enableDetailedLogging) {
|
|
1124
1177
|
logger.log('info', `Connection ${connectionId} established to target ${finalTargetHost}:${finalTargetPort}`, {
|
|
1125
1178
|
connectionId,
|
|
@@ -1135,6 +1188,56 @@ export class RouteConnectionHandler {
|
|
|
1135
1188
|
// Add the normal error handler for established connections
|
|
1136
1189
|
targetSocket.on('error', this.connectionManager.handleError('outgoing', record));
|
|
1137
1190
|
|
|
1191
|
+
// Check if we should send PROXY protocol header
|
|
1192
|
+
const shouldSendProxyProtocol = record.routeConfig?.action?.sendProxyProtocol ||
|
|
1193
|
+
this.settings.sendProxyProtocol;
|
|
1194
|
+
|
|
1195
|
+
if (shouldSendProxyProtocol) {
|
|
1196
|
+
try {
|
|
1197
|
+
// Generate PROXY protocol header
|
|
1198
|
+
const proxyInfo = {
|
|
1199
|
+
protocol: (record.remoteIP.includes(':') ? 'TCP6' : 'TCP4') as 'TCP4' | 'TCP6',
|
|
1200
|
+
sourceIP: record.remoteIP,
|
|
1201
|
+
sourcePort: record.remotePort || socket.remotePort || 0,
|
|
1202
|
+
destinationIP: socket.localAddress || '',
|
|
1203
|
+
destinationPort: socket.localPort || 0
|
|
1204
|
+
};
|
|
1205
|
+
|
|
1206
|
+
const proxyHeader = ProxyProtocolParser.generate(proxyInfo);
|
|
1207
|
+
|
|
1208
|
+
// Send PROXY protocol header first
|
|
1209
|
+
await new Promise<void>((resolve, reject) => {
|
|
1210
|
+
targetSocket.write(proxyHeader, (err) => {
|
|
1211
|
+
if (err) {
|
|
1212
|
+
logger.log('error', `Failed to send PROXY protocol header`, {
|
|
1213
|
+
connectionId,
|
|
1214
|
+
error: err.message,
|
|
1215
|
+
component: 'route-handler'
|
|
1216
|
+
});
|
|
1217
|
+
reject(err);
|
|
1218
|
+
} else {
|
|
1219
|
+
logger.log('info', `PROXY protocol header sent to backend`, {
|
|
1220
|
+
connectionId,
|
|
1221
|
+
targetHost: finalTargetHost,
|
|
1222
|
+
targetPort: finalTargetPort,
|
|
1223
|
+
sourceIP: proxyInfo.sourceIP,
|
|
1224
|
+
sourcePort: proxyInfo.sourcePort,
|
|
1225
|
+
component: 'route-handler'
|
|
1226
|
+
});
|
|
1227
|
+
resolve();
|
|
1228
|
+
}
|
|
1229
|
+
});
|
|
1230
|
+
});
|
|
1231
|
+
} catch (error) {
|
|
1232
|
+
logger.log('error', `Error sending PROXY protocol header`, {
|
|
1233
|
+
connectionId,
|
|
1234
|
+
error: error.message,
|
|
1235
|
+
component: 'route-handler'
|
|
1236
|
+
});
|
|
1237
|
+
// Continue anyway - don't break the connection
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1138
1241
|
// Flush any pending data to target
|
|
1139
1242
|
if (record.pendingData.length > 0) {
|
|
1140
1243
|
const combinedData = Buffer.concat(record.pendingData);
|