Compare commits

...

6 Commits

4 changed files with 49 additions and 22 deletions

View File

@ -1,5 +1,26 @@
# Changelog # Changelog
## 2025-02-27 - 3.16.9 - fix(portproxy)
Extend domain input validation to support string arrays in port proxy configurations.
- Modify IDomainConfig interface to allow domain specification as string array.
- Update connection setup logic to handle multiple domain patterns.
- Enhance domain rejection logging to include all domain patterns.
## 2025-02-27 - 3.16.8 - fix(PortProxy)
Fix IP filtering for domain and global default allowed lists and improve port-based routing logic.
- Improved logic to prioritize domain-specific allowed IPs over global defaults.
- Fixed port-based rules application to handle global port ranges more effectively.
- Enhanced rejection handling for unauthorized IP addresses in both domain-specific and default global lists.
## 2025-02-27 - 3.16.7 - fix(PortProxy)
Improved IP validation logic in PortProxy to ensure correct domain matching and fallback
- Refactored the setupConnection function inside PortProxy to enhance IP address validation.
- Domain-specific allowed IP preference is applied before default list lookup.
- Removed redundant condition checks to streamline connection rejection paths.
## 2025-02-27 - 3.16.6 - fix(PortProxy) ## 2025-02-27 - 3.16.6 - fix(PortProxy)
Optimize connection cleanup logic in PortProxy by removing unnecessary delays. Optimize connection cleanup logic in PortProxy by removing unnecessary delays.

View File

@ -1,6 +1,6 @@
{ {
"name": "@push.rocks/smartproxy", "name": "@push.rocks/smartproxy",
"version": "3.16.6", "version": "3.16.9",
"private": false, "private": false,
"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.", "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.",
"main": "dist_ts/index.js", "main": "dist_ts/index.js",

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@push.rocks/smartproxy', name: '@push.rocks/smartproxy',
version: '3.16.6', version: '3.16.9',
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.' 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.'
} }

View File

