@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.
Files changed (59) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/common/port80-adapter.d.ts +11 -0
  3. package/dist_ts/common/port80-adapter.js +61 -0
  4. package/dist_ts/examples/forwarding-example.d.ts +1 -0
  5. package/dist_ts/examples/forwarding-example.js +96 -0
  6. package/dist_ts/index.d.ts +1 -0
  7. package/dist_ts/index.js +3 -1
  8. package/dist_ts/smartproxy/classes.pp.connectionhandler.js +179 -30
  9. package/dist_ts/smartproxy/classes.pp.domainconfigmanager.d.ts +39 -0
  10. package/dist_ts/smartproxy/classes.pp.domainconfigmanager.js +172 -20
  11. package/dist_ts/smartproxy/classes.pp.interfaces.d.ts +3 -11
  12. package/dist_ts/smartproxy/classes.pp.portrangemanager.js +17 -10
  13. package/dist_ts/smartproxy/classes.pp.securitymanager.d.ts +19 -2
  14. package/dist_ts/smartproxy/classes.pp.securitymanager.js +27 -4
  15. package/dist_ts/smartproxy/classes.pp.timeoutmanager.js +3 -3
  16. package/dist_ts/smartproxy/classes.smartproxy.js +45 -13
  17. package/dist_ts/smartproxy/forwarding/domain-config.d.ts +12 -0
  18. package/dist_ts/smartproxy/forwarding/domain-config.js +12 -0
  19. package/dist_ts/smartproxy/forwarding/domain-manager.d.ts +86 -0
  20. package/dist_ts/smartproxy/forwarding/domain-manager.js +241 -0
  21. package/dist_ts/smartproxy/forwarding/forwarding.factory.d.ts +24 -0
  22. package/dist_ts/smartproxy/forwarding/forwarding.factory.js +137 -0
  23. package/dist_ts/smartproxy/forwarding/forwarding.handler.d.ts +55 -0
  24. package/dist_ts/smartproxy/forwarding/forwarding.handler.js +94 -0
  25. package/dist_ts/smartproxy/forwarding/http.handler.d.ts +25 -0
  26. package/dist_ts/smartproxy/forwarding/http.handler.js +123 -0
  27. package/dist_ts/smartproxy/forwarding/https-passthrough.handler.d.ts +24 -0
  28. package/dist_ts/smartproxy/forwarding/https-passthrough.handler.js +154 -0
  29. package/dist_ts/smartproxy/forwarding/https-terminate-to-http.handler.d.ts +36 -0
  30. package/dist_ts/smartproxy/forwarding/https-terminate-to-http.handler.js +229 -0
  31. package/dist_ts/smartproxy/forwarding/https-terminate-to-https.handler.d.ts +35 -0
  32. package/dist_ts/smartproxy/forwarding/https-terminate-to-https.handler.js +254 -0
  33. package/dist_ts/smartproxy/forwarding/index.d.ts +16 -0
  34. package/dist_ts/smartproxy/forwarding/index.js +23 -0
  35. package/dist_ts/smartproxy/types/forwarding.types.d.ts +104 -0
  36. package/dist_ts/smartproxy/types/forwarding.types.js +50 -0
  37. package/package.json +2 -2
  38. package/readme.md +158 -8
  39. package/readme.plan.md +471 -42
  40. package/ts/00_commitinfo_data.ts +1 -1
  41. package/ts/common/port80-adapter.ts +87 -0
  42. package/ts/index.ts +3 -0
  43. package/ts/smartproxy/classes.pp.connectionhandler.ts +231 -44
  44. package/ts/smartproxy/classes.pp.domainconfigmanager.ts +198 -24
  45. package/ts/smartproxy/classes.pp.interfaces.ts +3 -11
  46. package/ts/smartproxy/classes.pp.portrangemanager.ts +17 -10
  47. package/ts/smartproxy/classes.pp.securitymanager.ts +29 -5
  48. package/ts/smartproxy/classes.pp.timeoutmanager.ts +3 -3
  49. package/ts/smartproxy/classes.smartproxy.ts +68 -15
  50. package/ts/smartproxy/forwarding/domain-config.ts +28 -0
  51. package/ts/smartproxy/forwarding/domain-manager.ts +283 -0
  52. package/ts/smartproxy/forwarding/forwarding.factory.ts +155 -0
  53. package/ts/smartproxy/forwarding/forwarding.handler.ts +127 -0
  54. package/ts/smartproxy/forwarding/http.handler.ts +140 -0
  55. package/ts/smartproxy/forwarding/https-passthrough.handler.ts +182 -0
  56. package/ts/smartproxy/forwarding/https-terminate-to-http.handler.ts +264 -0
  57. package/ts/smartproxy/forwarding/https-terminate-to-https.handler.ts +292 -0
  58. package/ts/smartproxy/forwarding/index.ts +52 -0
  59. 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 NetworkProxy handling if available
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 NetworkProxy port
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
- // Use domain-specific NetworkProxy port if configured
191
- if (domainConfig && this.domainConfigManager.shouldUseNetworkProxy(domainConfig)) {
192
- const networkProxyPort = this.domainConfigManager.getNetworkProxyPort(domainConfig);
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
- if (this.settings.enableDetailedLogging) {
195
- console.log(
196
- `[${connectionId}] Using domain-specific NetworkProxy for ${serverName} on port ${networkProxyPort}`
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
- // Forward to NetworkProxy with domain-specific port
201
- this.networkProxyBridge.forwardToNetworkProxy(
202
- connectionId,
203
- socket,
204
- record,
205
- chunk,
206
- networkProxyPort,
207
- (reason) => this.connectionManager.initiateCleanupOnce(record, reason)
208
- );
209
- return;
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, use normal direct connection
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
- // Skip IP validation if allowedIPs is empty
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.settings.defaultAllowedIPs &&
436
- this.settings.defaultAllowedIPs.length > 0 &&
437
- !this.securityManager.isIPAuthorized(record.remoteIP, this.settings.defaultAllowedIPs)
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
- setupConnection(
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
- if (
628
- this.settings.defaultAllowedIPs &&
629
- this.settings.defaultAllowedIPs.length > 0 &&
630
- !this.securityManager.isIPAuthorized(record.remoteIP, this.settings.defaultAllowedIPs)
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 = overridePort !== undefined ? overridePort : this.settings.toPort;
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
- domain.portRanges.length > 0 &&
54
- this.isPortInRanges(port, domain.portRanges)
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
- if (domainConfig.targetIPs && domainConfig.targetIPs.length > 0) {
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 = domainConfig.targetIPs[currentIndex % domainConfig.targetIPs.length];
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
- return !!domainConfig.useNetworkProxy;
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
- return domainConfig.useNetworkProxy
91
- ? (domainConfig.networkProxyPort || this.settings.networkProxyPort)
92
- : undefined;
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
- ...domainConfig.allowedIPs,
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?.connectionTimeout) {
119
- return domainConfig.connectionTimeout;
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 per-domain allowed port ranges */
9
+ /** Domain configuration with forwarding configuration */
9
10
  export interface IDomainConfig {
10
11
  domains: string[]; // Glob patterns for domain(s)
11
- allowedIPs: string[]; // Glob patterns for allowed IPs
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 */