diff --git a/changelog.md b/changelog.md index 4fe34d3..162a68d 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,12 @@ # Changelog +## 2025-02-21 - 3.5.0 - feat(PortProxy) +Enhance PortProxy to support domain-specific target IPs + +- Introduced support for domain-specific target IP configurations in PortProxy. +- Updated connection handling to prioritize domain-specific target IPs if provided. +- Added tests to verify forwarding based on domain-specific target IPs. + ## 2025-02-21 - 3.4.4 - fix(PortProxy) Fixed handling of SNI domain connections and IP allowance checks diff --git a/test/test.portproxy.ts b/test/test.portproxy.ts index 99ae65b..e0ea8a7 100644 --- a/test/test.portproxy.ts +++ b/test/test.portproxy.ts @@ -95,6 +95,54 @@ tap.test('should forward TCP connections to custom host', async () => { await customHostProxy.stop(); }); +tap.test('should forward connections based on domain-specific target IP', async () => { + // Create a second test server on a different port + const TEST_SERVER_PORT_2 = TEST_SERVER_PORT + 100; + const testServer2 = await createTestServer(TEST_SERVER_PORT_2); + + // Create a proxy with domain-specific target IPs + const domainProxy = new PortProxy({ + fromPort: PROXY_PORT + 2, + toPort: TEST_SERVER_PORT, // default port + toHost: 'localhost', // default host + domains: [{ + domain: 'domain1.test', + allowedIPs: ['127.0.0.1'], + targetIP: '127.0.0.1' + }, { + domain: 'domain2.test', + allowedIPs: ['127.0.0.1'], + targetIP: 'localhost' + }], + sniEnabled: false, // We'll test without SNI first since this is a TCP proxy test + defaultAllowedIPs: ['127.0.0.1'] + }); + + await domainProxy.start(); + + // Test default connection (should use default host) + const response1 = await createTestClient(PROXY_PORT + 2, TEST_DATA); + expect(response1).toEqual(`Echo: ${TEST_DATA}`); + + // Create another proxy with different default host + const domainProxy2 = new PortProxy({ + fromPort: PROXY_PORT + 3, + toPort: TEST_SERVER_PORT, + toHost: '127.0.0.1', + domains: [], + sniEnabled: false, + defaultAllowedIPs: ['127.0.0.1'] + }); + + await domainProxy2.start(); + const response2 = await createTestClient(PROXY_PORT + 3, TEST_DATA); + expect(response2).toEqual(`Echo: ${TEST_DATA}`); + + await domainProxy.stop(); + await domainProxy2.stop(); + await new Promise((resolve) => testServer2.close(() => resolve())); +}); + tap.test('should handle multiple concurrent connections', async () => { const concurrentRequests = 5; const requests = Array(concurrentRequests).fill(null).map((_, i) => diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 3c40f55..8b12886 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.4.4', + version: '3.5.0', description: 'a proxy for handling high workloads of proxying' } diff --git a/ts/smartproxy.portproxy.ts b/ts/smartproxy.portproxy.ts index 214a508..780b5a3 100644 --- a/ts/smartproxy.portproxy.ts +++ b/ts/smartproxy.portproxy.ts @@ -4,6 +4,7 @@ import * as plugins from './smartproxy.plugins.js'; export interface DomainConfig { domain: string; // glob pattern for domain allowedIPs: string[]; // glob patterns for IPs allowed to access this domain + targetIP?: string; // Optional target IP for this domain } export interface ProxySettings extends plugins.tls.TlsOptions { @@ -118,11 +119,15 @@ export class PortProxy { return; } + // Determine target host - use domain-specific targetIP if available + const domainConfig = serverName ? findMatchingDomain(serverName) : undefined; + const targetHost = domainConfig?.targetIP || this.settings.toHost!; + const to = plugins.net.createConnection({ - host: this.settings.toHost!, + host: targetHost, port: this.settings.toPort, }); - console.log(`Connection established: ${remoteIP} -> ${this.settings.toHost}:${this.settings.toPort}${serverName ? ` (SNI: ${serverName})` : ''}`); + console.log(`Connection established: ${remoteIP} -> ${targetHost}:${this.settings.toPort}${serverName ? ` (SNI: ${serverName})` : ''}`); from.setTimeout(120000); from.pipe(to); to.pipe(from);