@push.rocks/smartproxy 19.3.9 → 19.3.11
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 +2 -2
- package/dist_ts/core/utils/index.d.ts +1 -0
- package/dist_ts/core/utils/index.js +2 -1
- package/dist_ts/core/utils/logger.d.ts +2 -0
- package/dist_ts/core/utils/logger.js +8 -0
- package/dist_ts/plugins.d.ts +3 -1
- package/dist_ts/plugins.js +4 -2
- package/dist_ts/proxies/http-proxy/handlers/static-handler.d.ts +1 -1
- package/dist_ts/proxies/http-proxy/handlers/static-handler.js +11 -3
- package/dist_ts/proxies/smart-proxy/certificate-manager.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/certificate-manager.js +28 -26
- package/dist_ts/proxies/smart-proxy/connection-manager.js +90 -27
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +272 -70
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +46 -26
- package/package.json +2 -1
- package/readme.hints.md +24 -1
- package/readme.md +8 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/core/utils/index.ts +1 -0
- package/ts/core/utils/logger.ts +10 -0
- package/ts/plugins.ts +4 -0
- package/ts/proxies/http-proxy/handlers/static-handler.ts +12 -2
- package/ts/proxies/smart-proxy/certificate-manager.ts +28 -26
- package/ts/proxies/smart-proxy/connection-manager.ts +95 -48
- package/ts/proxies/smart-proxy/route-connection-handler.ts +290 -142
- package/ts/proxies/smart-proxy/smart-proxy.ts +46 -27
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
2
|
import type { IConnectionRecord, ISmartProxyOptions } from './models/interfaces.js';
|
|
3
|
+
import { logger } from '../../core/utils/logger.js';
|
|
3
4
|
// Route checking functions have been removed
|
|
4
5
|
import type { IRouteConfig, IRouteAction, IRouteContext } from './models/route-types.js';
|
|
5
6
|
import { ConnectionManager } from './connection-manager.js';
|
|
@@ -83,7 +84,7 @@ export class RouteConnectionHandler {
|
|
|
83
84
|
// Validate IP against rate limits and connection limits
|
|
84
85
|
const ipValidation = this.securityManager.validateIP(remoteIP);
|
|
85
86
|
if (!ipValidation.allowed) {
|
|
86
|
-
|
|
87
|
+
logger.log('warn', `Connection rejected`, { remoteIP, reason: ipValidation.reason, component: 'route-handler' });
|
|
87
88
|
socket.end();
|
|
88
89
|
socket.destroy();
|
|
89
90
|
return;
|
|
@@ -114,21 +115,35 @@ export class RouteConnectionHandler {
|
|
|
114
115
|
} catch (err) {
|
|
115
116
|
// Ignore errors - these are optional enhancements
|
|
116
117
|
if (this.settings.enableDetailedLogging) {
|
|
117
|
-
|
|
118
|
+
logger.log('warn', `Enhanced TCP keep-alive settings not supported`, { connectionId, error: err, component: 'route-handler' });
|
|
118
119
|
}
|
|
119
120
|
}
|
|
120
121
|
}
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
if (this.settings.enableDetailedLogging) {
|
|
124
|
-
|
|
125
|
-
`
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
logger.log('info',
|
|
126
|
+
`New connection from ${remoteIP} on port ${localPort}. ` +
|
|
127
|
+
`Keep-Alive: ${record.hasKeepAlive ? 'Enabled' : 'Disabled'}. ` +
|
|
128
|
+
`Active connections: ${this.connectionManager.getConnectionCount()}`,
|
|
129
|
+
{
|
|
130
|
+
connectionId,
|
|
131
|
+
remoteIP,
|
|
132
|
+
localPort,
|
|
133
|
+
keepAlive: record.hasKeepAlive ? 'Enabled' : 'Disabled',
|
|
134
|
+
activeConnections: this.connectionManager.getConnectionCount(),
|
|
135
|
+
component: 'route-handler'
|
|
136
|
+
}
|
|
128
137
|
);
|
|
129
138
|
} else {
|
|
130
|
-
|
|
131
|
-
`New connection from ${remoteIP} on port ${localPort}. Active connections: ${this.connectionManager.getConnectionCount()}
|
|
139
|
+
logger.log('info',
|
|
140
|
+
`New connection from ${remoteIP} on port ${localPort}. Active connections: ${this.connectionManager.getConnectionCount()}`,
|
|
141
|
+
{
|
|
142
|
+
remoteIP,
|
|
143
|
+
localPort,
|
|
144
|
+
activeConnections: this.connectionManager.getConnectionCount(),
|
|
145
|
+
component: 'route-handler'
|
|
146
|
+
}
|
|
132
147
|
);
|
|
133
148
|
}
|
|
134
149
|
|
|
@@ -147,14 +162,20 @@ export class RouteConnectionHandler {
|
|
|
147
162
|
// Set an initial timeout for handshake data
|
|
148
163
|
let initialTimeout: NodeJS.Timeout | null = setTimeout(() => {
|
|
149
164
|
if (!initialDataReceived) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
165
|
+
logger.log('warn', `No initial data received from ${record.remoteIP} after ${this.settings.initialDataTimeout}ms for connection ${connectionId}`, {
|
|
166
|
+
connectionId,
|
|
167
|
+
timeout: this.settings.initialDataTimeout,
|
|
168
|
+
remoteIP: record.remoteIP,
|
|
169
|
+
component: 'route-handler'
|
|
170
|
+
});
|
|
153
171
|
|
|
154
172
|
// Add a grace period
|
|
155
173
|
setTimeout(() => {
|
|
156
174
|
if (!initialDataReceived) {
|
|
157
|
-
|
|
175
|
+
logger.log('warn', `Final initial data timeout after grace period for connection ${connectionId}`, {
|
|
176
|
+
connectionId,
|
|
177
|
+
component: 'route-handler'
|
|
178
|
+
});
|
|
158
179
|
if (record.incomingTerminationReason === null) {
|
|
159
180
|
record.incomingTerminationReason = 'initial_timeout';
|
|
160
181
|
this.connectionManager.incrementTerminationStat('incoming', 'initial_timeout');
|
|
@@ -187,10 +208,11 @@ export class RouteConnectionHandler {
|
|
|
187
208
|
|
|
188
209
|
// Block non-TLS connections on port 443
|
|
189
210
|
if (!this.tlsManager.isTlsHandshake(chunk) && localPort === 443) {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
211
|
+
logger.log('warn', `Non-TLS connection ${connectionId} detected on port 443. Terminating connection - only TLS traffic is allowed on standard HTTPS port.`, {
|
|
212
|
+
connectionId,
|
|
213
|
+
message: 'Terminating connection - only TLS traffic is allowed on standard HTTPS port.',
|
|
214
|
+
component: 'route-handler'
|
|
215
|
+
});
|
|
194
216
|
if (record.incomingTerminationReason === null) {
|
|
195
217
|
record.incomingTerminationReason = 'non_tls_blocked';
|
|
196
218
|
this.connectionManager.incrementTerminationStat('incoming', 'non_tls_blocked');
|
|
@@ -223,7 +245,10 @@ export class RouteConnectionHandler {
|
|
|
223
245
|
|
|
224
246
|
// Check if we should reject connections without SNI
|
|
225
247
|
if (!serverName && this.settings.allowSessionTicket === false) {
|
|
226
|
-
|
|
248
|
+
logger.log('warn', `No SNI detected in TLS ClientHello for connection ${connectionId}; sending TLS alert`, {
|
|
249
|
+
connectionId,
|
|
250
|
+
component: 'route-handler'
|
|
251
|
+
});
|
|
227
252
|
if (record.incomingTerminationReason === null) {
|
|
228
253
|
record.incomingTerminationReason = 'session_ticket_blocked_no_sni';
|
|
229
254
|
this.connectionManager.incrementTerminationStat(
|
|
@@ -245,7 +270,11 @@ export class RouteConnectionHandler {
|
|
|
245
270
|
}
|
|
246
271
|
|
|
247
272
|
if (this.settings.enableDetailedLogging) {
|
|
248
|
-
|
|
273
|
+
logger.log('info', `TLS connection with SNI`, {
|
|
274
|
+
connectionId,
|
|
275
|
+
serverName: serverName || '(empty)',
|
|
276
|
+
component: 'route-handler'
|
|
277
|
+
});
|
|
249
278
|
}
|
|
250
279
|
}
|
|
251
280
|
}
|
|
@@ -278,12 +307,18 @@ export class RouteConnectionHandler {
|
|
|
278
307
|
});
|
|
279
308
|
|
|
280
309
|
if (!routeMatch) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
310
|
+
logger.log('warn', `No route found for ${serverName || 'connection'} on port ${localPort} (connection: ${connectionId})`, {
|
|
311
|
+
connectionId,
|
|
312
|
+
serverName: serverName || 'connection',
|
|
313
|
+
localPort,
|
|
314
|
+
component: 'route-handler'
|
|
315
|
+
});
|
|
284
316
|
|
|
285
317
|
// No matching route, use default/fallback handling
|
|
286
|
-
|
|
318
|
+
logger.log('info', `Using default route handling for connection ${connectionId}`, {
|
|
319
|
+
connectionId,
|
|
320
|
+
component: 'route-handler'
|
|
321
|
+
});
|
|
287
322
|
|
|
288
323
|
// Check default security settings
|
|
289
324
|
const defaultSecuritySettings = this.settings.defaults?.security;
|
|
@@ -296,7 +331,11 @@ export class RouteConnectionHandler {
|
|
|
296
331
|
);
|
|
297
332
|
|
|
298
333
|
if (!isAllowed) {
|
|
299
|
-
|
|
334
|
+
logger.log('warn', `IP ${remoteIP} not in default allowed list for connection ${connectionId}`, {
|
|
335
|
+
connectionId,
|
|
336
|
+
remoteIP,
|
|
337
|
+
component: 'route-handler'
|
|
338
|
+
});
|
|
300
339
|
socket.end();
|
|
301
340
|
this.connectionManager.cleanupConnection(record, 'ip_blocked');
|
|
302
341
|
return;
|
|
@@ -321,7 +360,10 @@ export class RouteConnectionHandler {
|
|
|
321
360
|
);
|
|
322
361
|
} else {
|
|
323
362
|
// No default target available, terminate the connection
|
|
324
|
-
|
|
363
|
+
logger.log('warn', `No default target configured for connection ${connectionId}. Closing connection`, {
|
|
364
|
+
connectionId,
|
|
365
|
+
component: 'route-handler'
|
|
366
|
+
});
|
|
325
367
|
socket.end();
|
|
326
368
|
this.connectionManager.cleanupConnection(record, 'no_default_target');
|
|
327
369
|
return;
|
|
@@ -332,11 +374,13 @@ export class RouteConnectionHandler {
|
|
|
332
374
|
const route = routeMatch.route;
|
|
333
375
|
|
|
334
376
|
if (this.settings.enableDetailedLogging) {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
377
|
+
logger.log('info', `Route matched`, {
|
|
378
|
+
connectionId,
|
|
379
|
+
routeName: route.name || 'unnamed',
|
|
380
|
+
serverName: serverName || 'connection',
|
|
381
|
+
localPort,
|
|
382
|
+
component: 'route-handler'
|
|
383
|
+
});
|
|
340
384
|
}
|
|
341
385
|
|
|
342
386
|
|
|
@@ -352,11 +396,15 @@ export class RouteConnectionHandler {
|
|
|
352
396
|
return this.handleBlockAction(socket, record, route);
|
|
353
397
|
|
|
354
398
|
case 'static':
|
|
355
|
-
this.handleStaticAction(socket, record, route);
|
|
399
|
+
this.handleStaticAction(socket, record, route, initialChunk);
|
|
356
400
|
return;
|
|
357
401
|
|
|
358
402
|
default:
|
|
359
|
-
|
|
403
|
+
logger.log('error', `Unknown action type '${(route.action as any).type}' for connection ${connectionId}`, {
|
|
404
|
+
connectionId,
|
|
405
|
+
actionType: (route.action as any).type,
|
|
406
|
+
component: 'route-handler'
|
|
407
|
+
});
|
|
360
408
|
socket.end();
|
|
361
409
|
this.connectionManager.cleanupConnection(record, 'unknown_action');
|
|
362
410
|
}
|
|
@@ -381,30 +429,36 @@ export class RouteConnectionHandler {
|
|
|
381
429
|
|
|
382
430
|
// Log the connection for monitoring purposes
|
|
383
431
|
if (this.settings.enableDetailedLogging) {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
432
|
+
logger.log('info', `NFTables forwarding (kernel-level)`, {
|
|
433
|
+
connectionId: record.id,
|
|
434
|
+
source: `${record.remoteIP}:${socket.remotePort}`,
|
|
435
|
+
destination: `${socket.localAddress}:${record.localPort}`,
|
|
436
|
+
routeName: route.name || 'unnamed',
|
|
437
|
+
domain: record.lockedDomain || 'n/a',
|
|
438
|
+
component: 'route-handler'
|
|
439
|
+
});
|
|
389
440
|
} else {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
441
|
+
logger.log('info', `NFTables forwarding`, {
|
|
442
|
+
connectionId: record.id,
|
|
443
|
+
remoteIP: record.remoteIP,
|
|
444
|
+
localPort: record.localPort,
|
|
445
|
+
routeName: route.name || 'unnamed',
|
|
446
|
+
component: 'route-handler'
|
|
447
|
+
});
|
|
395
448
|
}
|
|
396
449
|
|
|
397
450
|
// Additional NFTables-specific logging if configured
|
|
398
451
|
if (action.nftables) {
|
|
399
452
|
const nftConfig = action.nftables;
|
|
400
453
|
if (this.settings.enableDetailedLogging) {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
454
|
+
logger.log('info', `NFTables config`, {
|
|
455
|
+
connectionId: record.id,
|
|
456
|
+
protocol: nftConfig.protocol || 'tcp',
|
|
457
|
+
preserveSourceIP: nftConfig.preserveSourceIP || false,
|
|
458
|
+
priority: nftConfig.priority || 'default',
|
|
459
|
+
maxRate: nftConfig.maxRate || 'unlimited',
|
|
460
|
+
component: 'route-handler'
|
|
461
|
+
});
|
|
408
462
|
}
|
|
409
463
|
}
|
|
410
464
|
|
|
@@ -419,7 +473,10 @@ export class RouteConnectionHandler {
|
|
|
419
473
|
|
|
420
474
|
// We should have a target configuration for forwarding
|
|
421
475
|
if (!action.target) {
|
|
422
|
-
|
|
476
|
+
logger.log('error', `Forward action missing target configuration for connection ${connectionId}`, {
|
|
477
|
+
connectionId,
|
|
478
|
+
component: 'route-handler'
|
|
479
|
+
});
|
|
423
480
|
socket.end();
|
|
424
481
|
this.connectionManager.cleanupConnection(record, 'missing_target');
|
|
425
482
|
return;
|
|
@@ -447,14 +504,18 @@ export class RouteConnectionHandler {
|
|
|
447
504
|
try {
|
|
448
505
|
targetHost = action.target.host(routeContext);
|
|
449
506
|
if (this.settings.enableDetailedLogging) {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
);
|
|
507
|
+
logger.log('info', `Dynamic host resolved to ${Array.isArray(targetHost) ? targetHost.join(', ') : targetHost} for connection ${connectionId}`, {
|
|
508
|
+
connectionId,
|
|
509
|
+
targetHost: Array.isArray(targetHost) ? targetHost.join(', ') : targetHost,
|
|
510
|
+
component: 'route-handler'
|
|
511
|
+
});
|
|
455
512
|
}
|
|
456
513
|
} catch (err) {
|
|
457
|
-
|
|
514
|
+
logger.log('error', `Error in host mapping function for connection ${connectionId}: ${err}`, {
|
|
515
|
+
connectionId,
|
|
516
|
+
error: err,
|
|
517
|
+
component: 'route-handler'
|
|
518
|
+
});
|
|
458
519
|
socket.end();
|
|
459
520
|
this.connectionManager.cleanupConnection(record, 'host_mapping_error');
|
|
460
521
|
return;
|
|
@@ -474,14 +535,21 @@ export class RouteConnectionHandler {
|
|
|
474
535
|
try {
|
|
475
536
|
targetPort = action.target.port(routeContext);
|
|
476
537
|
if (this.settings.enableDetailedLogging) {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
538
|
+
logger.log('info', `Dynamic port mapping from ${record.localPort} to ${targetPort} for connection ${connectionId}`, {
|
|
539
|
+
connectionId,
|
|
540
|
+
sourcePort: record.localPort,
|
|
541
|
+
targetPort,
|
|
542
|
+
component: 'route-handler'
|
|
543
|
+
});
|
|
480
544
|
}
|
|
481
545
|
// Store the resolved target port in the context for potential future use
|
|
482
546
|
routeContext.targetPort = targetPort;
|
|
483
547
|
} catch (err) {
|
|
484
|
-
|
|
548
|
+
logger.log('error', `Error in port mapping function for connection ${connectionId}: ${err}`, {
|
|
549
|
+
connectionId,
|
|
550
|
+
error: err,
|
|
551
|
+
component: 'route-handler'
|
|
552
|
+
});
|
|
485
553
|
socket.end();
|
|
486
554
|
this.connectionManager.cleanupConnection(record, 'port_mapping_error');
|
|
487
555
|
return;
|
|
@@ -503,7 +571,12 @@ export class RouteConnectionHandler {
|
|
|
503
571
|
case 'passthrough':
|
|
504
572
|
// For TLS passthrough, just forward directly
|
|
505
573
|
if (this.settings.enableDetailedLogging) {
|
|
506
|
-
|
|
574
|
+
logger.log('info', `Using TLS passthrough to ${selectedHost}:${targetPort} for connection ${connectionId}`, {
|
|
575
|
+
connectionId,
|
|
576
|
+
targetHost: selectedHost,
|
|
577
|
+
targetPort,
|
|
578
|
+
component: 'route-handler'
|
|
579
|
+
});
|
|
507
580
|
}
|
|
508
581
|
|
|
509
582
|
return this.setupDirectConnection(
|
|
@@ -521,9 +594,11 @@ export class RouteConnectionHandler {
|
|
|
521
594
|
// For TLS termination, use HttpProxy
|
|
522
595
|
if (this.httpProxyBridge.getHttpProxy()) {
|
|
523
596
|
if (this.settings.enableDetailedLogging) {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
597
|
+
logger.log('info', `Using HttpProxy for TLS termination to ${Array.isArray(action.target.host) ? action.target.host.join(', ') : action.target.host} for connection ${connectionId}`, {
|
|
598
|
+
connectionId,
|
|
599
|
+
targetHost: action.target.host,
|
|
600
|
+
component: 'route-handler'
|
|
601
|
+
});
|
|
527
602
|
}
|
|
528
603
|
|
|
529
604
|
// If we have an initial chunk with TLS data, start processing it
|
|
@@ -540,12 +615,18 @@ export class RouteConnectionHandler {
|
|
|
540
615
|
}
|
|
541
616
|
|
|
542
617
|
// This shouldn't normally happen - we should have TLS data at this point
|
|
543
|
-
|
|
618
|
+
logger.log('error', `TLS termination route without TLS data for connection ${connectionId}`, {
|
|
619
|
+
connectionId,
|
|
620
|
+
component: 'route-handler'
|
|
621
|
+
});
|
|
544
622
|
socket.end();
|
|
545
623
|
this.connectionManager.cleanupConnection(record, 'tls_error');
|
|
546
624
|
return;
|
|
547
625
|
} else {
|
|
548
|
-
|
|
626
|
+
logger.log('error', `HttpProxy not available for TLS termination for connection ${connectionId}`, {
|
|
627
|
+
connectionId,
|
|
628
|
+
component: 'route-handler'
|
|
629
|
+
});
|
|
549
630
|
socket.end();
|
|
550
631
|
this.connectionManager.cleanupConnection(record, 'no_http_proxy');
|
|
551
632
|
return;
|
|
@@ -558,9 +639,11 @@ export class RouteConnectionHandler {
|
|
|
558
639
|
if (isHttpProxyPort && this.httpProxyBridge.getHttpProxy()) {
|
|
559
640
|
// Forward non-TLS connections to HttpProxy if configured
|
|
560
641
|
if (this.settings.enableDetailedLogging) {
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
642
|
+
logger.log('info', `Using HttpProxy for non-TLS connection ${connectionId} on port ${record.localPort}`, {
|
|
643
|
+
connectionId,
|
|
644
|
+
port: record.localPort,
|
|
645
|
+
component: 'route-handler'
|
|
646
|
+
});
|
|
564
647
|
}
|
|
565
648
|
|
|
566
649
|
this.httpProxyBridge.forwardToHttpProxy(
|
|
@@ -575,9 +658,12 @@ export class RouteConnectionHandler {
|
|
|
575
658
|
} else {
|
|
576
659
|
// Basic forwarding
|
|
577
660
|
if (this.settings.enableDetailedLogging) {
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
661
|
+
logger.log('info', `Using basic forwarding to ${Array.isArray(action.target.host) ? action.target.host.join(', ') : action.target.host}:${action.target.port} for connection ${connectionId}`, {
|
|
662
|
+
connectionId,
|
|
663
|
+
targetHost: action.target.host,
|
|
664
|
+
targetPort: action.target.port,
|
|
665
|
+
component: 'route-handler'
|
|
666
|
+
});
|
|
581
667
|
}
|
|
582
668
|
|
|
583
669
|
// Get the appropriate host value
|
|
@@ -633,7 +719,10 @@ export class RouteConnectionHandler {
|
|
|
633
719
|
): void {
|
|
634
720
|
// For TLS connections, we can't do redirects at the TCP level
|
|
635
721
|
if (record.isTLS) {
|
|
636
|
-
|
|
722
|
+
logger.log('warn', `Cannot redirect TLS connection ${record.id} at TCP level`, {
|
|
723
|
+
connectionId: record.id,
|
|
724
|
+
component: 'route-handler'
|
|
725
|
+
});
|
|
637
726
|
socket.end();
|
|
638
727
|
this.connectionManager.cleanupConnection(record, 'tls_redirect_error');
|
|
639
728
|
return;
|
|
@@ -658,9 +747,11 @@ export class RouteConnectionHandler {
|
|
|
658
747
|
const connectionId = record.id;
|
|
659
748
|
|
|
660
749
|
if (this.settings.enableDetailedLogging) {
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
750
|
+
logger.log('info', `Blocking connection ${connectionId} based on route '${route.name || 'unnamed'}'`, {
|
|
751
|
+
connectionId,
|
|
752
|
+
routeName: route.name || 'unnamed',
|
|
753
|
+
component: 'route-handler'
|
|
754
|
+
});
|
|
664
755
|
}
|
|
665
756
|
|
|
666
757
|
// Simply close the connection
|
|
@@ -674,14 +765,15 @@ export class RouteConnectionHandler {
|
|
|
674
765
|
private async handleStaticAction(
|
|
675
766
|
socket: plugins.net.Socket,
|
|
676
767
|
record: IConnectionRecord,
|
|
677
|
-
route: IRouteConfig
|
|
768
|
+
route: IRouteConfig,
|
|
769
|
+
initialChunk?: Buffer
|
|
678
770
|
): Promise<void> {
|
|
679
771
|
// Delegate to HttpProxy's StaticHandler
|
|
680
772
|
await StaticHandler.handleStatic(socket, route, {
|
|
681
773
|
connectionId: record.id,
|
|
682
774
|
connectionManager: this.connectionManager,
|
|
683
775
|
settings: this.settings
|
|
684
|
-
}, record);
|
|
776
|
+
}, record, initialChunk);
|
|
685
777
|
}
|
|
686
778
|
|
|
687
779
|
/**
|
|
@@ -698,8 +790,16 @@ export class RouteConnectionHandler {
|
|
|
698
790
|
targetSocket.once('error', (err) => {
|
|
699
791
|
// This handler runs only once during the initial connection phase
|
|
700
792
|
const code = (err as any).code;
|
|
701
|
-
|
|
702
|
-
`
|
|
793
|
+
logger.log('error',
|
|
794
|
+
`Connection setup error for ${connectionId} to ${finalTargetHost}:${finalTargetPort}: ${err.message} (${code})`,
|
|
795
|
+
{
|
|
796
|
+
connectionId,
|
|
797
|
+
targetHost: finalTargetHost,
|
|
798
|
+
targetPort: finalTargetPort,
|
|
799
|
+
errorMessage: err.message,
|
|
800
|
+
errorCode: code,
|
|
801
|
+
component: 'route-handler'
|
|
802
|
+
}
|
|
703
803
|
);
|
|
704
804
|
|
|
705
805
|
// Resume the incoming socket to prevent it from hanging
|
|
@@ -707,29 +807,57 @@ export class RouteConnectionHandler {
|
|
|
707
807
|
|
|
708
808
|
// Log specific error types for easier debugging
|
|
709
809
|
if (code === 'ECONNREFUSED') {
|
|
710
|
-
|
|
711
|
-
`
|
|
712
|
-
|
|
810
|
+
logger.log('error',
|
|
811
|
+
`Connection ${connectionId}: Target ${finalTargetHost}:${finalTargetPort} refused connection. Check if the target service is running and listening on that port.`,
|
|
812
|
+
{
|
|
813
|
+
connectionId,
|
|
814
|
+
targetHost: finalTargetHost,
|
|
815
|
+
targetPort: finalTargetPort,
|
|
816
|
+
recommendation: 'Check if the target service is running and listening on that port.',
|
|
817
|
+
component: 'route-handler'
|
|
818
|
+
}
|
|
713
819
|
);
|
|
714
820
|
} else if (code === 'ETIMEDOUT') {
|
|
715
|
-
|
|
716
|
-
`
|
|
717
|
-
|
|
821
|
+
logger.log('error',
|
|
822
|
+
`Connection ${connectionId} to ${finalTargetHost}:${finalTargetPort} timed out. Check network conditions, firewall rules, or if the target is too far away.`,
|
|
823
|
+
{
|
|
824
|
+
connectionId,
|
|
825
|
+
targetHost: finalTargetHost,
|
|
826
|
+
targetPort: finalTargetPort,
|
|
827
|
+
recommendation: 'Check network conditions, firewall rules, or if the target is too far away.',
|
|
828
|
+
component: 'route-handler'
|
|
829
|
+
}
|
|
718
830
|
);
|
|
719
831
|
} else if (code === 'ECONNRESET') {
|
|
720
|
-
|
|
721
|
-
`
|
|
722
|
-
|
|
832
|
+
logger.log('error',
|
|
833
|
+
`Connection ${connectionId} to ${finalTargetHost}:${finalTargetPort} was reset. The target might have closed the connection abruptly.`,
|
|
834
|
+
{
|
|
835
|
+
connectionId,
|
|
836
|
+
targetHost: finalTargetHost,
|
|
837
|
+
targetPort: finalTargetPort,
|
|
838
|
+
recommendation: 'The target might have closed the connection abruptly.',
|
|
839
|
+
component: 'route-handler'
|
|
840
|
+
}
|
|
723
841
|
);
|
|
724
842
|
} else if (code === 'EHOSTUNREACH') {
|
|
725
|
-
|
|
726
|
-
`
|
|
727
|
-
|
|
843
|
+
logger.log('error',
|
|
844
|
+
`Connection ${connectionId}: Host ${finalTargetHost} is unreachable. Check DNS settings, network routing, or firewall rules.`,
|
|
845
|
+
{
|
|
846
|
+
connectionId,
|
|
847
|
+
targetHost: finalTargetHost,
|
|
848
|
+
recommendation: 'Check DNS settings, network routing, or firewall rules.',
|
|
849
|
+
component: 'route-handler'
|
|
850
|
+
}
|
|
728
851
|
);
|
|
729
852
|
} else if (code === 'ENOTFOUND') {
|
|
730
|
-
|
|
731
|
-
`
|
|
732
|
-
|
|
853
|
+
logger.log('error',
|
|
854
|
+
`Connection ${connectionId}: DNS lookup failed for ${finalTargetHost}. Check your DNS settings or if the hostname is correct.`,
|
|
855
|
+
{
|
|
856
|
+
connectionId,
|
|
857
|
+
targetHost: finalTargetHost,
|
|
858
|
+
recommendation: 'Check your DNS settings or if the hostname is correct.',
|
|
859
|
+
component: 'route-handler'
|
|
860
|
+
}
|
|
733
861
|
);
|
|
734
862
|
}
|
|
735
863
|
|
|
@@ -778,9 +906,12 @@ export class RouteConnectionHandler {
|
|
|
778
906
|
record.targetPort = finalTargetPort;
|
|
779
907
|
|
|
780
908
|
if (this.settings.enableDetailedLogging) {
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
909
|
+
logger.log('info', `Setting up direct connection ${connectionId} to ${finalTargetHost}:${finalTargetPort}`, {
|
|
910
|
+
connectionId,
|
|
911
|
+
targetHost: finalTargetHost,
|
|
912
|
+
targetPort: finalTargetPort,
|
|
913
|
+
component: 'route-handler'
|
|
914
|
+
});
|
|
784
915
|
}
|
|
785
916
|
|
|
786
917
|
// Setup connection options
|
|
@@ -825,9 +956,11 @@ export class RouteConnectionHandler {
|
|
|
825
956
|
} catch (err) {
|
|
826
957
|
// Ignore errors - these are optional enhancements
|
|
827
958
|
if (this.settings.enableDetailedLogging) {
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
959
|
+
logger.log('warn', `Enhanced TCP keep-alive not supported for outgoing socket on connection ${connectionId}: ${err}`, {
|
|
960
|
+
connectionId,
|
|
961
|
+
error: err,
|
|
962
|
+
component: 'route-handler'
|
|
963
|
+
});
|
|
831
964
|
}
|
|
832
965
|
}
|
|
833
966
|
}
|
|
@@ -847,22 +980,23 @@ export class RouteConnectionHandler {
|
|
|
847
980
|
socket.on('timeout', () => {
|
|
848
981
|
// For keep-alive connections, just log a warning instead of closing
|
|
849
982
|
if (record.hasKeepAlive) {
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
);
|
|
983
|
+
logger.log('warn', `Timeout event on incoming keep-alive connection ${connectionId} from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}. Connection preserved.`, {
|
|
984
|
+
connectionId,
|
|
985
|
+
remoteIP: record.remoteIP,
|
|
986
|
+
timeout: plugins.prettyMs(this.settings.socketTimeout || 3600000),
|
|
987
|
+
status: 'Connection preserved',
|
|
988
|
+
component: 'route-handler'
|
|
989
|
+
});
|
|
857
990
|
return;
|
|
858
991
|
}
|
|
859
992
|
|
|
860
993
|
// For non-keep-alive connections, proceed with normal cleanup
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
994
|
+
logger.log('warn', `Timeout on incoming side for connection ${connectionId} from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}`, {
|
|
995
|
+
connectionId,
|
|
996
|
+
remoteIP: record.remoteIP,
|
|
997
|
+
timeout: plugins.prettyMs(this.settings.socketTimeout || 3600000),
|
|
998
|
+
component: 'route-handler'
|
|
999
|
+
});
|
|
866
1000
|
if (record.incomingTerminationReason === null) {
|
|
867
1001
|
record.incomingTerminationReason = 'timeout';
|
|
868
1002
|
this.connectionManager.incrementTerminationStat('incoming', 'timeout');
|
|
@@ -873,22 +1007,23 @@ export class RouteConnectionHandler {
|
|
|
873
1007
|
targetSocket.on('timeout', () => {
|
|
874
1008
|
// For keep-alive connections, just log a warning instead of closing
|
|
875
1009
|
if (record.hasKeepAlive) {
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
);
|
|
1010
|
+
logger.log('warn', `Timeout event on outgoing keep-alive connection ${connectionId} from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}. Connection preserved.`, {
|
|
1011
|
+
connectionId,
|
|
1012
|
+
remoteIP: record.remoteIP,
|
|
1013
|
+
timeout: plugins.prettyMs(this.settings.socketTimeout || 3600000),
|
|
1014
|
+
status: 'Connection preserved',
|
|
1015
|
+
component: 'route-handler'
|
|
1016
|
+
});
|
|
883
1017
|
return;
|
|
884
1018
|
}
|
|
885
1019
|
|
|
886
1020
|
// For non-keep-alive connections, proceed with normal cleanup
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
1021
|
+
logger.log('warn', `Timeout on outgoing side for connection ${connectionId} from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}`, {
|
|
1022
|
+
connectionId,
|
|
1023
|
+
remoteIP: record.remoteIP,
|
|
1024
|
+
timeout: plugins.prettyMs(this.settings.socketTimeout || 3600000),
|
|
1025
|
+
component: 'route-handler'
|
|
1026
|
+
});
|
|
892
1027
|
if (record.outgoingTerminationReason === null) {
|
|
893
1028
|
record.outgoingTerminationReason = 'timeout';
|
|
894
1029
|
this.connectionManager.incrementTerminationStat('outgoing', 'timeout');
|
|
@@ -908,9 +1043,12 @@ export class RouteConnectionHandler {
|
|
|
908
1043
|
// Wait for the outgoing connection to be ready before setting up piping
|
|
909
1044
|
targetSocket.once('connect', () => {
|
|
910
1045
|
if (this.settings.enableDetailedLogging) {
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
1046
|
+
logger.log('info', `Connection ${connectionId} established to target ${finalTargetHost}:${finalTargetPort}`, {
|
|
1047
|
+
connectionId,
|
|
1048
|
+
targetHost: finalTargetHost,
|
|
1049
|
+
targetPort: finalTargetPort,
|
|
1050
|
+
component: 'route-handler'
|
|
1051
|
+
});
|
|
914
1052
|
}
|
|
915
1053
|
|
|
916
1054
|
// Clear the initial connection error handler
|
|
@@ -932,7 +1070,11 @@ export class RouteConnectionHandler {
|
|
|
932
1070
|
// Write pending data immediately
|
|
933
1071
|
targetSocket.write(combinedData, (err) => {
|
|
934
1072
|
if (err) {
|
|
935
|
-
|
|
1073
|
+
logger.log('error', `Error writing pending data to target for connection ${connectionId}: ${err.message}`, {
|
|
1074
|
+
connectionId,
|
|
1075
|
+
error: err.message,
|
|
1076
|
+
component: 'route-handler'
|
|
1077
|
+
});
|
|
936
1078
|
return this.connectionManager.initiateCleanupOnce(record, 'write_error');
|
|
937
1079
|
}
|
|
938
1080
|
});
|
|
@@ -953,15 +1095,17 @@ export class RouteConnectionHandler {
|
|
|
953
1095
|
});
|
|
954
1096
|
|
|
955
1097
|
// Log successful connection
|
|
956
|
-
|
|
1098
|
+
logger.log('info',
|
|
957
1099
|
`Connection established: ${record.remoteIP} -> ${finalTargetHost}:${finalTargetPort}` +
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1100
|
+
`${serverName ? ` (SNI: ${serverName})` : record.lockedDomain ? ` (Domain: ${record.lockedDomain})` : ''}`,
|
|
1101
|
+
{
|
|
1102
|
+
remoteIP: record.remoteIP,
|
|
1103
|
+
targetHost: finalTargetHost,
|
|
1104
|
+
targetPort: finalTargetPort,
|
|
1105
|
+
sni: serverName || undefined,
|
|
1106
|
+
domain: !serverName && record.lockedDomain ? record.lockedDomain : undefined,
|
|
1107
|
+
component: 'route-handler'
|
|
1108
|
+
}
|
|
965
1109
|
);
|
|
966
1110
|
|
|
967
1111
|
// Add TLS renegotiation handler if needed
|
|
@@ -989,17 +1133,21 @@ export class RouteConnectionHandler {
|
|
|
989
1133
|
socket.on('data', renegotiationHandler);
|
|
990
1134
|
|
|
991
1135
|
if (this.settings.enableDetailedLogging) {
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1136
|
+
logger.log('info', `TLS renegotiation handler installed for connection ${connectionId} with SNI ${serverName}`, {
|
|
1137
|
+
connectionId,
|
|
1138
|
+
serverName,
|
|
1139
|
+
component: 'route-handler'
|
|
1140
|
+
});
|
|
995
1141
|
}
|
|
996
1142
|
}
|
|
997
1143
|
|
|
998
1144
|
// Set connection timeout
|
|
999
1145
|
record.cleanupTimer = this.timeoutManager.setupConnectionTimeout(record, (record, reason) => {
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1146
|
+
logger.log('warn', `Connection ${connectionId} from ${record.remoteIP} exceeded max lifetime, forcing cleanup`, {
|
|
1147
|
+
connectionId,
|
|
1148
|
+
remoteIP: record.remoteIP,
|
|
1149
|
+
component: 'route-handler'
|
|
1150
|
+
});
|
|
1003
1151
|
this.connectionManager.initiateCleanupOnce(record, reason);
|
|
1004
1152
|
});
|
|
1005
1153
|
|