|
|
|
@ -67,71 +67,100 @@ export class PortProxy {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const server = this.settings.sniEnabled
|
|
|
|
|
? plugins.tls.createServer(this.settings)
|
|
|
|
|
? plugins.tls.createServer({
|
|
|
|
|
...this.settings,
|
|
|
|
|
SNICallback: (serverName: string, cb: (err: Error | null, ctx?: plugins.tls.SecureContext) => void) => {
|
|
|
|
|
console.log(`SNI request for domain: ${serverName}`);
|
|
|
|
|
const domainConfig = findMatchingDomain(serverName);
|
|
|
|
|
if (!domainConfig) {
|
|
|
|
|
console.log(`SNI rejected: No matching domain config for ${serverName}`);
|
|
|
|
|
cb(new Error(`No configuration for domain: ${serverName}`));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Create context with the provided TLS settings
|
|
|
|
|
const ctx = plugins.tls.createSecureContext(this.settings);
|
|
|
|
|
cb(null, ctx);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
: plugins.net.createServer();
|
|
|
|
|
|
|
|
|
|
this.netServer = server.on('connection', (from: plugins.net.Socket) => {
|
|
|
|
|
const handleConnection = (from: plugins.net.Socket | plugins.tls.TLSSocket) => {
|
|
|
|
|
const remoteIP = from.remoteAddress || '';
|
|
|
|
|
if (this.settings.sniEnabled && from instanceof plugins.tls.TLSSocket) {
|
|
|
|
|
const serverName = (from as any).servername || '';
|
|
|
|
|
const domainConfig = findMatchingDomain(serverName);
|
|
|
|
|
let 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;
|
|
|
|
|
}
|
|
|
|
|
if (this.settings.sniEnabled && from instanceof plugins.tls.TLSSocket) {
|
|
|
|
|
serverName = (from as any).servername || '';
|
|
|
|
|
console.log(`TLS Connection from ${remoteIP} for domain: ${serverName}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For TLS connections, we've already validated the domain in SNICallback
|
|
|
|
|
if (!this.settings.sniEnabled || from instanceof plugins.tls.TLSSocket) {
|
|
|
|
|
const domainConfig = serverName ? findMatchingDomain(serverName) : undefined;
|
|
|
|
|
|
|
|
|
|
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 || 'non-SNI'} from IP ${remoteIP}`);
|
|
|
|
|
from.end();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else if (!this.settings.defaultAllowedIPs || !isAllowed(remoteIP, this.settings.defaultAllowedIPs)) {
|
|
|
|
|
} 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const to = plugins.net.createConnection({
|
|
|
|
|
host: this.settings.toHost!,
|
|
|
|
|
port: this.settings.toPort,
|
|
|
|
|
});
|
|
|
|
|
console.log(`Connection established: ${remoteIP} -> ${this.settings.toHost}:${this.settings.toPort}${this.settings.sniEnabled ? ` (SNI: ${(from as any).servername || 'none'})` : ''}`);
|
|
|
|
|
from.setTimeout(120000);
|
|
|
|
|
from.pipe(to);
|
|
|
|
|
to.pipe(from);
|
|
|
|
|
from.on('error', () => {
|
|
|
|
|
cleanUpSockets(from, to);
|
|
|
|
|
});
|
|
|
|
|
to.on('error', () => {
|
|
|
|
|
cleanUpSockets(from, to);
|
|
|
|
|
});
|
|
|
|
|
from.on('close', () => {
|
|
|
|
|
cleanUpSockets(from, to);
|
|
|
|
|
});
|
|
|
|
|
to.on('close', () => {
|
|
|
|
|
cleanUpSockets(from, to);
|
|
|
|
|
});
|
|
|
|
|
from.on('timeout', () => {
|
|
|
|
|
cleanUpSockets(from, to);
|
|
|
|
|
});
|
|
|
|
|
to.on('timeout', () => {
|
|
|
|
|
cleanUpSockets(from, to);
|
|
|
|
|
});
|
|
|
|
|
from.on('end', () => {
|
|
|
|
|
cleanUpSockets(from, to);
|
|
|
|
|
});
|
|
|
|
|
to.on('end', () => {
|
|
|
|
|
cleanUpSockets(from, to);
|
|
|
|
|
});
|
|
|
|
|
const to = plugins.net.createConnection({
|
|
|
|
|
host: this.settings.toHost!,
|
|
|
|
|
port: this.settings.toPort,
|
|
|
|
|
});
|
|
|
|
|
console.log(`Connection established: ${remoteIP} -> ${this.settings.toHost}:${this.settings.toPort}${serverName ? ` (SNI: ${serverName})` : ''}`);
|
|
|
|
|
from.setTimeout(120000);
|
|
|
|
|
from.pipe(to);
|
|
|
|
|
to.pipe(from);
|
|
|
|
|
from.on('error', () => {
|
|
|
|
|
cleanUpSockets(from, to);
|
|
|
|
|
});
|
|
|
|
|
to.on('error', () => {
|
|
|
|
|
cleanUpSockets(from, to);
|
|
|
|
|
});
|
|
|
|
|
from.on('close', () => {
|
|
|
|
|
cleanUpSockets(from, to);
|
|
|
|
|
});
|
|
|
|
|
to.on('close', () => {
|
|
|
|
|
cleanUpSockets(from, to);
|
|
|
|
|
});
|
|
|
|
|
from.on('timeout', () => {
|
|
|
|
|
cleanUpSockets(from, to);
|
|
|
|
|
});
|
|
|
|
|
to.on('timeout', () => {
|
|
|
|
|
cleanUpSockets(from, to);
|
|
|
|
|
});
|
|
|
|
|
from.on('end', () => {
|
|
|
|
|
cleanUpSockets(from, to);
|
|
|
|
|
});
|
|
|
|
|
to.on('end', () => {
|
|
|
|
|
cleanUpSockets(from, to);
|
|
|
|
|
});
|
|
|
|
|
this.netServer = server
|
|
|
|
|
.on('connection', handleConnection)
|
|
|
|
|
.on('secureConnection', handleConnection)
|
|
|
|
|
.on('tlsClientError', (err, tlsSocket) => {
|
|
|
|
|
console.log(`TLS Client Error: ${err.message}`);
|
|
|
|
|
})
|
|
|
|
|
.on('error', (err) => {
|
|
|
|
|
console.log(`Server Error: ${err.message}`);
|
|
|
|
|
})
|
|
|
|
|
.listen(this.settings.fromPort);
|
|
|
|
|
console.log(`PortProxy -> OK: Now listening on port ${this.settings.fromPort}`);
|
|
|
|
|
console.log(`PortProxy -> OK: Now listening on port ${this.settings.fromPort}${this.settings.sniEnabled ? ' (SNI enabled)' : ''}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async stop() {
|
|
|
|
|