smtp-server
repository·master·Indexed 21 days ago
https://github.com/nodemailer/smtp-serverA programmable SMTP and LMTP server implementation for Node.js, part of the Nodemailer ecosystem. It allows developers to create custom servers on the fly to intercept, process, or relay incoming emails using the SMTPServer class and SMTPStream. Features include support for TLS with SNI, PROXY protocol v1, message size limiting via the SIZE extension, and a variety of session handlers such as onConnect, onAuth, and onData.
What's inside smtp-server
- smtp-server is a Node.js module that allows you to create SMTP and LMTP server instances on the fly. It is part of the Nodemailer ecosystem.
Using the SIZE extension to limit message size
masterYou can advertise a maximum message size to clients by setting the
sizeoption in theSMTPServerconfiguration.When a client declares a size in the
MAIL FROM:<addr> SIZE=nnnparameter, the server checks it against your limit. However, the server does not automatically block the transfer; instead, it streams the data to your application. You must handle enforcement within youronDatahandler by checking thestream.sizeExceededflag.To prevent large transfers from consuming too much bandwidth, you can check the
stream.sizeExceededflag during thedataevent to stop the transfer early, rather than waiting for theendevent.const server = new SMTPServer({ size: 1024 * 1024, // 1 MiB limit onData(stream, session, callback) { stream.on('end', () => { if (stream.sizeExceeded) { const err = new Error('Message too large'); err.responseCode = 552; return callback(err); } callback(null, 'OK'); }); stream.resume(); // Consume the stream } });Test your SMTP server using sendmail
masterOnce your
SMTPServeris running, you can test it by sending an email from your terminal using thesendmailcommand.- Create a text file (e.g.,
email.txt) containing the email headers and body. - Run your server script (e.g.,
node smtp.js). - Use
sendmailto pipe the file content to a local address.
Example workflow:
# 1. Create the email file cat <<EOF > email.txt Subject: Terminal Email Send Email Content line 1 Email Content line 2 EOF # 2. Run the server (may require sudo for port 25) sudo node smtp.js & # 3. Send the email sendmail test@localhost < email.txtsudo node smtp.js & sendmail test@localhost < email.txt- Create a text file (e.g.,
Handle SMTP session events with SMTPServer handlers
masterInstead of using an event emitter for everything, you can provide shorthand handler functions directly in the
optionsobject passed to theSMTPServerconstructor. These handlers allow you to intercept different stages of the SMTP lifecycle:onConnect(session, callback): Called when a client connects.onSecure(socket, session, callback): Called when a connection is secured via TLS.onAuth(auth, session, callback): Called when a client attempts to authenticate.authcontains the method and credentials.onMailFrom(address, session, callback): Called when theMAIL FROMcommand is received.onRcptTo(address, session, callback): Called when theRCPT TOcommand is received.onData(stream, session, callback): Called when theDATAcommand is received. Thestreamargument is a readable stream containing the email content.onClose(session): Called when the connection is closed.
const server = new SMTPServer({ onConnect: (session, callback) => { console.log('Client connected'); callback(); }, onData: (stream, session, callback) => { stream.on('data', (chunk) => { console.log('Received chunk:', chunk.toString()); }); stream.on('end', () => { callback(); }); } });Create a basic SMTP server with SMTPServer
masterTo create a basic SMTP server, instantiate
SMTPServerfrom thesmtp-serverpackage. You can configure the server using an options object.Key configuration options used in this example:
disabledCommands: An array of SMTP commands to disable (e.g.,['STARTTLS', 'AUTH']).logger: Set totrueto enable logging.onData: A callback function triggered when data is received. It receives astream(the email data), thesessionobject, and acallbackto signal completion.
After instantiation, call
.listen(port)to start the server on the specified port.// smtp.js const { SMTPServer } = require('smtp-server'); const server = new SMTPServer({ // disable STARTTLS to allow authentication in clear text mode disabledCommands: ['STARTTLS', 'AUTH'], logger: true, onData(stream, session, callback) { stream.pipe(process.stdout); // print message to console stream.on('end', callback); } }); server.listen(25);Configure maxCommandLength in SMTPStream
masterWhen instantiating
SMTPStream, you can provide anoptionsobject to set themaxCommandLength. This defines the maximum number of bytes allowed for a single command line before an error is thrown. If not provided, it defaults to4096(4KB).const options = { maxCommandLength: 8192 }; const stream = new SMTPStream(options);Use PROXY protocol for connection information
masterThe
SMTPServersupports the PROXY protocol (v1) to retrieve the original client's IP address and port when the server is behind a load balancer or proxy.To enable this, set
useProxyin your options.useProxycan be a boolean or an array of allowed IP addresses (or'*'). IfuseProxyis enabled, the server will attempt to read the PROXY header from the start of the connection. If the header is malformed or exceedsMAX_PROXY_HEADER_LENGTH(1024 bytes), the connection will be rejected with a* BAD Invalid PROXY headerresponse.Configure SNI and multiple TLS contexts
masterThe
SMTPServersupports Server Name Indication (SNI), allowing you to serve different TLS certificates based on the hostname requested by the client.You can configure this using the
sniOptionsproperty in the server options.sniOptionscan be an object where keys are domain names and values are TLS option objects, or aMapwith the same structure.Example using an object:
const server = new SMTPServer({ secure: true, sniOptions: { 'example.com': { key: fs.readFileSync('example.key'), cert: fs.readFileSync('example.crt') }, 'test.org': { key: fs.readFileSync('test.key'), cert: fs.readFileSync('test.crt') } } });Get TLS options for the SMTP server
masterUse the
getTLSOptionsfunction to generate a configuration object for TLS. This function merges your custom options with a set of default values.Warning: The default configuration includes pregenerated certificates for
localhost. These are intended for development and testing only; do not use them in production.Default settings include:
honorCipherOrder:truerequestOCSP:falseminVersion:'TLSv1'(to support older SMTP clients)sessionIdContext: A hash derived fromprocess.argv
const getTLSOptions = require('smtp-server/lib/tls-options'); const options = getTLSOptions({ key: 'path/to/your/private-key.pem', cert: 'path/to/your/certificate.pem' });Close the SMTPServer gracefully
masterTo shut down the server, call the
.close(callback)method. This method performs a two-step shutdown:- It stops accepting new connections via the underlying network server.
- It waits for a period defined by
options.closeTimeout(or a default of 30 seconds) for existing connections to finish. If connections are still active after this timeout, the server will forcibly close them by sending a421 Server shutting downresponse and terminating the connections.
If a
callbackis provided to.close(), it will be executed once the server has stopped accepting new connections. If you want to wait for the full timeout/cleanup, you may need to manage that logic via thecloseevent emitted by the server.Create an SMTP server with SMTPServer
masterThe
SMTPServerclass is the primary entrypoint for creating and configuring an SMTP or LMTP server instance. You instantiate it by passing anoptionsobject containing connection and SMTP settings. The server can be started by calling.listen()and stopped by calling.close().Common configuration options include:
secure: Boolean indicating if the server should use TLS.authMethods: Array of allowed authentication methods (defaults to['LOGIN', 'PLAIN']).disabledCommands: Array of SMTP commands to disable.hideENHANCEDSTATUSCODES: Boolean (defaults totrue).hideDSN: Boolean (defaults totrue).hideREQUIRETLS: Boolean (defaults totrue).closeTimeout: Time in milliseconds to wait for pending connections to close before forcing them shut (defaults to 30,000ms).
const { SMTPServer } = require('smtp-server'); const server = new SMTPServer({ host: '127.0.0.1', port: 587, secure: false, auth: async (data, session, callback) => { // Implement authentication logic callback(null, { user: 'user' }); } }); server.listen(587, () => { console.log('Server is listening'); });Use startDataMode() to receive message content
masterWhen an SMTP
DATAcommand is received, you should switch theSMTPStreamfrom command mode to data mode usingstartDataMode(maxBytes). This allows you to receive the actual email body as a stream.Workflow:
- Switch Modes: Call
startDataMode(maxBytes)inside youroncommandhandler. This returns aPassThroughstream representing the message content. - Handle Data: Listen to the returned stream for data. The stream automatically handles unescaping dots (e.g.,
..at the start of a line is treated as a literal dot). - Monitor Size: The returned stream has two useful properties you can monitor mid-transfer:
byteLength: The current number of bytes received.sizeExceeded: A boolean that becomestrueif the received bytes exceed themaxByteslimit you provided.
- Finish Processing: Once you have finished processing the data stream, call
continue()on theSMTPStreaminstance to return to command mode.
// Inside your oncommand override: if (cmd === 'DATA') { const dataStream = this.startDataMode(10 * 1024 * 1024); // 10MB limit dataStream.on('data', (chunk) => { console.log('Received chunk:', chunk.length, 'bytes'); if (dataStream.sizeExceeded) { console.warn('Warning: Message size exceeded limit'); } }); dataStream.on('end', () => { console.log('Data transfer complete'); this.continue(); // Return to command mode }); }- Switch Modes: Call