Haraka 2.8.28 → 3.0.1
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.
- package/.eslintrc.yaml +2 -10
- package/Changes.md +84 -2
- package/Dockerfile +1 -1
- package/Plugins.md +9 -4
- package/README.md +2 -6
- package/bin/haraka +5 -4
- package/config/outbound.ini +0 -7
- package/config/plugins +1 -1
- package/config/smtp.ini +1 -1
- package/config/smtp_forward.ini +2 -8
- package/config/smtp_proxy.ini +0 -6
- package/connection.js +178 -204
- package/coverage/lcov.info +13863 -0
- package/coverage/tmp/coverage-42958-1658373250585-0.json +1 -0
- package/coverage/tmp/coverage-42961-1658373250529-0.json +1 -0
- package/dkim.js +66 -73
- package/docs/Body.md +1 -22
- package/docs/CoreConfig.md +2 -2
- package/docs/Header.md +1 -47
- package/docs/Outbound.md +8 -36
- package/endpoint.js +1 -1
- package/haraka.js +1 -1
- package/host_pool.js +8 -12
- package/logger.js +25 -32
- package/outbound/client_pool.js +11 -153
- package/outbound/config.js +5 -11
- package/outbound/hmail.js +109 -143
- package/outbound/index.js +13 -25
- package/outbound/mx_lookup.js +10 -7
- package/outbound/queue.js +8 -12
- package/outbound/timer_queue.js +2 -4
- package/outbound/tls.js +17 -18
- package/outbound/todo.js +1 -0
- package/package.json +57 -55
- package/plugins/auth/auth_base.js +39 -63
- package/plugins/auth/auth_bridge.js +3 -4
- package/plugins/auth/auth_proxy.js +16 -16
- package/plugins/auth/auth_vpopmaild.js +30 -37
- package/plugins/auth/flat_file.js +9 -13
- package/plugins/avg.js +9 -11
- package/plugins/backscatterer.js +1 -1
- package/plugins/block_me.js +2 -6
- package/plugins/bounce.js +106 -124
- package/plugins/clamd.js +59 -63
- package/plugins/data.signatures.js +6 -6
- package/plugins/data.uribl.js +1 -415
- package/plugins/delay_deny.js +19 -20
- package/plugins/dkim_sign.js +56 -62
- package/plugins/dkim_verify.js +9 -8
- package/plugins/dns_list_base.js +43 -42
- package/plugins/dnsbl.js +41 -46
- package/plugins/dnswl.js +23 -26
- package/plugins/early_talker.js +24 -28
- package/plugins/esets.js +8 -11
- package/plugins/greylist.js +161 -190
- package/plugins/helo.checks.js +175 -197
- package/plugins/mail_from.is_resolvable.js +38 -38
- package/plugins/messagesniffer.js +33 -40
- package/plugins/prevent_credential_leaks.js +7 -5
- package/plugins/process_title.js +16 -17
- package/plugins/queue/deliver.js +2 -2
- package/plugins/queue/lmtp.js +5 -6
- package/plugins/queue/qmail-queue.js +11 -13
- package/plugins/queue/quarantine.js +25 -34
- package/plugins/queue/rabbitmq.js +3 -2
- package/plugins/queue/rabbitmq_amqplib.js +9 -9
- package/plugins/queue/smtp_bridge.js +5 -4
- package/plugins/queue/smtp_forward.js +81 -89
- package/plugins/queue/smtp_proxy.js +21 -22
- package/plugins/queue/test.js +2 -1
- package/plugins/rcpt_to.host_list_base.js +20 -30
- package/plugins/rcpt_to.in_host_list.js +12 -14
- package/plugins/rcpt_to.max_count.js +7 -5
- package/plugins/record_envelope_addresses.js +4 -6
- package/plugins/relay.js +64 -74
- package/plugins/reseed_rng.js +1 -2
- package/plugins/spamassassin.js +56 -68
- package/plugins/status.js +2 -3
- package/plugins/tarpit.js +8 -11
- package/plugins/tls.js +14 -17
- package/plugins/toobusy.js +6 -8
- package/plugins/xclient.js +14 -25
- package/plugins.js +24 -29
- package/rfc1869.js +2 -2
- package/server.js +3 -13
- package/smtp_client.js +138 -215
- package/tests/config/smtp_forward.ini +0 -6
- package/tests/fixtures/line_socket.js +1 -1
- package/tests/fixtures/util_hmailitem.js +5 -7
- package/tests/fixtures/vm_harness.js +2 -2
- package/tests/host_pool.js +13 -14
- package/tests/installation/plugins/inherits.js +1 -2
- package/tests/logger.js +2 -2
- package/tests/plugins/bounce.js +6 -8
- package/tests/plugins/dkim_signer.js +7 -7
- package/tests/plugins/dns_list_base.js +7 -7
- package/tests/plugins/helo.checks.js +1 -1
- package/tests/plugins/mail_from.is_resolvable.js +10 -54
- package/tests/plugins/queue/smtp_forward.js +11 -11
- package/tests/plugins/rcpt_to.host_list_base.js +1 -1
- package/tests/plugins/rcpt_to.in_host_list.js +1 -1
- package/tests/plugins/spamassassin.js +1 -1
- package/tests/queue/multibyte +0 -0
- package/tests/queue/plain +0 -0
- package/tests/rfc1869.js +4 -1
- package/tests/server.js +15 -9
- package/tests/smtp_client/auth.js +4 -14
- package/tests/smtp_client/basic.js +5 -15
- package/tests/smtp_client.js +7 -3
- package/tests/transaction.js +72 -19
- package/tls_socket.js +75 -85
- package/transaction.js +7 -9
- package/attachment_stream.js +0 -118
- package/bin/spf +0 -48
- package/chunkemitter.js +0 -75
- package/config/data.uribl.excludes +0 -202
- package/config/data.uribl.ini +0 -37
- package/config/spf.ini +0 -1
- package/docs/plugins/attachment.md +0 -92
- package/docs/plugins/data.uribl.md +0 -120
- package/docs/plugins/spf.md +0 -142
- package/mailbody.js +0 -502
- package/mailheader.js +0 -304
- package/messagestream.js +0 -441
- package/plugins/aliases.js +0 -120
- package/plugins/attachment.js +0 -503
- package/plugins/connect.p0f.js +0 -5
- package/plugins/spf.js +0 -327
- package/spf.js +0 -689
- package/tests/mailbody.js +0 -348
- package/tests/mailheader.js +0 -138
- package/tests/messagestream.js +0 -34
- package/tests/plugins/aliases.js +0 -376
- package/tests/plugins/spf.js +0 -251
- package/tests/spf.js +0 -96
package/plugins/clamd.js
CHANGED
|
@@ -4,73 +4,71 @@ const sock = require('./line_socket');
|
|
|
4
4
|
const utils = require('haraka-utils');
|
|
5
5
|
|
|
6
6
|
exports.load_excludes = function () {
|
|
7
|
-
const plugin = this;
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
const list =
|
|
11
|
-
|
|
8
|
+
this.loginfo('Loading excludes file');
|
|
9
|
+
const list = this.config.get('clamd.excludes','list', () => {
|
|
10
|
+
this.load_excludes();
|
|
12
11
|
});
|
|
13
12
|
|
|
14
13
|
const new_skip_list_exclude = [];
|
|
15
14
|
const new_skip_list = [];
|
|
16
|
-
for (
|
|
15
|
+
for (const element of list) {
|
|
17
16
|
let re;
|
|
18
|
-
switch (
|
|
17
|
+
switch (element[0]) {
|
|
19
18
|
case '!':
|
|
20
19
|
|
|
21
|
-
if (
|
|
20
|
+
if (element[1] === '/') {
|
|
22
21
|
// Regexp exclude
|
|
23
22
|
try {
|
|
24
|
-
re = new RegExp(
|
|
23
|
+
re = new RegExp(element.substr(2, element.length-2),'i');
|
|
25
24
|
new_skip_list_exclude.push(re);
|
|
26
25
|
}
|
|
27
26
|
catch (e) {
|
|
28
|
-
|
|
27
|
+
this.logerror(`${e.message} (entry: ${element})`);
|
|
29
28
|
}
|
|
30
29
|
}
|
|
31
30
|
else {
|
|
32
31
|
// Wildcard exclude
|
|
33
32
|
try {
|
|
34
33
|
re = new RegExp(
|
|
35
|
-
utils.wildcard_to_regexp(
|
|
34
|
+
utils.wildcard_to_regexp(element.substr(1)),'i');
|
|
36
35
|
new_skip_list_exclude.push(re);
|
|
37
36
|
}
|
|
38
37
|
catch (e) {
|
|
39
|
-
|
|
38
|
+
this.logerror(`${e.message} (entry: ${element})`);
|
|
40
39
|
}
|
|
41
40
|
}
|
|
42
41
|
break;
|
|
43
42
|
case '/':
|
|
44
43
|
// Regexp skip
|
|
45
44
|
try {
|
|
46
|
-
re = new RegExp(
|
|
45
|
+
re = new RegExp(element.substr(1, element.length-2),'i');
|
|
47
46
|
new_skip_list.push(re);
|
|
48
47
|
}
|
|
49
48
|
catch (e) {
|
|
50
|
-
|
|
49
|
+
this.logerror(`${e.message} (entry: ${element})`);
|
|
51
50
|
}
|
|
52
51
|
break;
|
|
53
52
|
default:
|
|
54
53
|
// Wildcard skip
|
|
55
54
|
try {
|
|
56
|
-
re = new RegExp(utils.wildcard_to_regexp(
|
|
55
|
+
re = new RegExp(utils.wildcard_to_regexp(element),'i');
|
|
57
56
|
new_skip_list.push(re);
|
|
58
57
|
}
|
|
59
58
|
catch (e) {
|
|
60
|
-
|
|
59
|
+
this.logerror(`${e.message} (entry: ${element})`);
|
|
61
60
|
}
|
|
62
61
|
}
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
// Make the new lists visible
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
this.skip_list_exclude = new_skip_list_exclude;
|
|
66
|
+
this.skip_list = new_skip_list;
|
|
68
67
|
}
|
|
69
68
|
|
|
70
69
|
exports.load_clamd_ini = function () {
|
|
71
|
-
const plugin = this;
|
|
72
70
|
|
|
73
|
-
|
|
71
|
+
this.cfg = this.config.get('clamd.ini', {
|
|
74
72
|
booleans: [
|
|
75
73
|
'-main.randomize_host_order',
|
|
76
74
|
'-main.only_with_attachments',
|
|
@@ -96,7 +94,7 @@ exports.load_clamd_ini = function () {
|
|
|
96
94
|
'+check.local_ip'
|
|
97
95
|
],
|
|
98
96
|
}, () => {
|
|
99
|
-
|
|
97
|
+
this.load_clamd_ini();
|
|
100
98
|
});
|
|
101
99
|
|
|
102
100
|
const defaults = {
|
|
@@ -107,8 +105,8 @@ exports.load_clamd_ini = function () {
|
|
|
107
105
|
};
|
|
108
106
|
|
|
109
107
|
for (const key in defaults) {
|
|
110
|
-
if (
|
|
111
|
-
|
|
108
|
+
if (this.cfg.main[key] === undefined) {
|
|
109
|
+
this.cfg.main[key] = defaults[key];
|
|
112
110
|
}
|
|
113
111
|
}
|
|
114
112
|
|
|
@@ -127,39 +125,37 @@ exports.load_clamd_ini = function () {
|
|
|
127
125
|
const enabled_reject_opts = [];
|
|
128
126
|
Object.keys(rejectPatterns).forEach(opt => {
|
|
129
127
|
all_reject_opts.push(rejectPatterns[opt]);
|
|
130
|
-
if (!
|
|
128
|
+
if (!this.cfg.reject[opt]) return;
|
|
131
129
|
enabled_reject_opts.push(rejectPatterns[opt]);
|
|
132
130
|
});
|
|
133
131
|
|
|
134
132
|
if (enabled_reject_opts.length) {
|
|
135
|
-
|
|
136
|
-
|
|
133
|
+
this.allRE = new RegExp(all_reject_opts.join('|'));
|
|
134
|
+
this.rejectRE = new RegExp(enabled_reject_opts.join('|'));
|
|
137
135
|
}
|
|
138
136
|
|
|
139
137
|
// resolve mismatch between docs (...attachment) and code (...attachments)
|
|
140
|
-
if (
|
|
141
|
-
|
|
142
|
-
!!
|
|
138
|
+
if (this.cfg.main.only_with_attachment !== undefined) {
|
|
139
|
+
this.cfg.main.only_with_attachments =
|
|
140
|
+
!!this.cfg.main.only_with_attachment;
|
|
143
141
|
}
|
|
144
142
|
}
|
|
145
143
|
|
|
146
144
|
exports.register = function () {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
plugin.load_clamd_ini();
|
|
145
|
+
this.load_excludes();
|
|
146
|
+
this.load_clamd_ini();
|
|
150
147
|
}
|
|
151
148
|
|
|
152
149
|
exports.hook_data = function (next, connection) {
|
|
153
|
-
const plugin = this;
|
|
154
150
|
|
|
155
|
-
if (!
|
|
151
|
+
if (!this.cfg.main.only_with_attachments) return next();
|
|
156
152
|
|
|
157
|
-
if (!
|
|
153
|
+
if (!this.should_check(connection)) return next();
|
|
158
154
|
|
|
159
155
|
const txn = connection.transaction;
|
|
160
156
|
txn.parse_body = true;
|
|
161
157
|
txn.attachment_hooks((ctype, filename, body) => {
|
|
162
|
-
connection.logdebug(
|
|
158
|
+
connection.logdebug(this,
|
|
163
159
|
`found ctype=${ctype}, filename=${filename}`);
|
|
164
160
|
txn.notes.clamd_found_attachment = true;
|
|
165
161
|
});
|
|
@@ -169,11 +165,10 @@ exports.hook_data = function (next, connection) {
|
|
|
169
165
|
|
|
170
166
|
exports.hook_data_post = function (next, connection) {
|
|
171
167
|
const plugin = this;
|
|
172
|
-
const txn = connection.transaction;
|
|
173
|
-
const cfg = plugin.cfg;
|
|
174
|
-
|
|
175
168
|
if (!plugin.should_check(connection)) return next();
|
|
176
169
|
|
|
170
|
+
const txn = connection.transaction;
|
|
171
|
+
const { cfg } = plugin;
|
|
177
172
|
// Do we need to run?
|
|
178
173
|
if (cfg.main.only_with_attachments && !txn.notes.clamd_found_attachment) {
|
|
179
174
|
connection.logdebug(plugin, 'skipping: no attachments found');
|
|
@@ -279,15 +274,15 @@ exports.hook_data_post = function (next, connection) {
|
|
|
279
274
|
if (!plugin.cfg.reject.virus) { return next(); }
|
|
280
275
|
|
|
281
276
|
// Check skip list exclusions
|
|
282
|
-
for (
|
|
283
|
-
if (!
|
|
277
|
+
for (const element of plugin.skip_list_exclude) {
|
|
278
|
+
if (!element.test(virus)) continue;
|
|
284
279
|
return next(DENY,
|
|
285
280
|
`Message is infected with ${virus || 'UNKNOWN'}`);
|
|
286
281
|
}
|
|
287
282
|
|
|
288
283
|
// Check skip list
|
|
289
|
-
for (
|
|
290
|
-
if (!
|
|
284
|
+
for (const element of plugin.skip_list) {
|
|
285
|
+
if (!element.test(virus)) continue;
|
|
291
286
|
connection.logwarn(plugin, `${virus} matches exclusion`);
|
|
292
287
|
txn.add_header('X-Haraka-Virus', virus);
|
|
293
288
|
return next();
|
|
@@ -325,31 +320,31 @@ exports.hook_data_post = function (next, connection) {
|
|
|
325
320
|
}
|
|
326
321
|
|
|
327
322
|
exports.should_check = function (connection) {
|
|
328
|
-
const plugin = this;
|
|
329
323
|
|
|
330
324
|
let result = true; // default
|
|
325
|
+
if (!connection?.transaction) return false
|
|
331
326
|
|
|
332
|
-
if (
|
|
333
|
-
connection.transaction.results.add(
|
|
327
|
+
if (this.cfg.check.authenticated == false && connection.notes.auth_user) {
|
|
328
|
+
connection.transaction.results.add(this, { skip: 'authed'});
|
|
334
329
|
result = false;
|
|
335
330
|
}
|
|
336
331
|
|
|
337
|
-
if (
|
|
338
|
-
connection.transaction.results.add(
|
|
332
|
+
if (this.cfg.check.relay == false && connection.relaying) {
|
|
333
|
+
connection.transaction.results.add(this, { skip: 'relay'});
|
|
339
334
|
result = false;
|
|
340
335
|
}
|
|
341
336
|
|
|
342
|
-
if (
|
|
343
|
-
connection.transaction.results.add(
|
|
337
|
+
if (this.cfg.check.local_ip == false && connection.remote.is_local) {
|
|
338
|
+
connection.transaction.results.add(this, { skip: 'local_ip'});
|
|
344
339
|
result = false;
|
|
345
340
|
}
|
|
346
341
|
|
|
347
|
-
if (
|
|
348
|
-
if (
|
|
342
|
+
if (this.cfg.check.private_ip == false && connection.remote.is_private) {
|
|
343
|
+
if (this.cfg.check.local_ip == true && connection.remote.is_local) {
|
|
349
344
|
// local IPs are included in private IPs
|
|
350
345
|
}
|
|
351
346
|
else {
|
|
352
|
-
connection.transaction.results.add(
|
|
347
|
+
connection.transaction.results.add(this, { skip: 'private_ip'});
|
|
353
348
|
result = false;
|
|
354
349
|
}
|
|
355
350
|
}
|
|
@@ -368,18 +363,19 @@ exports.send_clamd_predata = (socket, cb) => {
|
|
|
368
363
|
}
|
|
369
364
|
|
|
370
365
|
function clamd_connect (socket, host) {
|
|
371
|
-
|
|
366
|
+
|
|
372
367
|
if (host.match(/^\//)) {
|
|
373
|
-
//
|
|
374
|
-
|
|
368
|
+
socket.connect(host); // starts with /, unix socket
|
|
369
|
+
return
|
|
375
370
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
// IP:port, hostname:port or hostname
|
|
382
|
-
const hostport = host.split(/:/);
|
|
383
|
-
socket.connect((hostport[1] || 3310), hostport[0]);
|
|
371
|
+
|
|
372
|
+
const match = /^\[([^\] ]+)\](?::(\d+))?/.exec(host);
|
|
373
|
+
if (match) {
|
|
374
|
+
socket.connect((match[2] || 3310), match[1]); // IPv6 literal
|
|
375
|
+
return
|
|
384
376
|
}
|
|
377
|
+
|
|
378
|
+
// IP:port, hostname:port or hostname
|
|
379
|
+
const hostport = host.split(/:/);
|
|
380
|
+
socket.connect((hostport[1] || 3310), hostport[0]);
|
|
385
381
|
}
|
|
@@ -3,11 +3,15 @@
|
|
|
3
3
|
|
|
4
4
|
exports.hook_data = (next, connection) => {
|
|
5
5
|
// enable mail body parsing
|
|
6
|
+
if (!connection?.transaction) return next();
|
|
7
|
+
|
|
6
8
|
connection.transaction.parse_body = true;
|
|
7
9
|
next();
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
exports.hook_data_post = function (next, connection) {
|
|
13
|
+
if (!connection?.transaction) return next();
|
|
14
|
+
|
|
11
15
|
const sigs = this.config.get('data.signatures', 'list');
|
|
12
16
|
|
|
13
17
|
if (check_sigs(sigs, connection.transaction.body)) {
|
|
@@ -18,15 +22,11 @@ exports.hook_data_post = function (next, connection) {
|
|
|
18
22
|
|
|
19
23
|
function check_sigs (sigs, body) {
|
|
20
24
|
for (let i=0,l=sigs.length; i < l; i++) {
|
|
21
|
-
if (body.bodytext.includes(sigs[i]))
|
|
22
|
-
return 1;
|
|
23
|
-
}
|
|
25
|
+
if (body.bodytext.includes(sigs[i])) return 1;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
for (let i=0,l=body.children.length; i < l; i++) {
|
|
27
|
-
if (check_sigs(sigs, body.children[i]))
|
|
28
|
-
return 1;
|
|
29
|
-
}
|
|
29
|
+
if (check_sigs(sigs, body.children[i])) return 1;
|
|
30
30
|
}
|
|
31
31
|
return 0;
|
|
32
32
|
}
|