@ -2,7 +2,7 @@ import * as plugins from './plugins.js';
/** Domain configuration with perdomain allowed port ranges */ /** Domain configuration with perdomain allowed port ranges */
export interface IDomainConfig { export interface IDomainConfig {
domain: string; // Glob pattern for domain domain: string | string[]; // Glob pattern or patterns for domain(s)
allowedIPs: string[]; // Glob patterns for allowed IPs allowedIPs: string[]; // Glob patterns for allowed IPs
targetIP?: string; // Optional target IP for this domain targetIP?: string; // Optional target IP for this domain
portRanges?: Array<{ from: number; to: number }>; // Optional domain-specific allowed port ranges portRanges?: Array<{ from: number; to: number }>; // Optional domain-specific allowed port ranges
@ -211,18 +211,26 @@ export class PortProxy {
*/ */
const setupConnection = (serverName: string, initialChunk?: Buffer, forcedDomain?: IDomainConfig, overridePort?: number) => { const setupConnection = (serverName: string, initialChunk?: Buffer, forcedDomain?: IDomainConfig, overridePort?: number) => {
// If a forcedDomain is provided (port-based routing), use it; otherwise, use SNI-based lookup. // If a forcedDomain is provided (port-based routing), use it; otherwise, use SNI-based lookup.
const domainConfig = forcedDomain ? forcedDomain : (serverName ? this.settings.domains.find(config => plugins.minimatch(serverName, config.domain)) : undefined); const domainConfig = forcedDomain
const defaultAllowed = this.settings.defaultAllowedIPs && isAllowed(remoteIP, this.settings.defaultAllowedIPs); ? forcedDomain
: (serverName ? this.settings.domains.find(config => {
if (typeof config.domain === 'string') {
return plugins.minimatch(serverName, config.domain);
} else {
return config.domain.some(d => plugins.minimatch(serverName, d));
}
}) : undefined);
if (!defaultAllowed && serverName && !forcedDomain) { // If a matching domain config exists, check its allowedIPs.
if (!domainConfig) { if (domainConfig) {
return rejectIncomingConnection('rejected', `Connection rejected: No matching domain config for ${serverName} from ${remoteIP}`);
}
if (!isAllowed(remoteIP, domainConfig.allowedIPs)) { if (!isAllowed(remoteIP, domainConfig.allowedIPs)) {
return rejectIncomingConnection('rejected', `Connection rejected: IP ${remoteIP} not allowed for domain ${serverName}`); return rejectIncomingConnection('rejected', `Connection rejected: IP ${remoteIP} not allowed for domain ${Array.isArray(domainConfig.domain) ? domainConfig.domain.join(', ') : domainConfig.domain}`);
}
} else if (this.settings.defaultAllowedIPs) {
// Only check default allowed IPs if no domain config matched.
if (!isAllowed(remoteIP, this.settings.defaultAllowedIPs)) {
return rejectIncomingConnection('rejected', `Connection rejected: IP ${remoteIP} not allowed by default allowed list`);
} }
} else if (defaultAllowed && !serverName) {
console.log(`Connection allowed: IP ${remoteIP} is in default allowed list`);
} }
const targetHost = domainConfig?.targetIP || this.settings.targetIP!; const targetHost = domainConfig?.targetIP || this.settings.targetIP!;
const connectionOptions: plugins.net.NetConnectOpts = { const connectionOptions: plugins.net.NetConnectOpts = {
@ -239,7 +247,7 @@ export class PortProxy {
console.log( console.log(
`Connection established: ${remoteIP} -> ${targetHost}:${connectionOptions.port}` + `Connection established: ${remoteIP} -> ${targetHost}:${connectionOptions.port}` +
`${serverName ? ` (SNI: ${serverName})` : forcedDomain ? ` (Port-based for domain: ${forcedDomain.domain})` : ''}` `${serverName ? ` (SNI: ${serverName})` : forcedDomain ? ` (Port-based for domain: ${Array.isArray(forcedDomain.domain) ? forcedDomain.domain.join(', ') : forcedDomain.domain})` : ''}`
); );
if (initialChunk) { if (initialChunk) {
@ -311,9 +319,8 @@ export class PortProxy {
}; };
// --- PORT RANGE-BASED HANDLING --- // --- PORT RANGE-BASED HANDLING ---
// If the local port is one of the globally listened ports, we may have port-based rules. // Only apply port-based rules if the incoming port is within one of the global port ranges.
if (this.settings.globalPortRanges && this.settings.globalPortRanges.length > 0) { if (this.settings.globalPortRanges && isPortInRanges(localPort, this.settings.globalPortRanges)) {
// If forwardAllGlobalRanges is enabled, always forward using the global targetIP.
if (this.settings.forwardAllGlobalRanges) { if (this.settings.forwardAllGlobalRanges) {
if (this.settings.defaultAllowedIPs && !isAllowed(remoteIP, this.settings.defaultAllowedIPs)) { if (this.settings.defaultAllowedIPs && !isAllowed(remoteIP, this.settings.defaultAllowedIPs)) {
console.log(`Connection from ${remoteIP} rejected: IP ${remoteIP} not allowed in global default allowed list.`); console.log(`Connection from ${remoteIP} rejected: IP ${remoteIP} not allowed in global default allowed list.`);
@ -334,13 +341,12 @@ export class PortProxy {
domain => domain.portRanges && domain.portRanges.length > 0 && isPortInRanges(localPort, domain.portRanges) domain => domain.portRanges && domain.portRanges.length > 0 && isPortInRanges(localPort, domain.portRanges)
); );
if (forcedDomain) { if (forcedDomain) {
const defaultAllowed = this.settings.defaultAllowedIPs && isAllowed(remoteIP, this.settings.defaultAllowedIPs); if (!isAllowed(remoteIP, forcedDomain.allowedIPs)) {
if (!defaultAllowed && !isAllowed(remoteIP, forcedDomain.allowedIPs)) { console.log(`Connection from ${remoteIP} rejected: IP not allowed for domain ${Array.isArray(forcedDomain.domain) ? forcedDomain.domain.join(', ') : forcedDomain.domain} on port ${localPort}.`);
console.log(`Connection from ${remoteIP} rejected: IP not allowed for domain ${forcedDomain.domain} on port ${localPort}.`);
socket.end(); socket.end();
return; return;
} }
console.log(`Port-based connection from ${remoteIP} on port ${localPort} matched domain ${forcedDomain.domain}.`); console.log(`Port-based connection from ${remoteIP} on port ${localPort} matched domain ${Array.isArray(forcedDomain.domain) ? forcedDomain.domain.join(', ') : forcedDomain.domain}.`);
setupConnection('', undefined, forcedDomain, localPort); setupConnection('', undefined, forcedDomain, localPort);
return; return;
} }