Security News
vlt Debuts New JavaScript Package Manager and Serverless Registry at NodeConf EU
vlt introduced its new package manager and a serverless registry this week, innovating in a space where npm has stagnated.
smtp-server
Advanced tools
The smtp-server npm package is a simple and powerful SMTP server library for Node.js. It allows you to create custom SMTP servers for receiving and processing emails. This package is useful for testing email sending functionality, building email processing systems, and more.
Basic SMTP Server
This code sets up a basic SMTP server that listens on port 25. It prints incoming email messages to the console.
const SMTPServer = require('smtp-server').SMTPServer;
const server = new SMTPServer({
onData(stream, session, callback) {
stream.pipe(process.stdout); // Print message to console
stream.on('end', callback);
}
});
server.listen(25, () => {
console.log('SMTP server is listening on port 25');
});
Authentication
This code sets up an SMTP server with basic authentication. It checks the username and password provided by the client.
const SMTPServer = require('smtp-server').SMTPServer;
const server = new SMTPServer({
onAuth(auth, session, callback) {
if (auth.username === 'user' && auth.password === 'pass') {
callback(null, { user: 'user' });
} else {
callback(new Error('Invalid username or password'));
}
}
});
server.listen(25, () => {
console.log('SMTP server with authentication is listening on port 25');
});
Custom Message Handling
This code sets up an SMTP server that parses incoming email messages and logs the subject and text content to the console.
const SMTPServer = require('smtp-server').SMTPServer;
const simpleParser = require('mailparser').simpleParser;
const server = new SMTPServer({
onData(stream, session, callback) {
simpleParser(stream, (err, parsed) => {
if (err) {
callback(err);
} else {
console.log('Subject:', parsed.subject);
console.log('Text:', parsed.text);
callback();
}
});
}
});
server.listen(25, () => {
console.log('SMTP server with custom message handling is listening on port 25');
});
Nodemailer is a module for Node.js applications to allow easy email sending. While smtp-server focuses on receiving and processing emails, Nodemailer is designed for sending emails. It supports various transport methods, including SMTP, and is widely used for its simplicity and flexibility.
MailDev is a simple way to test your project's generated emails during development. It acts as an SMTP server and a web interface to view and inspect emails. Unlike smtp-server, which is a library for creating custom SMTP servers, MailDev is a complete tool with a built-in web interface for email inspection.
smtp-protocol is a low-level SMTP protocol library for Node.js. It allows you to create custom SMTP clients and servers. While smtp-server provides a higher-level API for creating SMTP servers, smtp-protocol offers more granular control over the SMTP protocol, making it suitable for advanced use cases.
Create SMTP and LMTP server instances on the fly. This is not a full-blown server application like Haraka but an easy way to add custom SMTP listeners to your app. This module is the successor for the server part of the (now deprecated) SMTP module simplesmtp. For matching SMTP client see smtp-connection.
Requires Node v0.12 or iojs. The module does not run on Node v0.10 as it uses Buffer.compare and TLSSocket.
Install with npm
npm install smtp-server
Require in your script
var SMTPServer = require('smtp-server').SMTPServer;
var server = new SMTPServer(options);
Where
true
, the connection will use TLS. The default is false
. If the server doesn't start in TLS mode, it is still possible to upgrade clear text socket to TLS socket with the STARTTLS command (unless you disable support for it). If secure is true
, additional tls options for tls.createServer can be added directly onto this options object.os.hostname()
)['PLAIN', 'LOGIN']
. Only the methods listed in this array are allowed, so if you set it to ['XOAUTH2']
then PLAIN and LOGIN are not available. Use ['PLAIN', 'LOGIN', 'XOAUTH2']
to allow all three. Authentication is only allowed in secure mode (either the server is started with secure: true
option or STARTTLS command is used)['AUTH']
as this value. If you want to allow authentication in clear text, set it to ['STARTTLS']
.true
then logs to console. If value is not set or is false
then nothing is loggedInfinity
session.xClient
(Map object) for the details provided by the clientsession.xForward
(Map object) for the details provided by the clientAdditionally you can use the options from net.createServer and tls.createServer (applies if secure
is set to true)
The server
object returned from new SMTPServer
has the following methods:
callback
is invoked once all client connections are closedIf you use secure: true
option or you do not disable STARTTLS command then you SHOULD also define the key
, cert
and possibly ca
properties to use a proper certificate. If you do no specify your own certificate then a pregenerated self-signed certificate for 'localhost' is used. Any respectful client refuses to accept such certificate.
Example
// This example starts a SMTP server using TLS with your own certificate and key
var server = new SMTPServer({
secure: true,
key: fs.readFileSync('private.key'),
cert: fs.readFileSync('server.crt')
});
server.listen(465);
server.listen(port[,host][,callback]);
Where
Errors can be handled by setting an 'error' event listener to the server instance
server.on('error', function(err){
console.log('Error %s', err.message);
});
Authentication calls can be handled with onAuth
handler
var server = new SMTPServer({
onAuth: function(auth, session, callback){}
});
Where
true
if the response matches the passwordremoteAddress
for the remote IP, see details here(error, response)
responseCode
to the error objectThis module supports CRAM-MD5
but the use of it is discouraged as it requires access to unencrypted user passwords during the authentication process. You shouldn't store passwords unencrypted.
var server = new SMTPServer({
onAuth: function(auth, session, callback){
if(auth.username !== 'abc' || auth.password !== 'def'){
return callback(new Error('Invalid username or password'));
}
callback(null, {user: 123}); // where 123 is the user id or similar property
}
});
XOAUTH2 support needs to enabled with the authMethods
array option as it is disabled by default.
If you support multiple authentication mechanisms, then you can check the used mechanism from the method
property.
var server = new SMTPServer({
authMethods: ['XOAUTH2'], // XOAUTH2 is not enabled by default
onAuth: function(auth, session, callback){
if(auth.method !== 'XOAUTH2'){
// should never occur in this case as only XOAUTH2 is allowed
return callback(new Error('Expecting XOAUTH2'));
}
if(auth.username !== 'abc' || auth.accessToken !== 'def'){
return callback(null, {
data: {
status: '401',
schemes: 'bearer mac',
scope: 'my_smtp_access_scope_name'
}
});
}
callback(null, {user: 123}); // where 123 is the user id or similar property
}
});
CRAM-MD5 support needs to enabled with the authMethods
array option as it is disabled by default.
If you support multiple authentication mechanisms, then you can check the used mechanism from the method
property.
This authentication method does not return a password with the username but a response to a challenge. To validate the returned challenge response, the authentication object includes a method validatePassword
that takes the actual plaintext password as an argument and returns either true
if the password matches with the challenge response or false
if it does not.
var server = new SMTPServer({
authMethods: ['CRAM-MD5'], // CRAM-MD5 is not enabled by default
onAuth: function(auth, session, callback){
if(auth.method !== 'CRAM-MD5'){
// should never occur in this case as only CRAM-MD5 is allowed
return callback(new Error('Expecting CRAM-MD5'));
}
// CRAM-MD5 does not provide a password but a challenge response
// that can be validated against the actual password of the user
if(auth.username !== 'abc' || !auth.validatePassword('def')){
return callback(new Error('Invalid username or password'));
}
callback(null, {user: 123}); // where 123 is the user id or similar property
}
});
By default any client connection is allowed. If you want to check the remoteAddress or clientHostname before
any other command, you can set a handler for it with onConnect
var server = new SMTPServer({
onConnect: function(session, callback){}
});
Where
remoteAddress
and clientHostname
valuesvar server = new SMTPServer({
onConnect: function(session, callback){
if(session.remoteAddress === '127.0.0.1'){
return callback(new Error('No connections from localhost allowed'));
}
return callback(); // Accept the connection
}
});
If you also need to detect when a connection is closed use onClose
. This method does not expect you to run a callback function as it is purely informational.
var server = new SMTPServer({
onClose: function(session){}
});
By default all sender addresses (as long as these are in valid email format) are allowed. If you want to check
the address before it is accepted you can set a handler for it with onMailFrom
var server = new SMTPServer({
onMailFrom: function(address, session, callback){}
});
Where
MAIL FROM:
commandenvelope
object and user
data if logged in, see details herevar server = new SMTPServer({
onMailFrom: function(address, session, callback){
if(address.address !== 'allowed@example.com'){
return callback(new Error('Only allowed@example.com is allowed to send mail'));
}
return callback(); // Accept the address
}
});
By default all recipient addresses (as long as these are in valid email format) are allowed. If you want to check
the address before it is accepted you can set a handler for it with onRcptTo
var server = new SMTPServer({
onRcptTo: function(address, session, callback){}
});
Where
RCPT TO:
commandenvelope
object and user
data if logged in, see details herevar server = new SMTPServer({
onRcptTo: function(address, session, callback){
if(address.address !== 'allowed@example.com'){
return callback(new Error('Only allowed@example.com is allowed to receive mail'));
}
return callback(); // Accept the address
}
});
You can get the stream for the incoming message with onData
handler
var server = new SMTPServer({
onData: function(stream, session, callback){}
});
Where
envelope
object and user
data if logged in, see details herevar server = new SMTPServer({
onData: function(stream, session, callback){
stream.pipe(process.stdout); // print message to console
stream.on('end', callback);
}
});
This module does not prepend Received
or any other header field to the streamed message. The entire message is streamed as-is with no modifications whatsoever. For compliancy you should add the Received data to the message yourself, see rfc5321 4.4. Trace Information for details.
When creating the server you can define maximum allowed message size with the size
option, see RFC1870 for details. This is not a strict limitation, the client is informed about the size limit but the client can still send a larger message than allowed, it is up to your application to reject or accept the oversized message. To check if the message was oversized, see stream.sizeExceeded
property.
var server = new SMTPServer({
size: 1024, // allow messages up to 1 kb
onData: function(stream, session, callback){
stream.pipe(process.stdout); // print message to console
stream.on('end', function(){
var err;
if(stream.sizeExceeded){
err = new Error('Message exceeds fixed maximum message size');
err.responseCode = 552;
return callback(err);
}
callback(null, 'Message queued as abcdef');
});
}
});
If lmtp
option is set to true when starting the server, then LMTP protocol is used instead of SMTP. The main
difference between these two is how multiple recipients are handled. In case of SMTP the message either fails or succeeds
but in LMTP the message might fail and succeed individually for every recipient.
If your LMTP server application does not distinguish between different recipients then you do not need to care about it. On the other hand if you want to report results separately for every recipient you can do this by providing an array of responses instead of a single error or success message. The array must contain responses in the same order as in the envelope rcptTo array.
var server = new SMTPServer({
lmtp: true,
onData: function(stream, session, callback){
stream.pipe(process.stdout); // print message to console
stream.on('end', function(){
// reject every other recipient
var response = session.envelope.rcptTo.map(function (rcpt, i) {
if (i % 2) {
return new Error('<' + rcpt.address + '> Not accepted');
} else {
return '<' + rcpt.address + '> Accepted';
}
});
callback(null, response);
});
}
});
If you provide a single error by invoking callback(err)
or single success message callback(null, 'OK')
like when dealing with SMTP then every recipient gets the same response.
Session object that is passed to the handler functions includes the following properties
user
value returned with the authentication handlerAddress object in the mailFrom
and rcptTo
values include the following properties
For example if the client runs the following commands:
C: MAIL FROM:<sender@example.com> SIZE=12345 RET=HDRS
C: RCPT TO:<recipient@example.com> NOTIFY=NEVER
then the envelope object is going go look like this:
{
"mailFrom": {
"address": "sender@example.com",
"args": {
"SIZE": "12345",
"RET": "HDRS"
}
},
"rcptTo": [
{
"address": "receiver@example.com",
"args": {
"NOTIFY": "NEVER"
}
}
]
}
authMethods: ['XOAUTH2']
to enableMost notably, the ENHANCEDSTATUSCODES extension is not supported, all response codes use the standard three digit format and nothing else. I might change this in the future if I have time to revisit all responses and find the appropriate response codes.
CHUNKING is also missing. I might add support for it in the future but not at this moment since DATA already accepts a stream and CHUNKING is not supported everywhere.
MIT
v1.13.1 2016-07-29
FAQs
Create custom SMTP servers on the fly
The npm package smtp-server receives a total of 110,226 weekly downloads. As such, smtp-server popularity was classified as popular.
We found that smtp-server demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
vlt introduced its new package manager and a serverless registry this week, innovating in a space where npm has stagnated.
Security News
Research
The Socket Research Team uncovered a malicious Python package typosquatting the popular 'fabric' SSH library, silently exfiltrating AWS credentials from unsuspecting developers.
Security News
At its inaugural meeting, the JSR Working Group outlined plans for an open governance model and a roadmap to enhance JavaScript package management.