smtp-stack
Configurable SMTP stack and server with async middleware.
Supports TLS and the following SMTP extensions:
- AUTH PLAIN LOGIN XOAUTH2 CRAM-MD5
- XCLIENT
- XFORWARD
- PROXY
Installation
npm install smtp-stack
Usage and Options
const fs = require('fs');
const { SmtpServer, LogLevels } = require('smtp-stack');
const server = new SmtpServer({
name: 'mail.example.com',
secure: true,
authMethods: ['PLAIN', 'LOGIN', 'XOAUTH2', 'CRAM-MD5'],
disabledCommands: ['STARTTLS'],
allowInsecureAuth: false,
noAuthReply: true,
key: fs.readFileSync('key.pem').toString(),
cert: fs.readFileSync('cert.pem').toString(),
session: undefined,
requestCert: false,
rejectUnauthorized: true,
requestOCSP: false,
sessionIdContext: Math.random().toString().substring(2),
honorCipherOrder: true,
minVersion: 'TLSv1',
sniOptions: {
'mail.example.com': {
honorCipherOrder: true,
}
},
SNICallback(hostname, callback) {
callback(null, server.secureContext.get(hostname));
},
socketTimeout: 5 * 1000,
closeTimeout: 10 * 1000,
upgradeTimeout: 2 * 1000,
logLevel: LogLevels.info,
logBackend: console,
size: 1024,
useProxy: true,
onSocket(socket, callback) {
setImmediate(callback);
},
onConnect(session, callback) {
setImmediate(callback);
},
onAuth(session, callback) {
setImmediate(() => callback(null, { user: 'anonymous' }));
},
onMailFrom(session, callback) {
setImmediate(callback);
},
onRcptTo(session, callback) {
setImmediate(callback);
},
onData(session, callback) {
setImmediate(callback);
},
onClose(connection, callback) {
setImmediate(callback);
}
});
server.on('error', () => {
});
server.use(async (ctx, next) => {
await next();
if (ctx.session.auth) {
let valid = true;
if (valid) {
ctx.session.user = {
name: 'John Smith',
};
ctx.send(235, 'Authentication successful');
} else {
ctx.send(535, 'Authentication failed');
ctx.close();
}
ctx.session.auth = null;
}
});
server.use(async (ctx, next) => {
if (ctx.commandName === 'MAIL') {
}
if (ctx.commandName === 'RCPT') {
}
await next();
});
server.use(async (ctx, next) => {
await next();
if (ctx.commandName === 'DATA' && ctx.session.envelope.dataStream) {
const message = Object.assign({}, ctx.session.envelope);
const chunks = [];
ctx.session.envelope.dataStream.on('data', (chunk) => chunks.push(chunk));
ctx.session.envelope.dataStream.once('end', () => {
message.body = Buffer.concat(chunks).toString();
});
}
});
server.start(25, 'localhost', () => {
console.log('SMTP server started');
});
Testing
Tested with the following SMTP clients:
nodemailer
client NPM package (integrations tests in test
directory)ssmtp
CLI SMTP client (see the instructions)- Mozilla Thunderbird (make sure you have valid certificate for testing w/ TLS)