Haraka 3.0.5 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CONTRIBUTORS.md +6 -5
  2. package/Changes.md +172 -27
  3. package/LICENSE +1 -1
  4. package/Plugins.md +3 -3
  5. package/bin/haraka +2 -1
  6. package/config/connection.ini +63 -0
  7. package/config/smtp.ini +0 -18
  8. package/connection.js +53 -63
  9. package/docs/CoreConfig.md +17 -78
  10. package/docs/HAProxy.md +10 -28
  11. package/docs/Outbound.md +8 -9
  12. package/docs/plugins/aliases.md +4 -6
  13. package/docs/plugins/block_me.md +2 -4
  14. package/docs/plugins/data.signatures.md +2 -4
  15. package/docs/plugins/max_unrecognized_commands.md +2 -4
  16. package/docs/plugins/prevent_credential_leaks.md +2 -5
  17. package/docs/plugins/process_title.md +1 -2
  18. package/docs/plugins/queue/test.md +9 -0
  19. package/docs/plugins/rcpt_to.in_host_list.md +1 -2
  20. package/docs/plugins/rcpt_to.max_count.md +2 -10
  21. package/docs/plugins/record_envelope_addresses.md +3 -6
  22. package/docs/plugins/relay.md +1 -155
  23. package/docs/plugins/reseed_rng.md +1 -2
  24. package/docs/plugins/tarpit.md +7 -10
  25. package/docs/plugins/toobusy.md +2 -4
  26. package/docs/plugins/xclient.md +1 -2
  27. package/eslint.config.mjs +27 -0
  28. package/logger.js +1 -8
  29. package/outbound/hmail.js +8 -1
  30. package/package.json +56 -53
  31. package/plugins/auth/auth_base.js +0 -1
  32. package/plugins/queue/test.js +5 -3
  33. package/server.js +0 -14
  34. package/test/connection.js +18 -7
  35. package/.eslintrc.yaml +0 -11
  36. package/config/connection_close_message +0 -1
  37. package/config/databytes +0 -1
  38. package/config/dhparams.pem +0 -8
  39. package/config/early_talker.ini +0 -11
  40. package/config/mail_from.is_resolvable.ini +0 -6
  41. package/config/max_unrecognized_commands +0 -1
  42. package/config/smtp.json +0 -17
  43. package/docs/plugins/early_talker.md +0 -22
  44. package/docs/plugins/mail_from.is_resolvable.md +0 -29
  45. package/plugins/early_talker.js +0 -155
  46. package/plugins/mail_from.is_resolvable.js +0 -132
  47. package/plugins/relay.js +0 -207
  48. package/test/plugins/early_talker.js +0 -104
  49. package/test/plugins/mail_from.is_resolvable.js +0 -35
  50. package/test/plugins/relay.js +0 -303
  51. /package/docs/{plugins → deprecated}/access.md +0 -0
  52. /package/docs/{plugins → deprecated}/backscatterer.md +0 -0
  53. /package/docs/{plugins → deprecated}/data.headers.md +0 -0
