Research
Security News
Malicious npm Package Targets Solana Developers and Hijacks Funds
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
smtp-server-mit
Advanced tools
This is a fork of smtp-server module just before it changed a license from MIT to EUPL-v1.1.
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.
NB! this module does not make any email deliveries by itself. smtp-server allows you to listen on ports 25/24/465/587/etc using SMTP protocol and that's it. Your own application is responsible of accepting and delivering the message to destination.
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-mit
Require in your script
var SMTPServer = require('smtp-server-mit').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()
)stream.sizeExceeded
['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
onRcptTo: function (address, session, callback) {
// do not accept messages larger than 100 bytes to specific recipients
var expectedSize = Number(session.envelope.mailFrom.args.SIZE) || 0;
if (address.address === 'almost-full@example.com' && expectedSize > 100) {
err = new Error('Insufficient channel storage: ' + address.address);
err.responseCode = 452;
return callback(err);
}
callback();
},
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
FAQs
Create custom SMTP servers on the fly
The npm package smtp-server-mit receives a total of 0 weekly downloads. As such, smtp-server-mit popularity was classified as not popular.
We found that smtp-server-mit demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer 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.
Research
Security News
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
Security News
Research
Socket researchers have discovered malicious npm packages targeting crypto developers, stealing credentials and wallet data using spyware delivered through typosquats of popular cryptographic libraries.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.