import * as plugins from '../../plugins.js'; import * as paths from '../../paths.js'; import { EventEmitter } from 'events'; import { logger } from '../../logger.js'; import { SecurityLogger, SecurityLogLevel, SecurityEventType } from '../../security/index.js'; import { DKIMCreator } from '../security/classes.dkimcreator.js'; import { IPReputationChecker } from '../../security/classes.ipreputationchecker.js'; import { RustSecurityBridge } from '../../security/classes.rustsecuritybridge.js'; import { EmailRouter } from './classes.email.router.js'; import { Email } from '../core/classes.email.js'; import { DomainRegistry } from './classes.domain.registry.js'; import { DnsManager } from './classes.dns.manager.js'; import { BounceManager, BounceType, BounceCategory } from '../core/classes.bouncemanager.js'; import { createSmtpServer } from '../delivery/smtpserver/index.js'; import { createPooledSmtpClient } from '../delivery/smtpclient/create-client.js'; import { MultiModeDeliverySystem } from '../delivery/classes.delivery.system.js'; import { UnifiedDeliveryQueue } from '../delivery/classes.delivery.queue.js'; import { UnifiedRateLimiter } from '../delivery/classes.unified.rate.limiter.js'; import { SmtpState } from '../delivery/interfaces.js'; /** * Unified email server that handles all email traffic with pattern-based routing */ export class UnifiedEmailServer extends EventEmitter { dcRouter; options; emailRouter; domainRegistry; servers = []; stats; // Add components needed for sending and securing emails dkimCreator; rustBridge; ipReputationChecker; bounceManager; ipWarmupManager; senderReputationMonitor; deliveryQueue; deliverySystem; rateLimiter; // TODO: Implement rate limiting in SMTP server handlers dkimKeys = new Map(); // domain -> private key smtpClients = new Map(); // host:port -> client constructor(dcRouter, options) { super(); this.dcRouter = dcRouter; // Set default options this.options = { ...options, banner: options.banner || `${options.hostname} ESMTP UnifiedEmailServer`, maxMessageSize: options.maxMessageSize || 10 * 1024 * 1024, // 10MB maxClients: options.maxClients || 100, maxConnections: options.maxConnections || 1000, connectionTimeout: options.connectionTimeout || 60000, // 1 minute socketTimeout: options.socketTimeout || 60000 // 1 minute }; // Initialize Rust security bridge (singleton) this.rustBridge = RustSecurityBridge.getInstance(); // Initialize DKIM creator with storage manager this.dkimCreator = new DKIMCreator(paths.keysDir, dcRouter.storageManager); // Initialize IP reputation checker with storage manager this.ipReputationChecker = IPReputationChecker.getInstance({ enableLocalCache: true, enableDNSBL: true, enableIPInfo: true }, dcRouter.storageManager); // Initialize bounce manager with storage manager this.bounceManager = new BounceManager({ maxCacheSize: 10000, cacheTTL: 30 * 24 * 60 * 60 * 1000, // 30 days storageManager: dcRouter.storageManager }); // IP warmup manager and sender reputation monitor are optional // They will be initialized when the deliverability module is available this.ipWarmupManager = null; this.senderReputationMonitor = null; // Initialize domain registry this.domainRegistry = new DomainRegistry(options.domains, options.defaults); // Initialize email router with routes and storage manager this.emailRouter = new EmailRouter(options.routes || [], { storageManager: dcRouter.storageManager, persistChanges: true }); // Initialize rate limiter this.rateLimiter = new UnifiedRateLimiter(options.rateLimits || { global: { maxConnectionsPerIP: 10, maxMessagesPerMinute: 100, maxRecipientsPerMessage: 50, maxErrorsPerIP: 10, maxAuthFailuresPerIP: 5, blockDuration: 300000 // 5 minutes } }); // Initialize delivery components const queueOptions = { storageType: 'memory', // Default to memory storage maxRetries: 3, baseRetryDelay: 300000, // 5 minutes maxRetryDelay: 3600000 // 1 hour }; this.deliveryQueue = new UnifiedDeliveryQueue(queueOptions); const deliveryOptions = { globalRateLimit: 100, // Default to 100 emails per minute concurrentDeliveries: 10, processBounces: true, bounceHandler: { processSmtpFailure: this.processSmtpFailure.bind(this) }, onDeliverySuccess: async (item, _result) => { // Record delivery success event for reputation monitoring const email = item.processingResult; const senderDomain = email.from.split('@')[1]; if (senderDomain) { this.recordReputationEvent(senderDomain, { type: 'delivered', count: email.to.length }); } } }; this.deliverySystem = new MultiModeDeliverySystem(this.deliveryQueue, deliveryOptions, this); // Initialize statistics this.stats = { startTime: new Date(), connections: { current: 0, total: 0 }, messages: { processed: 0, delivered: 0, failed: 0 }, processingTime: { avg: 0, max: 0, min: 0 } }; // We'll create the SMTP servers during the start() method } /** * Get or create an SMTP client for the given host and port * Uses connection pooling for efficiency */ getSmtpClient(host, port = 25) { const clientKey = `${host}:${port}`; // Check if we already have a client for this destination let client = this.smtpClients.get(clientKey); if (!client) { // Create a new pooled SMTP client client = createPooledSmtpClient({ host, port, secure: port === 465, connectionTimeout: this.options.outbound?.connectionTimeout || 30000, socketTimeout: this.options.outbound?.socketTimeout || 120000, maxConnections: this.options.outbound?.maxConnections || 10, maxMessages: 1000, // Messages per connection before reconnect pool: true, debug: false }); this.smtpClients.set(clientKey, client); logger.log('info', `Created new SMTP client pool for ${clientKey}`); } return client; } /** * Start the unified email server */ async start() { logger.log('info', `Starting UnifiedEmailServer on ports: ${this.options.ports.join(', ')}`); try { // Initialize the delivery queue await this.deliveryQueue.initialize(); logger.log('info', 'Email delivery queue initialized'); // Start the delivery system await this.deliverySystem.start(); logger.log('info', 'Email delivery system started'); // Start Rust security bridge (non-blocking — server works without it) const bridgeOk = await this.rustBridge.start(); if (bridgeOk) { logger.log('info', 'Rust security bridge started — using Rust for DKIM/SPF/DMARC verification'); } else { logger.log('warn', 'Rust security bridge unavailable — falling back to TypeScript security verification'); } // Set up DKIM for all domains await this.setupDkimForDomains(); logger.log('info', 'DKIM configuration completed for all domains'); // Create DNS manager and ensure all DNS records are created const dnsManager = new DnsManager(this.dcRouter); await dnsManager.ensureDnsRecords(this.domainRegistry.getAllConfigs(), this.dkimCreator); logger.log('info', 'DNS records ensured for all configured domains'); // Apply per-domain rate limits this.applyDomainRateLimits(); logger.log('info', 'Per-domain rate limits configured'); // Check and rotate DKIM keys if needed await this.checkAndRotateDkimKeys(); logger.log('info', 'DKIM key rotation check completed'); // Skip server creation in socket-handler mode if (this.options.useSocketHandler) { logger.log('info', 'UnifiedEmailServer started in socket-handler mode (no port listening)'); this.emit('started'); return; } // Ensure we have the necessary TLS options const hasTlsConfig = this.options.tls?.keyPath && this.options.tls?.certPath; // Prepare the certificate and key if available let key; let cert; if (hasTlsConfig) { try { key = plugins.fs.readFileSync(this.options.tls.keyPath, 'utf8'); cert = plugins.fs.readFileSync(this.options.tls.certPath, 'utf8'); logger.log('info', 'TLS certificates loaded successfully'); } catch (error) { logger.log('warn', `Failed to load TLS certificates: ${error.message}`); } } // Create a SMTP server for each port for (const port of this.options.ports) { // Create a reference object to hold the MTA service during setup const mtaRef = { config: { smtp: { hostname: this.options.hostname }, security: { checkIPReputation: false, verifyDkim: true, verifySpf: true, verifyDmarc: true } }, // Security verification delegated to the Rust bridge when available dkimVerifier: { verify: async (rawMessage) => { if (this.rustBridge.running) { try { const results = await this.rustBridge.verifyDkim(rawMessage); const first = results[0]; return { isValid: first?.is_valid ?? false, domain: first?.domain ?? '' }; } catch (err) { logger.log('warn', `Rust DKIM verification failed, accepting: ${err.message}`); return { isValid: true, domain: '' }; } } return { isValid: true, domain: '' }; // No bridge — accept } }, spfVerifier: { verifyAndApply: async (session) => { if (this.rustBridge.running && session?.remoteAddress && session.remoteAddress !== '127.0.0.1') { try { const result = await this.rustBridge.checkSpf({ ip: session.remoteAddress, heloDomain: session.clientHostname || '', hostname: this.options.hostname, mailFrom: session.envelope?.mailFrom?.address || session.mailFrom || '', }); return result.result === 'pass' || result.result === 'none' || result.result === 'neutral'; } catch (err) { logger.log('warn', `Rust SPF check failed, accepting: ${err.message}`); return true; } } return true; // No bridge or localhost — accept } }, dmarcVerifier: { verify: async () => ({}), applyPolicy: () => true }, processIncomingEmail: async (email) => { // Process email using the new route-based system await this.processEmailByMode(email, { id: 'session-' + Math.random().toString(36).substring(2), state: SmtpState.FINISHED, mailFrom: email.from, rcptTo: email.to, emailData: email.toRFC822String(), // Use the proper method to get the full email content useTLS: false, connectionEnded: true, remoteAddress: '127.0.0.1', clientHostname: '', secure: false, authenticated: false, envelope: { mailFrom: { address: email.from, args: {} }, rcptTo: email.to.map(recipient => ({ address: recipient, args: {} })) } }); return true; } }; // Create server options const serverOptions = { port, hostname: this.options.hostname, key, cert }; // Create and start the SMTP server const smtpServer = createSmtpServer(mtaRef, serverOptions); this.servers.push(smtpServer); // Start the server await new Promise((resolve, reject) => { try { // Leave this empty for now, smtpServer.start() is handled by the SMTPServer class internally // The server is started when it's created logger.log('info', `UnifiedEmailServer listening on port ${port}`); // Event handlers are managed internally by the SmtpServer class // No need to access the private server property resolve(); } catch (err) { if (err.code === 'EADDRINUSE') { logger.log('error', `Port ${port} is already in use`); reject(new Error(`Port ${port} is already in use`)); } else { logger.log('error', `Error starting server on port ${port}: ${err.message}`); reject(err); } } }); } logger.log('info', 'UnifiedEmailServer started successfully'); this.emit('started'); } catch (error) { logger.log('error', `Failed to start UnifiedEmailServer: ${error.message}`); throw error; } } /** * Handle a socket from smartproxy in socket-handler mode * @param socket The socket to handle * @param port The port this connection is for (25, 587, 465) */ async handleSocket(socket, port) { if (!this.options.useSocketHandler) { logger.log('error', 'handleSocket called but useSocketHandler is not enabled'); socket.destroy(); return; } logger.log('info', `Handling socket for port ${port}`); // Create a temporary SMTP server instance for this connection // We need a full server instance because the SMTP protocol handler needs all components const smtpServerOptions = { port, hostname: this.options.hostname, key: this.options.tls?.keyPath ? plugins.fs.readFileSync(this.options.tls.keyPath, 'utf8') : undefined, cert: this.options.tls?.certPath ? plugins.fs.readFileSync(this.options.tls.certPath, 'utf8') : undefined }; // Create the SMTP server instance const smtpServer = createSmtpServer(this, smtpServerOptions); // Get the connection manager from the server const connectionManager = smtpServer.connectionManager; if (!connectionManager) { logger.log('error', 'Could not get connection manager from SMTP server'); socket.destroy(); return; } // Determine if this is a secure connection // Port 465 uses implicit TLS, so the socket is already secure const isSecure = port === 465 || socket instanceof plugins.tls.TLSSocket; // Pass the socket to the connection manager try { await connectionManager.handleConnection(socket, isSecure); } catch (error) { logger.log('error', `Error handling socket connection: ${error.message}`); socket.destroy(); } } /** * Stop the unified email server */ async stop() { logger.log('info', 'Stopping UnifiedEmailServer'); try { // Clear the servers array - servers will be garbage collected this.servers = []; // Stop Rust security bridge await this.rustBridge.stop(); // Stop the delivery system if (this.deliverySystem) { await this.deliverySystem.stop(); logger.log('info', 'Email delivery system stopped'); } // Shut down the delivery queue if (this.deliveryQueue) { await this.deliveryQueue.shutdown(); logger.log('info', 'Email delivery queue shut down'); } // Close all SMTP client connections for (const [clientKey, client] of this.smtpClients) { try { await client.close(); logger.log('info', `Closed SMTP client pool for ${clientKey}`); } catch (error) { logger.log('warn', `Error closing SMTP client for ${clientKey}: ${error.message}`); } } this.smtpClients.clear(); logger.log('info', 'UnifiedEmailServer stopped successfully'); this.emit('stopped'); } catch (error) { logger.log('error', `Error stopping UnifiedEmailServer: ${error.message}`); throw error; } } /** * Verify inbound email security (DKIM/SPF/DMARC) using the Rust bridge. * Falls back gracefully if the bridge is not running. */ async verifyInboundSecurity(email, session) { if (!this.rustBridge.running) { return; // Bridge not available — skip verification } try { const rawMessage = session.emailData || email.toRFC822String(); const result = await this.rustBridge.verifyEmail({ rawMessage, ip: session.remoteAddress, heloDomain: session.clientHostname || '', hostname: this.options.hostname, mailFrom: session.envelope?.mailFrom?.address || session.mailFrom || '', }); // Apply DKIM result headers if (result.dkim && result.dkim.length > 0) { const dkimSummary = result.dkim .map(d => `${d.status}${d.domain ? ` (${d.domain})` : ''}`) .join(', '); email.addHeader('X-DKIM-Result', dkimSummary); } // Apply SPF result header if (result.spf) { email.addHeader('Received-SPF', `${result.spf.result} (domain: ${result.spf.domain}, ip: ${result.spf.ip})`); // Mark as spam on SPF hard fail if (result.spf.result === 'fail') { email.mightBeSpam = true; logger.log('warn', `SPF fail for ${session.remoteAddress} — marking as potential spam`); } } // Apply DMARC result header and policy if (result.dmarc) { email.addHeader('X-DMARC-Result', `${result.dmarc.action} (policy=${result.dmarc.policy}, dkim=${result.dmarc.dkim_result}, spf=${result.dmarc.spf_result})`); if (result.dmarc.action === 'reject') { email.mightBeSpam = true; logger.log('warn', `DMARC reject for domain ${result.dmarc.domain} — marking as spam`); } else if (result.dmarc.action === 'quarantine') { email.mightBeSpam = true; logger.log('info', `DMARC quarantine for domain ${result.dmarc.domain} — marking as potential spam`); } } logger.log('info', `Inbound security verified for email from ${session.remoteAddress}: DKIM=${result.dkim?.[0]?.status ?? 'none'}, SPF=${result.spf?.result ?? 'none'}, DMARC=${result.dmarc?.action ?? 'none'}`); } catch (err) { logger.log('warn', `Inbound security verification failed: ${err.message} — accepting email`); } } /** * Process email based on routing rules */ async processEmailByMode(emailData, session) { // Convert Buffer to Email if needed let email; if (Buffer.isBuffer(emailData)) { // Parse the email data buffer into an Email object try { const parsed = await plugins.mailparser.simpleParser(emailData); email = new Email({ from: parsed.from?.value[0]?.address || session.envelope.mailFrom.address, to: session.envelope.rcptTo[0]?.address || '', subject: parsed.subject || '', text: parsed.text || '', html: parsed.html || undefined, attachments: parsed.attachments?.map(att => ({ filename: att.filename || '', content: att.content, contentType: att.contentType })) || [] }); } catch (error) { logger.log('error', `Error parsing email data: ${error.message}`); throw new Error(`Error parsing email data: ${error.message}`); } } else { email = emailData; } // Run inbound security verification (DKIM/SPF/DMARC) via Rust bridge if (session.remoteAddress && session.remoteAddress !== '127.0.0.1') { await this.verifyInboundSecurity(email, session); } // First check if this is a bounce notification email // Look for common bounce notification subject patterns const subject = email.subject || ''; const isBounceLike = /mail delivery|delivery (failed|status|notification)|failure notice|returned mail|undeliverable|delivery problem/i.test(subject); if (isBounceLike) { logger.log('info', `Email subject matches bounce notification pattern: "${subject}"`); // Try to process as a bounce const isBounce = await this.processBounceNotification(email); if (isBounce) { logger.log('info', 'Successfully processed as bounce notification, skipping regular processing'); return email; } logger.log('info', 'Not a valid bounce notification, continuing with regular processing'); } // Find matching route const context = { email, session }; const route = await this.emailRouter.evaluateRoutes(context); if (!route) { // No matching route - reject throw new Error('No matching route for email'); } // Store matched route in session session.matchedRoute = route; // Execute action based on route await this.executeAction(route.action, email, context); // Return the processed email return email; } /** * Execute action based on route configuration */ async executeAction(action, email, context) { switch (action.type) { case 'forward': await this.handleForwardAction(action, email, context); break; case 'process': await this.handleProcessAction(action, email, context); break; case 'deliver': await this.handleDeliverAction(action, email, context); break; case 'reject': await this.handleRejectAction(action, email, context); break; default: throw new Error(`Unknown action type: ${action.type}`); } } /** * Handle forward action */ async handleForwardAction(_action, email, context) { if (!_action.forward) { throw new Error('Forward action requires forward configuration'); } const { host, port = 25, auth, addHeaders } = _action.forward; logger.log('info', `Forwarding email to ${host}:${port}`); // Add forwarding headers if (addHeaders) { for (const [key, value] of Object.entries(addHeaders)) { email.headers[key] = value; } } // Add standard forwarding headers email.headers['X-Forwarded-For'] = context.session.remoteAddress || 'unknown'; email.headers['X-Forwarded-To'] = email.to.join(', '); email.headers['X-Forwarded-Date'] = new Date().toISOString(); // Get SMTP client const client = this.getSmtpClient(host, port); try { // Send email await client.sendMail(email); logger.log('info', `Successfully forwarded email to ${host}:${port}`); SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.INFO, type: SecurityEventType.EMAIL_FORWARDING, message: 'Email forwarded successfully', ipAddress: context.session.remoteAddress, details: { sessionId: context.session.id, routeName: context.session.matchedRoute?.name, targetHost: host, targetPort: port, recipients: email.to }, success: true }); } catch (error) { logger.log('error', `Failed to forward email: ${error.message}`); SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.ERROR, type: SecurityEventType.EMAIL_FORWARDING, message: 'Email forwarding failed', ipAddress: context.session.remoteAddress, details: { sessionId: context.session.id, routeName: context.session.matchedRoute?.name, targetHost: host, targetPort: port, error: error.message }, success: false }); // Handle as bounce for (const recipient of email.getAllRecipients()) { await this.bounceManager.processSmtpFailure(recipient, error.message, { sender: email.from, originalEmailId: email.headers['Message-ID'] }); } throw error; } } /** * Handle process action */ async handleProcessAction(action, email, context) { logger.log('info', `Processing email with action options`); // Apply scanning if requested if (action.process?.scan) { // Use existing content scanner // Note: ContentScanner integration would go here logger.log('info', 'Content scanning requested'); } // Note: DKIM signing will be applied at delivery time to ensure signature validity // Queue for delivery const queue = action.process?.queue || 'normal'; await this.deliveryQueue.enqueue(email, 'process', context.session.matchedRoute); logger.log('info', `Email queued for delivery in ${queue} queue`); } /** * Handle deliver action */ async handleDeliverAction(_action, email, context) { logger.log('info', `Delivering email locally`); // Queue for local delivery await this.deliveryQueue.enqueue(email, 'mta', context.session.matchedRoute); logger.log('info', 'Email queued for local delivery'); } /** * Handle reject action */ async handleRejectAction(action, email, context) { const code = action.reject?.code || 550; const message = action.reject?.message || 'Message rejected'; logger.log('info', `Rejecting email with code ${code}: ${message}`); SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.WARN, type: SecurityEventType.EMAIL_PROCESSING, message: 'Email rejected by routing rule', ipAddress: context.session.remoteAddress, details: { sessionId: context.session.id, routeName: context.session.matchedRoute?.name, rejectCode: code, rejectMessage: message, from: email.from, to: email.to }, success: false }); // Throw error with SMTP code and message const error = new Error(message); error.responseCode = code; throw error; } /** * Handle email in MTA mode (programmatic processing) */ async _handleMtaMode(email, session) { logger.log('info', `Handling email in MTA mode for session ${session.id}`); try { // Apply MTA rule options if provided if (session.matchedRoute?.action.options?.mtaOptions) { const options = session.matchedRoute.action.options.mtaOptions; // Apply DKIM signing if enabled if (options.dkimSign && options.dkimOptions) { // Sign the email with DKIM logger.log('info', `Signing email with DKIM for domain ${options.dkimOptions.domainName}`); try { // Ensure DKIM keys exist for the domain await this.dkimCreator.handleDKIMKeysForDomain(options.dkimOptions.domainName); // Convert Email to raw format for signing const rawEmail = email.toRFC822String(); // Create headers object const headers = {}; for (const [key, value] of Object.entries(email.headers)) { headers[key] = value; } // Sign the email const dkimDomain = options.dkimOptions.domainName; const dkimSelector = options.dkimOptions.keySelector || 'mta'; const dkimPrivateKey = (await this.dkimCreator.readDKIMKeys(dkimDomain)).privateKey; const signResult = await plugins.dkimSign(rawEmail, { signingDomain: dkimDomain, selector: dkimSelector, privateKey: dkimPrivateKey, canonicalization: 'relaxed/relaxed', algorithm: 'rsa-sha256', signTime: new Date(), signatureData: [ { signingDomain: dkimDomain, selector: dkimSelector, privateKey: dkimPrivateKey, algorithm: 'rsa-sha256', canonicalization: 'relaxed/relaxed' } ] }); // Add the DKIM-Signature header to the email if (signResult.signatures) { email.addHeader('DKIM-Signature', signResult.signatures); logger.log('info', `Successfully added DKIM signature for ${options.dkimOptions.domainName}`); } } catch (error) { logger.log('error', `Failed to sign email with DKIM: ${error.message}`); } } } // Get email content for logging/processing const subject = email.subject; const recipients = email.getAllRecipients().join(', '); logger.log('info', `Email processed by MTA: ${subject} to ${recipients}`); SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.INFO, type: SecurityEventType.EMAIL_PROCESSING, message: 'Email processed by MTA', ipAddress: session.remoteAddress, details: { sessionId: session.id, ruleName: session.matchedRoute?.name || 'default', subject, recipients }, success: true }); } catch (error) { logger.log('error', `Failed to process email in MTA mode: ${error.message}`); SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.ERROR, type: SecurityEventType.EMAIL_PROCESSING, message: 'MTA processing failed', ipAddress: session.remoteAddress, details: { sessionId: session.id, ruleName: session.matchedRoute?.name || 'default', error: error.message }, success: false }); throw error; } } /** * Handle email in process mode (store-and-forward with scanning) */ async _handleProcessMode(email, session) { logger.log('info', `Handling email in process mode for session ${session.id}`); try { const route = session.matchedRoute; // Apply content scanning if enabled if (route?.action.options?.contentScanning && route.action.options.scanners && route.action.options.scanners.length > 0) { logger.log('info', 'Performing content scanning'); // Apply each scanner for (const scanner of route.action.options.scanners) { switch (scanner.type) { case 'spam': logger.log('info', 'Scanning for spam content'); // Implement spam scanning break; case 'virus': logger.log('info', 'Scanning for virus content'); // Implement virus scanning break; case 'attachment': logger.log('info', 'Scanning attachments'); // Check for blocked extensions if (scanner.blockedExtensions && scanner.blockedExtensions.length > 0) { for (const attachment of email.attachments) { const ext = this.getFileExtension(attachment.filename); if (scanner.blockedExtensions.includes(ext)) { if (scanner.action === 'reject') { throw new Error(`Blocked attachment type: ${ext}`); } else { // tag email.addHeader('X-Attachment-Warning', `Potentially unsafe attachment: ${attachment.filename}`); } } } } break; } } } // Apply transformations if defined if (route?.action.options?.transformations && route.action.options.transformations.length > 0) { logger.log('info', 'Applying email transformations'); for (const transform of route.action.options.transformations) { switch (transform.type) { case 'addHeader': if (transform.header && transform.value) { email.addHeader(transform.header, transform.value); } break; } } } logger.log('info', `Email successfully processed in store-and-forward mode`); SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.INFO, type: SecurityEventType.EMAIL_PROCESSING, message: 'Email processed and queued', ipAddress: session.remoteAddress, details: { sessionId: session.id, ruleName: route?.name || 'default', contentScanning: route?.action.options?.contentScanning || false, subject: email.subject }, success: true }); } catch (error) { logger.log('error', `Failed to process email: ${error.message}`); SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.ERROR, type: SecurityEventType.EMAIL_PROCESSING, message: 'Email processing failed', ipAddress: session.remoteAddress, details: { sessionId: session.id, ruleName: session.matchedRoute?.name || 'default', error: error.message }, success: false }); throw error; } } /** * Get file extension from filename */ getFileExtension(filename) { return filename.substring(filename.lastIndexOf('.')).toLowerCase(); } /** * Set up DKIM configuration for all domains */ async setupDkimForDomains() { const domainConfigs = this.domainRegistry.getAllConfigs(); if (domainConfigs.length === 0) { logger.log('warn', 'No domains configured for DKIM'); return; } for (const domainConfig of domainConfigs) { const domain = domainConfig.domain; const selector = domainConfig.dkim?.selector || 'default'; try { // Check if DKIM keys already exist for this domain let keyPair; try { // Try to read existing keys keyPair = await this.dkimCreator.readDKIMKeys(domain); logger.log('info', `Using existing DKIM keys for domain: ${domain}`); } catch (error) { // Generate new keys if they don't exist keyPair = await this.dkimCreator.createDKIMKeys(); // Store them for future use await this.dkimCreator.createAndStoreDKIMKeys(domain); logger.log('info', `Generated new DKIM keys for domain: ${domain}`); } // Store the private key for signing this.dkimKeys.set(domain, keyPair.privateKey); // DNS record creation is now handled by DnsManager logger.log('info', `DKIM keys loaded for domain: ${domain} with selector: ${selector}`); } catch (error) { logger.log('error', `Failed to set up DKIM for domain ${domain}: ${error.message}`); } } } /** * Apply per-domain rate limits from domain configurations */ applyDomainRateLimits() { const domainConfigs = this.domainRegistry.getAllConfigs(); for (const domainConfig of domainConfigs) { if (domainConfig.rateLimits) { const domain = domainConfig.domain; const rateLimitConfig = {}; // Convert domain-specific rate limits to the format expected by UnifiedRateLimiter if (domainConfig.rateLimits.outbound) { if (domainConfig.rateLimits.outbound.messagesPerMinute) { rateLimitConfig.maxMessagesPerMinute = domainConfig.rateLimits.outbound.messagesPerMinute; } // Note: messagesPerHour and messagesPerDay would need additional implementation in rate limiter } if (domainConfig.rateLimits.inbound) { if (domainConfig.rateLimits.inbound.messagesPerMinute) { rateLimitConfig.maxMessagesPerMinute = domainConfig.rateLimits.inbound.messagesPerMinute; } if (domainConfig.rateLimits.inbound.connectionsPerIp) { rateLimitConfig.maxConnectionsPerIP = domainConfig.rateLimits.inbound.connectionsPerIp; } if (domainConfig.rateLimits.inbound.recipientsPerMessage) { rateLimitConfig.maxRecipientsPerMessage = domainConfig.rateLimits.inbound.recipientsPerMessage; } } // Apply the rate limits if we have any if (Object.keys(rateLimitConfig).length > 0) { this.rateLimiter.applyDomainLimits(domain, rateLimitConfig); logger.log('info', `Applied rate limits for domain ${domain}:`, rateLimitConfig); } } } } /** * Check and rotate DKIM keys if needed */ async checkAndRotateDkimKeys() { const domainConfigs = this.domainRegistry.getAllConfigs(); for (const domainConfig of domainConfigs) { const domain = domainConfig.domain; const selector = domainConfig.dkim?.selector || 'default'; const rotateKeys = domainConfig.dkim?.rotateKeys || false; const rotationInterval = domainConfig.dkim?.rotationInterval || 90; const keySize = domainConfig.dkim?.keySize || 2048; if (!rotateKeys) { logger.log('debug', `DKIM key rotation disabled for ${domain}`); continue; } try { // Check if keys need rotation const needsRotation = await this.dkimCreator.needsRotation(domain, selector, rotationInterval); if (needsRotation) { logger.log('info', `DKIM keys need rotation for ${domain} (selector: ${selector})`); // Rotate the keys const newSelector = await this.dkimCreator.rotateDkimKeys(domain, selector, keySize); // Update the domain config with new selector domainConfig.dkim = { ...domainConfig.dkim, selector: newSelector }; // Re-register DNS handler for new selector if internal-dns mode if (domainConfig.dnsMode === 'internal-dns' && this.dcRouter.dnsServer) { // Get new public key const keyPair = await this.dkimCreator.readDKIMKeysForSelector(domain, newSelector); const publicKeyBase64 = keyPair.publicKey .replace(/-----BEGIN PUBLIC KEY-----/g, '') .replace(/-----END PUBLIC KEY-----/g, '') .replace(/\s/g, ''); const ttl = domainConfig.dns?.internal?.ttl || 3600; // Register new selector this.dcRouter.dnsServer.registerHandler(`${newSelector}._domainkey.${domain}`, ['TXT'], () => ({ name: `${newSelector}._domainkey.${domain}`, type: 'TXT', class: 'IN', ttl: ttl, data: `v=DKIM1; k=rsa; p=${publicKeyBase64}` })); logger.log('info', `DKIM DNS handler registered for new selector: ${newSelector}._domainkey.${domain}`); // Store the updated public key in storage await this.dcRouter.storageManager.set(`/email/dkim/${domain}/public.key`, keyPair.publicKey); } // Clean up old keys after grace period (async, don't wait) this.dkimCreator.cleanupOldKeys(domain, 30).catch(error => { logger.log('warn', `Failed to cleanup old DKIM keys for ${domain}: ${error.message}`); }); } else { logger.log('debug', `DKIM keys for ${domain} are up to date`); } } catch (error) { logger.log('error', `Failed to check/rotate DKIM keys for ${domain}: ${error.message}`); } } } /** * Generate SmartProxy routes for email ports */ generateProxyRoutes(portMapping) { const routes = []; const defaultPortMapping = { 25: 10025, 587: 10587, 465: 10465 }; const actualPortMapping = portMapping || defaultPortMapping; // Generate routes for each configured port for (const externalPort of this.options.ports) { const internalPort = actualPortMapping[externalPort] || externalPort + 10000; let routeName = 'email-route'; let tlsMode = 'passthrough'; // Configure based on port switch (externalPort) { case 25: routeName = 'smtp-route'; tlsMode = 'passthrough'; // STARTTLS break; case 587: routeName = 'submission-route'; tlsMode = 'passthrough'; // STARTTLS break; case 465: routeName = 'smtps-route'; tlsMode = 'terminate'; // Implicit TLS break; default: routeName = `email-port-${externalPort}-route`; } routes.push({ name: routeName, match: { ports: [externalPort] }, action: { type: 'forward', target: { host: 'localhost', port: internalPort }, tls: { mode: tlsMode } } }); } return routes; } /** * Update server configuration */ updateOptions(options) { // Stop the server if changing ports const portsChanged = options.ports && (!this.options.ports || JSON.stringify(options.ports) !== JSON.stringify(this.options.ports)); if (portsChanged) { this.stop().then(() => { this.options = { ...this.options, ...options }; this.start(); }); } else { // Update options without restart this.options = { ...this.options, ...options }; // Update domain registry if domains changed if (options.domains) { this.domainRegistry = new DomainRegistry(options.domains, options.defaults || this.options.defaults); } // Update email router if routes changed if (options.routes) { this.emailRouter.updateRoutes(options.routes); } } } /** * Update email routes */ updateEmailRoutes(routes) { this.options.routes = routes; this.emailRouter.updateRoutes(routes); } /** * Get server statistics */ getStats() { return { ...this.stats }; } /** * Get domain registry */ getDomainRegistry() { return this.domainRegistry; } /** * Update email routes dynamically */ updateRoutes(routes) { this.emailRouter.setRoutes(routes); logger.log('info', `Updated email routes with ${routes.length} routes`); } /** * Send an email through the delivery system * @param email The email to send * @param mode The processing mode to use * @param rule Optional rule to apply * @param options Optional sending options * @returns The ID of the queued email */ async sendEmail(email, mode = 'mta', route, options) { logger.log('info', `Sending email: ${email.subject} to ${email.to.join(', ')}`); try { // Validate the email if (!email.from) { throw new Error('Email must have a sender address'); } if (!email.to || email.to.length === 0) { throw new Error('Email must have at least one recipient'); } // Check if any recipients are on the suppression list (unless explicitly skipped) if (!options?.skipSuppressionCheck) { const suppressedRecipients = email.to.filter(recipient => this.isEmailSuppressed(recipient)); if (suppressedRecipients.length > 0) { // Filter out suppressed recipients const originalCount = email.to.length; const suppressed = suppressedRecipients.map(recipient => { const info = this.getSuppressionInfo(recipient); return { email: recipient, reason: info?.reason || 'Unknown', until: info?.expiresAt ? new Date(info.expiresAt).toISOString() : 'permanent' }; }); logger.log('warn', `Filtering out ${suppressedRecipients.length} suppressed recipient(s)`, { suppressed }); // If all recipients are suppressed, throw an error if (suppressedRecipients.length === originalCount) { throw new Error('All recipients are on the suppression list'); } // Filter the recipients list to only include non-suppressed addresses email.to = email.to.filter(recipient => !this.isEmailSuppressed(recipient)); } } // IP warmup handling let ipAddress = options?.ipAddress; // If no specific IP was provided, use IP warmup manager to find the best IP if (!ipAddress) { const domain = email.from.split('@')[1]; ipAddress = this.getBestIPForSending({ from: email.from, to: email.to, domain, isTransactional: options?.isTransactional }); if (ipAddress) { logger.log('info', `Selected IP ${ipAddress} for sending based on warmup status`); } } // If an IP is provided or selected by warmup manager, check its capacity if (ipAddress) { // Check if the IP can send more today if (!this.canIPSendMoreToday(ipAddress)) { logger.log('warn', `IP ${ipAddress} has reached its daily sending limit, email will be queued for later delivery`); } // Check if the IP can send more this hour if (!this.canIPSendMoreThisHour(ipAddress)) { logger.log('warn', `IP ${ipAddress} has reached its hourly sending limit, email will be queued for later delivery`); } // Record the send for IP warmup tracking this.recordIPSend(ipAddress); // Add IP header to the email email.addHeader('X-Sending-IP', ipAddress); } // Check if the sender domain has DKIM keys and sign the email if needed if (mode === 'mta' && route?.action.options?.mtaOptions?.dkimSign) { const domain = email.from.split('@')[1]; await this.handleDkimSigning(email, domain, route.action.options.mtaOptions.dkimOptions?.keySelector || 'mta'); } // Generate a unique ID for this email const id = plugins.uuid.v4(); // Queue the email for delivery await this.deliveryQueue.enqueue(email, mode, route); // Record 'sent' event for domain reputation monitoring const senderDomain = email.from.split('@')[1]; if (senderDomain) { this.recordReputationEvent(senderDomain, { type: 'sent', count: email.to.length }); } logger.log('info', `Email queued with ID: ${id}`); return id; } catch (error) { logger.log('error', `Failed to send email: ${error.message}`); throw error; } } /** * Handle DKIM signing for an email * @param email The email to sign * @param domain The domain to sign with * @param selector The DKIM selector */ async handleDkimSigning(email, domain, selector) { try { // Ensure we have DKIM keys for this domain await this.dkimCreator.handleDKIMKeysForDomain(domain); // Get the private key const { privateKey } = await this.dkimCreator.readDKIMKeys(domain); // Convert Email to raw format for signing const rawEmail = email.toRFC822String(); // Sign the email const signResult = await plugins.dkimSign(rawEmail, { signingDomain: domain, selector: selector, privateKey: privateKey, canonicalization: 'relaxed/relaxed', algorithm: 'rsa-sha256', signTime: new Date(), signatureData: [ { signingDomain: domain, selector: selector, privateKey: privateKey, algorithm: 'rsa-sha256', canonicalization: 'relaxed/relaxed' } ] }); // Add the DKIM-Signature header to the email if (signResult.signatures) { email.addHeader('DKIM-Signature', signResult.signatures); logger.log('info', `Successfully added DKIM signature for ${domain}`); } } catch (error) { logger.log('error', `Failed to sign email with DKIM: ${error.message}`); // Continue without DKIM rather than failing the send } } /** * Process a bounce notification email * @param bounceEmail The email containing bounce notification information * @returns Processed bounce record or null if not a bounce */ async processBounceNotification(bounceEmail) { logger.log('info', 'Processing potential bounce notification email'); try { // Process as a bounce notification (no conversion needed anymore) const bounceRecord = await this.bounceManager.processBounceEmail(bounceEmail); if (bounceRecord) { logger.log('info', `Successfully processed bounce notification for ${bounceRecord.recipient}`, { bounceType: bounceRecord.bounceType, bounceCategory: bounceRecord.bounceCategory }); // Notify any registered listeners about the bounce this.emit('bounceProcessed', bounceRecord); // Record bounce event for domain reputation tracking if (bounceRecord.domain) { this.recordReputationEvent(bounceRecord.domain, { type: 'bounce', hardBounce: bounceRecord.bounceCategory === BounceCategory.HARD, receivingDomain: bounceRecord.recipient.split('@')[1] }); } // Log security event SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.INFO, type: SecurityEventType.EMAIL_VALIDATION, message: `Bounce notification processed for recipient`, domain: bounceRecord.domain, details: { recipient: bounceRecord.recipient, bounceType: bounceRecord.bounceType, bounceCategory: bounceRecord.bounceCategory }, success: true }); return true; } else { logger.log('info', 'Email not recognized as a bounce notification'); return false; } } catch (error) { logger.log('error', `Error processing bounce notification: ${error.message}`); SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.ERROR, type: SecurityEventType.EMAIL_VALIDATION, message: 'Failed to process bounce notification', details: { error: error.message, subject: bounceEmail.subject }, success: false }); return false; } } /** * Process an SMTP failure as a bounce * @param recipient Recipient email that failed * @param smtpResponse SMTP error response * @param options Additional options for bounce processing * @returns Processed bounce record */ async processSmtpFailure(recipient, smtpResponse, options = {}) { logger.log('info', `Processing SMTP failure for ${recipient}: ${smtpResponse}`); try { // Process the SMTP failure through the bounce manager const bounceRecord = await this.bounceManager.processSmtpFailure(recipient, smtpResponse, options); logger.log('info', `Successfully processed SMTP failure for ${recipient} as ${bounceRecord.bounceCategory} bounce`, { bounceType: bounceRecord.bounceType }); // Notify any registered listeners about the bounce this.emit('bounceProcessed', bounceRecord); // Record bounce event for domain reputation tracking if (bounceRecord.domain) { this.recordReputationEvent(bounceRecord.domain, { type: 'bounce', hardBounce: bounceRecord.bounceCategory === BounceCategory.HARD, receivingDomain: bounceRecord.recipient.split('@')[1] }); } // Log security event SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.INFO, type: SecurityEventType.EMAIL_VALIDATION, message: `SMTP failure processed for recipient`, domain: bounceRecord.domain, details: { recipient: bounceRecord.recipient, bounceType: bounceRecord.bounceType, bounceCategory: bounceRecord.bounceCategory, smtpResponse }, success: true }); return true; } catch (error) { logger.log('error', `Error processing SMTP failure: ${error.message}`); SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.ERROR, type: SecurityEventType.EMAIL_VALIDATION, message: 'Failed to process SMTP failure', details: { recipient, smtpResponse, error: error.message }, success: false }); return false; } } /** * Check if an email address is suppressed (has bounced previously) * @param email Email address to check * @returns Whether the email is suppressed */ isEmailSuppressed(email) { return this.bounceManager.isEmailSuppressed(email); } /** * Get suppression information for an email * @param email Email address to check * @returns Suppression information or null if not suppressed */ getSuppressionInfo(email) { return this.bounceManager.getSuppressionInfo(email); } /** * Get bounce history information for an email * @param email Email address to check * @returns Bounce history or null if no bounces */ getBounceHistory(email) { return this.bounceManager.getBounceInfo(email); } /** * Get all suppressed email addresses * @returns Array of suppressed email addresses */ getSuppressionList() { return this.bounceManager.getSuppressionList(); } /** * Get all hard bounced email addresses * @returns Array of hard bounced email addresses */ getHardBouncedAddresses() { return this.bounceManager.getHardBouncedAddresses(); } /** * Add an email to the suppression list * @param email Email address to suppress * @param reason Reason for suppression * @param expiresAt Optional expiration time (undefined for permanent) */ addToSuppressionList(email, reason, expiresAt) { this.bounceManager.addToSuppressionList(email, reason, expiresAt); logger.log('info', `Added ${email} to suppression list: ${reason}`); } /** * Remove an email from the suppression list * @param email Email address to remove from suppression */ removeFromSuppressionList(email) { this.bounceManager.removeFromSuppressionList(email); logger.log('info', `Removed ${email} from suppression list`); } /** * Get the status of IP warmup process * @param ipAddress Optional specific IP to check * @returns Status of IP warmup */ getIPWarmupStatus(ipAddress) { return this.ipWarmupManager.getWarmupStatus(ipAddress); } /** * Add a new IP address to the warmup process * @param ipAddress IP address to add */ addIPToWarmup(ipAddress) { this.ipWarmupManager.addIPToWarmup(ipAddress); } /** * Remove an IP address from the warmup process * @param ipAddress IP address to remove */ removeIPFromWarmup(ipAddress) { this.ipWarmupManager.removeIPFromWarmup(ipAddress); } /** * Update metrics for an IP in the warmup process * @param ipAddress IP address * @param metrics Metrics to update */ updateIPWarmupMetrics(ipAddress, metrics) { this.ipWarmupManager.updateMetrics(ipAddress, metrics); } /** * Check if an IP can send more emails today * @param ipAddress IP address to check * @returns Whether the IP can send more today */ canIPSendMoreToday(ipAddress) { return this.ipWarmupManager.canSendMoreToday(ipAddress); } /** * Check if an IP can send more emails in the current hour * @param ipAddress IP address to check * @returns Whether the IP can send more this hour */ canIPSendMoreThisHour(ipAddress) { return this.ipWarmupManager.canSendMoreThisHour(ipAddress); } /** * Get the best IP to use for sending an email based on warmup status * @param emailInfo Information about the email being sent * @returns Best IP to use or null */ getBestIPForSending(emailInfo) { return this.ipWarmupManager.getBestIPForSending(emailInfo); } /** * Set the active IP allocation policy for warmup * @param policyName Name of the policy to set */ setIPAllocationPolicy(policyName) { this.ipWarmupManager.setActiveAllocationPolicy(policyName); } /** * Record that an email was sent using a specific IP * @param ipAddress IP address used for sending */ recordIPSend(ipAddress) { this.ipWarmupManager.recordSend(ipAddress); } /** * Get reputation data for a domain * @param domain Domain to get reputation for * @returns Domain reputation metrics */ getDomainReputationData(domain) { return this.senderReputationMonitor.getReputationData(domain); } /** * Get summary reputation data for all monitored domains * @returns Summary data for all domains */ getReputationSummary() { return this.senderReputationMonitor.getReputationSummary(); } /** * Add a domain to the reputation monitoring system * @param domain Domain to add */ addDomainToMonitoring(domain) { this.senderReputationMonitor.addDomain(domain); } /** * Remove a domain from the reputation monitoring system * @param domain Domain to remove */ removeDomainFromMonitoring(domain) { this.senderReputationMonitor.removeDomain(domain); } /** * Record an email event for domain reputation tracking * @param domain Domain sending the email * @param event Event details */ recordReputationEvent(domain, event) { this.senderReputationMonitor.recordSendEvent(domain, event); } /** * Check if DKIM key exists for a domain * @param domain Domain to check */ hasDkimKey(domain) { return this.dkimKeys.has(domain); } /** * Record successful email delivery * @param domain Sending domain */ recordDelivery(domain) { this.recordReputationEvent(domain, { type: 'delivered', count: 1 }); } /** * Record email bounce * @param domain Sending domain * @param receivingDomain Receiving domain that bounced * @param bounceType Type of bounce (hard/soft) * @param reason Bounce reason */ recordBounce(domain, receivingDomain, bounceType, reason) { // Record bounce in bounce manager const bounceRecord = { id: `bounce_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`, recipient: `user@${receivingDomain}`, sender: `user@${domain}`, domain: domain, bounceType: bounceType === 'hard' ? BounceType.INVALID_RECIPIENT : BounceType.TEMPORARY_FAILURE, bounceCategory: bounceType === 'hard' ? BounceCategory.HARD : BounceCategory.SOFT, timestamp: Date.now(), smtpResponse: reason, diagnosticCode: reason, statusCode: bounceType === 'hard' ? '550' : '450', processed: false }; // Process the bounce this.bounceManager.processBounce(bounceRecord); // Record reputation event this.recordReputationEvent(domain, { type: 'bounce', count: 1, hardBounce: bounceType === 'hard', receivingDomain }); } /** * Get the rate limiter instance * @returns The unified rate limiter */ getRateLimiter() { return this.rateLimiter; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy51bmlmaWVkLmVtYWlsLnNlcnZlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL21haWwvcm91dGluZy9jbGFzc2VzLnVuaWZpZWQuZW1haWwuc2VydmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxLQUFLLEtBQUssTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sUUFBUSxDQUFDO0FBQ3RDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUN6QyxPQUFPLEVBQ0wsY0FBYyxFQUNkLGdCQUFnQixFQUNoQixpQkFBaUIsRUFDbEIsTUFBTSx5QkFBeUIsQ0FBQztBQUNqQyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sb0NBQW9DLENBQUM7QUFDakUsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sK0NBQStDLENBQUM7QUFDcEYsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sOENBQThDLENBQUM7QUE4QmxGLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUV4RCxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDakQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBQzlELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUN0RCxPQUFPLEVBQUUsYUFBYSxFQUFFLFVBQVUsRUFBRSxjQUFjLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQUM3RixPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUNuRSxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQztBQUVqRixPQUFPLEVBQUUsdUJBQXVCLEVBQWtDLE1BQU0sd0NBQXdDLENBQUM7QUFD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