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
|
@@ -7,40 +7,37 @@
|
|
|
7
7
|
const smtp_client_mod = require('./smtp_client');
|
|
8
8
|
|
|
9
9
|
exports.register = function () {
|
|
10
|
-
const plugin = this;
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
this.load_smtp_proxy_ini();
|
|
13
12
|
|
|
14
|
-
if (
|
|
15
|
-
|
|
13
|
+
if (this.cfg.main.enable_outbound) {
|
|
14
|
+
this.register_hook('queue_outbound', 'hook_queue');
|
|
16
15
|
}
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
exports.load_smtp_proxy_ini = function () {
|
|
20
|
-
const plugin = this;
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
this.cfg = this.config.get('smtp_proxy.ini', {
|
|
23
21
|
booleans: [
|
|
24
22
|
'-main.enable_tls',
|
|
25
23
|
'+main.enable_outbound',
|
|
26
24
|
],
|
|
27
25
|
},
|
|
28
26
|
() => {
|
|
29
|
-
|
|
27
|
+
this.load_smtp_proxy_ini();
|
|
30
28
|
});
|
|
31
29
|
|
|
32
|
-
if (
|
|
33
|
-
|
|
30
|
+
if (this.cfg.main.enable_outbound) {
|
|
31
|
+
this.lognotice('outbound enabled, will default to disabled in Haraka v3 (see #1472)');
|
|
34
32
|
}
|
|
35
33
|
}
|
|
36
34
|
|
|
37
35
|
exports.hook_mail = function (next, connection, params) {
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
connection.loginfo(plugin, `forwarding to ${
|
|
36
|
+
const c = this.cfg.main;
|
|
37
|
+
connection.loginfo(this, `forwarding to ${
|
|
41
38
|
c.forwarding_host_pool ? "configured forwarding_host_pool" : `${c.host}:${c.port}`}`
|
|
42
39
|
);
|
|
43
|
-
smtp_client_mod.get_client_plugin(
|
|
40
|
+
smtp_client_mod.get_client_plugin(this, connection, c, (err, smtp_client) => {
|
|
44
41
|
connection.notes.smtp_client = smtp_client;
|
|
45
42
|
smtp_client.next = next;
|
|
46
43
|
|
|
@@ -49,7 +46,7 @@ exports.hook_mail = function (next, connection, params) {
|
|
|
49
46
|
smtp_client.on('data', smtp_client.call_next);
|
|
50
47
|
|
|
51
48
|
smtp_client.on('dot', () => {
|
|
52
|
-
if (smtp_client.is_dead_sender(
|
|
49
|
+
if (smtp_client.is_dead_sender(this, connection)) {
|
|
53
50
|
delete connection.notes.smtp_client;
|
|
54
51
|
return;
|
|
55
52
|
}
|
|
@@ -71,7 +68,7 @@ exports.hook_mail = function (next, connection, params) {
|
|
|
71
68
|
// errors are OK for rcpt, but nothing else
|
|
72
69
|
// this can also happen if the destination server
|
|
73
70
|
// times out, but that is okay.
|
|
74
|
-
connection.loginfo(
|
|
71
|
+
connection.loginfo(this, "message denied, proxying failed");
|
|
75
72
|
smtp_client.release();
|
|
76
73
|
delete connection.notes.smtp_client;
|
|
77
74
|
}
|
|
@@ -80,25 +77,27 @@ exports.hook_mail = function (next, connection, params) {
|
|
|
80
77
|
}
|
|
81
78
|
|
|
82
79
|
exports.hook_rcpt_ok = (next, connection, recipient) => {
|
|
83
|
-
const smtp_client = connection.notes
|
|
80
|
+
const { smtp_client } = connection.notes;
|
|
84
81
|
if (!smtp_client) return next();
|
|
85
82
|
smtp_client.next = next;
|
|
86
83
|
smtp_client.send_command('RCPT', `TO:${recipient.format(!smtp_client.smtp_utf8)}`);
|
|
87
84
|
}
|
|
88
85
|
|
|
89
86
|
exports.hook_data = (next, connection) => {
|
|
90
|
-
const smtp_client = connection.notes
|
|
87
|
+
const { smtp_client } = connection.notes;
|
|
91
88
|
if (!smtp_client) return next();
|
|
92
89
|
smtp_client.next = next;
|
|
93
90
|
smtp_client.send_command("DATA");
|
|
94
91
|
}
|
|
95
92
|
|
|
96
93
|
exports.hook_queue = function (next, connection) {
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
if (!connection?.transaction || !connection?.notes) return next();
|
|
95
|
+
|
|
96
|
+
const { smtp_client } = connection.notes;
|
|
99
97
|
if (!smtp_client) return next();
|
|
98
|
+
|
|
100
99
|
smtp_client.next = next;
|
|
101
|
-
if (smtp_client.is_dead_sender(
|
|
100
|
+
if (smtp_client.is_dead_sender(this, connection)) {
|
|
102
101
|
delete connection.notes.smtp_client;
|
|
103
102
|
return;
|
|
104
103
|
}
|
|
@@ -106,7 +105,7 @@ exports.hook_queue = function (next, connection) {
|
|
|
106
105
|
}
|
|
107
106
|
|
|
108
107
|
exports.hook_rset = (next, connection) => {
|
|
109
|
-
const smtp_client = connection.notes
|
|
108
|
+
const { smtp_client } = connection.notes;
|
|
110
109
|
if (!smtp_client) return next();
|
|
111
110
|
smtp_client.release();
|
|
112
111
|
delete connection.notes.smtp_client;
|
|
@@ -116,7 +115,7 @@ exports.hook_rset = (next, connection) => {
|
|
|
116
115
|
exports.hook_quit = exports.hook_rset;
|
|
117
116
|
|
|
118
117
|
exports.hook_disconnect = (next, connection) => {
|
|
119
|
-
const smtp_client = connection.notes
|
|
118
|
+
const { smtp_client } = connection.notes;
|
|
120
119
|
if (!smtp_client) return next();
|
|
121
120
|
smtp_client.release();
|
|
122
121
|
delete connection.notes.smtp_client;
|
package/plugins/queue/test.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
|
|
2
1
|
const fs = require('fs');
|
|
3
2
|
const os = require('os');
|
|
4
3
|
|
|
5
4
|
const tempDir = os.tmpdir();
|
|
6
5
|
|
|
7
6
|
exports.hook_queue = function (next, connection) {
|
|
7
|
+
if (!connection?.transaction) return next();
|
|
8
|
+
|
|
8
9
|
const ws = fs.createWriteStream(`${tempDir}/mail.eml`);
|
|
9
10
|
connection.logdebug(this, `Saving to ${tempDir}/mail.eml`);
|
|
10
11
|
ws.once('close', () => next(OK));
|
|
@@ -2,78 +2,68 @@
|
|
|
2
2
|
// Base class for plugins that use config/host_list
|
|
3
3
|
|
|
4
4
|
exports.load_host_list = function () {
|
|
5
|
-
const plugin = this;
|
|
6
5
|
|
|
7
6
|
const lowered_list = {}; // assemble
|
|
8
|
-
const raw_list =
|
|
9
|
-
|
|
7
|
+
const raw_list = this.config.get('host_list', 'list', () => {
|
|
8
|
+
this.load_host_list();
|
|
10
9
|
});
|
|
11
10
|
|
|
12
11
|
for (const i in raw_list) {
|
|
13
12
|
lowered_list[raw_list[i].toLowerCase()] = true;
|
|
14
13
|
}
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
this.host_list = lowered_list;
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
exports.load_host_list_regex = function () {
|
|
20
|
-
const plugin = this;
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
this.host_list_regex = this.config.get(
|
|
23
21
|
'host_list_regex',
|
|
24
22
|
'list',
|
|
25
|
-
() => {
|
|
23
|
+
() => { this.load_host_list_regex(); }
|
|
26
24
|
);
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
plugin.host_list_regex.join('|')})$`, 'i');
|
|
26
|
+
this.hl_re = new RegExp (`^(?:${this.host_list_regex.join('|')})$`, 'i');
|
|
30
27
|
}
|
|
31
28
|
|
|
32
29
|
exports.hook_mail = function (next, connection, params) {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
if (!txn) { return; }
|
|
30
|
+
const txn = connection?.transaction;
|
|
31
|
+
if (!txn) return;
|
|
36
32
|
|
|
37
33
|
const email = params[0].address();
|
|
38
34
|
if (!email) {
|
|
39
|
-
txn.results.add(
|
|
35
|
+
txn.results.add(this, {skip: 'mail_from.null', emit: true});
|
|
40
36
|
return next();
|
|
41
37
|
}
|
|
42
38
|
|
|
43
39
|
const domain = params[0].host.toLowerCase();
|
|
44
40
|
|
|
45
|
-
const anti_spoof =
|
|
41
|
+
const anti_spoof = this.config.get('host_list.anti_spoof') || false;
|
|
46
42
|
|
|
47
|
-
if (
|
|
43
|
+
if (this.in_host_list(domain) || this.in_host_regex(domain)) {
|
|
48
44
|
if (anti_spoof && !connection.relaying) {
|
|
49
|
-
txn.results.add(
|
|
45
|
+
txn.results.add(this, {fail: 'mail_from.anti_spoof'});
|
|
50
46
|
return next(DENY, `Mail from domain '${domain}' is not allowed from your host`);
|
|
51
47
|
}
|
|
52
|
-
txn.results.add(
|
|
48
|
+
txn.results.add(this, {pass: 'mail_from'});
|
|
53
49
|
txn.notes.local_sender = true;
|
|
54
50
|
return next();
|
|
55
51
|
}
|
|
56
52
|
|
|
57
|
-
txn.results.add(
|
|
53
|
+
txn.results.add(this, {msg: 'mail_from!local'});
|
|
58
54
|
return next();
|
|
59
55
|
}
|
|
60
56
|
|
|
61
57
|
exports.in_host_list = function (domain) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (plugin.host_list[domain]) {
|
|
65
|
-
return true;
|
|
66
|
-
}
|
|
67
|
-
return false;
|
|
58
|
+
this.logdebug(`checking ${domain} in config/host_list`);
|
|
59
|
+
return !!(this.host_list[domain]);
|
|
68
60
|
}
|
|
69
61
|
|
|
70
62
|
exports.in_host_regex = function (domain) {
|
|
71
|
-
|
|
72
|
-
if (!
|
|
73
|
-
if (!plugin.host_list_regex.length) return false;
|
|
63
|
+
if (!this.host_list_regex) return false;
|
|
64
|
+
if (!this.host_list_regex.length) return false;
|
|
74
65
|
|
|
75
|
-
|
|
66
|
+
this.logdebug(`checking ${domain} against config/host_list_regex `);
|
|
76
67
|
|
|
77
|
-
|
|
78
|
-
return false;
|
|
68
|
+
return !!(this.hl_re.test(domain));
|
|
79
69
|
}
|
|
@@ -10,49 +10,47 @@
|
|
|
10
10
|
// is enabled and the sending domain is local, the receipt is OK.
|
|
11
11
|
|
|
12
12
|
exports.register = function () {
|
|
13
|
-
|
|
14
|
-
plugin.inherits('rcpt_to.host_list_base');
|
|
13
|
+
this.inherits('rcpt_to.host_list_base');
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
this.load_host_list();
|
|
16
|
+
this.load_host_list_regex();
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
exports.hook_rcpt = function (next, connection, params) {
|
|
21
|
-
const
|
|
22
|
-
const txn = connection.transaction;
|
|
20
|
+
const txn = connection?.transaction;
|
|
23
21
|
if (!txn) return;
|
|
24
22
|
|
|
25
23
|
const rcpt = params[0];
|
|
26
24
|
|
|
27
25
|
// Check for RCPT TO without an @ first - ignore those here
|
|
28
26
|
if (!rcpt.host) {
|
|
29
|
-
txn.results.add(
|
|
27
|
+
txn.results.add(this, {fail: 'rcpt!domain'});
|
|
30
28
|
return next();
|
|
31
29
|
}
|
|
32
30
|
|
|
33
|
-
connection.logdebug(
|
|
31
|
+
connection.logdebug(this, `Checking if ${rcpt} host is in host_list`);
|
|
34
32
|
|
|
35
33
|
const domain = rcpt.host.toLowerCase();
|
|
36
34
|
|
|
37
|
-
if (
|
|
38
|
-
txn.results.add(
|
|
35
|
+
if (this.in_host_list(domain)) {
|
|
36
|
+
txn.results.add(this, {pass: 'rcpt_to'});
|
|
39
37
|
return next(OK);
|
|
40
38
|
}
|
|
41
39
|
|
|
42
|
-
if (
|
|
43
|
-
txn.results.add(
|
|
40
|
+
if (this.in_host_regex(domain)) {
|
|
41
|
+
txn.results.add(this, {pass: 'rcpt_to'});
|
|
44
42
|
return next(OK);
|
|
45
43
|
}
|
|
46
44
|
|
|
47
45
|
// in this case, a client with relaying privileges is sending FROM a local
|
|
48
46
|
// domain. For them, any RCPT address is accepted.
|
|
49
47
|
if (connection.relaying && txn.notes.local_sender) {
|
|
50
|
-
txn.results.add(
|
|
48
|
+
txn.results.add(this, {pass: 'relaying local_sender'});
|
|
51
49
|
return next(OK);
|
|
52
50
|
}
|
|
53
51
|
|
|
54
52
|
// the MAIL FROM domain is not local and neither is the RCPT TO
|
|
55
53
|
// Another RCPT plugin may yet vouch for this recipient.
|
|
56
|
-
txn.results.add(
|
|
54
|
+
txn.results.add(this, {msg: 'rcpt!local'});
|
|
57
55
|
return next();
|
|
58
56
|
}
|
|
@@ -5,17 +5,19 @@
|
|
|
5
5
|
// Consider using the karma plugin. It supports limiting the number
|
|
6
6
|
// of recipients based on past behavior (good, bad, unknown)
|
|
7
7
|
|
|
8
|
-
exports.hook_rcpt = function (next, connection) {
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
exports.hook_rcpt = function (next, connection = {}) {
|
|
9
|
+
const { notes } = connection.transaction
|
|
10
|
+
|
|
11
|
+
if (notes?.rcpt_to_count) {
|
|
12
|
+
notes.rcpt_to_count++;
|
|
11
13
|
}
|
|
12
14
|
else {
|
|
13
|
-
|
|
15
|
+
notes.rcpt_to_count = 1;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
const max_count = this.config.get('rcpt_to.max_count') || 40;
|
|
17
19
|
|
|
18
|
-
if (
|
|
20
|
+
if (notes.rcpt_to_count > max_count) {
|
|
19
21
|
return next(DENYDISCONNECT, "Too many recipient attempts");
|
|
20
22
|
}
|
|
21
23
|
return next();
|
|
@@ -3,17 +3,15 @@
|
|
|
3
3
|
// documentation via: haraka -h plugins/record_envelope_addresses
|
|
4
4
|
|
|
5
5
|
exports.hook_rcpt = (next, connection, params) => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
txn.add_header('X-Envelope-To', params[0].address());
|
|
6
|
+
if (connection?.transaction) {
|
|
7
|
+
connection.transaction.add_header('X-Envelope-To', params[0].address());
|
|
9
8
|
}
|
|
10
9
|
next();
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
exports.hook_mail = (next, connection, params) => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
txn.add_header('X-Envelope-From', params[0].address());
|
|
13
|
+
if (connection?.transaction) {
|
|
14
|
+
connection.transaction.add_header('X-Envelope-From', params[0].address());
|
|
17
15
|
}
|
|
18
16
|
next();
|
|
19
17
|
}
|
package/plugins/relay.js
CHANGED
|
@@ -6,36 +6,34 @@ const ipaddr = require('ipaddr.js');
|
|
|
6
6
|
const net = require('net');
|
|
7
7
|
|
|
8
8
|
exports.register = function () {
|
|
9
|
-
const plugin = this;
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
this.load_relay_ini(); // plugin.cfg = { }
|
|
12
11
|
|
|
13
|
-
if (
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
if (this.cfg.relay.acl) {
|
|
13
|
+
this.load_acls(); // plugin.acl_allow = [..]
|
|
14
|
+
this.register_hook('connect_init', 'acl');
|
|
15
|
+
this.register_hook('connect', 'pass_relaying');
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
if (
|
|
20
|
-
|
|
18
|
+
if (this.cfg.relay.force_routing || this.cfg.relay.dest_domains) {
|
|
19
|
+
this.load_dest_domains(); // plugin.dest.domains = { }
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
if (
|
|
24
|
-
|
|
22
|
+
if (this.cfg.relay.force_routing) {
|
|
23
|
+
this.register_hook('get_mx', 'force_routing');
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
if (
|
|
28
|
-
|
|
26
|
+
if (this.cfg.relay.dest_domains) {
|
|
27
|
+
this.register_hook('rcpt', 'dest_domains');
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
if (
|
|
32
|
-
|
|
30
|
+
if (this.cfg.relay.all) {
|
|
31
|
+
this.register_hook('rcpt', 'all');
|
|
33
32
|
}
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
exports.load_relay_ini = function () {
|
|
37
|
-
|
|
38
|
-
plugin.cfg = plugin.config.get('relay.ini', {
|
|
36
|
+
this.cfg = this.config.get('relay.ini', {
|
|
39
37
|
booleans: [
|
|
40
38
|
'+relay.acl',
|
|
41
39
|
'+relay.force_routing',
|
|
@@ -43,52 +41,49 @@ exports.load_relay_ini = function () {
|
|
|
43
41
|
'-relay.dest_domains',
|
|
44
42
|
],
|
|
45
43
|
}, () => {
|
|
46
|
-
|
|
44
|
+
this.load_relay_ini();
|
|
47
45
|
});
|
|
48
46
|
}
|
|
49
47
|
|
|
50
48
|
exports.load_dest_domains = function () {
|
|
51
|
-
|
|
52
|
-
plugin.dest = plugin.config.get(
|
|
49
|
+
this.dest = this.config.get(
|
|
53
50
|
'relay_dest_domains.ini',
|
|
54
51
|
'ini',
|
|
55
|
-
() => {
|
|
52
|
+
() => { this.load_dest_domains(); }
|
|
56
53
|
);
|
|
57
54
|
}
|
|
58
55
|
|
|
59
56
|
exports.load_acls = function () {
|
|
60
|
-
const plugin = this;
|
|
61
57
|
const file_name = 'relay_acl_allow';
|
|
62
58
|
|
|
63
59
|
// load with a self-referential callback
|
|
64
|
-
|
|
65
|
-
|
|
60
|
+
this.acl_allow = this.config.get(file_name, 'list', () => {
|
|
61
|
+
this.load_acls();
|
|
66
62
|
});
|
|
67
63
|
|
|
68
|
-
for (let i=0; i<
|
|
69
|
-
const cidr =
|
|
64
|
+
for (let i=0; i<this.acl_allow.length; i++) {
|
|
65
|
+
const cidr = this.acl_allow[i].split('/');
|
|
70
66
|
if (!net.isIP(cidr[0])) {
|
|
71
|
-
|
|
67
|
+
this.logerror(this, `invalid entry in ${file_name}: ${cidr[0]}`);
|
|
72
68
|
}
|
|
73
69
|
if (!cidr[1]) {
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
this.logerror(this, `appending missing CIDR suffix in: ${file_name}`);
|
|
71
|
+
this.acl_allow[i] = `${cidr[0] }/32`;
|
|
76
72
|
}
|
|
77
73
|
}
|
|
78
74
|
}
|
|
79
75
|
|
|
80
76
|
exports.acl = function (next, connection) {
|
|
81
|
-
|
|
82
|
-
if (!plugin.cfg.relay.acl) { return next(); }
|
|
77
|
+
if (!this.cfg.relay.acl) { return next(); }
|
|
83
78
|
|
|
84
79
|
connection.logdebug(this, `checking ${connection.remote.ip} in relay_acl_allow`);
|
|
85
80
|
|
|
86
|
-
if (!
|
|
87
|
-
connection.results.add(
|
|
81
|
+
if (!this.is_acl_allowed(connection)) {
|
|
82
|
+
connection.results.add(this, {skip: 'acl(unlisted)'});
|
|
88
83
|
return next();
|
|
89
84
|
}
|
|
90
85
|
|
|
91
|
-
connection.results.add(
|
|
86
|
+
connection.results.add(this, {pass: 'acl'});
|
|
92
87
|
connection.relaying = true;
|
|
93
88
|
return next(OK);
|
|
94
89
|
}
|
|
@@ -102,16 +97,14 @@ exports.pass_relaying = (next, connection) => {
|
|
|
102
97
|
}
|
|
103
98
|
|
|
104
99
|
exports.is_acl_allowed = function (connection) {
|
|
105
|
-
|
|
106
|
-
if (!
|
|
107
|
-
if (!plugin.acl_allow.length) { return false; }
|
|
100
|
+
if (!this.acl_allow) { return false; }
|
|
101
|
+
if (!this.acl_allow.length) { return false; }
|
|
108
102
|
|
|
109
|
-
const ip = connection.remote
|
|
103
|
+
const { ip } = connection.remote;
|
|
110
104
|
|
|
111
|
-
for (
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const cidr = plugin.acl_allow[i].split('/');
|
|
105
|
+
for (const item of this.acl_allow) {
|
|
106
|
+
connection.logdebug(this, `checking if ${ip} is in ${item}`);
|
|
107
|
+
const cidr = item.split('/');
|
|
115
108
|
const c_net = cidr[0];
|
|
116
109
|
const c_mask = cidr[1] || 32;
|
|
117
110
|
|
|
@@ -120,7 +113,7 @@ exports.is_acl_allowed = function (connection) {
|
|
|
120
113
|
if (net.isIPv6(ip) && net.isIPv4(c_net)) continue;
|
|
121
114
|
|
|
122
115
|
if (ipaddr.parse(ip).match(ipaddr.parse(c_net), c_mask)) {
|
|
123
|
-
connection.logdebug(
|
|
116
|
+
connection.logdebug(this, `checking if ${ip} is in ${item}: yes`);
|
|
124
117
|
return true;
|
|
125
118
|
}
|
|
126
119
|
}
|
|
@@ -128,37 +121,37 @@ exports.is_acl_allowed = function (connection) {
|
|
|
128
121
|
}
|
|
129
122
|
|
|
130
123
|
exports.dest_domains = function (next, connection, params) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
124
|
+
if (!this.cfg.relay.dest_domains) { return next(); }
|
|
125
|
+
const { relaying, transaction } = connection ?? {}
|
|
126
|
+
if (!transaction) return next();
|
|
134
127
|
|
|
135
128
|
// Skip this if the host is already allowed to relay
|
|
136
|
-
if (
|
|
137
|
-
transaction.results.add(
|
|
129
|
+
if (relaying) {
|
|
130
|
+
transaction.results.add(this, {skip: 'relay_dest_domain(relay)'});
|
|
138
131
|
return next();
|
|
139
132
|
}
|
|
140
133
|
|
|
141
|
-
if (!
|
|
142
|
-
transaction.results.add(
|
|
134
|
+
if (!this.dest) {
|
|
135
|
+
transaction.results.add(this, {err: 'relay_dest_domain(no config!)'});
|
|
143
136
|
return next();
|
|
144
137
|
}
|
|
145
138
|
|
|
146
|
-
if (!
|
|
147
|
-
transaction.results.add(
|
|
139
|
+
if (!this.dest.domains) {
|
|
140
|
+
transaction.results.add(this, {skip: 'relay_dest_domain(config)'});
|
|
148
141
|
return next();
|
|
149
142
|
}
|
|
150
143
|
|
|
151
144
|
const dest_domain = params[0].host;
|
|
152
|
-
connection.logdebug(
|
|
145
|
+
connection.logdebug(this, `dest_domain = ${dest_domain}`);
|
|
153
146
|
|
|
154
|
-
const dst_cfg =
|
|
147
|
+
const dst_cfg = this.dest.domains[dest_domain];
|
|
155
148
|
if (!dst_cfg) {
|
|
156
|
-
transaction.results.add(
|
|
149
|
+
transaction.results.add(this, {fail: 'relay_dest_domain'});
|
|
157
150
|
return next(DENY, "You are not allowed to relay");
|
|
158
151
|
}
|
|
159
152
|
|
|
160
|
-
const action = JSON.parse(dst_cfg)
|
|
161
|
-
connection.logdebug(
|
|
153
|
+
const { action } = JSON.parse(dst_cfg);
|
|
154
|
+
connection.logdebug(this, `found config for ${dest_domain}: ${action}`);
|
|
162
155
|
|
|
163
156
|
switch (action) {
|
|
164
157
|
case "accept":
|
|
@@ -166,53 +159,50 @@ exports.dest_domains = function (next, connection, params) {
|
|
|
166
159
|
// address to be considered 'local'. What advantage does relaying
|
|
167
160
|
// bring?
|
|
168
161
|
connection.relaying = true;
|
|
169
|
-
transaction.results.add(
|
|
162
|
+
transaction.results.add(this, {pass: 'relay_dest_domain'});
|
|
170
163
|
return next(OK);
|
|
171
164
|
case "continue":
|
|
172
165
|
// why oh why? Only reason I can think of is to enable outbound.
|
|
173
166
|
connection.relaying = true;
|
|
174
|
-
transaction.results.add(
|
|
167
|
+
transaction.results.add(this, {pass: 'relay_dest_domain'});
|
|
175
168
|
return next(CONT); // same as next()
|
|
176
169
|
case "deny":
|
|
177
|
-
transaction.results.add(
|
|
170
|
+
transaction.results.add(this, {fail: 'relay_dest_domain'});
|
|
178
171
|
return next(DENY, "You are not allowed to relay");
|
|
179
172
|
}
|
|
180
173
|
|
|
181
|
-
transaction.results.add(
|
|
174
|
+
transaction.results.add(this, {fail: 'relay_dest_domain'});
|
|
182
175
|
return next(DENY, "Mail for that recipient is not accepted here.");
|
|
183
176
|
}
|
|
184
177
|
|
|
185
178
|
exports.force_routing = function (next, hmail, domain) {
|
|
186
|
-
|
|
187
|
-
if (!
|
|
188
|
-
if (!
|
|
189
|
-
|
|
190
|
-
let route = plugin.dest.domains[domain];
|
|
179
|
+
if (!this.cfg.relay.force_routing) { return next(); }
|
|
180
|
+
if (!this.dest) { return next(); }
|
|
181
|
+
if (!this.dest.domains) { return next(); }
|
|
182
|
+
let route = this.dest.domains[domain];
|
|
191
183
|
|
|
192
184
|
if (!route) {
|
|
193
|
-
route =
|
|
185
|
+
route = this.dest.domains.any;
|
|
194
186
|
if (!route) {
|
|
195
|
-
|
|
187
|
+
this.logdebug(this, `using normal MX lookup for: ${domain}`);
|
|
196
188
|
return next();
|
|
197
189
|
}
|
|
198
190
|
}
|
|
199
191
|
|
|
200
|
-
const nexthop = JSON.parse(route)
|
|
192
|
+
const { nexthop } = JSON.parse(route);
|
|
201
193
|
if (!nexthop) {
|
|
202
|
-
|
|
194
|
+
this.logdebug(this, `using normal MX lookup for: ${domain}`);
|
|
203
195
|
return next();
|
|
204
196
|
}
|
|
205
197
|
|
|
206
|
-
|
|
198
|
+
this.logdebug(this, `using ${nexthop} for: ${domain}`);
|
|
207
199
|
return next(OK, nexthop);
|
|
208
200
|
}
|
|
209
201
|
|
|
210
202
|
exports.all = function (next, connection, params) {
|
|
211
|
-
|
|
212
|
-
const plugin = this;
|
|
213
|
-
if (!plugin.cfg.relay.all) { return next(); }
|
|
203
|
+
if (!this.cfg.relay.all) { return next(); }
|
|
214
204
|
|
|
215
|
-
connection.loginfo(
|
|
205
|
+
connection.loginfo(this, `confirming recipient ${params[0]}`);
|
|
216
206
|
connection.relaying = true;
|
|
217
207
|
next(OK);
|
|
218
208
|
}
|
package/plugins/reseed_rng.js
CHANGED