@@ -1,22 +0,0 @@
1
- early\_talker
2
- ============
3
-
4
- Early talkers are violators of the SMTP specification, which require that
5
- clients must wait for certain responses before sending the next command.
6
-
7
- This plugin introduces a configurable delay before the connection banner
8
- and after the DATA command for Haraka to detect if it talks early.
9
-
10
- If an early talker is detected at connection or DATA, then a DENY is
11
- returned with the message 'You talk too soon'.
12
-
13
- Configuration
14
- -------------
15
-
16
- The config file early\_talker.ini has two options:
17
-
18
- - pause: the delay in seconds before each SMTP command. Default is no pause.
19
-
20
- - reject: whether or not to reject for early talkers. Default is true;
21
-
22
- - [ip_whitelist]: list of IP addresses and/or subnets to whitelist
@@ -1,29 +0,0 @@
1
- mail\_from.is\_resolvable
2
- =======================
3
-
4
- This plugin checks that the domain used in MAIL FROM is resolvable to an MX
5
- record.
6
-
7
- Configuration
8
- -------------
9
-
10
- This plugin uses the INI-style file format and accepts the following options:
11
-
12
- * timeout
13
-
14
- Default: 30
15
-
16
- Maximum limit in seconds for queries to complete. If the timeout is
17
- reached a TEMPFAIL is returned to the client.
18
-
19
- * allow\_mx\_ip=[0|1]
20
-
21
- Allow MX records that return IP addresses instead of hostnames.
22
- This is not allowed as per the RFC, but some MTAs allow it.
23
-
24
- * reject\_no\_mx=[0|1]
25
-
26
- Return DENY and reject the command if no MX record is found. Otherwise a
27
- DENYSOFT (TEMPFAIL) is returned and the client will retry later.
28
-
29
- DNS errors always return DENYSOFT, so this should be safe to enable.
@@ -1,155 +0,0 @@
1
- // This plugin checks for clients that talk before we sent a response
2
-
3
- const { isIPv6 } = require('node:net');
4
-
5
- const ipaddr = require('ipaddr.js');
6
-
7
- exports.register = function () {
8
- this.load_config();
9
- this.register_hook('connect_init', 'early_talker');
10
- this.register_hook('data', 'early_talker');
11
- }
12
-
13
- exports.load_config = function () {
14
-
15
- this.cfg = this.config.get('early_talker.ini', {
16
- booleans: [
17
- '+main.reject'
18
- ]
19
- },
20
- () => {
21
- this.load_config();
22
- });
23
-
24
- // Generate a white list of IP addresses
25
- this.whitelist = this.load_ip_list(Object.keys(this.cfg.ip_whitelist));
26
-
27
- if (this.cfg.main?.pause) {
28
- this.pause = this.cfg.main.pause * 1000;
29
- return;
30
- }
31
-
32
- // config/early_talker.pause is in milliseconds
33
- this.pause = this.config.get('early_talker.pause', () => {
34
- this.load_config();
35
- });
36
- }
37
-
38
- exports.early_talker = function (next, connection) {
39
- const plugin = this;
40
- if (!plugin.pause) return next();
41
- if (!plugin.should_check(connection)) return next();
42
-
43
- function check () {
44
- if (!connection) return next();
45
- if (!connection.early_talker) {
46
- connection.results.add(plugin, {pass: 'early'});
47
- return next();
48
- }
49
- connection.results.add(plugin, {fail: 'early'});
50
- if (!plugin.cfg.main.reject) return next();
51
- return next(DENYDISCONNECT, "You talk too soon");
52
- }
53
-
54
- let { pause } = plugin;
55
- if (plugin.hook === 'connect_init') {
56
- const elapsed = (Date.now() - connection.start_time);
57
- if (elapsed > plugin.pause) {
58
- // Something else already waited
59
- return check();
60
- }
61
- pause = plugin.pause - elapsed;
62
- }
63
-
64
- setTimeout(() => { check(); }, pause);
65
- }
66
-
67
-
68
- /**
69
- * Check if an ip is whitelisted
70
- *
71
- * @param {String} ip The remote IP to verify
72
- * @return {Boolean} True if is whitelisted
73
- */
74
- exports.ip_in_list = function (ip) {
75
-
76
- if (!this.whitelist) return false;
77
-
78
- const ipobj = ipaddr.parse(ip);
79
-
80
- for (const element of this.whitelist) {
81
- try {
82
- if (ipobj.match(element)) {
83
- return true;
84
- }
85
- }
86
- catch (ignore) {
87
- }
88
- }
89
- return false;
90
- }
91
-
92
-
93
- /**
94
- * Convert config ip to ipaddr objects
95
- *
96
- * @param {Array} list A list of IP addresses / subnets
97
- * @return {Array} The converted array
98
- */
99
- exports.load_ip_list = list => {
100
- const whitelist = [];
101
-
102
- for (const element of list) {
103
- try {
104
- let addr = element;
105
- if (addr.match(/\/\d+$/)) {
106
- addr = ipaddr.parseCIDR(addr);
107
- }
108
- else {
109
- addr = ipaddr.parseCIDR(addr + ((isIPv6(addr)) ? '/128' : '/32'));
110
- }
111
-
112
- whitelist.push(addr);
113
- }
114
- catch (ignore) {
115
- }
116
- }
117
- return whitelist;
118
- }
119
-
120
- exports.should_check = function (connection) {
121
- // Skip delays for privileged senders
122
-
123
- if (connection.notes.auth_user) {
124
- connection.results.add(this, { skip: 'authed'});
125
- return false;
126
- }
127
-
128
- if (connection.relaying) {
129
- connection.results.add(this, { skip: 'relay'});
130
- return false;
131
- }
132
-
133
- if (this.ip_in_list(connection.remote.ip)) {
134
- connection.results.add(this, { skip: 'whitelist' });
135
- return false;
136
- }
137
-
138
- const karma = connection.results.get('karma');
139
- if (karma && karma.good > 0) {
140
- connection.results.add(this, { skip: '+karma' });
141
- return false;
142
- }
143
-
144
- if (connection.remote.is_local) {
145
- connection.results.add(this, { skip: 'local_ip'});
146
- return false;
147
- }
148
-
149
- if (connection.remote.is_private) {
150
- connection.results.add(this, { skip: 'private_ip'});
151
- return false;
152
- }
153
-
154
- return true;
155
- }
@@ -1,132 +0,0 @@
1
- 'use strict';
2
-
3
- // Check MAIL FROM domain is resolvable to an MX
4
- const net = require('node:net');
5
-
6
- const net_utils = require('haraka-net-utils');
7
-
8
- exports.register = function () {
9
- this.load_ini();
10
- }
11
-
12
- exports.load_ini = function () {
13
- this.cfg = this.config.get('mail_from.is_resolvable.ini', {
14
- booleans: [
15
- '-main.allow_mx_ip',
16
- '+reject.no_mx',
17
- ],
18
- }, () => {
19
- this.load_ini();
20
- });
21
-
22
- // compat. Sunset 4.0
23
- if (this.cfg.main.reject_no_mx) {
24
- this.cfg.reject.no_mx = this.cfg.main.reject_no_mx
25
- }
26
-
27
- if (isNaN(this.cfg.main.timeout)) {
28
- this.cfg.main.timeout = 29;
29
- }
30
-
31
- if (this.timeout) {
32
- if (this.timeout <= this.cfg.main.timeout) {
33
- this.cfg.main.timeout = this.timeout - 1;
34
- this.logwarn(`reducing plugin timeout to ${this.cfg.main.timeout}s`);
35
- }
36
- }
37
-
38
- this.re_bogus_ip = new RegExp(this.cfg.main.re_bogus_ip ||
39
- '^(?:0\\.0\\.0\\.0|255\\.255\\.255\\.255|127\\.)' );
40
- }
41
-
42
- exports.hook_mail = function (next, connection, params) {
43
- const plugin = this;
44
- const mail_from = params[0];
45
- const txn = connection?.transaction;
46
- if (!txn) return next();
47
- const { results } = txn;
48
-
49
- // ignore MAIL FROM without an @
50
- if (!mail_from.host) {
51
- results.add(plugin, {skip: 'null host'});
52
- return next();
53
- }
54
-
55
- let called_next = 0;
56
- const domain = mail_from.host;
57
- const timeout_id = setTimeout(() => {
58
- connection.logdebug(plugin, `DNS timeout resolving MX for ${domain}`);
59
- called_next++;
60
- if (txn) results.add(plugin, {err: `timeout(${domain})`});
61
- next(DENYSOFT, 'Temporary resolver error (timeout)');
62
- }, this.cfg.main.timeout * 1000);
63
-
64
- function mxDone (code, reply) {
65
- if (called_next) return;
66
- clearTimeout(timeout_id);
67
- called_next++;
68
- next(...arguments);
69
- }
70
-
71
- function mxErr (err) {
72
- if (!connection.transaction) return;
73
- results.add(plugin, {err: `${domain}:${err.message}`});
74
- mxDone(DENYSOFT, `Temp. resolver error (${err.code})`);
75
- }
76
-
77
- connection.logdebug(plugin, `resolving MX for domain ${domain}`)
78
-
79
- net_utils
80
- .get_mx(domain)
81
- .then((exchanges) => {
82
- if (!txn) return;
83
-
84
- connection.logdebug(plugin, `${domain}: MX => ${JSON.stringify(exchanges)}`)
85
-
86
- if (!exchanges || !exchanges.length) {
87
- results.add(this, {fail: 'has_fwd_dns'});
88
- return mxDone(
89
- ((this.cfg.reject.no_mx) ? DENY : DENYSOFT),
90
- 'No MX for your FROM address'
91
- );
92
- }
93
-
94
- if (this.cfg.main.allow_mx_ip) {
95
- for (const mx of exchanges) {
96
- if (net.isIPv4(mx.exchange) && !this.re_bogus_ip.test(mx.exchange)) {
97
- txn.results.add(this, {pass: 'implicit_mx', emit: true});
98
- return mxDone()
99
- }
100
- if (net.isIPv6(mx.exchange) && !net_utils.ipv6_bogus(mx.exchange)) {
101
- txn.results.add(this, {pass: 'implicit_mx', emit: true});
102
- return mxDone()
103
- }
104
- }
105
- }
106
-
107
- // filter out the implicit MX and resolve the MX hostnames
108
- net_utils
109
- .resolve_mx_hosts(exchanges.filter(a => !net.isIP(a.exchange)))
110
- .then(resolved => {
111
- connection.logdebug(plugin, `resolved MX => ${JSON.stringify(resolved)}`);
112
-
113
- for (const mx of resolved) {
114
- if (net.isIPv4(mx.exchange) && !this.re_bogus_ip.test(mx.exchange)) {
115
- txn.results.add(this, {pass: 'has_fwd_dns', emit: true});
116
- return mxDone()
117
- }
118
- if (net.isIPv6(mx.exchange) && !net_utils.ipv6_bogus(mx.exchange)) {
119
- txn.results.add(this, {pass: 'has_fwd_dns', emit: true});
120
- return mxDone()
121
- }
122
- }
123
-
124
- mxDone(
125
- ((this.cfg.main.reject_no_mx) ? DENY : DENYSOFT),
126
- 'No valid MX for your FROM address'
127
- );
128
- })
129
- .catch(mxErr)
130
- })
131
- .catch(mxErr)
132
- }
package/plugins/relay.js DELETED
@@ -1,207 +0,0 @@
1
- // relay
2
- //
3
- // documentation via: haraka -h relay
4
-
5
- const net = require('node:net');
6
-
7
- const ipaddr = require('ipaddr.js');
8
-
9
- exports.register = function () {
10
-
11
- this.load_relay_ini(); // plugin.cfg = { }
12
-
13
- if (this.cfg.relay.acl) {
14
- this.load_acls(); // plugin.acl_allow = [..]
15
- this.register_hook('connect_init', 'acl');
16
- this.register_hook('connect', 'pass_relaying');
17
- }
18
-
19
- if (this.cfg.relay.force_routing || this.cfg.relay.dest_domains) {
20
- this.load_dest_domains(); // plugin.dest.domains = { }
21
- }
22
-
23
- if (this.cfg.relay.force_routing) {
24
- this.register_hook('get_mx', 'force_routing');
25
- }
26
-
27
- if (this.cfg.relay.dest_domains) {
28
- this.register_hook('rcpt', 'dest_domains');
29
- }
30
-
31
- if (this.cfg.relay.all) {
32
- this.register_hook('rcpt', 'all');
33
- }
34
- }
35
-
36
- exports.load_relay_ini = function () {
37
- this.cfg = this.config.get('relay.ini', {
38
- booleans: [
39
- '+relay.acl',
40
- '+relay.force_routing',
41
- '-relay.all',
42
- '-relay.dest_domains',
43
- ],
44
- }, () => {
45
- this.load_relay_ini();
46
- });
47
- }
48
-
49
- exports.load_dest_domains = function () {
50
- this.dest = this.config.get(
51
- 'relay_dest_domains.ini',
52
- 'ini',
53
- () => { this.load_dest_domains(); }
54
- );
55
- }
56
-
57
- exports.load_acls = function () {
58
- const file_name = 'relay_acl_allow';
59
-
60
- // load with a self-referential callback
61
- this.acl_allow = this.config.get(file_name, 'list', () => {
62
- this.load_acls();
63
- });
64
-
65
- for (let i=0; i<this.acl_allow.length; i++) {
66
- const cidr = this.acl_allow[i].split('/');
67
- if (!net.isIP(cidr[0])) {
68
- this.logerror(this, `invalid entry in ${file_name}: ${cidr[0]}`);
69
- }
70
- if (!cidr[1]) {
71
- this.logerror(this, `appending missing CIDR suffix in: ${file_name}`);
72
- this.acl_allow[i] = `${cidr[0] }/32`;
73
- }
74
- }
75
- }
76
-
77
- exports.acl = function (next, connection) {
78
- if (!this.cfg.relay.acl) { return next(); }
79
-
80
- connection.logdebug(this, `checking ${connection.remote.ip} in relay_acl_allow`);
81
-
82
- if (!this.is_acl_allowed(connection)) {
83
- connection.results.add(this, {skip: 'acl(unlisted)'});
84
- return next();
85
- }
86
-
87
- connection.results.add(this, {pass: 'acl'});
88
- connection.relaying = true;
89
- return next(OK);
90
- }
91
-
92
- exports.pass_relaying = (next, connection) => {
93
- if (connection.relaying) return next(OK);
94
-
95
- next();
96
- }
97
-
98
- exports.is_acl_allowed = function (connection) {
99
- if (!this.acl_allow) { return false; }
100
- if (!this.acl_allow.length) { return false; }
101
-
102
- const { ip } = connection.remote;
103
-
104
- for (const item of this.acl_allow) {
105
- connection.logdebug(this, `checking if ${ip} is in ${item}`);
106
- const cidr = item.split('/');
107
- const c_net = cidr[0];
108
- const c_mask = cidr[1] || 32;
109
-
110
- if (!net.isIP(c_net)) continue; // bad config entry
111
- if (net.isIPv4(ip) && net.isIPv6(c_net)) continue;
112
- if (net.isIPv6(ip) && net.isIPv4(c_net)) continue;
113
-
114
- if (ipaddr.parse(ip).match(ipaddr.parse(c_net), c_mask)) {
115
- connection.logdebug(this, `checking if ${ip} is in ${item}: yes`);
116
- return true;
117
- }
118
- }
119
- return false;
120
- }
121
-
122
- exports.dest_domains = function (next, connection, params) {
123
- if (!this.cfg.relay.dest_domains) { return next(); }
124
- const { relaying, transaction } = connection ?? {}
125
- if (!transaction) return next();
126
-
127
- // Skip this if the host is already allowed to relay
128
- if (relaying) {
129
- transaction.results.add(this, {skip: 'relay_dest_domain(relay)'});
130
- return next();
131
- }
132
-
133
- if (!this.dest) {
134
- transaction.results.add(this, {err: 'relay_dest_domain(no config!)'});
135
- return next();
136
- }
137
-
138
- if (!this.dest.domains) {
139
- transaction.results.add(this, {skip: 'relay_dest_domain(config)'});
140
- return next();
141
- }
142
-
143
- const dest_domain = params[0].host;
144
- connection.logdebug(this, `dest_domain = ${dest_domain}`);
145
-
146
- const dst_cfg = this.dest.domains[dest_domain];
147
- if (!dst_cfg) {
148
- transaction.results.add(this, {fail: 'relay_dest_domain'});
149
- return next(DENY, "You are not allowed to relay");
150
- }
151
-
152
- const { action } = JSON.parse(dst_cfg);
153
- connection.logdebug(this, `found config for ${dest_domain}: ${action}`);
154
-
155
- switch (action) {
156
- case "accept":
157
- // why enable relaying here? Returning next(OK) will allow the
158
- // address to be considered 'local'. What advantage does relaying
159
- // bring?
160
- connection.relaying = true;
161
- transaction.results.add(this, {pass: 'relay_dest_domain'});
162
- return next(OK);
163
- case "continue":
164
- // why oh why? Only reason I can think of is to enable outbound.
165
- connection.relaying = true;
166
- transaction.results.add(this, {pass: 'relay_dest_domain'});
167
- return next(CONT); // same as next()
168
- case "deny":
169
- transaction.results.add(this, {fail: 'relay_dest_domain'});
170
- return next(DENY, "You are not allowed to relay");
171
- }
172
-
173
- transaction.results.add(this, {fail: 'relay_dest_domain'});
174
- next(DENY, "Mail for that recipient is not accepted here.");
175
- }
176
-
177
- exports.force_routing = function (next, hmail, domain) {
178
- if (!this.cfg.relay.force_routing) { return next(); }
179
- if (!this.dest) { return next(); }
180
- if (!this.dest.domains) { return next(); }
181
- let route = this.dest.domains[domain];
182
-
183
- if (!route) {
184
- route = this.dest.domains.any;
185
- if (!route) {
186
- this.logdebug(this, `using normal MX lookup for: ${domain}`);
187
- return next();
188
- }
189
- }
190
-
191
- const { nexthop } = JSON.parse(route);
192
- if (!nexthop) {
193
- this.logdebug(this, `using normal MX lookup for: ${domain}`);
194
- return next();
195
- }
196
-
197
- this.logdebug(this, `using ${nexthop} for: ${domain}`);
198
- next(OK, nexthop);
199
- }
200
-
201
- exports.all = function (next, connection, params) {
202
- if (!this.cfg.relay.all) { return next(); }
203
-
204
- connection.loginfo(this, `confirming recipient ${params[0]}`);
205
- connection.relaying = true;
206
- next(OK);
207
- }
@@ -1,104 +0,0 @@
1
- 'use strict';
2
- const assert = require('node:assert')
3
-
4
- const fixtures = require('haraka-test-fixtures');
5
-
6
- const _set_up = (done) => {
7
- this.plugin = new fixtures.plugin('early_talker');
8
- this.plugin.cfg = { main: { reject: true } };
9
-
10
- this.connection = fixtures.connection.createConnection();
11
- done();
12
- }
13
-
14
- describe('early_talker', () => {
15
- beforeEach(_set_up)
16
-
17
- it('no config', (done) => {
18
- this.plugin.early_talker((rc, msg) => {
19
- assert.equal(rc, undefined);
20
- assert.equal(msg, undefined);
21
- done();
22
- }, this.connection);
23
- })
24
-
25
- it('relaying', (done) => {
26
- this.plugin.pause = 1;
27
- this.connection.relaying = true;
28
- this.plugin.early_talker((rc, msg) => {
29
- assert.equal(rc, undefined);
30
- assert.equal(msg, undefined);
31
- done();
32
- }, this.connection);
33
- })
34
-
35
- it('is an early talker', (done) => {
36
- const before = Date.now();
37
- this.plugin.pause = 1001;
38
- this.connection.early_talker = true;
39
- this.plugin.early_talker((rc, msg) => {
40
- assert.ok(Date.now() >= before + 1000);
41
- assert.equal(rc, DENYDISCONNECT);
42
- assert.equal(msg, 'You talk too soon');
43
- done();
44
- }, this.connection);
45
- })
46
-
47
- it('is an early talker, reject=false', (done) => {
48
- const before = Date.now();
49
- this.plugin.pause = 1001;
50
- this.plugin.cfg.main.reject = false;
51
- this.connection.early_talker = true;
52
- this.plugin.early_talker((rc, msg) => {
53
- assert.ok(Date.now() >= before + 1000);
54
- assert.equal(undefined, rc);
55
- assert.equal(undefined, msg);
56
- assert.ok(this.connection.results.has('early_talker', 'fail', 'early'));
57
- done();
58
- }, this.connection);
59
- })
60
-
61
- it('relay whitelisted ip', (done) => {
62
- this.plugin.pause = 1000;
63
- this.plugin.whitelist = this.plugin.load_ip_list(['127.0.0.1']);
64
- this.connection.remote.ip = '127.0.0.1';
65
- this.connection.early_talker = true;
66
- this.plugin.early_talker((rc, msg) => {
67
- assert.equal(undefined, rc);
68
- assert.equal(undefined, msg);
69
- assert.ok(this.connection.results.has('early_talker', 'skip', 'whitelist'));
70
- done();
71
- }, this.connection);
72
- })
73
-
74
- it('relay whitelisted subnet', (done) => {
75
- this.plugin.pause = 1000;
76
- this.plugin.whitelist = this.plugin.load_ip_list(['127.0.0.0/16']);
77
- this.connection.remote.ip = '127.0.0.88';
78
- this.connection.early_talker = true;
79
- this.plugin.early_talker((rc, msg) => {
80
- assert.equal(undefined, rc);
81
- assert.equal(undefined, msg);
82
- assert.ok(this.connection.results.has('early_talker', 'skip', 'whitelist'));
83
- done();
84
- }, this.connection);
85
- })
86
-
87
- it('relay good senders', (done) => {
88
- this.plugin.pause = 1000;
89
- this.connection.results.add('karma', {good: 10});
90
- this.connection.early_talker = true;
91
- this.plugin.early_talker((rc, msg) => {
92
- assert.equal(undefined, rc);
93
- assert.equal(undefined, msg);
94
- assert.ok(this.connection.results.has('early_talker', 'skip', '+karma'));
95
- done();
96
- }, this.connection);
97
- })
98
-
99
- it('test loading ip list', () => {
100
- const whitelist = this.plugin.load_ip_list(['123.123.123.123', '127.0.0.0/16']);
101
- assert.equal(whitelist[0][1], 32);
102
- assert.equal(whitelist[1][1], 16);
103
- })
104
- })
@@ -1,35 +0,0 @@
1
- 'use strict';
2
- const assert = require('node:assert')
3
- const dns = require('node:dns');
4
-
5
- const fixtures = require('haraka-test-fixtures');
6
- const Address = require('address-rfc2821').Address
7
-
8
- const _set_up = (done) => {
9
-
10
- this.plugin = new fixtures.plugin('mail_from.is_resolvable');
11
- this.plugin.register();
12
-
13
- this.connection = fixtures.connection.createConnection();
14
- this.connection.init_transaction()
15
-
16
- done();
17
- }
18
-
19
- describe('mail_from.is_resolvable', () => {
20
- beforeEach(_set_up)
21
-
22
- describe('hook_mail', () => {
23
- it('any.com, no err code', (done) => {
24
- const txn = this.connection.transaction;
25
- this.plugin.hook_mail((code, msg) => {
26
- // console.log()
27
- assert.deepEqual(txn.results.get('mail_from.is_resolvable').pass, ['has_fwd_dns']);
28
- done();
29
- },
30
- this.connection,
31
- [new Address('<test@any.com>')]
32
- )
33
- })
34
- })
35
- })