feat: Implement Deno-native STARTTLS handler and connection wrapper

- Refactored STARTTLS implementation to use Deno's native TLS via Deno.startTls().
- Introduced ConnectionWrapper to provide a Node.js net.Socket-compatible interface for Deno.Conn and Deno.TlsConn.
- Updated TlsHandler to utilize the new STARTTLS implementation.
- Added comprehensive SMTP authentication tests for PLAIN and LOGIN mechanisms.
- Implemented rate limiting tests for SMTP server connections and commands.
- Enhanced error handling and logging throughout the STARTTLS and connection upgrade processes.
This commit is contained in:
2025-10-28 18:51:33 +00:00
parent 9cd15342e0
commit 6523c55516
14 changed files with 1328 additions and 429 deletions

View File

@@ -168,10 +168,11 @@ Deno.test({
const response = await readSmtpResponse(conn);
// Some servers accept it (221), others reject it (501)
assertMatch(response, /^(221|501)/, 'Should either accept or reject QUIT with extra params');
// RFC 5321 Section 4.1.1.10: QUIT syntax is "QUIT <CRLF>" (no parameters)
// Should return 501 (syntax error in parameters)
assertMatch(response, /^501/, 'Should reject QUIT with extra params with 501');
console.log(`✓ QUIT with extra parameters handled: ${response.substring(0, 3)}`);
console.log('✓ QUIT with extra parameters correctly rejected with 501');
} finally {
try {
conn.close();
@@ -199,11 +200,11 @@ Deno.test({
const response = await readSmtpResponse(conn);
// Should return 501 (syntax error) or 553 (bad address)
assertMatch(response, /^(501|553)/, 'Should reject malformed email with 501 or 553');
// RFC 5321: "<not an email>" is a syntax/format error, should return 501
assertMatch(response, /^501/, 'Should reject malformed email with 501');
await sendSmtpCommand(conn, 'QUIT', '221');
console.log('✓ Malformed email address rejected');
console.log('✓ Malformed email address correctly rejected with 501');
} finally {
try {
conn.close();
@@ -255,18 +256,19 @@ Deno.test({
try {
await waitForGreeting(conn);
// Send EHLO with excessively long hostname
// Send EHLO with excessively long hostname (>512 octets)
const longString = 'A'.repeat(1000);
const encoder = new TextEncoder();
await conn.write(encoder.encode(`EHLO ${longString}\r\n`));
const response = await readSmtpResponse(conn);
// Some servers accept long hostnames (250), others reject (500/501)
assertMatch(response, /^(250|500|501)/, 'Should handle long commands (accept or reject)');
// RFC 5321 Section 4.5.3.1.4: Max command line is 512 octets
// Should reject with 500 (syntax error) or 501 (parameter error)
assertMatch(response, /^(500|501)/, 'Should reject command >512 octets with 500 or 501');
await sendSmtpCommand(conn, 'QUIT', '221');
console.log(`✓ Excessively long command handled: ${response.substring(0, 3)}`);
console.log('✓ Excessively long command correctly rejected');
} finally {
try {
conn.close();