smartproxy/ts/smartproxy.portproxy.ts

122 lines
3.9 KiB
TypeScript
Raw Normal View History

2022-07-29 00:49:46 +02:00
import * as plugins from './smartproxy.plugins.js';
2019-08-22 15:09:48 +02:00
import * as net from 'net';
import * as tls from 'tls';
export interface DomainConfig {
domain: string; // glob pattern for domain
allowedIPs: string[]; // glob patterns for IPs allowed to access this domain
}
export interface ProxySettings {
domains: DomainConfig[];
sniEnabled?: boolean;
tlsOptions?: tls.TlsOptions;
defaultAllowedIPs?: string[]; // Optional default IP patterns if no matching domain found
}
2019-08-22 15:09:48 +02:00
2022-07-29 00:49:46 +02:00
export class PortProxy {
2022-07-29 03:39:05 +02:00
netServer: plugins.net.Server;
2022-07-29 00:49:46 +02:00
fromPort: number;
toPort: number;
settings: ProxySettings;
2022-07-29 00:49:46 +02:00
constructor(fromPortArg: number, toPortArg: number, settings: ProxySettings) {
2022-07-29 00:49:46 +02:00
this.fromPort = fromPortArg;
2022-07-29 01:52:34 +02:00
this.toPort = toPortArg;
this.settings = settings;
2022-07-29 00:49:46 +02:00
}
2022-07-29 01:52:34 +02:00
public async start() {
2021-02-02 21:59:24 +00:00
const cleanUpSockets = (from: plugins.net.Socket, to: plugins.net.Socket) => {
from.end();
to.end();
from.removeAllListeners();
2021-02-02 21:59:54 +00:00
to.removeAllListeners();
2021-02-02 21:59:24 +00:00
from.unpipe();
to.unpipe();
2021-02-03 00:13:29 +00:00
from.destroy();
to.destroy();
2022-07-29 01:52:34 +02:00
};
const isAllowed = (value: string, patterns: string[]): boolean => {
return patterns.some(pattern => plugins.minimatch(value, pattern));
};
const findMatchingDomain = (serverName: string): DomainConfig | undefined => {
return this.settings.domains.find(config => plugins.minimatch(serverName, config.domain));
};
const server = this.settings.sniEnabled ? tls.createServer(this.settings.tlsOptions || {}) : net.createServer();
this.netServer = server.on('connection', (from: net.Socket) => {
const remoteIP = from.remoteAddress || '';
if (this.settings.sniEnabled && from instanceof tls.TLSSocket) {
const serverName = (from as any).servername || '';
const domainConfig = findMatchingDomain(serverName);
if (!domainConfig) {
// If no matching domain config found, check default IPs if available
if (!this.settings.defaultAllowedIPs || !isAllowed(remoteIP, this.settings.defaultAllowedIPs)) {
console.log(`Connection rejected: No matching domain config for ${serverName} from IP ${remoteIP}`);
from.end();
return;
}
} else {
// Check if IP is allowed for this domain
if (!isAllowed(remoteIP, domainConfig.allowedIPs)) {
console.log(`Connection rejected: IP ${remoteIP} not allowed for domain ${serverName}`);
from.end();
return;
}
}
} else if (!this.settings.defaultAllowedIPs || !isAllowed(remoteIP, this.settings.defaultAllowedIPs)) {
console.log(`Connection rejected: IP ${remoteIP} not allowed for non-SNI connection`);
from.end();
return;
}
2020-02-07 13:04:11 +00:00
const to = net.createConnection({
host: 'localhost',
2022-07-29 00:49:46 +02:00
port: this.toPort,
2021-02-02 15:55:25 +00:00
});
2021-02-03 00:16:11 +00:00
from.setTimeout(120000);
2021-02-02 15:55:25 +00:00
from.pipe(to);
to.pipe(from);
from.on('error', () => {
2021-02-02 21:59:24 +00:00
cleanUpSockets(from, to);
2021-02-02 15:55:25 +00:00
});
to.on('error', () => {
2021-02-02 21:59:24 +00:00
cleanUpSockets(from, to);
2020-02-07 13:04:11 +00:00
});
2021-02-02 00:53:57 +00:00
from.on('close', () => {
2021-02-02 21:59:24 +00:00
cleanUpSockets(from, to);
2021-02-02 15:55:25 +00:00
});
2021-02-02 21:59:24 +00:00
to.on('close', () => {
cleanUpSockets(from, to);
});
from.on('timeout', () => {
cleanUpSockets(from, to);
});
to.on('timeout', () => {
cleanUpSockets(from, to);
});
from.on('end', () => {
cleanUpSockets(from, to);
2022-07-29 01:52:34 +02:00
});
2021-02-02 21:59:24 +00:00
to.on('end', () => {
cleanUpSockets(from, to);
2022-07-29 01:52:34 +02:00
});
2020-02-07 13:04:11 +00:00
})
2022-07-29 00:49:46 +02:00
.listen(this.fromPort);
console.log(`PortProxy -> OK: Now listening on port ${this.fromPort}`);
}
public async stop() {
2019-08-22 15:09:48 +02:00
const done = plugins.smartpromise.defer();
2022-07-29 03:39:05 +02:00
this.netServer.close(() => {
done.resolve();
2019-08-22 15:09:48 +02:00
});
await done.promise;
2022-07-29 00:49:46 +02:00
}
2022-07-29 01:52:34 +02:00
}