@push.rocks/smartproxy 10.2.0 → 12.0.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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/common/port80-adapter.d.ts +11 -0
- package/dist_ts/common/port80-adapter.js +61 -0
- package/dist_ts/examples/forwarding-example.d.ts +1 -0
- package/dist_ts/examples/forwarding-example.js +96 -0
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +3 -1
- package/dist_ts/smartproxy/classes.pp.connectionhandler.js +179 -30
- package/dist_ts/smartproxy/classes.pp.domainconfigmanager.d.ts +39 -0
- package/dist_ts/smartproxy/classes.pp.domainconfigmanager.js +172 -20
- package/dist_ts/smartproxy/classes.pp.interfaces.d.ts +3 -11
- package/dist_ts/smartproxy/classes.pp.portrangemanager.js +17 -10
- package/dist_ts/smartproxy/classes.pp.securitymanager.d.ts +19 -2
- package/dist_ts/smartproxy/classes.pp.securitymanager.js +27 -4
- package/dist_ts/smartproxy/classes.pp.timeoutmanager.js +3 -3
- package/dist_ts/smartproxy/classes.smartproxy.js +45 -13
- package/dist_ts/smartproxy/forwarding/domain-config.d.ts +12 -0
- package/dist_ts/smartproxy/forwarding/domain-config.js +12 -0
- package/dist_ts/smartproxy/forwarding/domain-manager.d.ts +86 -0
- package/dist_ts/smartproxy/forwarding/domain-manager.js +241 -0
- package/dist_ts/smartproxy/forwarding/forwarding.factory.d.ts +24 -0
- package/dist_ts/smartproxy/forwarding/forwarding.factory.js +137 -0
- package/dist_ts/smartproxy/forwarding/forwarding.handler.d.ts +55 -0
- package/dist_ts/smartproxy/forwarding/forwarding.handler.js +94 -0
- package/dist_ts/smartproxy/forwarding/http.handler.d.ts +25 -0
- package/dist_ts/smartproxy/forwarding/http.handler.js +123 -0
- package/dist_ts/smartproxy/forwarding/https-passthrough.handler.d.ts +24 -0
- package/dist_ts/smartproxy/forwarding/https-passthrough.handler.js +154 -0
- package/dist_ts/smartproxy/forwarding/https-terminate-to-http.handler.d.ts +36 -0
- package/dist_ts/smartproxy/forwarding/https-terminate-to-http.handler.js +229 -0
- package/dist_ts/smartproxy/forwarding/https-terminate-to-https.handler.d.ts +35 -0
- package/dist_ts/smartproxy/forwarding/https-terminate-to-https.handler.js +254 -0
- package/dist_ts/smartproxy/forwarding/index.d.ts +16 -0
- package/dist_ts/smartproxy/forwarding/index.js +23 -0
- package/dist_ts/smartproxy/types/forwarding.types.d.ts +104 -0
- package/dist_ts/smartproxy/types/forwarding.types.js +50 -0
- package/package.json +2 -2
- package/readme.md +158 -8
- package/readme.plan.md +471 -42
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/common/port80-adapter.ts +87 -0
- package/ts/index.ts +3 -0
- package/ts/smartproxy/classes.pp.connectionhandler.ts +231 -44
- package/ts/smartproxy/classes.pp.domainconfigmanager.ts +198 -24
- package/ts/smartproxy/classes.pp.interfaces.ts +3 -11
- package/ts/smartproxy/classes.pp.portrangemanager.ts +17 -10
- package/ts/smartproxy/classes.pp.securitymanager.ts +29 -5
- package/ts/smartproxy/classes.pp.timeoutmanager.ts +3 -3
- package/ts/smartproxy/classes.smartproxy.ts +68 -15
- package/ts/smartproxy/forwarding/domain-config.ts +28 -0
- package/ts/smartproxy/forwarding/domain-manager.ts +283 -0
- package/ts/smartproxy/forwarding/forwarding.factory.ts +155 -0
- package/ts/smartproxy/forwarding/forwarding.handler.ts +127 -0
- package/ts/smartproxy/forwarding/http.handler.ts +140 -0
- package/ts/smartproxy/forwarding/https-passthrough.handler.ts +182 -0
- package/ts/smartproxy/forwarding/https-terminate-to-http.handler.ts +264 -0
- package/ts/smartproxy/forwarding/https-terminate-to-https.handler.ts +292 -0
- package/ts/smartproxy/forwarding/index.ts +52 -0
- package/ts/smartproxy/types/forwarding.types.ts +162 -0
|
@@ -11,6 +11,8 @@ import { TlsManager } from './classes.pp.tlsmanager.js';
|
|
|
11
11
|
import { NetworkProxyBridge } from './classes.pp.networkproxybridge.js';
|
|
12
12
|
import { TimeoutManager } from './classes.pp.timeoutmanager.js';
|
|
13
13
|
import { PortRangeManager } from './classes.pp.portrangemanager.js';
|
|
14
|
+
import type { IForwardingHandler } from './types/forwarding.types.js';
|
|
15
|
+
import type { ForwardingType } from './types/forwarding.types.js';
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* Handles new connection processing and setup logic
|
|
@@ -176,37 +178,73 @@ export class ConnectionHandler {
|
|
|
176
178
|
destPort: socket.localPort || 0,
|
|
177
179
|
};
|
|
178
180
|
|
|
179
|
-
// Extract SNI for domain-specific
|
|
181
|
+
// Extract SNI for domain-specific forwarding if available
|
|
180
182
|
const serverName = this.tlsManager.extractSNI(chunk, connInfo);
|
|
181
183
|
|
|
182
184
|
// For NetworkProxy connections, we'll allow session tickets even without SNI
|
|
183
|
-
// We'll only use the serverName if available to determine the specific
|
|
185
|
+
// We'll only use the serverName if available to determine the specific forwarding
|
|
184
186
|
if (serverName) {
|
|
185
187
|
// Save domain config and SNI in connection record
|
|
186
188
|
const domainConfig = this.domainConfigManager.findDomainConfig(serverName);
|
|
187
189
|
record.domainConfig = domainConfig;
|
|
188
190
|
record.lockedDomain = serverName;
|
|
189
191
|
|
|
190
|
-
//
|
|
191
|
-
if (domainConfig
|
|
192
|
-
|
|
192
|
+
// If we have a domain config and it has a forwarding config
|
|
193
|
+
if (domainConfig) {
|
|
194
|
+
try {
|
|
195
|
+
// Get the forwarding type for this domain
|
|
196
|
+
const forwardingType = this.domainConfigManager.getForwardingType(domainConfig);
|
|
197
|
+
|
|
198
|
+
// For TLS termination types, use NetworkProxy
|
|
199
|
+
if (forwardingType === 'https-terminate-to-http' ||
|
|
200
|
+
forwardingType === 'https-terminate-to-https') {
|
|
201
|
+
const networkProxyPort = this.domainConfigManager.getNetworkProxyPort(domainConfig);
|
|
202
|
+
|
|
203
|
+
if (this.settings.enableDetailedLogging) {
|
|
204
|
+
console.log(
|
|
205
|
+
`[${connectionId}] Using TLS termination (${forwardingType}) for ${serverName} on port ${networkProxyPort}`
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Forward to NetworkProxy with domain-specific port
|
|
210
|
+
this.networkProxyBridge.forwardToNetworkProxy(
|
|
211
|
+
connectionId,
|
|
212
|
+
socket,
|
|
213
|
+
record,
|
|
214
|
+
chunk,
|
|
215
|
+
networkProxyPort,
|
|
216
|
+
(reason) => this.connectionManager.initiateCleanupOnce(record, reason)
|
|
217
|
+
);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
193
220
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
);
|
|
198
|
-
}
|
|
221
|
+
// For HTTPS passthrough, use the forwarding handler directly
|
|
222
|
+
if (forwardingType === 'https-passthrough') {
|
|
223
|
+
const handler = this.domainConfigManager.getForwardingHandler(domainConfig);
|
|
199
224
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
225
|
+
if (this.settings.enableDetailedLogging) {
|
|
226
|
+
console.log(
|
|
227
|
+
`[${connectionId}] Using forwarding handler for SNI passthrough to ${serverName}`
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Handle the connection using the handler
|
|
232
|
+
handler.handleConnection(socket);
|
|
233
|
+
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// For HTTP-only, we shouldn't get TLS connections
|
|
238
|
+
if (forwardingType === 'http-only') {
|
|
239
|
+
console.log(`[${connectionId}] Received TLS connection for HTTP-only domain ${serverName}`);
|
|
240
|
+
socket.end();
|
|
241
|
+
this.connectionManager.cleanupConnection(record, 'wrong_protocol');
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
} catch (err) {
|
|
245
|
+
console.log(`[${connectionId}] Error using forwarding handler: ${err}`);
|
|
246
|
+
// Fall through to default NetworkProxy handling
|
|
247
|
+
}
|
|
210
248
|
}
|
|
211
249
|
} else if (
|
|
212
250
|
this.settings.allowSessionTicket === false &&
|
|
@@ -229,10 +267,38 @@ export class ConnectionHandler {
|
|
|
229
267
|
(reason) => this.connectionManager.initiateCleanupOnce(record, reason)
|
|
230
268
|
);
|
|
231
269
|
} else {
|
|
232
|
-
// If not TLS,
|
|
270
|
+
// If not TLS, handle as plain HTTP
|
|
233
271
|
console.log(
|
|
234
272
|
`[${connectionId}] Non-TLS connection on NetworkProxy port ${record.localPort}`
|
|
235
273
|
);
|
|
274
|
+
|
|
275
|
+
// Check if we have a domain config based on port
|
|
276
|
+
const portBasedDomainConfig = this.domainConfigManager.findDomainConfigForPort(record.localPort);
|
|
277
|
+
|
|
278
|
+
if (portBasedDomainConfig) {
|
|
279
|
+
try {
|
|
280
|
+
// If this domain supports HTTP via a forwarding handler, use it
|
|
281
|
+
if (this.domainConfigManager.supportsHttp(portBasedDomainConfig)) {
|
|
282
|
+
const handler = this.domainConfigManager.getForwardingHandler(portBasedDomainConfig);
|
|
283
|
+
|
|
284
|
+
if (this.settings.enableDetailedLogging) {
|
|
285
|
+
console.log(
|
|
286
|
+
`[${connectionId}] Using forwarding handler for non-TLS connection to port ${record.localPort}`
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Handle the connection using the handler
|
|
291
|
+
handler.handleConnection(socket);
|
|
292
|
+
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
} catch (err) {
|
|
296
|
+
console.log(`[${connectionId}] Error using forwarding handler for HTTP: ${err}`);
|
|
297
|
+
// Fall through to direct connection
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Use legacy direct connection as fallback
|
|
236
302
|
this.setupDirectConnection(socket, record, undefined, undefined, chunk);
|
|
237
303
|
}
|
|
238
304
|
});
|
|
@@ -380,9 +446,8 @@ export class ConnectionHandler {
|
|
|
380
446
|
if (domainConfig) {
|
|
381
447
|
const ipRules = this.domainConfigManager.getEffectiveIPRules(domainConfig);
|
|
382
448
|
|
|
383
|
-
//
|
|
449
|
+
// Perform IP validation using security rules
|
|
384
450
|
if (
|
|
385
|
-
domainConfig.allowedIPs.length > 0 &&
|
|
386
451
|
!this.securityManager.isIPAuthorized(
|
|
387
452
|
record.remoteIP,
|
|
388
453
|
ipRules.allowedIPs,
|
|
@@ -431,10 +496,31 @@ export class ConnectionHandler {
|
|
|
431
496
|
// Only apply port-based rules if the incoming port is within one of the global port ranges.
|
|
432
497
|
if (this.portRangeManager.isPortInGlobalRanges(localPort)) {
|
|
433
498
|
if (this.portRangeManager.shouldUseGlobalForwarding(localPort)) {
|
|
499
|
+
// Create a virtual domain config for global forwarding with security settings
|
|
500
|
+
const globalDomainConfig = {
|
|
501
|
+
domains: ['global'],
|
|
502
|
+
forwarding: {
|
|
503
|
+
type: 'http-only' as ForwardingType,
|
|
504
|
+
target: {
|
|
505
|
+
host: this.settings.targetIP!,
|
|
506
|
+
port: this.settings.toPort
|
|
507
|
+
},
|
|
508
|
+
security: {
|
|
509
|
+
allowedIps: this.settings.defaultAllowedIPs || [],
|
|
510
|
+
blockedIps: this.settings.defaultBlockedIPs || []
|
|
511
|
+
}
|
|
512
|
+
},
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
// Use the same IP filtering mechanism as domain-specific configs
|
|
516
|
+
const ipRules = this.domainConfigManager.getEffectiveIPRules(globalDomainConfig);
|
|
517
|
+
|
|
434
518
|
if (
|
|
435
|
-
this.
|
|
436
|
-
|
|
437
|
-
|
|
519
|
+
!this.securityManager.isIPAuthorized(
|
|
520
|
+
record.remoteIP,
|
|
521
|
+
ipRules.allowedIPs,
|
|
522
|
+
ipRules.blockedIPs
|
|
523
|
+
)
|
|
438
524
|
) {
|
|
439
525
|
console.log(
|
|
440
526
|
`[${connectionId}] Connection from ${record.remoteIP} rejected: IP ${record.remoteIP} not allowed in global default allowed list.`
|
|
@@ -442,29 +528,21 @@ export class ConnectionHandler {
|
|
|
442
528
|
socket.end();
|
|
443
529
|
return;
|
|
444
530
|
}
|
|
531
|
+
|
|
445
532
|
if (this.settings.enableDetailedLogging) {
|
|
446
533
|
console.log(
|
|
447
534
|
`[${connectionId}] Port-based connection from ${record.remoteIP} on port ${localPort} forwarded to global target IP ${this.settings.targetIP}.`
|
|
448
535
|
);
|
|
449
536
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
undefined,
|
|
453
|
-
{
|
|
454
|
-
domains: ['global'],
|
|
455
|
-
allowedIPs: this.settings.defaultAllowedIPs || [],
|
|
456
|
-
blockedIPs: this.settings.defaultBlockedIPs || [],
|
|
457
|
-
targetIPs: [this.settings.targetIP!],
|
|
458
|
-
portRanges: [],
|
|
459
|
-
},
|
|
460
|
-
localPort
|
|
461
|
-
);
|
|
537
|
+
|
|
538
|
+
setupConnection('', undefined, globalDomainConfig, localPort);
|
|
462
539
|
return;
|
|
463
540
|
} else {
|
|
464
541
|
// Attempt to find a matching forced domain config based on the local port.
|
|
465
542
|
const forcedDomain = this.domainConfigManager.findDomainConfigForPort(localPort);
|
|
466
543
|
|
|
467
544
|
if (forcedDomain) {
|
|
545
|
+
// Get effective IP rules from the domain config's forwarding security settings
|
|
468
546
|
const ipRules = this.domainConfigManager.getEffectiveIPRules(forcedDomain);
|
|
469
547
|
|
|
470
548
|
if (
|
|
@@ -624,10 +702,18 @@ export class ConnectionHandler {
|
|
|
624
702
|
initialDataReceived = true;
|
|
625
703
|
record.hasReceivedInitialData = true;
|
|
626
704
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
this.settings.defaultAllowedIPs
|
|
630
|
-
|
|
705
|
+
// Create default security settings for non-SNI connections
|
|
706
|
+
const defaultSecurity = {
|
|
707
|
+
allowedIPs: this.settings.defaultAllowedIPs || [],
|
|
708
|
+
blockedIPs: this.settings.defaultBlockedIPs || []
|
|
709
|
+
};
|
|
710
|
+
|
|
711
|
+
if (defaultSecurity.allowedIPs.length > 0 &&
|
|
712
|
+
!this.securityManager.isIPAuthorized(
|
|
713
|
+
record.remoteIP,
|
|
714
|
+
defaultSecurity.allowedIPs,
|
|
715
|
+
defaultSecurity.blockedIPs
|
|
716
|
+
)
|
|
631
717
|
) {
|
|
632
718
|
return rejectIncomingConnection(
|
|
633
719
|
'rejected',
|
|
@@ -652,13 +738,99 @@ export class ConnectionHandler {
|
|
|
652
738
|
): void {
|
|
653
739
|
const connectionId = record.id;
|
|
654
740
|
|
|
741
|
+
// If we have a domain config, try to use a forwarding handler
|
|
742
|
+
if (domainConfig) {
|
|
743
|
+
try {
|
|
744
|
+
// Get the forwarding handler for this domain
|
|
745
|
+
const forwardingHandler = this.domainConfigManager.getForwardingHandler(domainConfig);
|
|
746
|
+
|
|
747
|
+
// Check the forwarding type to determine how to handle the connection
|
|
748
|
+
const forwardingType = this.domainConfigManager.getForwardingType(domainConfig);
|
|
749
|
+
|
|
750
|
+
// For TLS connections, handle differently based on forwarding type
|
|
751
|
+
if (record.isTLS) {
|
|
752
|
+
// For HTTP-only, we shouldn't get TLS connections
|
|
753
|
+
if (forwardingType === 'http-only') {
|
|
754
|
+
console.log(`[${connectionId}] Received TLS connection for HTTP-only domain ${serverName || 'unknown'}`);
|
|
755
|
+
socket.end();
|
|
756
|
+
this.connectionManager.initiateCleanupOnce(record, 'wrong_protocol');
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// For HTTPS passthrough, use the handler's connection handling
|
|
761
|
+
if (forwardingType === 'https-passthrough') {
|
|
762
|
+
// If there's initial data, process it first
|
|
763
|
+
if (initialChunk) {
|
|
764
|
+
record.bytesReceived += initialChunk.length;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// Let the handler take over
|
|
768
|
+
if (this.settings.enableDetailedLogging) {
|
|
769
|
+
console.log(`[${connectionId}] Using forwarding handler for ${forwardingType} connection to ${serverName || 'unknown'}`);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// Pass the connection to the handler
|
|
773
|
+
forwardingHandler.handleConnection(socket);
|
|
774
|
+
|
|
775
|
+
// Set metadata fields
|
|
776
|
+
record.usingNetworkProxy = false;
|
|
777
|
+
|
|
778
|
+
// Add connection information to record
|
|
779
|
+
if (serverName) {
|
|
780
|
+
record.lockedDomain = serverName;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// For TLS termination types, we'll fall through to the legacy connection setup
|
|
787
|
+
// because NetworkProxy is used for termination
|
|
788
|
+
}
|
|
789
|
+
// For non-TLS connections, check if we support HTTP
|
|
790
|
+
else if (!record.isTLS && this.domainConfigManager.supportsHttp(domainConfig)) {
|
|
791
|
+
// For HTTP handling that the handler supports natively
|
|
792
|
+
if (forwardingType === 'http-only' ||
|
|
793
|
+
(forwardingType === 'https-terminate-to-http' || forwardingType === 'https-terminate-to-https')) {
|
|
794
|
+
|
|
795
|
+
// If there's redirect to HTTPS configured and this is a normal HTTP connection
|
|
796
|
+
if (this.domainConfigManager.shouldRedirectToHttps(domainConfig)) {
|
|
797
|
+
// We'll let the handler deal with the HTTP request and potential redirect
|
|
798
|
+
// Once an HTTP request arrives, it can redirect as needed
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// Let the handler take over for HTTP handling
|
|
802
|
+
if (this.settings.enableDetailedLogging) {
|
|
803
|
+
console.log(`[${connectionId}] Using forwarding handler for HTTP connection to ${serverName || 'unknown'}`);
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
// Pass the connection to the handler
|
|
807
|
+
forwardingHandler.handleConnection(socket);
|
|
808
|
+
|
|
809
|
+
// Add connection information to record
|
|
810
|
+
if (serverName) {
|
|
811
|
+
record.lockedDomain = serverName;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
} catch (err) {
|
|
818
|
+
console.log(`[${connectionId}] Error using forwarding handler: ${err}`);
|
|
819
|
+
// Fall through to legacy connection handling
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// If we get here, we'll use legacy connection handling
|
|
824
|
+
|
|
655
825
|
// Determine target host
|
|
656
826
|
const targetHost = domainConfig
|
|
657
827
|
? this.domainConfigManager.getTargetIP(domainConfig)
|
|
658
828
|
: this.settings.targetIP!;
|
|
659
829
|
|
|
660
|
-
// Determine target port
|
|
661
|
-
const targetPort =
|
|
830
|
+
// Determine target port - first try forwarding config, then fallback
|
|
831
|
+
const targetPort = domainConfig
|
|
832
|
+
? this.domainConfigManager.getTargetPort(domainConfig, overridePort !== undefined ? overridePort : this.settings.toPort)
|
|
833
|
+
: (overridePort !== undefined ? overridePort : this.settings.toPort);
|
|
662
834
|
|
|
663
835
|
// Setup connection options
|
|
664
836
|
const connectionOptions: plugins.net.NetConnectOpts = {
|
|
@@ -842,6 +1014,21 @@ export class ConnectionHandler {
|
|
|
842
1014
|
this.connectionManager.incrementTerminationStat('outgoing', 'connection_failed');
|
|
843
1015
|
}
|
|
844
1016
|
|
|
1017
|
+
// If we have a forwarding handler for this domain, let it handle the error
|
|
1018
|
+
if (domainConfig) {
|
|
1019
|
+
try {
|
|
1020
|
+
const forwardingHandler = this.domainConfigManager.getForwardingHandler(domainConfig);
|
|
1021
|
+
forwardingHandler.emit('connection_error', {
|
|
1022
|
+
socket,
|
|
1023
|
+
error: err,
|
|
1024
|
+
connectionId
|
|
1025
|
+
});
|
|
1026
|
+
} catch (handlerErr) {
|
|
1027
|
+
// If getting the handler fails, just log and continue with normal cleanup
|
|
1028
|
+
console.log(`Error getting forwarding handler for error handling: ${handlerErr}`);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
|
|
845
1032
|
// Clean up the connection
|
|
846
1033
|
this.connectionManager.initiateCleanupOnce(record, `connection_failed_${code}`);
|
|
847
1034
|
});
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as plugins from '../plugins.js';
|
|
2
2
|
import type { IDomainConfig, ISmartProxyOptions } from './classes.pp.interfaces.js';
|
|
3
|
+
import type { ForwardingType, IForwardConfig, IForwardingHandler } from './types/forwarding.types.js';
|
|
4
|
+
import { ForwardingHandlerFactory } from './forwarding/forwarding.factory.js';
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Manages domain configurations and target selection
|
|
@@ -7,7 +9,10 @@ import type { IDomainConfig, ISmartProxyOptions } from './classes.pp.interfaces.
|
|
|
7
9
|
export class DomainConfigManager {
|
|
8
10
|
// Track round-robin indices for domain configs
|
|
9
11
|
private domainTargetIndices: Map<IDomainConfig, number> = new Map();
|
|
10
|
-
|
|
12
|
+
|
|
13
|
+
// Cache forwarding handlers for each domain config
|
|
14
|
+
private forwardingHandlers: Map<IDomainConfig, IForwardingHandler> = new Map();
|
|
15
|
+
|
|
11
16
|
constructor(private settings: ISmartProxyOptions) {}
|
|
12
17
|
|
|
13
18
|
/**
|
|
@@ -15,7 +20,7 @@ export class DomainConfigManager {
|
|
|
15
20
|
*/
|
|
16
21
|
public updateDomainConfigs(newDomainConfigs: IDomainConfig[]): void {
|
|
17
22
|
this.settings.domainConfigs = newDomainConfigs;
|
|
18
|
-
|
|
23
|
+
|
|
19
24
|
// Reset target indices for removed configs
|
|
20
25
|
const currentConfigSet = new Set(newDomainConfigs);
|
|
21
26
|
for (const [config] of this.domainTargetIndices) {
|
|
@@ -23,6 +28,31 @@ export class DomainConfigManager {
|
|
|
23
28
|
this.domainTargetIndices.delete(config);
|
|
24
29
|
}
|
|
25
30
|
}
|
|
31
|
+
|
|
32
|
+
// Clear handlers for removed configs and create handlers for new configs
|
|
33
|
+
const handlersToRemove: IDomainConfig[] = [];
|
|
34
|
+
for (const [config] of this.forwardingHandlers) {
|
|
35
|
+
if (!currentConfigSet.has(config)) {
|
|
36
|
+
handlersToRemove.push(config);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Remove handlers that are no longer needed
|
|
41
|
+
for (const config of handlersToRemove) {
|
|
42
|
+
this.forwardingHandlers.delete(config);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Create handlers for new configs
|
|
46
|
+
for (const config of newDomainConfigs) {
|
|
47
|
+
if (!this.forwardingHandlers.has(config)) {
|
|
48
|
+
try {
|
|
49
|
+
const handler = this.createForwardingHandler(config);
|
|
50
|
+
this.forwardingHandlers.set(config, handler);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
console.log(`Error creating forwarding handler for domain ${config.domains.join(', ')}: ${err}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
26
56
|
}
|
|
27
57
|
|
|
28
58
|
/**
|
|
@@ -48,10 +78,12 @@ export class DomainConfigManager {
|
|
|
48
78
|
*/
|
|
49
79
|
public findDomainConfigForPort(port: number): IDomainConfig | undefined {
|
|
50
80
|
return this.settings.domainConfigs.find(
|
|
51
|
-
(domain) =>
|
|
52
|
-
domain.portRanges
|
|
53
|
-
|
|
54
|
-
|
|
81
|
+
(domain) => {
|
|
82
|
+
const portRanges = domain.forwarding?.advanced?.portRanges;
|
|
83
|
+
return portRanges &&
|
|
84
|
+
portRanges.length > 0 &&
|
|
85
|
+
this.isPortInRanges(port, portRanges);
|
|
86
|
+
}
|
|
55
87
|
);
|
|
56
88
|
}
|
|
57
89
|
|
|
@@ -66,48 +98,102 @@ export class DomainConfigManager {
|
|
|
66
98
|
* Get target IP with round-robin support
|
|
67
99
|
*/
|
|
68
100
|
public getTargetIP(domainConfig: IDomainConfig): string {
|
|
69
|
-
|
|
101
|
+
const targetHosts = Array.isArray(domainConfig.forwarding.target.host)
|
|
102
|
+
? domainConfig.forwarding.target.host
|
|
103
|
+
: [domainConfig.forwarding.target.host];
|
|
104
|
+
|
|
105
|
+
if (targetHosts.length > 0) {
|
|
70
106
|
const currentIndex = this.domainTargetIndices.get(domainConfig) || 0;
|
|
71
|
-
const ip =
|
|
107
|
+
const ip = targetHosts[currentIndex % targetHosts.length];
|
|
72
108
|
this.domainTargetIndices.set(domainConfig, currentIndex + 1);
|
|
73
109
|
return ip;
|
|
74
110
|
}
|
|
75
|
-
|
|
111
|
+
|
|
76
112
|
return this.settings.targetIP || 'localhost';
|
|
77
113
|
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get target host with round-robin support (for tests)
|
|
117
|
+
* This is just an alias for getTargetIP for easier test compatibility
|
|
118
|
+
*/
|
|
119
|
+
public getTargetHost(domainConfig: IDomainConfig): string {
|
|
120
|
+
return this.getTargetIP(domainConfig);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Get target port from domain config
|
|
125
|
+
*/
|
|
126
|
+
public getTargetPort(domainConfig: IDomainConfig, defaultPort: number): number {
|
|
127
|
+
return domainConfig.forwarding.target.port || defaultPort;
|
|
128
|
+
}
|
|
78
129
|
|
|
79
130
|
/**
|
|
80
131
|
* Checks if a domain should use NetworkProxy
|
|
81
132
|
*/
|
|
82
133
|
public shouldUseNetworkProxy(domainConfig: IDomainConfig): boolean {
|
|
83
|
-
|
|
134
|
+
const forwardingType = this.getForwardingType(domainConfig);
|
|
135
|
+
return forwardingType === 'https-terminate-to-http' ||
|
|
136
|
+
forwardingType === 'https-terminate-to-https';
|
|
84
137
|
}
|
|
85
|
-
|
|
138
|
+
|
|
86
139
|
/**
|
|
87
140
|
* Gets the NetworkProxy port for a domain
|
|
88
141
|
*/
|
|
89
142
|
public getNetworkProxyPort(domainConfig: IDomainConfig): number | undefined {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
143
|
+
// First check if we should use NetworkProxy at all
|
|
144
|
+
if (!this.shouldUseNetworkProxy(domainConfig)) {
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return domainConfig.forwarding.advanced?.networkProxyPort || this.settings.networkProxyPort;
|
|
93
149
|
}
|
|
94
150
|
|
|
95
151
|
/**
|
|
96
152
|
* Get effective allowed and blocked IPs for a domain
|
|
153
|
+
*
|
|
154
|
+
* This method combines domain-specific security rules from the forwarding configuration
|
|
155
|
+
* with global security defaults when necessary.
|
|
97
156
|
*/
|
|
98
157
|
public getEffectiveIPRules(domainConfig: IDomainConfig): {
|
|
99
158
|
allowedIPs: string[],
|
|
100
159
|
blockedIPs: string[]
|
|
101
160
|
} {
|
|
161
|
+
// Start with empty arrays
|
|
162
|
+
const allowedIPs: string[] = [];
|
|
163
|
+
const blockedIPs: string[] = [];
|
|
164
|
+
|
|
165
|
+
// Add IPs from forwarding security settings if available
|
|
166
|
+
if (domainConfig.forwarding?.security?.allowedIps) {
|
|
167
|
+
allowedIPs.push(...domainConfig.forwarding.security.allowedIps);
|
|
168
|
+
} else {
|
|
169
|
+
// If no allowed IPs are specified in forwarding config and global defaults exist, use them
|
|
170
|
+
if (this.settings.defaultAllowedIPs && this.settings.defaultAllowedIPs.length > 0) {
|
|
171
|
+
allowedIPs.push(...this.settings.defaultAllowedIPs);
|
|
172
|
+
} else {
|
|
173
|
+
// Default to allow all if no specific rules
|
|
174
|
+
allowedIPs.push('*');
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Add blocked IPs from forwarding security settings if available
|
|
179
|
+
if (domainConfig.forwarding?.security?.blockedIps) {
|
|
180
|
+
blockedIPs.push(...domainConfig.forwarding.security.blockedIps);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Always add global blocked IPs, even if domain has its own rules
|
|
184
|
+
// This ensures that global blocks take precedence
|
|
185
|
+
if (this.settings.defaultBlockedIPs && this.settings.defaultBlockedIPs.length > 0) {
|
|
186
|
+
// Add only unique IPs that aren't already in the list
|
|
187
|
+
for (const ip of this.settings.defaultBlockedIPs) {
|
|
188
|
+
if (!blockedIPs.includes(ip)) {
|
|
189
|
+
blockedIPs.push(ip);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
102
194
|
return {
|
|
103
|
-
allowedIPs
|
|
104
|
-
|
|
105
|
-
...(this.settings.defaultAllowedIPs || [])
|
|
106
|
-
],
|
|
107
|
-
blockedIPs: [
|
|
108
|
-
...(domainConfig.blockedIPs || []),
|
|
109
|
-
...(this.settings.defaultBlockedIPs || [])
|
|
110
|
-
]
|
|
195
|
+
allowedIPs,
|
|
196
|
+
blockedIPs
|
|
111
197
|
};
|
|
112
198
|
}
|
|
113
199
|
|
|
@@ -115,9 +201,97 @@ export class DomainConfigManager {
|
|
|
115
201
|
* Get connection timeout for a domain
|
|
116
202
|
*/
|
|
117
203
|
public getConnectionTimeout(domainConfig?: IDomainConfig): number {
|
|
118
|
-
if (domainConfig?.
|
|
119
|
-
return domainConfig.
|
|
204
|
+
if (domainConfig?.forwarding.advanced?.timeout) {
|
|
205
|
+
return domainConfig.forwarding.advanced.timeout;
|
|
120
206
|
}
|
|
207
|
+
|
|
121
208
|
return this.settings.maxConnectionLifetime || 86400000; // 24 hours default
|
|
122
209
|
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Creates a forwarding handler for a domain configuration
|
|
213
|
+
*/
|
|
214
|
+
private createForwardingHandler(domainConfig: IDomainConfig): IForwardingHandler {
|
|
215
|
+
// Create a new handler using the factory
|
|
216
|
+
const handler = ForwardingHandlerFactory.createHandler(domainConfig.forwarding);
|
|
217
|
+
|
|
218
|
+
// Initialize the handler
|
|
219
|
+
handler.initialize().catch(err => {
|
|
220
|
+
console.log(`Error initializing forwarding handler for ${domainConfig.domains.join(', ')}: ${err}`);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
return handler;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Gets a forwarding handler for a domain config
|
|
228
|
+
* If no handler exists, creates one
|
|
229
|
+
*/
|
|
230
|
+
public getForwardingHandler(domainConfig: IDomainConfig): IForwardingHandler {
|
|
231
|
+
// If we already have a handler, return it
|
|
232
|
+
if (this.forwardingHandlers.has(domainConfig)) {
|
|
233
|
+
return this.forwardingHandlers.get(domainConfig)!;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Otherwise create a new handler
|
|
237
|
+
const handler = this.createForwardingHandler(domainConfig);
|
|
238
|
+
this.forwardingHandlers.set(domainConfig, handler);
|
|
239
|
+
|
|
240
|
+
return handler;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Gets the forwarding type for a domain config
|
|
245
|
+
*/
|
|
246
|
+
public getForwardingType(domainConfig?: IDomainConfig): ForwardingType | undefined {
|
|
247
|
+
if (!domainConfig?.forwarding) return undefined;
|
|
248
|
+
return domainConfig.forwarding.type;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Checks if the forwarding type requires TLS termination
|
|
253
|
+
*/
|
|
254
|
+
public requiresTlsTermination(domainConfig?: IDomainConfig): boolean {
|
|
255
|
+
if (!domainConfig) return false;
|
|
256
|
+
|
|
257
|
+
const forwardingType = this.getForwardingType(domainConfig);
|
|
258
|
+
return forwardingType === 'https-terminate-to-http' ||
|
|
259
|
+
forwardingType === 'https-terminate-to-https';
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Checks if the forwarding type supports HTTP
|
|
264
|
+
*/
|
|
265
|
+
public supportsHttp(domainConfig?: IDomainConfig): boolean {
|
|
266
|
+
if (!domainConfig) return false;
|
|
267
|
+
|
|
268
|
+
const forwardingType = this.getForwardingType(domainConfig);
|
|
269
|
+
|
|
270
|
+
// HTTP-only always supports HTTP
|
|
271
|
+
if (forwardingType === 'http-only') return true;
|
|
272
|
+
|
|
273
|
+
// For termination types, check the HTTP settings
|
|
274
|
+
if (forwardingType === 'https-terminate-to-http' ||
|
|
275
|
+
forwardingType === 'https-terminate-to-https') {
|
|
276
|
+
// HTTP is supported by default for termination types
|
|
277
|
+
return domainConfig.forwarding?.http?.enabled !== false;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// HTTPS-passthrough doesn't support HTTP
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Checks if HTTP requests should be redirected to HTTPS
|
|
286
|
+
*/
|
|
287
|
+
public shouldRedirectToHttps(domainConfig?: IDomainConfig): boolean {
|
|
288
|
+
if (!domainConfig?.forwarding) return false;
|
|
289
|
+
|
|
290
|
+
// Only check for redirect if HTTP is enabled
|
|
291
|
+
if (this.supportsHttp(domainConfig)) {
|
|
292
|
+
return !!domainConfig.forwarding.http?.redirectToHttps;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
123
297
|
}
|
|
@@ -1,23 +1,15 @@
|
|
|
1
1
|
import * as plugins from '../plugins.js';
|
|
2
|
+
import type { IForwardConfig } from './forwarding/index.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Provision object for static or HTTP-01 certificate
|
|
5
6
|
*/
|
|
6
7
|
export type ISmartProxyCertProvisionObject = plugins.tsclass.network.ICert | 'http01';
|
|
7
8
|
|
|
8
|
-
/** Domain configuration with
|
|
9
|
+
/** Domain configuration with forwarding configuration */
|
|
9
10
|
export interface IDomainConfig {
|
|
10
11
|
domains: string[]; // Glob patterns for domain(s)
|
|
11
|
-
|
|
12
|
-
blockedIPs?: string[]; // Glob patterns for blocked IPs
|
|
13
|
-
targetIPs?: string[]; // If multiple targetIPs are given, use round robin.
|
|
14
|
-
portRanges?: Array<{ from: number; to: number }>; // Optional port ranges
|
|
15
|
-
// Allow domain-specific timeout override
|
|
16
|
-
connectionTimeout?: number; // Connection timeout override (ms)
|
|
17
|
-
|
|
18
|
-
// NetworkProxy integration options for this specific domain
|
|
19
|
-
useNetworkProxy?: boolean; // Whether to use NetworkProxy for this domain
|
|
20
|
-
networkProxyPort?: number; // Override default NetworkProxy port for this domain
|
|
12
|
+
forwarding: IForwardConfig; // Unified forwarding configuration
|
|
21
13
|
}
|
|
22
14
|
|
|
23
15
|
/** Port proxy settings including global allowed port ranges */
|