Skip to content

Commit

Permalink
v3.13.0
Browse files Browse the repository at this point in the history
  • Loading branch information
andris9 committed Aug 15, 2023
1 parent 83be6b2 commit ce14fcc
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 23 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## v3.13.0 2023-08-15

- new event handler `onSecure`
- specified license identifier in package.json (from `MIT` to `MIT-0`)

## v3.12.0 2023-05-24

- new option `authRequiredMessage`
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2015-2022 Andris Reinman
Copyright (c) 2015-2023 Andris Reinman

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
55 changes: 40 additions & 15 deletions lib/smtp-connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,21 @@ class SMTPConnection extends EventEmitter {

this._resetSession();

let onSecureIfNeeded = next => {
if (!this.session.secure) {
// no TLS
return next();
}

this.session.servername = this._socket.servername;
this._server.onSecure(this._socket, this.session, err => {
if (err) {
return this._onError(err);
}
next();
});
};

this._server.onConnect(this.session, err => {
this._server.logger.info(
{
Expand All @@ -177,20 +192,25 @@ class SMTPConnection extends EventEmitter {
return this.close();
}

this._ready = true; // Start accepting data from input
onSecureIfNeeded(() => {
this._ready = true; // Start accepting data from input

if (!this._server.options.useXClient && !this._server.options.useXForward) {
this.emitConnection();
}
if (!this._server.options.useXClient && !this._server.options.useXForward) {
this.emitConnection();
}

this.send(
220,
this.name + ' ' + (this._server.options.lmtp ? 'LMTP' : 'ESMTP') + (this._server.options.banner ? ' ' + this._server.options.banner : '')
);
this.send(
220,
this.name +
' ' +
(this._server.options.lmtp ? 'LMTP' : 'ESMTP') +
(this._server.options.banner ? ' ' + this._server.options.banner : '')
);

if (typeof next === 'function') {
next();
}
if (typeof next === 'function') {
next();
}
});
});
};

Expand Down Expand Up @@ -490,7 +510,10 @@ class SMTPConnection extends EventEmitter {

// Check if authentication is required
if (!this.session.user && this._isSupported('AUTH') && ['MAIL', 'RCPT', 'DATA'].includes(commandName) && !this._server.options.authOptional) {
this.send(530, typeof this._server.options.authRequiredMessage === 'string' ? this._server.options.authRequiredMessage : 'Error: authentication Required');
this.send(
530,
typeof this._server.options.authRequiredMessage === 'string' ? this._server.options.authRequiredMessage : 'Error: authentication Required'
);
return setImmediate(callback);
}

Expand Down Expand Up @@ -1435,13 +1458,15 @@ class SMTPConnection extends EventEmitter {
'Connection upgraded to TLS using ',
cipher || 'N/A'
);
this._server.onSecure(this._socket, this.session, () => {

this._server.onSecure(this._socket, this.session, err => {
if (err) {
return this._onError(err);
}
this._socket.pipe(this._parser);
if (typeof secureCallback === 'function') {
secureCallback();
}
})
});
});
}
}
Expand Down
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
{
"name": "smtp-server",
"version": "3.12.0",
"version": "3.13.0",
"description": "Create custom SMTP servers on the fly",
"main": "lib/smtp-server.js",
"scripts": {
"test": "grunt"
},
"author": "Andris Reinman",
"license": "MIT",
"license": "MIT-0",
"dependencies": {
"base32.js": "0.1.0",
"ipv6-normalize": "1.0.1",
"nodemailer": "6.9.2"
"nodemailer": "6.9.4"
},
"devDependencies": {
"chai": "4.3.7",
"eslint-config-nodemailer": "1.2.0",
"eslint-config-prettier": "8.8.0",
"eslint-config-prettier": "9.0.0",
"grunt": "1.6.1",
"grunt-cli": "1.4.3",
"grunt-eslint": "24.1.0",
"grunt-eslint": "24.3.0",
"grunt-mocha-test": "0.13.3",
"mocha": "10.2.0",
"pem": "1.14.7"
"pem": "1.14.8"
},
"repository": {
"type": "git",
Expand All @@ -35,6 +35,6 @@
"SMTP"
],
"engines": {
"node": ">=6.0.0"
"node": ">=12.0.0"
}
}
165 changes: 165 additions & 0 deletions test/smtp-connection-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1477,4 +1477,169 @@ describe('SMTPServer', function () {
});
});
});

describe('onSecure handler', function () {
let PORT = 1336;

it('should detect once a connection is established with TLS', function (done) {
let server;
pem.createCertificate({ days: 1, selfSigned: true }, (err, keys) => {
if (err) {
return done(err);
}

let secureCount = 0;

server = new SMTPServer({
secure: true,
logger: false,
key: keys.serviceKey,
cert: keys.certificate,

onSecure(socket, session, done) {
expect(session).to.exist;
expect(session.servername).to.equal('teretere1');
secureCount++;
done();
}
});

server.listen(PORT, '127.0.0.1');

let connection = new Client({
port: PORT,
host: '127.0.0.1',
secure: true,
tls: {
rejectUnauthorized: false,
servername: 'teretere1'
}
});

connection.connect(function () {
setTimeout(() => {
connection.quit();
server.close(() => {
expect(secureCount).to.equal(1);
done();
});
}, 100);
});

connection.on('error', err => {
server.close(() => done(err));
});
});
});

it('should detect once a connection is upgraded to TLS', function (done) {
let server;
pem.createCertificate({ days: 1, selfSigned: true }, (err, keys) => {
if (err) {
return done(err);
}

let secureCount = 0;

server = new SMTPServer({
secure: false,
logger: false,
key: keys.serviceKey,
cert: keys.certificate,

onSecure(socket, session, done) {
expect(session).to.exist;
expect(session.servername).to.equal('teretere2');
secureCount++;
done();
},
onConnect(session, done) {
done();
}
});

server.listen(PORT, '127.0.0.1');

let connection = new Client({
port: PORT,
host: '127.0.0.1',
secure: false,
tls: {
rejectUnauthorized: false,
servername: 'teretere2'
}
});

connection.connect(function () {
setTimeout(() => {
connection.quit();
server.close(() => {
expect(secureCount).to.equal(1);
done();
});
}, 100);
});

connection.on('error', err => {
server.close(() => done(err));
});
});
});

it('onSecure is not triggered for cleartext connections', function (done) {
let server;
pem.createCertificate({ days: 1, selfSigned: true }, (err, keys) => {
if (err) {
return done(err);
}

let secureCount = 0;

server = new SMTPServer({
secure: false,
logger: false,
key: keys.serviceKey,
cert: keys.certificate,

onSecure(socket, session, done) {
expect(session).to.exist;
expect(session.servername).to.equal('teretere2');
secureCount++;
done();
},

onConnect(session, done) {
done();
}
});

server.listen(PORT, '127.0.0.1');

let connection = new Client({
port: PORT,
host: '127.0.0.1',
secure: false,
ignoreTLS: true,
tls: {
rejectUnauthorized: false,
servername: 'teretere2'
}
});

connection.connect(function () {
setTimeout(() => {
connection.quit();
server.close(() => {
expect(secureCount).to.equal(0);
done();
});
}, 100);
});

connection.on('error', err => {
server.close(() => done(err));
});
});
});
});
});

0 comments on commit ce14fcc

Please sign in to comment.