import { tap, expect } from '@git.zone/tstest/tapbundle'; import * as plugins from '../../../ts/plugins.js'; import * as net from 'net'; import { startTestServer, stopTestServer } from '../../helpers/server.loader.js' import type { ITestServer } from '../../helpers/server.loader.js'; const TEST_PORT = 2525; let testServer: ITestServer; // Helper to wait for SMTP response const waitForResponse = (socket: net.Socket, expectedCode?: string, timeout = 5000): Promise => { return new Promise((resolve, reject) => { let buffer = ''; const timer = setTimeout(() => { socket.removeListener('data', handler); reject(new Error(`Timeout waiting for ${expectedCode || 'any'} response`)); }, timeout); const handler = (data: Buffer) => { buffer += data.toString(); const lines = buffer.split('\r\n'); for (const line of lines) { if (expectedCode) { if (line.startsWith(expectedCode + ' ')) { clearTimeout(timer); socket.removeListener('data', handler); resolve(buffer); return; } } else { // Look for any complete response if (line.match(/^\d{3} /)) { clearTimeout(timer); socket.removeListener('data', handler); resolve(buffer); return; } } } }; socket.on('data', handler); }); }; tap.test('setup - start test server', async (toolsArg) => { testServer = await startTestServer({ port: TEST_PORT }); await toolsArg.delayFor(1000); }); tap.test('Bounce Management - Invalid recipient domain', async (tools) => { const socket = net.createConnection({ host: 'localhost', port: TEST_PORT, timeout: 30000 }); try { // Wait for greeting await waitForResponse(socket, '220'); // Send EHLO socket.write('EHLO testclient\r\n'); await waitForResponse(socket, '250'); // Send MAIL FROM socket.write('MAIL FROM:\r\n'); await waitForResponse(socket, '250'); // Send to non-existent domain socket.write('RCPT TO:\r\n'); const rcptResponse = await waitForResponse(socket); if (rcptResponse.startsWith('550') || rcptResponse.startsWith('551') || rcptResponse.startsWith('553')) { console.log('Bounce management active - invalid recipient properly rejected'); expect(true).toEqual(true); } else if (rcptResponse.startsWith('250')) { // Server accepted, may generate bounce later console.log('Invalid recipient accepted - bounce may be generated later'); // Send DATA socket.write('DATA\r\n'); await waitForResponse(socket, '354'); const email = [ `From: sender@example.com`, `To: nonexistent@invalid-domain-that-does-not-exist.com`, `Subject: Bounce Management Test`, `Return-Path: `, `Date: ${new Date().toUTCString()}`, `Message-ID: `, '', 'This email is designed to test bounce management functionality.', '.', '' ].join('\r\n'); socket.write(email); const dataResponse = await waitForResponse(socket, '250'); console.log('Email accepted for processing - bounce will be generated'); expect(dataResponse.startsWith('250')).toEqual(true); } socket.write('QUIT\r\n'); await waitForResponse(socket, '221').catch(() => {}); } finally { socket.destroy(); } }); tap.test('Bounce Management - Empty return path (null sender)', async (tools) => { const socket = net.createConnection({ host: 'localhost', port: TEST_PORT, timeout: 30000 }); try { // Wait for greeting await waitForResponse(socket, '220'); // Send EHLO socket.write('EHLO testclient\r\n'); await waitForResponse(socket, '250'); // Empty return path (null sender) - used for bounce messages socket.write('MAIL FROM:<>\r\n'); const mailResponse = await waitForResponse(socket); if (mailResponse.startsWith('250')) { console.log('Null sender accepted (for bounce messages)'); // Send RCPT TO socket.write('RCPT TO:\r\n'); await waitForResponse(socket, '250'); // Send DATA socket.write('DATA\r\n'); const dataCommandResponse = await waitForResponse(socket); if (dataCommandResponse.startsWith('354')) { // Bounce message format const email = [ `From: MAILER-DAEMON@example.com`, `To: recipient@example.com`, `Subject: Mail delivery failed: returning message to sender`, `Date: ${new Date().toUTCString()}`, `Message-ID: `, `Auto-Submitted: auto-replied`, '', 'This message was created automatically by mail delivery software.', '', 'A message that you sent could not be delivered to one or more recipients.', '.', '' ].join('\r\n'); socket.write(email); const dataResponse = await waitForResponse(socket, '250'); console.log('Bounce message with null sender accepted'); expect(dataResponse.startsWith('250')).toEqual(true); } else if (dataCommandResponse.startsWith('503')) { // Server rejects DATA for null sender console.log('Server rejects DATA command for null sender (strict policy)'); expect(dataCommandResponse.startsWith('503')).toEqual(true); } } else { console.log('Null sender rejected'); expect(true).toEqual(true); } socket.write('QUIT\r\n'); await waitForResponse(socket, '221').catch(() => {}); } finally { socket.destroy(); } }); tap.test('Bounce Management - DSN headers', async (tools) => { const socket = net.createConnection({ host: 'localhost', port: TEST_PORT, timeout: 30000 }); try { // Wait for greeting await waitForResponse(socket, '220'); // Send EHLO socket.write('EHLO testclient\r\n'); await waitForResponse(socket, '250'); // Send MAIL FROM socket.write('MAIL FROM:\r\n'); await waitForResponse(socket, '250'); // Send RCPT TO socket.write('RCPT TO:\r\n'); await waitForResponse(socket, '250'); // Send DATA socket.write('DATA\r\n'); await waitForResponse(socket, '354'); // Email with DSN request headers const email = [ `From: sender@example.com`, `To: recipient@example.com`, `Subject: DSN Test`, `Return-Path: `, `Disposition-Notification-To: sender@example.com`, `Return-Receipt-To: sender@example.com`, `Date: ${new Date().toUTCString()}`, `Message-ID: `, '', 'This email requests delivery status notifications.', '.', '' ].join('\r\n'); socket.write(email); const dataResponse = await waitForResponse(socket, '250'); console.log('Email with DSN headers accepted'); expect(dataResponse.startsWith('250')).toEqual(true); socket.write('QUIT\r\n'); await waitForResponse(socket, '221').catch(() => {}); } finally { socket.destroy(); } }); tap.test('Bounce Management - Bounce loop prevention', async (tools) => { const socket = net.createConnection({ host: 'localhost', port: TEST_PORT, timeout: 30000 }); try { // Wait for greeting await waitForResponse(socket, '220'); // Send EHLO socket.write('EHLO testclient\r\n'); await waitForResponse(socket, '250'); // Null sender (bounce message) socket.write('MAIL FROM:<>\r\n'); await waitForResponse(socket, '250'); // To another mailer-daemon (potential loop) socket.write('RCPT TO:\r\n'); const rcptResponse = await waitForResponse(socket); if (rcptResponse.startsWith('550') || rcptResponse.startsWith('553')) { console.log('Bounce loop prevented - mailer-daemon recipient rejected'); expect(true).toEqual(true); } else if (rcptResponse.startsWith('250')) { console.log('Mailer-daemon recipient accepted - check for loop prevention'); // Send DATA socket.write('DATA\r\n'); const dataCommandResponse = await waitForResponse(socket); if (dataCommandResponse.startsWith('354')) { const email = [ `From: MAILER-DAEMON@example.com`, `To: mailer-daemon@another-server.com`, `Subject: Delivery Status Notification (Failure)`, `Date: ${new Date().toUTCString()}`, `Message-ID: `, `Auto-Submitted: auto-replied`, `X-Loop: example.com`, '', 'This is a bounce of a bounce - potential loop.', '.', '' ].join('\r\n'); socket.write(email); const dataResponse = await waitForResponse(socket); const result = dataResponse.startsWith('250') ? 'accepted' : 'rejected'; console.log(`Bounce loop test: ${result}`); expect(true).toEqual(true); } else if (dataCommandResponse.startsWith('503')) { // Server rejects DATA for null sender console.log('Bounce loop prevented at DATA stage (null sender rejection)'); expect(dataCommandResponse.startsWith('503')).toEqual(true); } } socket.write('QUIT\r\n'); await waitForResponse(socket, '221').catch(() => {}); } finally { socket.destroy(); } }); tap.test('Bounce Management - Valid email (control test)', async (tools) => { const socket = net.createConnection({ host: 'localhost', port: TEST_PORT, timeout: 30000 }); try { // Wait for greeting await waitForResponse(socket, '220'); // Send EHLO socket.write('EHLO testclient\r\n'); await waitForResponse(socket, '250'); // Send MAIL FROM socket.write('MAIL FROM:\r\n'); await waitForResponse(socket, '250'); // Send RCPT TO socket.write('RCPT TO:\r\n'); await waitForResponse(socket, '250'); // Send DATA socket.write('DATA\r\n'); await waitForResponse(socket, '354'); const email = [ `From: sender@example.com`, `To: valid@example.com`, `Subject: Valid Email Test`, `Return-Path: `, `Date: ${new Date().toUTCString()}`, `Message-ID: `, '', 'This is a valid email that should not trigger bounce.', '.', '' ].join('\r\n'); socket.write(email); const dataResponse = await waitForResponse(socket, '250'); console.log('Valid email accepted - no bounce expected'); expect(dataResponse.startsWith('250')).toEqual(true); socket.write('QUIT\r\n'); await waitForResponse(socket, '221').catch(() => {}); } finally { socket.destroy(); } }); tap.test('cleanup - stop test server', async () => { await stopTestServer(testServer); }); export default tap.start();