Permalink
Browse files

Merge pull request #1075 from msimerson/early_talker

Early talker
  • Loading branch information...
msimerson committed Jul 27, 2015
2 parents 759a353 + 8200c1c commit c9a7e7462aef2c2729be9c5e5cfaaf55810b3d76
View
@@ -0,0 +1,6 @@
; delay in seconds
pause=5
; terminate the connection? (default: true)
; reject=false

This file was deleted.

Oops, something went wrong.
View
@@ -178,8 +178,6 @@ function Connection(client, server) {
this.transaction = null;
this.tran_count = 0;
this.capabilities = null;
this.early_talker_delay = config.get('early_talker.pause') ||
config.get('early_talker_delay') || 1000;
this.banner_includes_uuid =
config.get('banner_includes_uuid') ? true : false;
this.deny_includes_uuid = config.get('deny_includes_uuid') || null;
@@ -373,8 +371,7 @@ Connection.prototype._process_data = function() {
this.logdebug('[early_talker] state=' + this.state + ' esmtp=' + this.esmtp + ' line="' + this_line + '"');
}
this.early_talker = true;
// If you talk early, we're going to give you a delay
setTimeout(function() { self._process_data(); }, this.early_talker_delay);
self._process_data();
break;
}
else if ((this.state === states.STATE_PAUSE || this.state === states.STATE_PAUSE_SMTP) && this.esmtp) {
@@ -415,9 +412,7 @@ Connection.prototype._process_data = function() {
' esmtp=' + this.esmtp + ' line="' + this_line + '"');
}
this.early_talker = true;
setTimeout(function() {
self._process_data();
}, this.early_talker_delay);
self._process_data();
}
break;
}
@@ -1,20 +1,20 @@
early\_talker
============
Early talkers are violators of the SMTP specification, which demands that
clients must wait for responses before sending the next command.
Early talkers are violators of the SMTP specification, which require that
clients must wait for certain responses before sending the next command.
Early talker detection is handled internally by Haraka (in connection.js).
This plugin introduces a configurable delay before the connection banner
and after the DATA command for Haraka to detect if it talks early.
At the DATA command, this plugin checks to see if an early talker was
detected.
Any plugin can detect early talkers by checking connection.early\_talker.
If an early talker is detected at connection or DATA, then a DENY is
returned with the message 'You talk too soon'.
Configuration
-------------
* early\_talker.pause
The config file early\_talker.ini has two options:
- pause: the delay in seconds before each SMTP command. Default is no pause.
Specifies a delay in milliseconds to delay before each SMTP command before
sending the response, while waiting for early talkers. Default is no pause.
- reject: whether or not to reject for early talkers. Default is true;
View
@@ -2,14 +2,28 @@
exports.register = function() {
var plugin = this;
plugin.load_config();
plugin.register_hook('data', 'early_talker');
plugin.register_hook('connect_init', 'early_talker');
plugin.register_hook('data', 'early_talker');
};
exports.load_config = function () {
var plugin = this;
plugin.cfg = plugin.config.get('early_talker.ini', {
booleans: [
'+main.reject',
]
},
function () {
plugin.load_config();
});
if (plugin.cfg.main && plugin.cfg.main.pause) {
plugin.pause = plugin.cfg.main.pause * 1000;
return;
}
// config/early_talker.pause is in milliseconds
plugin.pause = plugin.config.get('early_talker.pause', function () {
plugin.load_config();
@@ -18,17 +32,35 @@ exports.load_config = function () {
exports.early_talker = function(next, connection) {
var plugin = this;
if (!plugin.pause ) { return next(); } // config set to 0
if (connection.relaying) { // Don't pause AUTH/RELAY clients
if (!plugin.pause) return next();
if (connection.relaying) { // Don't delay AUTH/RELAY clients
if (connection.early_talker) {
connection.results.add(plugin,
{ skip: 'an early talking relaying client?!'});
connection.results.add(plugin, { skip: 'relay client'});
}
return next();
}
setTimeout(function () {
if (!connection.early_talker) { return next(); }
next(DENYDISCONNECT, "You talk too soon");
}, plugin.pause);
var check = function () {
if (!connection) return next();
if (!connection.early_talker) {
connection.results.add(plugin, {pass: 'early'});
return next();
}
connection.results.add(plugin, {fail: 'early'});
if (!plugin.cfg.main.reject) return next();
return next(DENYDISCONNECT, "You talk too soon");
};
var pause = plugin.pause;
if (plugin.hook === 'connect_init') {
var elapsed = (Date.now() - connection.start_time);
if (elapsed > plugin.pause) {
// Something else already waited
return check();
}
pause = plugin.pause - elapsed;
}
setTimeout(function () { check(); }, pause);
};
@@ -8,6 +8,7 @@ var _set_up = function (callback) {
this.plugin = new Plugin('early_talker');
this.plugin.config = config;
this.plugin.cfg = { main: { reject: true } };
this.connection = Connection.createConnection();
callback();
@@ -51,4 +52,19 @@ exports.early_talker = {
this.connection.early_talker = true;
this.plugin.early_talker(next, this.connection);
},
'is an early talker, reject=false': function (test) {
test.expect(4);
var before = Date.now();
var next = function (rc, msg) {
test.ok(Date.now() >= before + 1000);
test.equal(undefined, rc);
test.equal(undefined, msg);
test.ok(this.connection.results.has('early_talker', 'fail', 'early'));
test.done();
}.bind(this);
this.plugin.pause = 1000;
this.plugin.cfg.main.reject = false;
this.connection.early_talker = true;
this.plugin.early_talker(next, this.connection);
},
};

0 comments on commit c9a7e74

Please sign in to comment.