From ad12d1ff5fcad50a6dab97d3cf71ca3ec052ed14 Mon Sep 17 00:00:00 2001 From: James Perih Date: Wed, 2 Oct 2019 11:48:44 -0600 Subject: [PATCH 1/6] Update with initial response and PTR record required by modern SMTP senders --- README.md | 6 +++--- smtp.js | 22 ++++++++++++---------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index bb282d4..2f1b476 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,9 @@ Usage ----- 1. Replace `ip` and `name` with your own. -2. `node smtp.js` -3. Send an email to test@`ip` +1. Perform a reverse-lookup of your IP, and store the value in `ptr` +1. `node smtp.js` (you may have to `sudo`) +1. Send an email to test@`ip`, or to the domain resolved by your MX record. Notice ------ @@ -28,4 +29,3 @@ Usage Examples are provided in the `test` folder. `node test-parse.js Gmail` - diff --git a/smtp.js b/smtp.js index 6802c8d..c5892ca 100644 --- a/smtp.js +++ b/smtp.js @@ -1,13 +1,13 @@ var net = require( 'net' ), fs = require( 'fs' ), ip = '74.207.234.151', + ptr = 'reverse-dns-for-74.207.234.151', name = 'Send to Dropbox', port = 25, smtp = (function( ip, name ) { - var eol = "\r\n", commands = { - 'OPEN' : '220 ' + ip + ' ESMTP ' + name, + 'OPEN' : '220 ' + ptr + '(' + ip + ') ESMTP ' + name, 'EHLO' : [ '250-' + ip + ' OH HAI ', '250-SIZE 35651584', @@ -22,24 +22,24 @@ var net = require( 'net' ), '.' : '250 OK id=1778te-0009TT-00', 'QUIT' : '221 Peace Out' }; - + function sendResponse( socket, command, arg ) { - + var response = commands[ command ]; - + if ( arg ) { response = response.replace( '', arg ); } console.log( 'S: ' + response ); socket.write( response + eol ); - + }; - + return { sendResponse : sendResponse, commands : commands }; - + })( ip ), server = function( socket ) { @@ -68,7 +68,7 @@ var net = require( 'net' ), smtp.sendResponse( socket, command, parts[1] ); // Check for end of email } else if ( data.substr(-5) == "\r\n.\r\n" ) { - email += data.substring(0, data.length - 5); + email += data.substring(0, data.length - 5); smtp.sendResponse( socket, '.' ); // Build email } else { @@ -77,7 +77,7 @@ var net = require( 'net' ), clearTimeout( timeout ); timeout = setTimeout(function(){ - smtp.sendResponse(socket, 'MAIL'); + smtp.sendResponse(socket, 'MAIL'); }, 10000); }); @@ -88,6 +88,8 @@ var net = require( 'net' ), // Do something with the email here }); + // send initial response + smtpServer.sendResponse(socket, 'OPEN'); }, smtpServer = net.createServer( server ); From 4f19e0f32569b070aae8a459f042ada9d2df10f6 Mon Sep 17 00:00:00 2001 From: James Perih Date: Wed, 2 Oct 2019 12:31:18 -0600 Subject: [PATCH 2/6] Support for blocking open relay. --- smtp.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/smtp.js b/smtp.js index c5892ca..ec9077b 100644 --- a/smtp.js +++ b/smtp.js @@ -3,8 +3,9 @@ var net = require( 'net' ), ip = '74.207.234.151', ptr = 'reverse-dns-for-74.207.234.151', name = 'Send to Dropbox', + domain = 'yourdomain.com', port = 25, - smtp = (function( ip, name ) { + smtp = (function() { var eol = "\r\n", commands = { 'OPEN' : '220 ' + ptr + '(' + ip + ') ESMTP ' + name, @@ -20,7 +21,8 @@ var net = require( 'net' ), 'RCPT' : '250 Ok', 'DATA' : '354 End data with .', '.' : '250 OK id=1778te-0009TT-00', - 'QUIT' : '221 Peace Out' + 'QUIT' : '221 Peace Out', + 'NORELAY': '550 Relay Denied' }; function sendResponse( socket, command, arg ) { @@ -65,7 +67,17 @@ var net = require( 'net' ), // Check for a command if ( smtp.commands[ command ] ) { - smtp.sendResponse( socket, command, parts[1] ); + if (command === 'RCPT') { + // check for domain matching + if (!parts[1].includes('@' + domain + '>')) { + smtp.sendResponse( socket, 'NORELAY'); + } else { + smtp.sendResponse( socket, command, parts[1] ); + } + } else { + // Any other command + smtp.sendResponse( socket, command, parts[1] ); + } // Check for end of email } else if ( data.substr(-5) == "\r\n.\r\n" ) { email += data.substring(0, data.length - 5); From ba3bfe66ccc7bb3c9492bb54e300bc842469f118 Mon Sep 17 00:00:00 2001 From: James Perih Date: Wed, 2 Oct 2019 15:03:46 -0600 Subject: [PATCH 3/6] Change to switch and cleanup logic; mark the global constants --- smtp.js | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/smtp.js b/smtp.js index ec9077b..ed4853f 100644 --- a/smtp.js +++ b/smtp.js @@ -1,16 +1,16 @@ var net = require( 'net' ), fs = require( 'fs' ), - ip = '74.207.234.151', - ptr = 'reverse-dns-for-74.207.234.151', - name = 'Send to Dropbox', - domain = 'yourdomain.com', - port = 25, + IP = '74.207.234.151', + PTR = 'reverse-dns-for-74.207.234.151', + NAME = 'node-smtp', + DOMAIN = 'yourdomain.com', + PORT = 25, smtp = (function() { var eol = "\r\n", commands = { - 'OPEN' : '220 ' + ptr + '(' + ip + ') ESMTP ' + name, + 'OPEN' : '220 ' + PTR + '(' + IP + ') ESMTP ' + NAME, 'EHLO' : [ - '250-' + ip + ' OH HAI ', + '250-' + IP + ' OH HAI ', '250-SIZE 35651584', '250-PIPELINING', '250-ENHANCEDSTATUSCODES', @@ -42,7 +42,7 @@ var net = require( 'net' ), commands : commands }; - })( ip ), + })( IP ), server = function( socket ) { var email = "", @@ -67,18 +67,24 @@ var net = require( 'net' ), // Check for a command if ( smtp.commands[ command ] ) { - if (command === 'RCPT') { - // check for domain matching - if (!parts[1].includes('@' + domain + '>')) { - smtp.sendResponse( socket, 'NORELAY'); - } else { - smtp.sendResponse( socket, command, parts[1] ); - } - } else { - // Any other command + var isHandled = false; + switch (command) { + case 'RCPT': + if (!parts[1].includes('@' + DOMAIN + '>')) { + smtp.sendResponse( socket, 'NORELAY'); + isHandled = true; + } + break; + default: + // no other special case + } + + + if (!isHandled) { smtp.sendResponse( socket, command, parts[1] ); } - // Check for end of email + + // Check for end of email } else if ( data.substr(-5) == "\r\n.\r\n" ) { email += data.substring(0, data.length - 5); smtp.sendResponse( socket, '.' ); @@ -100,7 +106,7 @@ var net = require( 'net' ), // Do something with the email here }); - // send initial response + // send initial response for servers that are shy smtpServer.sendResponse(socket, 'OPEN'); }, smtpServer = net.createServer( server ); @@ -110,4 +116,4 @@ console.log( 'Starting email server' ); // C: are client commands // S: are server commands. -smtpServer.listen( port, ip ); +smtpServer.listen( PORT, IP ); From e4173475bf6f85e66e74346475986efc1ca18459 Mon Sep 17 00:00:00 2001 From: James Perih Date: Wed, 2 Oct 2019 15:04:12 -0600 Subject: [PATCH 4/6] Remove Pipelining feature, as we don't yet support it completely. --- smtp.js | 1 - 1 file changed, 1 deletion(-) diff --git a/smtp.js b/smtp.js index ed4853f..2fdbded 100644 --- a/smtp.js +++ b/smtp.js @@ -12,7 +12,6 @@ var net = require( 'net' ), 'EHLO' : [ '250-' + IP + ' OH HAI ', '250-SIZE 35651584', - '250-PIPELINING', '250-ENHANCEDSTATUSCODES', '250 8BITMIME' ].join( eol ), From 1170c564e01d23a682dc7632732df57959e2f698 Mon Sep 17 00:00:00 2001 From: James Perih Date: Wed, 2 Oct 2019 15:07:59 -0600 Subject: [PATCH 5/6] Update readme. --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2f1b476..f72db4e 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,9 @@ Node SMTP Server Usage ----- -1. Replace `ip` and `name` with your own. -1. Perform a reverse-lookup of your IP, and store the value in `ptr` +1. Replace `IP` and `NAME` with your own. +1. Perform a reverse-lookup of your IP, and store the value in `PRR` +1. Provide domains you will accept mails from in `DOMAIN`. Any other domains provided will be rejected, closing a potential Open Relay exploit. 1. `node smtp.js` (you may have to `sudo`) 1. Send an email to test@`ip`, or to the domain resolved by your MX record. From e25460e8f90cd5504dfc096d5d41babcf4822d99 Mon Sep 17 00:00:00 2001 From: James Perih Date: Wed, 2 Oct 2019 15:09:44 -0600 Subject: [PATCH 6/6] Update readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f72db4e..59ba215 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Usage 1. Replace `IP` and `NAME` with your own. 1. Perform a reverse-lookup of your IP, and store the value in `PRR` -1. Provide domains you will accept mails from in `DOMAIN`. Any other domains provided will be rejected, closing a potential Open Relay exploit. +1. Provide domain you will accept mails from in `DOMAIN`. Any other domain provided by a client will be rejected, closing a potential Open Relay exploit. 1. `node smtp.js` (you may have to `sudo`) 1. Send an email to test@`ip`, or to the domain resolved by your MX record.