This commit is contained in:
2025-05-21 14:28:33 +00:00
parent 38811dbf23
commit 10ab09894b
8 changed files with 652 additions and 162 deletions

View File

@ -171,7 +171,11 @@ export class CommandHandler implements ICommandHandler {
if (this.tlsHandler && this.tlsHandler.isTlsEnabled()) {
this.tlsHandler.handleStartTls(socket);
} else {
this.sendResponse(socket, `${SmtpResponseCode.COMMAND_NOT_IMPLEMENTED} STARTTLS not available`);
SmtpLogger.warn('STARTTLS requested but TLS is not enabled', {
remoteAddress: socket.remoteAddress,
remotePort: socket.remotePort
});
this.sendResponse(socket, `${SmtpResponseCode.TLS_UNAVAILABLE_TEMP} STARTTLS not available at this time`);
}
break;
@ -224,8 +228,21 @@ export class CommandHandler implements ICommandHandler {
return;
}
// Extract command and arguments from clientHostname
// EHLO/HELO might come with the command itself in the arguments string
let hostname = clientHostname;
if (hostname.toUpperCase().startsWith('EHLO ') || hostname.toUpperCase().startsWith('HELO ')) {
hostname = hostname.substring(5).trim();
}
// Check for empty hostname
if (!hostname) {
this.sendResponse(socket, `${SmtpResponseCode.SYNTAX_ERROR_PARAMETERS} Missing domain name`);
return;
}
// Validate EHLO hostname
const validation = validateEhlo(clientHostname);
const validation = validateEhlo(hostname);
if (!validation.isValid) {
this.sendResponse(socket, `${SmtpResponseCode.SYNTAX_ERROR_PARAMETERS} ${validation.errorMessage}`);
@ -233,7 +250,7 @@ export class CommandHandler implements ICommandHandler {
}
// Update session state and client hostname
session.clientHostname = validation.hostname || clientHostname;
session.clientHostname = validation.hostname || hostname;
this.sessionManager.updateSessionState(session, SmtpState.AFTER_EHLO);
// Set up EHLO response lines
@ -272,14 +289,32 @@ export class CommandHandler implements ICommandHandler {
return;
}
// Check if the client has sent EHLO/HELO first
if (session.state === SmtpState.GREETING) {
this.sendResponse(socket, `${SmtpResponseCode.BAD_SEQUENCE} Bad sequence of commands`);
return;
}
// Check if authentication is required but not provided
if (this.options.auth && this.options.auth.required && !session.authenticated) {
this.sendResponse(socket, `${SmtpResponseCode.AUTH_REQUIRED} Authentication required`);
return;
}
// Special handling for commands that include "MAIL FROM:" in the args
let processedArgs = args;
if (args.toUpperCase().startsWith('FROM')) {
processedArgs = args;
} else if (args.toUpperCase().includes('MAIL FROM')) {
// The command was already prepended to the args
const colonIndex = args.indexOf(':');
if (colonIndex !== -1) {
processedArgs = args.substring(colonIndex + 1).trim();
}
}
// Validate MAIL FROM syntax
const validation = validateMailFrom(args);
const validation = validateMailFrom(processedArgs);
if (!validation.isValid) {
this.sendResponse(socket, `${SmtpResponseCode.SYNTAX_ERROR_PARAMETERS} ${validation.errorMessage}`);
@ -336,8 +371,26 @@ export class CommandHandler implements ICommandHandler {
return;
}
// Check if MAIL FROM was provided first
if (session.state !== SmtpState.MAIL_FROM && session.state !== SmtpState.RCPT_TO) {
this.sendResponse(socket, `${SmtpResponseCode.BAD_SEQUENCE} Bad sequence of commands`);
return;
}
// Special handling for commands that include "RCPT TO:" in the args
let processedArgs = args;
if (args.toUpperCase().startsWith('TO:')) {
processedArgs = args;
} else if (args.toUpperCase().includes('RCPT TO')) {
// The command was already prepended to the args
const colonIndex = args.indexOf(':');
if (colonIndex !== -1) {
processedArgs = args.substring(colonIndex + 1).trim();
}
}
// Validate RCPT TO syntax
const validation = validateRcptTo(args);
const validation = validateRcptTo(processedArgs);
if (!validation.isValid) {
this.sendResponse(socket, `${SmtpResponseCode.SYNTAX_ERROR_PARAMETERS} ${validation.errorMessage}`);
@ -379,6 +432,12 @@ export class CommandHandler implements ICommandHandler {
return;
}
// Check command sequence - DATA must follow RCPT TO
if (session.state !== SmtpState.RCPT_TO) {
this.sendResponse(socket, `${SmtpResponseCode.BAD_SEQUENCE} Bad sequence of commands`);
return;
}
// Check if we have recipients
if (!session.rcptTo.length) {
this.sendResponse(socket, `${SmtpResponseCode.BAD_SEQUENCE} No recipients specified`);