From a2cb56ba654d10a1ece011fafca7bcb7f27c22d4 Mon Sep 17 00:00:00 2001 From: Philipp Kunz Date: Thu, 27 Feb 2025 12:41:20 +0000 Subject: [PATCH] feat(PortProxy): Enhancements made to PortProxy settings and capabilities --- changelog.md | 8 ++++++ ts/00_commitinfo_data.ts | 2 +- ts/classes.portproxy.ts | 62 ++++++++++++++++++++++++++-------------- 3 files changed, 49 insertions(+), 23 deletions(-) diff --git a/changelog.md b/changelog.md index 72fd4f8..a84994d 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,13 @@ # Changelog +## 2025-02-27 - 3.16.0 - feat(PortProxy) +Enhancements made to PortProxy settings and capabilities + +- Added 'forwardAllGlobalRanges' and 'targetIP' to IPortProxySettings. +- Improved PortProxy to forward connections based on domain-specific configurations. +- Added comprehensive handling for global port-range based connection forwarding. +- Enabled forwarding of all connections on global port ranges directly to global target IP. + ## 2025-02-27 - 3.15.0 - feat(classes.portproxy) Add support for port range-based routing with enhanced IP and port validation. diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 1646f74..5aa811e 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@push.rocks/smartproxy', - version: '3.15.0', + version: '3.16.0', description: 'A robust and versatile proxy package designed to handle high workloads, offering features like SSL redirection, port proxying, WebSocket support, and customizable routing and authentication.' } diff --git a/ts/classes.portproxy.ts b/ts/classes.portproxy.ts index cc381e8..4d3355f 100644 --- a/ts/classes.portproxy.ts +++ b/ts/classes.portproxy.ts @@ -5,20 +5,21 @@ export interface IDomainConfig { domain: string; // Glob pattern for domain allowedIPs: string[]; // Glob patterns for allowed IPs targetIP?: string; // Optional target IP for this domain - portRanges: Array<{ from: number; to: number }>; // Domain-specific allowed port ranges + portRanges?: Array<{ from: number; to: number }>; // Optional domain-specific allowed port ranges } /** Port proxy settings including global allowed port ranges */ export interface IPortProxySettings extends plugins.tls.TlsOptions { fromPort: number; toPort: number; - toHost?: string; // Target host to proxy to, defaults to 'localhost' + targetIP?: string; // Global target host to proxy to, defaults to 'localhost' domains: IDomainConfig[]; sniEnabled?: boolean; defaultAllowedIPs?: string[]; preserveSourceIP?: boolean; maxConnectionLifetime?: number; // (ms) force cleanup of long-lived connections globalPortRanges: Array<{ from: number; to: number }>; // Global allowed port ranges + forwardAllGlobalRanges?: boolean; // When true, forwards all connections on global port ranges to the global targetIP } /** @@ -111,7 +112,7 @@ export class PortProxy { constructor(settingsArg: IPortProxySettings) { this.settings = { ...settingsArg, - toHost: settingsArg.toHost || 'localhost', + targetIP: settingsArg.targetIP || 'localhost', maxConnectionLifetime: settingsArg.maxConnectionLifetime || 600000, }; } @@ -256,7 +257,7 @@ export class PortProxy { } else if (defaultAllowed && !serverName) { console.log(`Connection allowed: IP ${remoteIP} is in default allowed list`); } - const targetHost = domainConfig?.targetIP || this.settings.toHost!; + const targetHost = domainConfig?.targetIP || this.settings.targetIP!; const connectionOptions: plugins.net.NetConnectOpts = { host: targetHost, port: this.settings.toPort, @@ -350,26 +351,43 @@ export class PortProxy { socket.destroy(); return; } - // Find a matching domain config based on the incoming local port. - const forcedDomain = this.settings.domains.find( - domain => domain.portRanges && domain.portRanges.length > 0 && isPortInRanges(localPort, domain.portRanges) - ); - if (!forcedDomain) { - console.log(`Connection from ${remoteIP} rejected: port ${localPort} not configured in any domain's portRanges.`); - socket.destroy(); + if (this.settings.forwardAllGlobalRanges) { + // Forward connection to the global targetIP regardless of domain config. + if (this.settings.defaultAllowedIPs && !isAllowed(remoteIP, this.settings.defaultAllowedIPs)) { + console.log(`Connection from ${remoteIP} rejected: IP ${remoteIP} not allowed in global default allowed list.`); + socket.end(); + return; + } + console.log(`Port-based connection from ${remoteIP} on port ${localPort} forwarded to global target IP ${this.settings.targetIP}.`); + setupConnection('', undefined, { + domain: 'global', + allowedIPs: this.settings.defaultAllowedIPs || [], + targetIP: this.settings.targetIP, + portRanges: [] + }); + return; + } else { + // Find a matching domain config based on the incoming local port. + const forcedDomain = this.settings.domains.find( + domain => domain.portRanges && domain.portRanges.length > 0 && isPortInRanges(localPort, domain.portRanges) + ); + if (!forcedDomain) { + console.log(`Connection from ${remoteIP} rejected: port ${localPort} not configured in any domain's portRanges.`); + socket.destroy(); + return; + } + // Check allowed IPs for the forced domain. + const defaultAllowed = this.settings.defaultAllowedIPs && isAllowed(remoteIP, this.settings.defaultAllowedIPs); + if (!defaultAllowed && !isAllowed(remoteIP, forcedDomain.allowedIPs)) { + console.log(`Connection from ${remoteIP} rejected: IP not allowed for domain ${forcedDomain.domain} on port ${localPort}.`); + socket.end(); + return; + } + console.log(`Port-based connection from ${remoteIP} on port ${localPort} matched domain ${forcedDomain.domain}.`); + // Proceed immediately using the forced domain; ignore SNI. + setupConnection('', undefined, forcedDomain); return; } - // Check allowed IPs for the forced domain. - const defaultAllowed = this.settings.defaultAllowedIPs && isAllowed(remoteIP, this.settings.defaultAllowedIPs); - if (!defaultAllowed && !isAllowed(remoteIP, forcedDomain.allowedIPs)) { - console.log(`Connection from ${remoteIP} rejected: IP not allowed for domain ${forcedDomain.domain} on port ${localPort}.`); - socket.end(); - return; - } - console.log(`Port-based connection from ${remoteIP} on port ${localPort} matched domain ${forcedDomain.domain}.`); - // Proceed immediately using the forced domain; ignore SNI. - setupConnection('', undefined, forcedDomain); - return; } // --- FALLBACK: SNI-BASED HANDLING (if no global port ranges are defined) ---