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