diff --git a/server.js b/server.js index bc87db9..5668fdf 100644 --- a/server.js +++ b/server.js @@ -61,15 +61,36 @@ POP3Server.prototype.listen = function(port, callback){ var self = this; this.socket = net.createServer(function(socket){ var connection_id = ++self.COUNTER; - var cnx = new POP3Connnection(self, socket, connection_id); + var connection = new POP3Connnection(self, socket, connection_id); socket.on('data', function (data) { - self.onData(cnx, data); + self.onData(connection, data); + }); + socket.on('end', function (data) { + self.onEnd(connection, data); + }); + socket.on('error', function (err) { + self.onError(connection, err); + }); + }).listen(port, callback); +}; + +POP3Server.prototype.listenSSL = function(port, callback){ + var self = this; + if (!this.options.tls) { + return callback(new Error('listenSSL require tls options.')); + } + this.socket = tls.createServer(this.options.tls, function(socket){ + var connection_id = ++self.COUNTER; + var connection = new POP3Connnection(self, socket, connection_id); + connection.secure = true; + socket.on('data', function (data) { + self.onData(connection, data); }); socket.on('end', function (data) { - self.onEnd(cnx, data); + self.onEnd(connection, data); }); socket.on('error', function (err) { - self.onError(cnx, err); + self.onError(connection, err); }); }).listen(port, callback); }; @@ -154,11 +175,16 @@ POP3Server.prototype.cmdCAPA = function (connection, params) { if (this.authMethods) { var methods = []; for (var i in this.authMethods) { - if (this.authMethods.hasOwnProperty(i)) + if (this.authMethods.hasOwnProperty(i)) { methods.push(i); + } } - if (methods.length && connection.state == States.AUTHENTICATION) + if (methods.length && connection.state == States.AUTHENTICATION) { connection.response("SASL " + methods.join(" ")); + } + } + if (this.options.tls && !connection.secure && connection.state === States.AUTHENTICATION) { + connection.response("STLS"); } connection.response("."); } @@ -185,14 +211,16 @@ POP3Server.prototype.cmdQUIT = function (connection) { POP3Server.prototype.cmdSTLS = function (connection) { var self = this; - + if (connection.secure) { + return connection.response("-ERR Command not permitted when TLS active"); + } if (connection.state != States.AUTHENTICATION) { - return connection.response("-ERR Only allowed in authentication mode"); + return connection.response("-ERR Unknown command: STLS"); } if (!this.options.tls) { - return connection.response("-ERR invalid command"); + return connection.response("-ERR Unknown command: STLS"); } - connection.response("+OK begin TLS negotiation"); + connection.response("+OK Begin TLS negotiation now."); connection.socket.removeAllListeners(); var socketOptions = { secureContext: tls.createSecureContext(self.options.tls), @@ -201,6 +229,7 @@ POP3Server.prototype.cmdSTLS = function (connection) { connection.socket = new tls.TLSSocket(connection.socket, socketOptions); connection.socket.on('secure', function () { + connection.secure = true; connection.socket.on('data', function (data) { self.onData(connection, data); }); diff --git a/tests/index.js b/tests/index.js index c1ee490..91e1d7b 100644 --- a/tests/index.js +++ b/tests/index.js @@ -1,10 +1,19 @@ var expect = require('chai').expect; var net = require('net'); +var fs = require('fs'); +var path = require('path'); var POP3Client = require('mailx/lib/poplib'); var POP3Server = require('../'); var mailx = require('mailx'); var mailcomposer = require("mailcomposer"); -var PORT = 5050; + +const PORT = 11000; +const SSL_PORT = 9930; + +var tlsOptions = { + key: fs.readFileSync(path.join(__dirname, '../cert/privatekey.pem')), + cert: fs.readFileSync(path.join(__dirname, '../cert/certificate.pem')) +}; describe('POP3 server', function(){ var pop3Server; @@ -13,52 +22,7 @@ describe('POP3 server', function(){ before(function(done){ var messageStore = {}, authHandler = {}; pop3Server = new POP3Server({ - tls: { - key: '-----BEGIN RSA PRIVATE KEY-----\n' + - 'MIIEpAIBAAKCAQEA6Z5Qqhw+oWfhtEiMHE32Ht94mwTBpAfjt3vPpX8M7DMCTwHs\n' + - '1xcXvQ4lQ3rwreDTOWdoJeEEy7gMxXqH0jw0WfBx+8IIJU69xstOyT7FRFDvA1yT\n' + - 'RXY2yt9K5s6SKken/ebMfmZR+03ND4UFsDzkz0FfgcjrkXmrMF5Eh5UXX/+9YHeU\n' + - 'xlp0gMAt+/SumSmgCaysxZLjLpd4uXz+X+JVxsk1ACg1NoEO7lWJC/3WBP7MIcu2\n' + - 'wVsMd2XegLT0gWYfT1/jsIH64U/mS/SVXC9QhxMl9Yfko2kx1OiYhDxhHs75RJZh\n' + - 'rNRxgfiwgSb50Gw4NAQaDIxr/DJPdLhgnpY6UQIDAQABAoIBAE+tfzWFjJbgJ0ql\n' + - 's6Ozs020Sh4U8TZQuonJ4HhBbNbiTtdDgNObPK1uNadeNtgW5fOeIRdKN6iDjVeN\n' + - 'AuXhQrmqGDYVZ1HSGUfD74sTrZQvRlWPLWtzdhybK6Css41YAyPFo9k4bJ2ZW2b/\n' + - 'p4EEQ8WsNja9oBpttMU6YYUchGxo1gujN8hmfDdXUQx3k5Xwx4KA68dveJ8GasIt\n' + - 'd+0Jd/FVwCyyx8HTiF1FF8QZYQeAXxbXJgLBuCsMQJghlcpBEzWkscBR3Ap1U0Zi\n' + - '4oat8wrPZGCblaA6rNkRUVbc/+Vw0stnuJ/BLHbPxyBs6w495yBSjBqUWZMvljNz\n' + - 'm9/aK0ECgYEA9oVIVAd0enjSVIyAZNbw11ElidzdtBkeIJdsxqhmXzeIFZbB39Gd\n' + - 'bjtAVclVbq5mLsI1j22ER2rHA4Ygkn6vlLghK3ZMPxZa57oJtmL3oP0RvOjE4zRV\n' + - 'dzKexNGo9gU/x9SQbuyOmuauvAYhXZxeLpv+lEfsZTqqrvPUGeBiEQcCgYEA8poG\n' + - 'WVnykWuTmCe0bMmvYDsWpAEiZnFLDaKcSbz3O7RMGbPy1cypmqSinIYUpURBT/WY\n' + - 'wVPAGtjkuTXtd1Cy58m7PqziB7NNWMcsMGj+lWrTPZ6hCHIBcAImKEPpd+Y9vGJX\n' + - 'oatFJguqAGOz7rigBq6iPfeQOCWpmprNAuah++cCgYB1gcybOT59TnA7mwlsh8Qf\n' + - 'bm+tSllnin2A3Y0dGJJLmsXEPKtHS7x2Gcot2h1d98V/TlWHe5WNEUmx1VJbYgXB\n' + - 'pw8wj2ACxl4ojNYqWPxegaLd4DpRbtW6Tqe9e47FTnU7hIggR6QmFAWAXI+09l8y\n' + - 'amssNShqjE9lu5YDi6BTKwKBgQCuIlKGViLfsKjrYSyHnajNWPxiUhIgGBf4PI0T\n' + - '/Jg1ea/aDykxv0rKHnw9/5vYGIsM2st/kR7l5mMecg/2Qa145HsLfMptHo1ZOPWF\n' + - '9gcuttPTegY6aqKPhGthIYX2MwSDMM+X0ri6m0q2JtqjclAjG7yG4CjbtGTt/UlE\n' + - 'WMlSZwKBgQDslGeLUnkW0bsV5EG3AKRUyPKz/6DVNuxaIRRhOeWVKV101claqXAT\n' + - 'wXOpdKrvkjZbT4AzcNrlGtRl3l7dEVXTu+dN7/ZieJRu7zaStlAQZkIyP9O3DdQ3\n' + - 'rIcetQpfrJ1cAqz6Ng0pD0mh77vQ13WG1BBmDFa2A9BuzLoBituf4g==\n' + - '-----END RSA PRIVATE KEY-----', - cert: '-----BEGIN CERTIFICATE-----\n' + - 'MIICpDCCAYwCCQCuVLVKVTXnAjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwls\n' + - 'b2NhbGhvc3QwHhcNMTUwMjEyMTEzMjU4WhcNMjUwMjA5MTEzMjU4WjAUMRIwEAYD\n' + - 'VQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDp\n' + - 'nlCqHD6hZ+G0SIwcTfYe33ibBMGkB+O3e8+lfwzsMwJPAezXFxe9DiVDevCt4NM5\n' + - 'Z2gl4QTLuAzFeofSPDRZ8HH7wgglTr3Gy07JPsVEUO8DXJNFdjbK30rmzpIqR6f9\n' + - '5sx+ZlH7Tc0PhQWwPOTPQV+ByOuReaswXkSHlRdf/71gd5TGWnSAwC379K6ZKaAJ\n' + - 'rKzFkuMul3i5fP5f4lXGyTUAKDU2gQ7uVYkL/dYE/swhy7bBWwx3Zd6AtPSBZh9P\n' + - 'X+OwgfrhT+ZL9JVcL1CHEyX1h+SjaTHU6JiEPGEezvlElmGs1HGB+LCBJvnQbDg0\n' + - 'BBoMjGv8Mk90uGCeljpRAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABXm8GPdY0sc\n' + - 'mMUFlgDqFzcevjdGDce0QfboR+M7WDdm512Jz2SbRTgZD/4na42ThODOZz9z1AcM\n' + - 'zLgx2ZNZzVhBz0odCU4JVhOCEks/OzSyKeGwjIb4JAY7dh+Kju1+6MNfQJ4r1Hza\n' + - 'SVXH0+JlpJDaJ73NQ2JyfqELmJ1mTcptkA/N6rQWhlzycTBSlfogwf9xawgVPATP\n' + - '4AuwgjHl12JI2HVVs1gu65Y3slvaHRCr0B4+Kg1GYNLLcbFcK+NEHrHmPxy9TnTh\n' + - 'Zwp1dsNQU+Xkylz8IUANWSLHYZOMtN2e5SKIdwTtl5C8YxveuY8YKb1gDExnMraT\n' + - 'VGXQDqPleug=\n' + - '-----END CERTIFICATE-----' - }, + tls: tlsOptions, auth: function(user, checkPassword) { var password = false; if (user === 'jdoe' || user === 'jdoe2') { @@ -96,7 +60,12 @@ describe('POP3 server', function(){ } } }); - pop3Server.listen(PORT, done); + pop3Server.listen(PORT, function(err) { + if (err) { + return done(err); + } + pop3Server.listenSSL(SSL_PORT, done); + }); }); it('It should be listening on the appropriate port', function(done){ @@ -118,6 +87,7 @@ describe('POP3 server', function(){ return done(); }); }); + it('Should support QUIT command', function(done){ var client = new POP3Client(PORT, 'localhost'); client.connect(function(err, raw) { @@ -134,6 +104,27 @@ describe('POP3 server', function(){ }); }); + it('Should support STLS command', function(done){ + var client = new POP3Client(PORT, 'localhost', {ignoretlserrs: true}); + client.connect(function(err, raw) { + if (err) { + return done(err); + } + client.stls(function(err, raw) { + if (err) { + return done(err); + } + client.quit(function(err, raw) { + if (err) { + return done(err); + } + expect(raw).equal('+OK POP3 Server signing off\r\n'); + done(); + }); + }); + }); + }); + it('Should Accept valid Login credentials via USER-PASS sequence', function(done){ var client = new POP3Client(PORT, 'localhost'); client.connect(function(err, raw) {