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(', ')}`);
 | |
|   }
 | |
| } |