Skip to content

Instantly share code, notes, and snippets.

@btoews
Forked from TheNicholasNick/gist:1040044
Created January 7, 2012 21:48
Show Gist options
  • Save btoews/1576165 to your computer and use it in GitHub Desktop.
Save btoews/1576165 to your computer and use it in GitHub Desktop.
node.js SMTP Server
/*
smtpd.js is SMTP server written for node.js
#Changes#
-made to work with 0.6.2 (replaced sys.print with console.log)
-a few bug fixes
sudo node smtpd.js
MIT License
*/
var tcp = require('net');
var enable_debug = false;
var server = tcp.createServer( function( socket ) {
var eol = "\r\n";
// patterns for commands
var command_patterns = {
helo: /^HELO\s*/i,
ehlo: /^EHLO\s*/i,
quit: /^QUIT/i,
from: /^MAIL FROM:\s*/i,
rcpt: /^RCPT TO:\s*/i,
data: /^DATA/i,
noop: /^NOOP/i,
rset: /^RSET/i,
vrfy: /^VRFY\s+/i,
expn: /^EXPN\s+/,
help: /^HELP/i,
tls: /^STARTTLS/i,
auth: /^AUTH\s+/i
}
// our replies
var reply = {
send: function(s) {
debug( "reply: '" + (s || "null") + "'" );
socket.write( s + eol );
},
banner: function() {
reply.send("220 <hostname> ESMTP smtpd.js");
},
error: function(s) {
reply.send("500 " + s);
},
ok: function() {
reply.send("250 OK");
}
}
// list of domains we will accept mail for
var allowed_domains = [
"example.com",
"mail.example.com"
]
function Command( line ) {
function parseCommand( line ) {
for( var cmd in command_patterns) {
if (command_patterns[ cmd ].test( buffer ) ) {
return cmd;
}
}
}
function extractArguments( command ) {
return this.line.replace( command, '' ).replace(/^\s\s*/, '').replace(/\s\s*$/, '');
}
this.cmd = parseCommand( line );
if(typeof this.cmd != 'undefined'){
this.cmd = this.cmd.toLowerCase();
}
this.line = line;
this.in_data = false;
this.data = [];
this.isRecognized = function() {
return typeof this.cmd != "undefined";
}
this.exec = function() {
if ( callbacks[this.cmd] ) {
callbacks[this.cmd].callback(this.line);
}
else {
reply.error("command not implemented");
}
}
var that = this;
var callbacks = {
quit: {
callback: function () {
reply.send( '221 <hostname> closing connection' );
socket.end();
}
},
ehlo: {
callback: function() {
var hostname = extractArguments( 'EHLO' );
reply.send('250-<hostname> Hello ' + socket.remoteAddress );
reply.send('250 8BITMIME');
}
},
helo: {
callback: function() {
reply.send('250 <hostname> Hello ' + socket.remoteAddress );
}
},
from: {
callback: function() {
this.from = extractArguments( 'MAIL FROM:' );
reply.ok();
}
},
rcpt: {
callback: function() {
this.recipient = extractArguments( 'RCPT TO:' );
// only accept email to domains we want email for
var index = allowed_domains.indexOf(this.recipient.split("@")[1].replace(">",""))
if(index > -1){
reply.ok();
} else {
reply.send('550 Not accepting mail for ' + this.recipient);
}
}
},
data: {
callback: function() {
that.in_data = true;
reply.send("354 Terminate with line containing only '.'");
}
}
}
this.appendData = function( buffer ) {
var size = buffer.length;
var line = "";
for( var i = 0; i < buffer.length; i++ ){
var chr = buffer[i];
if( chr + buffer[i + 1] == eol ) {
this.data.push( line );
line = "";
i++;
continue;
}
line += chr;
}
var size = this.data.length - 1;
if( this.data[size] == '.' && typeof(this.data[size+1])=="undefined" ) {
this.in_data = false;
//this used to pop twice. that doesn't make sense....
this.data.pop();
}
}
return this;
}
function debug(s) {
if( enable_debug && s != null ) {
console.log( s.toString() + eol );
console.log('----------------------------' + eol );
}
}
socket.addListener('connect', function() {
reply.banner();
});
var buffer = "";
var cmd = {};
socket.addListener('data', function(packet) {
buffer += packet;
while( buffer.indexOf(eol) != -1 ) {
if ( cmd.in_data ) {
cmd.appendData( buffer );
// we're finished
if( !cmd.in_data ) {
reply.ok();
console.log(cmd.data)
}
}
else {
cmd = Command( buffer );
if ( cmd.isRecognized() ) {
cmd.exec();
} else {
reply.error('unrecognized command');
}
}
buffer = "";
}
});
socket.addListener('eof', function(){
socket.end();
});
});
server.listen( 25, "0.0.0.0" );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment