311 lines
9.0 KiB
TypeScript
311 lines
9.0 KiB
TypeScript
import * as plugins from './plugins.js';
|
|
|
|
/**
|
|
* Configuration options for TLS in SMTP connections
|
|
*/
|
|
export interface ISmtpTlsOptions {
|
|
/** Enable TLS for this SMTP port */
|
|
enabled: boolean;
|
|
/** Whether to use STARTTLS (upgrade plain connection) or implicit TLS */
|
|
useStartTls?: boolean;
|
|
/** Required TLS protocol version (defaults to TLSv1.2) */
|
|
minTlsVersion?: 'TLSv1.0' | 'TLSv1.1' | 'TLSv1.2' | 'TLSv1.3';
|
|
/** TLS ciphers to allow (comma-separated list) */
|
|
allowedCiphers?: string;
|
|
/** Whether to require client certificate for authentication */
|
|
requireClientCert?: boolean;
|
|
/** Whether to verify client certificate if provided */
|
|
verifyClientCert?: boolean;
|
|
}
|
|
|
|
/**
|
|
* Rate limiting options for SMTP connections
|
|
*/
|
|
export interface ISmtpRateLimitOptions {
|
|
/** Maximum connections per minute from a single IP */
|
|
maxConnectionsPerMinute?: number;
|
|
/** Maximum concurrent connections from a single IP */
|
|
maxConcurrentConnections?: number;
|
|
/** Maximum emails per minute from a single IP */
|
|
maxEmailsPerMinute?: number;
|
|
/** Maximum recipients per email */
|
|
maxRecipientsPerEmail?: number;
|
|
/** Maximum email size in bytes */
|
|
maxEmailSize?: number;
|
|
/** Action to take when rate limit is exceeded (default: 'tempfail') */
|
|
rateLimitAction?: 'tempfail' | 'drop' | 'delay';
|
|
}
|
|
|
|
/**
|
|
* Configuration for a specific SMTP port
|
|
*/
|
|
export interface ISmtpPortSettings {
|
|
/** The port number to listen on */
|
|
port: number;
|
|
/** Whether this port is enabled */
|
|
enabled?: boolean;
|
|
/** Port description (e.g., "Submission Port") */
|
|
description?: string;
|
|
/** Whether to require authentication for this port */
|
|
requireAuth?: boolean;
|
|
/** TLS options for this port */
|
|
tls?: ISmtpTlsOptions;
|
|
/** Rate limiting settings for this port */
|
|
rateLimit?: ISmtpRateLimitOptions;
|
|
/** Maximum message size in bytes for this port */
|
|
maxMessageSize?: number;
|
|
/** Whether to enable SMTP extensions like PIPELINING, 8BITMIME, etc. */
|
|
smtpExtensions?: {
|
|
/** Enable PIPELINING extension */
|
|
pipelining?: boolean;
|
|
/** Enable 8BITMIME extension */
|
|
eightBitMime?: boolean;
|
|
/** Enable SIZE extension */
|
|
size?: boolean;
|
|
/** Enable ENHANCEDSTATUSCODES extension */
|
|
enhancedStatusCodes?: boolean;
|
|
/** Enable DSN extension */
|
|
dsn?: boolean;
|
|
};
|
|
/** Custom SMTP greeting banner */
|
|
banner?: string;
|
|
}
|
|
|
|
/**
|
|
* Configuration manager for SMTP ports
|
|
*/
|
|
export class SmtpPortConfig {
|
|
/** Port configurations */
|
|
private portConfigs: Map<number, ISmtpPortSettings> = new Map();
|
|
|
|
/** Default port configurations */
|
|
private static readonly DEFAULT_CONFIGS: Record<number, Partial<ISmtpPortSettings>> = {
|
|
// Port 25: Standard SMTP
|
|
25: {
|
|
description: 'Standard SMTP',
|
|
requireAuth: false,
|
|
tls: {
|
|
enabled: true,
|
|
useStartTls: true,
|
|
minTlsVersion: 'TLSv1.2'
|
|
},
|
|
rateLimit: {
|
|
maxConnectionsPerMinute: 60,
|
|
maxConcurrentConnections: 10,
|
|
maxEmailsPerMinute: 30
|
|
},
|
|
maxMessageSize: 20 * 1024 * 1024 // 20MB
|
|
},
|
|
// Port 587: Submission
|
|
587: {
|
|
description: 'Submission Port',
|
|
requireAuth: true,
|
|
tls: {
|
|
enabled: true,
|
|
useStartTls: true,
|
|
minTlsVersion: 'TLSv1.2'
|
|
},
|
|
rateLimit: {
|
|
maxConnectionsPerMinute: 100,
|
|
maxConcurrentConnections: 20,
|
|
maxEmailsPerMinute: 60
|
|
},
|
|
maxMessageSize: 50 * 1024 * 1024 // 50MB
|
|
},
|
|
// Port 465: SMTPS (Legacy Implicit TLS)
|
|
465: {
|
|
description: 'SMTPS (Implicit TLS)',
|
|
requireAuth: true,
|
|
tls: {
|
|
enabled: true,
|
|
useStartTls: false,
|
|
minTlsVersion: 'TLSv1.2'
|
|
},
|
|
rateLimit: {
|
|
maxConnectionsPerMinute: 100,
|
|
maxConcurrentConnections: 20,
|
|
maxEmailsPerMinute: 60
|
|
},
|
|
maxMessageSize: 50 * 1024 * 1024 // 50MB
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Create a new SmtpPortConfig
|
|
* @param initialConfigs Optional initial port configurations
|
|
*/
|
|
constructor(initialConfigs?: ISmtpPortSettings[]) {
|
|
// Initialize with default configurations for standard SMTP ports
|
|
this.initializeDefaults();
|
|
|
|
// Apply custom configurations if provided
|
|
if (initialConfigs) {
|
|
for (const config of initialConfigs) {
|
|
this.setPortConfig(config);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize port configurations with defaults
|
|
*/
|
|
private initializeDefaults(): void {
|
|
// Set up default configurations for standard SMTP ports: 25, 587, 465
|
|
Object.entries(SmtpPortConfig.DEFAULT_CONFIGS).forEach(([portStr, defaults]) => {
|
|
const port = parseInt(portStr, 10);
|
|
this.portConfigs.set(port, {
|
|
port,
|
|
enabled: true,
|
|
...defaults
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get configuration for a specific port
|
|
* @param port Port number
|
|
* @returns Port configuration or null if not found
|
|
*/
|
|
public getPortConfig(port: number): ISmtpPortSettings | null {
|
|
return this.portConfigs.get(port) || null;
|
|
}
|
|
|
|
/**
|
|
* Get all configured ports
|
|
* @returns Array of port configurations
|
|
*/
|
|
public getAllPortConfigs(): ISmtpPortSettings[] {
|
|
return Array.from(this.portConfigs.values());
|
|
}
|
|
|
|
/**
|
|
* Get only enabled port configurations
|
|
* @returns Array of enabled port configurations
|
|
*/
|
|
public getEnabledPortConfigs(): ISmtpPortSettings[] {
|
|
return this.getAllPortConfigs().filter(config => config.enabled !== false);
|
|
}
|
|
|
|
/**
|
|
* Set configuration for a specific port
|
|
* @param config Port configuration
|
|
*/
|
|
public setPortConfig(config: ISmtpPortSettings): void {
|
|
// Get existing config if any
|
|
const existingConfig = this.portConfigs.get(config.port) || { port: config.port };
|
|
|
|
// Merge with new configuration
|
|
this.portConfigs.set(config.port, {
|
|
...existingConfig,
|
|
...config
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Remove configuration for a specific port
|
|
* @param port Port number
|
|
* @returns Whether the configuration was removed
|
|
*/
|
|
public removePortConfig(port: number): boolean {
|
|
return this.portConfigs.delete(port);
|
|
}
|
|
|
|
/**
|
|
* Disable a specific port
|
|
* @param port Port number
|
|
* @returns Whether the port was disabled
|
|
*/
|
|
public disablePort(port: number): boolean {
|
|
const config = this.portConfigs.get(port);
|
|
if (config) {
|
|
config.enabled = false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Enable a specific port
|
|
* @param port Port number
|
|
* @returns Whether the port was enabled
|
|
*/
|
|
public enablePort(port: number): boolean {
|
|
const config = this.portConfigs.get(port);
|
|
if (config) {
|
|
config.enabled = true;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Apply port configurations to SmartProxy settings
|
|
* @param smartProxy SmartProxy instance
|
|
*/
|
|
public applyToSmartProxy(smartProxy: plugins.smartproxy.SmartProxy): void {
|
|
if (!smartProxy) return;
|
|
|
|
const enabledPorts = this.getEnabledPortConfigs();
|
|
const settings = smartProxy.settings;
|
|
|
|
// Initialize globalPortRanges if needed
|
|
if (!settings.globalPortRanges) {
|
|
settings.globalPortRanges = [];
|
|
}
|
|
|
|
// Add configured ports to globalPortRanges
|
|
for (const portConfig of enabledPorts) {
|
|
// Add port to global port ranges if not already present
|
|
if (!settings.globalPortRanges.some((r) => r.from <= portConfig.port && portConfig.port <= r.to)) {
|
|
settings.globalPortRanges.push({ from: portConfig.port, to: portConfig.port });
|
|
}
|
|
|
|
// Apply TLS settings at SmartProxy level
|
|
if (portConfig.port === 465 && portConfig.tls?.enabled) {
|
|
// For implicit TLS on port 465
|
|
settings.sniEnabled = true;
|
|
}
|
|
}
|
|
|
|
// Group ports by TLS configuration to log them
|
|
const starttlsPorts = enabledPorts
|
|
.filter(p => p.tls?.enabled && p.tls?.useStartTls)
|
|
.map(p => p.port);
|
|
|
|
const implicitTlsPorts = enabledPorts
|
|
.filter(p => p.tls?.enabled && !p.tls?.useStartTls)
|
|
.map(p => p.port);
|
|
|
|
const nonTlsPorts = enabledPorts
|
|
.filter(p => !p.tls?.enabled)
|
|
.map(p => p.port);
|
|
|
|
if (starttlsPorts.length > 0) {
|
|
console.log(`Configured STARTTLS SMTP ports: ${starttlsPorts.join(', ')}`);
|
|
}
|
|
|
|
if (implicitTlsPorts.length > 0) {
|
|
console.log(`Configured Implicit TLS SMTP ports: ${implicitTlsPorts.join(', ')}`);
|
|
}
|
|
|
|
if (nonTlsPorts.length > 0) {
|
|
console.log(`Configured Plain SMTP ports: ${nonTlsPorts.join(', ')}`);
|
|
}
|
|
|
|
// Setup connection listeners for different port types
|
|
smartProxy.on('connection', (connection) => {
|
|
const port = connection.localPort;
|
|
|
|
// Check which type of port this is
|
|
if (implicitTlsPorts.includes(port)) {
|
|
console.log(`Implicit TLS SMTP connection on port ${port} from ${connection.remoteIP}`);
|
|
} else if (starttlsPorts.includes(port)) {
|
|
console.log(`STARTTLS SMTP connection on port ${port} from ${connection.remoteIP}`);
|
|
} else if (nonTlsPorts.includes(port)) {
|
|
console.log(`Plain SMTP connection on port ${port} from ${connection.remoteIP}`);
|
|
}
|
|
});
|
|
|
|
console.log(`Applied SMTP port configurations to SmartProxy: ${enabledPorts.map(p => p.port).join(', ')}`);
|
|
}
|
|
} |