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; tap.test('setup - start test server', async (toolsArg) => { testServer = await startTestServer({ port: TEST_PORT }); await toolsArg.delayFor(1000); }); tap.test('RFC 7208 SPF - Server handles SPF checks', async (tools) => { const done = tools.defer(); const socket = net.createConnection({ host: 'localhost', port: TEST_PORT, timeout: 30000 }); let dataBuffer = ''; let step = 'greeting'; const spfResults: any[] = []; // Test domains simulating different SPF scenarios const spfTestDomains = [ 'spf-pass.example.com', // Should have valid SPF record allowing sender 'spf-fail.example.com', // Should have SPF record that fails 'spf-neutral.example.com', // Should have neutral SPF record 'no-spf.example.com' // Should have no SPF record ]; let currentDomainIndex = 0; socket.on('data', (data) => { dataBuffer += data.toString(); console.log('Server response:', data.toString()); if (step === 'greeting' && dataBuffer.includes('220 ')) { step = 'ehlo'; socket.write('EHLO testclient\r\n'); dataBuffer = ''; } else if (step === 'ehlo' && dataBuffer.includes('250')) { // Check if server advertises SPF support const advertisesSpf = dataBuffer.toLowerCase().includes('spf'); console.log('Server advertises SPF:', advertisesSpf); step = 'test_domains'; testNextDomain(); } else if (step === 'test_domains') { if (dataBuffer.includes('250') && dataBuffer.includes('sender accepted')) { // MAIL FROM accepted socket.write(`RCPT TO:\r\n`); dataBuffer = ''; } else if (dataBuffer.includes('250') && dataBuffer.includes('recipient accepted')) { // RCPT TO accepted spfResults[currentDomainIndex].rcptAccepted = true; // Reset and test next domain socket.write('RSET\r\n'); dataBuffer = ''; } else if (dataBuffer.includes('250') && dataBuffer.includes('Reset')) { currentDomainIndex++; if (currentDomainIndex < spfTestDomains.length) { testNextDomain(); } else { // All tests complete console.log('SPF test results:', spfResults); // Check that server handled all domains const allDomainsHandled = spfResults.every(result => result.mailFromResponse !== undefined ); expect(allDomainsHandled).toEqual(true); socket.write('QUIT\r\n'); socket.end(); done.resolve(); } } else if (dataBuffer.includes('550') || dataBuffer.includes('553')) { // SPF failure (expected for some domains) spfResults[currentDomainIndex].mailFromResponse = dataBuffer.trim(); spfResults[currentDomainIndex].spfFailed = true; // Reset and test next domain socket.write('RSET\r\n'); dataBuffer = ''; } } }); function testNextDomain() { const domain = spfTestDomains[currentDomainIndex]; const testEmail = `spf-test@${domain}`; spfResults[currentDomainIndex] = { domain: domain, email: testEmail, mailFromAccepted: false, rcptAccepted: false, spfFailed: false }; console.log(`Testing SPF for domain: ${domain}`); socket.write(`MAIL FROM:<${testEmail}>\r\n`); spfResults[currentDomainIndex].mailFromResponse = 'pending'; dataBuffer = ''; } socket.on('error', (err) => { console.error('Socket error:', err); done.reject(err); }); await done.promise; }); tap.test('RFC 7208 SPF - SPF record syntax handling', async (tools) => { const done = tools.defer(); const socket = net.createConnection({ host: 'localhost', port: TEST_PORT, timeout: 30000 }); let dataBuffer = ''; let step = 'greeting'; socket.on('data', (data) => { dataBuffer += data.toString(); console.log('Server response:', data.toString()); if (step === 'greeting' && dataBuffer.includes('220 ')) { step = 'ehlo'; socket.write('EHLO testclient\r\n'); dataBuffer = ''; } else if (step === 'ehlo' && dataBuffer.includes('250')) { step = 'mail'; // Test with domain that might have complex SPF record socket.write('MAIL FROM:\r\n'); dataBuffer = ''; } else if (step === 'mail') { // Server should handle this appropriately (accept or reject based on SPF) const handled = dataBuffer.includes('250') || dataBuffer.includes('550') || dataBuffer.includes('553'); expect(handled).toEqual(true); console.log('SPF handling response:', dataBuffer.trim()); socket.write('QUIT\r\n'); socket.end(); done.resolve(); } }); socket.on('error', (err) => { console.error('Socket error:', err); done.reject(err); }); await done.promise; }); tap.test('RFC 7208 SPF - Received-SPF header', async (tools) => { const done = tools.defer(); const socket = net.createConnection({ host: 'localhost', port: TEST_PORT, timeout: 30000 }); let dataBuffer = ''; let step = 'greeting'; socket.on('data', (data) => { dataBuffer += data.toString(); console.log('Server response:', data.toString()); if (step === 'greeting' && dataBuffer.includes('220 ')) { step = 'ehlo'; socket.write('EHLO testclient\r\n'); dataBuffer = ''; } else if (step === 'ehlo' && dataBuffer.includes('250')) { step = 'mail'; socket.write('MAIL FROM:\r\n'); dataBuffer = ''; } else if (step === 'mail' && dataBuffer.includes('250')) { step = 'rcpt'; socket.write('RCPT TO:\r\n'); dataBuffer = ''; } else if (step === 'rcpt' && dataBuffer.includes('250')) { step = 'data'; socket.write('DATA\r\n'); dataBuffer = ''; } else if (step === 'data' && dataBuffer.includes('354')) { // Send email to check if server adds Received-SPF header const email = [ `Date: ${new Date().toUTCString()}`, `From: sender@example.com`, `To: recipient@example.com`, `Subject: SPF Header Test`, `Message-ID: <${Date.now()}@example.com>`, '', 'Testing if server adds Received-SPF header.', '.', '' ].join('\r\n'); socket.write(email); dataBuffer = ''; } else if (dataBuffer.includes('250 ') && dataBuffer.includes('Message accepted')) { console.log('Email accepted - server should process SPF'); socket.write('QUIT\r\n'); socket.end(); done.resolve(); } }); socket.on('error', (err) => { console.error('Socket error:', err); done.reject(err); }); await done.promise; }); tap.test('RFC 7208 SPF - IPv4 and IPv6 mechanism support', async (tools) => { const done = tools.defer(); const socket = net.createConnection({ host: 'localhost', port: TEST_PORT, timeout: 30000 }); let dataBuffer = ''; let step = 'greeting'; socket.on('data', (data) => { dataBuffer += data.toString(); console.log('Server response:', data.toString()); if (step === 'greeting' && dataBuffer.includes('220 ')) { step = 'ehlo'; // Test with IPv6 address representation socket.write('EHLO [::1]\r\n'); dataBuffer = ''; } else if (step === 'ehlo' && dataBuffer.includes('250')) { step = 'mail'; // Test domain with IP-based SPF mechanisms socket.write('MAIL FROM:\r\n'); dataBuffer = ''; } else if (step === 'mail') { // Server should handle IP-based SPF mechanisms const handled = dataBuffer.includes('250') || dataBuffer.includes('550') || dataBuffer.includes('553'); expect(handled).toEqual(true); console.log('IP mechanism SPF response:', dataBuffer.trim()); socket.write('QUIT\r\n'); socket.end(); done.resolve(); } }); socket.on('error', (err) => { console.error('Socket error:', err); done.reject(err); }); await done.promise; }); tap.test('cleanup - stop test server', async () => { await stopTestServer(testServer); }); tap.start();