246 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
		
		
			
		
	
	
			246 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * SMTP Logging Utilities
							 | 
						||
| 
								 | 
							
								 * Provides structured logging for SMTP server components
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import * as plugins from '../../../../plugins.ts';
							 | 
						||
| 
								 | 
							
								import { logger } from '../../../../logger.ts';
							 | 
						||
| 
								 | 
							
								import { SecurityLogLevel, SecurityEventType } from '../constants.ts';
							 | 
						||
| 
								 | 
							
								import type { ISmtpSession } from '../interfaces.ts';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * SMTP connection metadata to include in logs
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								export interface IConnectionMetadata {
							 | 
						||
| 
								 | 
							
								  remoteAddress?: string;
							 | 
						||
| 
								 | 
							
								  remotePort?: number;
							 | 
						||
| 
								 | 
							
								  socketId?: string;
							 | 
						||
| 
								 | 
							
								  secure?: boolean;
							 | 
						||
| 
								 | 
							
								  sessionId?: string;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Log levels for SMTP server
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Options for SMTP log
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								export interface ISmtpLogOptions {
							 | 
						||
| 
								 | 
							
								  level?: LogLevel;
							 | 
						||
| 
								 | 
							
								  sessionId?: string;
							 | 
						||
| 
								 | 
							
								  sessionState?: string;
							 | 
						||
| 
								 | 
							
								  remoteAddress?: string;
							 | 
						||
| 
								 | 
							
								  remotePort?: number;
							 | 
						||
| 
								 | 
							
								  command?: string;
							 | 
						||
| 
								 | 
							
								  error?: Error;
							 | 
						||
| 
								 | 
							
								  [key: string]: any;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * SMTP logger - provides structured logging for SMTP server
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								export class SmtpLogger {
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Log a message with context
							 | 
						||
| 
								 | 
							
								   * @param level - Log level
							 | 
						||
| 
								 | 
							
								   * @param message - Log message
							 | 
						||
| 
								 | 
							
								   * @param options - Additional log options
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  public static log(level: LogLevel, message: string, options: ISmtpLogOptions = {}): void {
							 | 
						||
| 
								 | 
							
								    // Extract error information if provided
							 | 
						||
| 
								 | 
							
								    const errorInfo = options.error ? {
							 | 
						||
| 
								 | 
							
								      errorMessage: options.error.message,
							 | 
						||
| 
								 | 
							
								      errorStack: options.error.stack,
							 | 
						||
| 
								 | 
							
								      errorName: options.error.name
							 | 
						||
| 
								 | 
							
								    } : {};
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    // Structure log data
							 | 
						||
| 
								 | 
							
								    const logData = {
							 | 
						||
| 
								 | 
							
								      component: 'smtp-server',
							 | 
						||
| 
								 | 
							
								      ...options,
							 | 
						||
| 
								 | 
							
								      ...errorInfo
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    // Remove error from log data to avoid duplication
							 | 
						||
| 
								 | 
							
								    if (logData.error) {
							 | 
						||
| 
								 | 
							
								      delete logData.error;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    // Log through the main logger
							 | 
						||
| 
								 | 
							
								    logger.log(level, message, logData);
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    // Also console log for immediate visibility during development
							 | 
						||
| 
								 | 
							
								    if (level === 'error' || level === 'warn') {
							 | 
						||
| 
								 | 
							
								      console[level](`[SMTP] ${message}`, logData);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Log debug level message
							 | 
						||
| 
								 | 
							
								   * @param message - Log message
							 | 
						||
| 
								 | 
							
								   * @param options - Additional log options
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  public static debug(message: string, options: ISmtpLogOptions = {}): void {
							 | 
						||
| 
								 | 
							
								    this.log('debug', message, options);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Log info level message
							 | 
						||
| 
								 | 
							
								   * @param message - Log message
							 | 
						||
| 
								 | 
							
								   * @param options - Additional log options
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  public static info(message: string, options: ISmtpLogOptions = {}): void {
							 | 
						||
| 
								 | 
							
								    this.log('info', message, options);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Log warning level message
							 | 
						||
| 
								 | 
							
								   * @param message - Log message
							 | 
						||
| 
								 | 
							
								   * @param options - Additional log options
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  public static warn(message: string, options: ISmtpLogOptions = {}): void {
							 | 
						||
| 
								 | 
							
								    this.log('warn', message, options);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Log error level message
							 | 
						||
| 
								 | 
							
								   * @param message - Log message
							 | 
						||
| 
								 | 
							
								   * @param options - Additional log options
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  public static error(message: string, options: ISmtpLogOptions = {}): void {
							 | 
						||
| 
								 | 
							
								    this.log('error', message, options);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Log command received from client
							 | 
						||
| 
								 | 
							
								   * @param command - The command string
							 | 
						||
| 
								 | 
							
								   * @param socket - The client socket
							 | 
						||
| 
								 | 
							
								   * @param session - The SMTP session
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  public static logCommand(command: string, socket: plugins.net.Socket | plugins.tls.TLSSocket, session?: ISmtpSession): void {
							 | 
						||
| 
								 | 
							
								    const clientInfo = {
							 | 
						||
| 
								 | 
							
								      remoteAddress: socket.remoteAddress,
							 | 
						||
| 
								 | 
							
								      remotePort: socket.remotePort,
							 | 
						||
| 
								 | 
							
								      secure: socket instanceof plugins.tls.TLSSocket,
							 | 
						||
| 
								 | 
							
								      sessionId: session?.id,
							 | 
						||
| 
								 | 
							
								      sessionState: session?.state
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    this.info(`Command received: ${command}`, {
							 | 
						||
| 
								 | 
							
								      ...clientInfo,
							 | 
						||
| 
								 | 
							
								      command: command.split(' ')[0]?.toUpperCase()
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    // Also log to console for easy debugging
							 | 
						||
| 
								 | 
							
								    console.log(`← ${command}`);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Log response sent to client
							 | 
						||
| 
								 | 
							
								   * @param response - The response string
							 | 
						||
| 
								 | 
							
								   * @param socket - The client socket
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  public static logResponse(response: string, socket: plugins.net.Socket | plugins.tls.TLSSocket): void {
							 | 
						||
| 
								 | 
							
								    const clientInfo = {
							 | 
						||
| 
								 | 
							
								      remoteAddress: socket.remoteAddress,
							 | 
						||
| 
								 | 
							
								      remotePort: socket.remotePort,
							 | 
						||
| 
								 | 
							
								      secure: socket instanceof plugins.tls.TLSSocket
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    // Get the response code from the beginning of the response
							 | 
						||
| 
								 | 
							
								    const responseCode = response.substring(0, 3);
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    // Log different levels based on response code
							 | 
						||
| 
								 | 
							
								    if (responseCode.startsWith('2') || responseCode.startsWith('3')) {
							 | 
						||
| 
								 | 
							
								      this.debug(`Response sent: ${response}`, clientInfo);
							 | 
						||
| 
								 | 
							
								    } else if (responseCode.startsWith('4')) {
							 | 
						||
| 
								 | 
							
								      this.warn(`Temporary error response: ${response}`, clientInfo);
							 | 
						||
| 
								 | 
							
								    } else if (responseCode.startsWith('5')) {
							 | 
						||
| 
								 | 
							
								      this.error(`Permanent error response: ${response}`, clientInfo);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    // Also log to console for easy debugging
							 | 
						||
| 
								 | 
							
								    console.log(`→ ${response}`);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Log client connection event
							 | 
						||
| 
								 | 
							
								   * @param socket - The client socket
							 | 
						||
| 
								 | 
							
								   * @param eventType - Type of connection event (connect, close, error)
							 | 
						||
| 
								 | 
							
								   * @param session - The SMTP session
							 | 
						||
| 
								 | 
							
								   * @param error - Optional error object for error events
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  public static logConnection(
							 | 
						||
| 
								 | 
							
								    socket: plugins.net.Socket | plugins.tls.TLSSocket,
							 | 
						||
| 
								 | 
							
								    eventType: 'connect' | 'close' | 'error',
							 | 
						||
| 
								 | 
							
								    session?: ISmtpSession,
							 | 
						||
| 
								 | 
							
								    error?: Error
							 | 
						||
| 
								 | 
							
								  ): void {
							 | 
						||
| 
								 | 
							
								    const clientInfo = {
							 | 
						||
| 
								 | 
							
								      remoteAddress: socket.remoteAddress,
							 | 
						||
| 
								 | 
							
								      remotePort: socket.remotePort,
							 | 
						||
| 
								 | 
							
								      secure: socket instanceof plugins.tls.TLSSocket,
							 | 
						||
| 
								 | 
							
								      sessionId: session?.id,
							 | 
						||
| 
								 | 
							
								      sessionState: session?.state
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    switch (eventType) {
							 | 
						||
| 
								 | 
							
								      case 'connect':
							 | 
						||
| 
								 | 
							
								        this.info(`New ${clientInfo.secure ? 'secure ' : ''}connection from ${clientInfo.remoteAddress}:${clientInfo.remotePort}`, clientInfo);
							 | 
						||
| 
								 | 
							
								        break;
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								      case 'close':
							 | 
						||
| 
								 | 
							
								        this.info(`Connection closed from ${clientInfo.remoteAddress}:${clientInfo.remotePort}`, clientInfo);
							 | 
						||
| 
								 | 
							
								        break;
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								      case 'error':
							 | 
						||
| 
								 | 
							
								        this.error(`Connection error from ${clientInfo.remoteAddress}:${clientInfo.remotePort}`, {
							 | 
						||
| 
								 | 
							
								          ...clientInfo,
							 | 
						||
| 
								 | 
							
								          error
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								        break;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Log security event
							 | 
						||
| 
								 | 
							
								   * @param level - Security log level
							 | 
						||
| 
								 | 
							
								   * @param type - Security event type
							 | 
						||
| 
								 | 
							
								   * @param message - Log message
							 | 
						||
| 
								 | 
							
								   * @param details - Event details
							 | 
						||
| 
								 | 
							
								   * @param ipAddress - Client IP address
							 | 
						||
| 
								 | 
							
								   * @param domain - Optional domain involved
							 | 
						||
| 
								 | 
							
								   * @param success - Whether the security check was successful
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  public static logSecurityEvent(
							 | 
						||
| 
								 | 
							
								    level: SecurityLogLevel,
							 | 
						||
| 
								 | 
							
								    type: SecurityEventType,
							 | 
						||
| 
								 | 
							
								    message: string,
							 | 
						||
| 
								 | 
							
								    details: Record<string, any>,
							 | 
						||
| 
								 | 
							
								    ipAddress?: string,
							 | 
						||
| 
								 | 
							
								    domain?: string,
							 | 
						||
| 
								 | 
							
								    success?: boolean
							 | 
						||
| 
								 | 
							
								  ): void {
							 | 
						||
| 
								 | 
							
								    // Map security log level to system log level
							 | 
						||
| 
								 | 
							
								    const logLevel: LogLevel = level === SecurityLogLevel.DEBUG ? 'debug' :
							 | 
						||
| 
								 | 
							
								                               level === SecurityLogLevel.INFO ? 'info' :
							 | 
						||
| 
								 | 
							
								                               level === SecurityLogLevel.WARN ? 'warn' : 'error';
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    // Log the security event
							 | 
						||
| 
								 | 
							
								    this.log(logLevel, message, {
							 | 
						||
| 
								 | 
							
								      component: 'smtp-security',
							 | 
						||
| 
								 | 
							
								      eventType: type,
							 | 
						||
| 
								 | 
							
								      success,
							 | 
						||
| 
								 | 
							
								      ipAddress,
							 | 
						||
| 
								 | 
							
								      domain,
							 | 
						||
| 
								 | 
							
								      ...details
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Default instance for backward compatibility
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								export const smtpLogger = SmtpLogger;
							 |