import { tap, expect } from '@git.zone/tstest/tapbundle'; import * as plugins from '../plugins.js'; import * as net from 'net'; import { startTestServer, stopTestServer, TEST_PORT, sendEmailWithRawSocket } from '../server.loader.js'; import type { SmtpServer } from '../../../ts/mail/delivery/smtpserver/index.js'; let testServer: SmtpServer; tap.test('setup - start test server', async () => { testServer = await startTestServer(); await plugins.smartdelay.delayFor(1000); }); tap.test('Extremely Long Headers - should handle single extremely long header', async (tools) => { const done = tools.defer(); const socket = net.createConnection({ host: 'localhost', port: TEST_PORT, timeout: 30000 }); let dataBuffer = ''; socket.on('data', (data) => { dataBuffer += data.toString(); console.log('Server response:', data.toString()); if (dataBuffer.includes('220 ')) { // Send EHLO socket.write('EHLO testclient\r\n'); } else if (dataBuffer.includes('250 ') && dataBuffer.includes('EHLO')) { // Send MAIL FROM socket.write('MAIL FROM:\r\n'); dataBuffer = ''; } else if (dataBuffer.includes('250 ') && dataBuffer.includes('sender accepted')) { // Send RCPT TO socket.write('RCPT TO:\r\n'); dataBuffer = ''; } else if (dataBuffer.includes('250 ') && dataBuffer.includes('recipient accepted')) { // Send DATA socket.write('DATA\r\n'); dataBuffer = ''; } else if (dataBuffer.includes('354 ')) { // Send email with extremely long header const longValue = 'X'.repeat(3000); const emailContent = [ `Subject: Test Email`, `From: sender@example.com`, `To: recipient@example.com`, `X-Long-Header: ${longValue}`, '', 'This email has an extremely long header.', '.', '' ].join('\r\n'); socket.write(emailContent); dataBuffer = ''; } else if (dataBuffer.includes('250 ') && dataBuffer.includes('Message accepted') || dataBuffer.includes('552 ') || dataBuffer.includes('554 ') || dataBuffer.includes('500 ')) { // Either accepted or gracefully rejected const accepted = dataBuffer.includes('250 '); console.log(`Long header test ${accepted ? 'accepted' : 'rejected'}`); socket.write('QUIT\r\n'); socket.end(); done.resolve(); } }); socket.on('error', (err) => { console.error('Socket error:', err); done.reject(err); }); socket.on('timeout', () => { console.error('Socket timeout'); socket.destroy(); done.reject(new Error('Socket timeout')); }); await done.promise; }); tap.test('Extremely Long Headers - should handle multi-line header with many segments', async (tools) => { const done = tools.defer(); const socket = net.createConnection({ host: 'localhost', port: TEST_PORT, timeout: 30000 }); let dataBuffer = ''; socket.on('data', (data) => { dataBuffer += data.toString(); console.log('Server response:', data.toString()); if (dataBuffer.includes('220 ')) { socket.write('EHLO testclient\r\n'); } else if (dataBuffer.includes('250 ') && dataBuffer.includes('EHLO')) { socket.write('MAIL FROM:\r\n'); dataBuffer = ''; } else if (dataBuffer.includes('250 ') && dataBuffer.includes('sender accepted')) { socket.write('RCPT TO:\r\n'); dataBuffer = ''; } else if (dataBuffer.includes('250 ') && dataBuffer.includes('recipient accepted')) { socket.write('DATA\r\n'); dataBuffer = ''; } else if (dataBuffer.includes('354 ')) { // Create multi-line header with 50 segments const segments = []; for (let i = 0; i < 50; i++) { segments.push(` Segment ${i}: ${' '.repeat(60)}value`); } const emailContent = [ `Subject: Test Email`, `From: sender@example.com`, `To: recipient@example.com`, `X-Multi-Line: Initial value`, ...segments, '', 'This email has a multi-line header with many segments.', '.', '' ].join('\r\n'); socket.write(emailContent); dataBuffer = ''; } else if (dataBuffer.includes('250 ') && dataBuffer.includes('Message accepted') || dataBuffer.includes('552 ') || dataBuffer.includes('554 ') || dataBuffer.includes('500 ')) { const accepted = dataBuffer.includes('250 '); console.log(`Multi-line header test ${accepted ? 'accepted' : 'rejected'}`); socket.write('QUIT\r\n'); socket.end(); done.resolve(); } }); socket.on('error', (err) => { console.error('Socket error:', err); done.reject(err); }); socket.on('timeout', () => { console.error('Socket timeout'); socket.destroy(); done.reject(new Error('Socket timeout')); }); await done.promise; }); tap.test('Extremely Long Headers - should handle multiple long headers in one email', async (tools) => { const done = tools.defer(); const socket = net.createConnection({ host: 'localhost', port: TEST_PORT, timeout: 30000 }); let dataBuffer = ''; socket.on('data', (data) => { dataBuffer += data.toString(); console.log('Server response:', data.toString()); if (dataBuffer.includes('220 ')) { socket.write('EHLO testclient\r\n'); } else if (dataBuffer.includes('250 ') && dataBuffer.includes('EHLO')) { socket.write('MAIL FROM:\r\n'); dataBuffer = ''; } else if (dataBuffer.includes('250 ') && dataBuffer.includes('sender accepted')) { socket.write('RCPT TO:\r\n'); dataBuffer = ''; } else if (dataBuffer.includes('250 ') && dataBuffer.includes('recipient accepted')) { socket.write('DATA\r\n'); dataBuffer = ''; } else if (dataBuffer.includes('354 ')) { // Create multiple long headers const header1 = 'A'.repeat(1000); const header2 = 'B'.repeat(1500); const header3 = 'C'.repeat(2000); const emailContent = [ `Subject: Test Email with Multiple Long Headers`, `From: sender@example.com`, `To: recipient@example.com`, `X-Long-Header-1: ${header1}`, `X-Long-Header-2: ${header2}`, `X-Long-Header-3: ${header3}`, '', 'This email has multiple long headers.', '.', '' ].join('\r\n'); const totalHeaderSize = header1.length + header2.length + header3.length; console.log(`Total header size: ${totalHeaderSize} bytes`); socket.write(emailContent); dataBuffer = ''; } else if (dataBuffer.includes('250 ') && dataBuffer.includes('Message accepted') || dataBuffer.includes('552 ') || dataBuffer.includes('554 ') || dataBuffer.includes('500 ')) { const accepted = dataBuffer.includes('250 '); console.log(`Multiple long headers test ${accepted ? 'accepted' : 'rejected'}`); socket.write('QUIT\r\n'); socket.end(); done.resolve(); } }); socket.on('error', (err) => { console.error('Socket error:', err); done.reject(err); }); socket.on('timeout', () => { console.error('Socket timeout'); socket.destroy(); done.reject(new Error('Socket timeout')); }); await done.promise; }); tap.test('Extremely Long Headers - should handle header with exactly RFC limit', async (tools) => { const done = tools.defer(); const socket = net.createConnection({ host: 'localhost', port: TEST_PORT, timeout: 30000 }); let dataBuffer = ''; socket.on('data', (data) => { dataBuffer += data.toString(); console.log('Server response:', data.toString()); if (dataBuffer.includes('220 ')) { socket.write('EHLO testclient\r\n'); } else if (dataBuffer.includes('250 ') && dataBuffer.includes('EHLO')) { socket.write('MAIL FROM:\r\n'); dataBuffer = ''; } else if (dataBuffer.includes('250 ') && dataBuffer.includes('sender accepted')) { socket.write('RCPT TO:\r\n'); dataBuffer = ''; } else if (dataBuffer.includes('250 ') && dataBuffer.includes('recipient accepted')) { socket.write('DATA\r\n'); dataBuffer = ''; } else if (dataBuffer.includes('354 ')) { // Create header line exactly at RFC 5322 limit (998 chars excluding CRLF) // Header name and colon take some space const headerName = 'X-RFC-Limit'; const colonSpace = ': '; const remainingSpace = 998 - headerName.length - colonSpace.length; const headerValue = 'X'.repeat(remainingSpace); const emailContent = [ `Subject: Test Email`, `From: sender@example.com`, `To: recipient@example.com`, `${headerName}${colonSpace}${headerValue}`, '', 'This email has a header at exactly the RFC limit.', '.', '' ].join('\r\n'); socket.write(emailContent); dataBuffer = ''; } else if (dataBuffer.includes('250 ') && dataBuffer.includes('Message accepted') || dataBuffer.includes('552 ') || dataBuffer.includes('554 ') || dataBuffer.includes('500 ')) { const accepted = dataBuffer.includes('250 '); console.log(`RFC limit header test ${accepted ? 'accepted' : 'rejected'}`); socket.write('QUIT\r\n'); socket.end(); done.resolve(); } }); socket.on('error', (err) => { console.error('Socket error:', err); done.reject(err); }); socket.on('timeout', () => { console.error('Socket timeout'); socket.destroy(); done.reject(new Error('Socket timeout')); }); await done.promise; }); tap.test('cleanup - stop test server', async () => { await stopTestServer(testServer); }); tap.start();