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:
		| @@ -12,7 +12,10 @@ export interface ITestServerConfig { | ||||
|   port: number; | ||||
|   hostname?: string; | ||||
|   tlsEnabled?: boolean; | ||||
|   secure?: boolean; // Direct TLS server (like SMTPS on port 465) | ||||
|   authRequired?: boolean; | ||||
|   authMethods?: ('PLAIN' | 'LOGIN' | 'OAUTH2')[]; | ||||
|   requireTLS?: boolean; // Whether to require TLS for AUTH (default: true) | ||||
|   timeout?: number; | ||||
|   testCertPath?: string; | ||||
|   testKeyPath?: string; | ||||
| @@ -176,7 +179,8 @@ export async function startTestServer(config: ITestServerConfig): Promise<ITestS | ||||
|     auth: serverConfig.authRequired | ||||
|       ? ({ | ||||
|           required: true, | ||||
|           methods: ['PLAIN', 'LOGIN'] as ('PLAIN' | 'LOGIN' | 'OAUTH2')[], | ||||
|           methods: (serverConfig.authMethods || ['PLAIN', 'LOGIN']) as ('PLAIN' | 'LOGIN' | 'OAUTH2')[], | ||||
|           requireTLS: serverConfig.requireTLS !== undefined ? serverConfig.requireTLS : true, // Default: require TLS for AUTH | ||||
|           validateUser: async (username: string, password: string) => { | ||||
|             // Test server accepts these credentials | ||||
|             return username === 'testuser' && password === 'testpass'; | ||||
|   | ||||
| @@ -359,3 +359,36 @@ export async function retryOperation<T>( | ||||
|  | ||||
|   throw lastError!; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Upgrade SMTP connection to TLS using STARTTLS command | ||||
|  * @param conn - Active SMTP connection | ||||
|  * @param hostname - Server hostname for TLS verification | ||||
|  * @returns Upgraded TLS connection | ||||
|  */ | ||||
| export async function upgradeToTls(conn: Deno.Conn, hostname: string = 'localhost'): Promise<Deno.TlsConn> { | ||||
|   const encoder = new TextEncoder(); | ||||
|  | ||||
|   // Send STARTTLS command | ||||
|   await conn.write(encoder.encode('STARTTLS\r\n')); | ||||
|  | ||||
|   // Read response | ||||
|   const response = await readSmtpResponse(conn); | ||||
|  | ||||
|   // Check for 220 Ready to start TLS | ||||
|   if (!response.startsWith('220')) { | ||||
|     throw new Error(`STARTTLS failed: ${response}`); | ||||
|   } | ||||
|  | ||||
|   // Read test certificate for self-signed cert validation | ||||
|   const certPath = new URL('../../test/fixtures/test-cert.pem', import.meta.url).pathname; | ||||
|   const certPem = await Deno.readTextFile(certPath); | ||||
|  | ||||
|   // Upgrade connection to TLS with certificate options | ||||
|   const tlsConn = await Deno.startTls(conn, { | ||||
|     hostname, | ||||
|     caCerts: [certPem], // Accept self-signed test certificate | ||||
|   }); | ||||
|  | ||||
|   return tlsConn; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user