Haraka 3.0.2 → 3.0.4
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 +5 -9
- package/.prettierrc.yml +1 -0
- package/CONTRIBUTORS.md +11 -0
- package/Changes.md +1393 -1211
- package/Dockerfile +3 -3
- package/Plugins.md +119 -106
- package/README.md +7 -16
- package/TODO +1 -24
- package/bin/haraka +197 -298
- package/config/auth_flat_file.ini +2 -0
- package/config/auth_vpopmaild.ini +4 -2
- package/config/dhparams.pem +8 -0
- package/config/mail_from.is_resolvable.ini +4 -2
- package/config/me +1 -0
- package/config/outbound.ini +0 -2
- package/config/plugins +36 -35
- package/config/rabbitmq_amqplib.ini +8 -1
- package/config/smtp.ini +0 -1
- package/config/smtp.json +17 -0
- package/config/tls_cert.pem +23 -0
- package/config/tls_key.pem +28 -0
- package/connection.js +46 -73
- package/contrib/bsd-rc.d/haraka +3 -1
- package/contrib/plugin2npm.sh +6 -36
- package/docs/Connection.md +1 -1
- package/docs/CoreConfig.md +2 -2
- package/docs/Logging.md +7 -21
- package/docs/Outbound.md +104 -210
- package/docs/Plugins.md +47 -40
- package/docs/Transaction.md +59 -82
- package/docs/{plugins → deprecated}/connect.rdns_access.md +1 -1
- package/docs/{plugins → deprecated}/mail_from.access.md +1 -1
- package/docs/{plugins → deprecated}/rcpt_to.access.md +1 -1
- package/docs/plugins/auth/auth_vpopmaild.md +15 -19
- package/docs/plugins/auth/flat_file.md +23 -30
- package/docs/plugins/queue/rabbitmq_amqplib.md +7 -0
- package/docs/plugins/queue/smtp_forward.md +1 -1
- package/docs/plugins/queue/smtp_proxy.md +5 -10
- package/docs/plugins/relay.md +2 -2
- package/docs/plugins/tls.md +29 -9
- package/endpoint.js +16 -13
- package/haraka.js +10 -14
- package/host_pool.js +5 -5
- package/line_socket.js +3 -4
- package/logger.js +44 -28
- package/outbound/client_pool.js +27 -23
- package/outbound/config.js +4 -6
- package/outbound/fsync_writestream.js +1 -1
- package/outbound/hmail.js +180 -220
- package/outbound/index.js +86 -99
- package/outbound/qfile.js +1 -1
- package/outbound/queue.js +55 -43
- package/outbound/timer_queue.js +3 -2
- package/outbound/tls.js +19 -7
- package/package.json +66 -55
- package/plugins/.eslintrc.yaml +0 -6
- package/plugins/auth/auth_base.js +30 -12
- package/plugins/auth/auth_proxy.js +14 -12
- package/plugins/auth/auth_vpopmaild.js +30 -20
- package/plugins/auth/flat_file.js +17 -12
- package/plugins/block_me.js +1 -1
- package/plugins/data.signatures.js +2 -4
- package/plugins/early_talker.js +2 -1
- package/plugins/mail_from.is_resolvable.js +65 -135
- package/plugins/queue/deliver.js +4 -5
- package/plugins/queue/lmtp.js +11 -14
- package/plugins/queue/qmail-queue.js +2 -2
- package/plugins/queue/quarantine.js +2 -2
- package/plugins/queue/rabbitmq.js +16 -17
- package/plugins/queue/rabbitmq_amqplib.js +1 -1
- package/plugins/queue/smtp_forward.js +6 -6
- package/plugins/queue/smtp_proxy.js +10 -1
- package/plugins/queue/test.js +2 -2
- package/plugins/rcpt_to.host_list_base.js +5 -5
- package/plugins/rcpt_to.in_host_list.js +2 -2
- package/plugins/relay.js +6 -7
- package/plugins/reseed_rng.js +1 -1
- package/plugins/status.js +37 -33
- package/plugins/tls.js +2 -2
- package/plugins/xclient.js +3 -2
- package/plugins.js +51 -54
- package/run_tests +3 -30
- package/server.js +190 -190
- package/smtp_client.js +30 -23
- package/{tests → test}/config/plugins +0 -2
- package/{tests → test}/config/smtp.ini +1 -1
- package/test/config/tls/example.com/_.example.com.key +28 -0
- package/test/config/tls/example.com/example.com.crt +25 -0
- package/test/connection.js +302 -0
- package/test/endpoint.js +94 -0
- package/{tests → test}/fixtures/line_socket.js +1 -1
- package/{tests → test}/fixtures/util_hmailitem.js +19 -25
- package/{tests → test}/host_pool.js +42 -57
- package/test/logger.js +258 -0
- package/test/outbound/hmail.js +141 -0
- package/test/outbound/index.js +220 -0
- package/test/outbound/qfile.js +126 -0
- package/test/outbound_bounce_net_errors.js +142 -0
- package/{tests → test}/outbound_bounce_rfc3464.js +110 -122
- package/test/plugins/auth/auth_base.js +484 -0
- package/test/plugins/auth/auth_vpopmaild.js +83 -0
- package/test/plugins/early_talker.js +104 -0
- package/test/plugins/mail_from.is_resolvable.js +35 -0
- package/test/plugins/queue/smtp_forward.js +206 -0
- package/test/plugins/rcpt_to.host_list_base.js +122 -0
- package/test/plugins/rcpt_to.in_host_list.js +193 -0
- package/test/plugins/relay.js +303 -0
- package/test/plugins/status.js +130 -0
- package/test/plugins/tls.js +70 -0
- package/test/plugins.js +228 -0
- package/{tests → test}/queue/multibyte +0 -0
- package/{tests → test}/queue/plain +0 -0
- package/test/rfc1869.js +73 -0
- package/test/server.js +491 -0
- package/test/smtp_client.js +299 -0
- package/test/tls_socket.js +273 -0
- package/test/transaction.js +270 -0
- package/tls_socket.js +202 -252
- package/transaction.js +9 -24
- package/CONTRIBUTING.md +0 -1
- package/bin/dkimverify +0 -40
- package/config/access.domains +0 -13
- package/config/attachment.ctype.regex +0 -2
- package/config/attachment.filename.regex +0 -1
- package/config/avg.ini +0 -5
- package/config/bounce.ini +0 -15
- package/config/data.headers.ini +0 -61
- package/config/dkim/dkim_key_gen.sh +0 -78
- package/config/dkim_sign.ini +0 -4
- package/config/dkim_verify.ini +0 -7
- package/config/dnsbl.ini +0 -23
- package/config/greylist.ini +0 -43
- package/config/helo.checks.ini +0 -52
- package/config/lookup_rdns.strict.ini +0 -12
- package/config/lookup_rdns.strict.timeout +0 -1
- package/config/lookup_rdns.strict.whitelist +0 -1
- package/config/lookup_rdns.strict.whitelist_regex +0 -5
- package/config/messagesniffer.ini +0 -18
- package/config/rcpt_to.blocklist +0 -1
- package/config/rdns.allow_regexps +0 -0
- package/config/rdns.deny_regexps +0 -0
- package/config/spamassassin.ini +0 -56
- package/config.js +0 -6
- package/dkim.js +0 -614
- package/docs/plugins/avg.md +0 -35
- package/docs/plugins/bounce.md +0 -69
- package/docs/plugins/clamd.md +0 -147
- package/docs/plugins/esets.md +0 -8
- package/docs/plugins/greylist.md +0 -90
- package/docs/plugins/helo.checks.md +0 -135
- package/docs/plugins/messagesniffer.md +0 -163
- package/docs/plugins/relay_acl.md +0 -29
- package/docs/plugins/relay_all.md +0 -15
- package/docs/plugins/relay_force_routing.md +0 -33
- package/docs/plugins/spamassassin.md +0 -180
- package/outbound/mx_lookup.js +0 -70
- package/plugins/auth/auth_ldap.js +0 -3
- package/plugins/avg.js +0 -162
- package/plugins/backscatterer.js +0 -25
- package/plugins/bounce.js +0 -381
- package/plugins/clamd.js +0 -381
- package/plugins/data.headers.js +0 -4
- package/plugins/data.uribl.js +0 -4
- package/plugins/dkim_sign.js +0 -395
- package/plugins/dkim_verify.js +0 -62
- package/plugins/dns_list_base.js +0 -221
- package/plugins/dnsbl.js +0 -146
- package/plugins/dnswl.js +0 -58
- package/plugins/esets.js +0 -71
- package/plugins/graph.js +0 -5
- package/plugins/greylist.js +0 -645
- package/plugins/helo.checks.js +0 -533
- package/plugins/messagesniffer.js +0 -381
- package/plugins/rcpt_to.ldap.js +0 -3
- package/plugins/rcpt_to.max_count.js +0 -24
- package/plugins/relay_all.js +0 -13
- package/plugins/spamassassin.js +0 -384
- package/tests/config/dkim/example.com/dns +0 -29
- package/tests/config/dkim/example.com/private +0 -6
- package/tests/config/dkim/example.com/public +0 -4
- package/tests/config/dkim/example.com/selector +0 -1
- package/tests/config/dkim.private.key +0 -6
- package/tests/config/dkim_sign.ini +0 -4
- package/tests/config/helo.checks.ini +0 -52
- package/tests/connection.js +0 -327
- package/tests/endpoint.js +0 -128
- package/tests/fixtures/vm_harness.js +0 -59
- package/tests/logger.js +0 -327
- package/tests/outbound/hmail.js +0 -112
- package/tests/outbound/index.js +0 -324
- package/tests/outbound/qfile.js +0 -67
- package/tests/outbound_bounce_net_errors.js +0 -173
- package/tests/plugins/auth/auth_base.js +0 -463
- package/tests/plugins/auth/auth_vpopmaild.js +0 -91
- package/tests/plugins/bounce.js +0 -307
- package/tests/plugins/clamd.js +0 -224
- package/tests/plugins/deprecated/relay_acl.js +0 -140
- package/tests/plugins/deprecated/relay_all.js +0 -59
- package/tests/plugins/dkim_sign.js +0 -315
- package/tests/plugins/dkim_signer.js +0 -108
- package/tests/plugins/dns_list_base.js +0 -259
- package/tests/plugins/dnsbl.js +0 -101
- package/tests/plugins/early_talker.js +0 -115
- package/tests/plugins/greylist.js +0 -58
- package/tests/plugins/helo.checks.js +0 -525
- package/tests/plugins/mail_from.is_resolvable.js +0 -116
- package/tests/plugins/queue/smtp_forward.js +0 -221
- package/tests/plugins/rcpt_to.host_list_base.js +0 -132
- package/tests/plugins/rcpt_to.in_host_list.js +0 -218
- package/tests/plugins/relay.js +0 -339
- package/tests/plugins/spamassassin.js +0 -171
- package/tests/plugins/status.js +0 -138
- package/tests/plugins/tls.js +0 -84
- package/tests/plugins.js +0 -247
- package/tests/rfc1869.js +0 -61
- package/tests/server.js +0 -510
- package/tests/smtp_client/auth.js +0 -105
- package/tests/smtp_client/basic.js +0 -101
- package/tests/smtp_client.js +0 -80
- package/tests/tls_socket.js +0 -333
- package/tests/transaction.js +0 -284
- /package/docs/{plugins → deprecated}/dkim_sign.md +0 -0
- /package/docs/{plugins → deprecated}/dkim_verify.md +0 -0
- /package/docs/{plugins → deprecated}/dnsbl.md +0 -0
- /package/docs/{plugins → deprecated}/dnswl.md +0 -0
- /package/docs/{plugins → deprecated}/rcpt_to.routes.md +0 -0
- /package/{tests → test}/.eslintrc.yaml +0 -0
- /package/{tests → test}/config/auth_flat_file.ini +0 -0
- /package/{tests → test}/config/dhparams.pem +0 -0
- /package/{tests → test}/config/host_list +0 -0
- /package/{tests → test}/config/outbound_tls_cert.pem +0 -0
- /package/{tests → test}/config/outbound_tls_key.pem +0 -0
- /package/{tests → test}/config/smtp_forward.ini +0 -0
- /package/{tests → test}/config/tls/ec.pem +0 -0
- /package/{tests → test}/config/tls/haraka.local.pem +0 -0
- /package/{tests → test}/config/tls/mismatched.pem +0 -0
- /package/{tests → test}/config/tls.ini +0 -0
- /package/{tests → test}/config/tls_cert.pem +0 -0
- /package/{tests → test}/config/tls_key.pem +0 -0
- /package/{tests → test}/fixtures/todo_qfile.txt +0 -0
- /package/{tests → test}/installation/config/test-plugin-flat +0 -0
- /package/{tests → test}/installation/config/test-plugin.ini +0 -0
- /package/{tests → test}/installation/config/tls.ini +0 -0
- /package/{tests → test}/installation/node_modules/load_first/index.js +0 -0
- /package/{tests → test}/installation/node_modules/load_first/package.json +0 -0
- /package/{tests → test}/installation/node_modules/test-plugin/config/test-plugin-flat +0 -0
- /package/{tests → test}/installation/node_modules/test-plugin/config/test-plugin.ini +0 -0
- /package/{tests → test}/installation/node_modules/test-plugin/package.json +0 -0
- /package/{tests → test}/installation/node_modules/test-plugin/test-plugin.js +0 -0
- /package/{tests → test}/installation/plugins/base_plugin.js +0 -0
- /package/{tests → test}/installation/plugins/folder_plugin/index.js +0 -0
- /package/{tests → test}/installation/plugins/folder_plugin/package.json +0 -0
- /package/{tests → test}/installation/plugins/inherits.js +0 -0
- /package/{tests → test}/installation/plugins/load_first.js +0 -0
- /package/{tests → test}/installation/plugins/plugin.js +0 -0
- /package/{tests → test}/installation/plugins/tls.js +0 -0
- /package/{tests → test}/loud/config/dhparams.pem +0 -0
- /package/{tests → test}/loud/config/tls/goobered.pem +0 -0
- /package/{tests → test}/loud/config/tls.ini +0 -0
- /package/{tests → test}/mail_specimen/base64-root-part.txt +0 -0
- /package/{tests → test}/mail_specimen/varied-fold-lengths-preserve-data.txt +0 -0
- /package/{tests → test}/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_fixed +0 -0
- /package/{tests → test}/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka +0 -0
- /package/{tests → test}/queue/1508269674999_1508269674999_0_34002_socVUF_1_haraka +0 -0
- /package/{tests → test}/queue/1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka +0 -0
- /package/{tests → test}/queue/zero-length +0 -0
- /package/{tests → test}/test-queue/delete-me +0 -0
package/plugins/helo.checks.js
DELETED
|
@@ -1,533 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
// Check various bits of the HELO string
|
|
3
|
-
|
|
4
|
-
const tlds = require('haraka-tld');
|
|
5
|
-
const dns = require('dns');
|
|
6
|
-
const net_utils = require('haraka-net-utils');
|
|
7
|
-
const utils = require('haraka-utils');
|
|
8
|
-
|
|
9
|
-
const checks = [
|
|
10
|
-
'match_re', // List of regexps
|
|
11
|
-
'bare_ip', // HELO is bare IP (vs required Address Literal)
|
|
12
|
-
'dynamic', // HELO hostname looks dynamic (dsl|dialup|etc...)
|
|
13
|
-
'big_company', // Well known HELOs that must match rdns
|
|
14
|
-
'valid_hostname', // HELO hostname is a legal DNS name
|
|
15
|
-
'rdns_match', // HELO hostname matches rDNS
|
|
16
|
-
'forward_dns', // HELO hostname resolves to the connecting IP
|
|
17
|
-
'host_mismatch', // hostname differs between invocations
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
exports.register = function () {
|
|
21
|
-
this.load_helo_checks_ini();
|
|
22
|
-
|
|
23
|
-
if (this.cfg.check.proto_mismatch) {
|
|
24
|
-
// NOTE: these *must* run before init
|
|
25
|
-
this.register_hook('helo', 'proto_mismatch_smtp');
|
|
26
|
-
this.register_hook('ehlo', 'proto_mismatch_esmtp');
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Always run init
|
|
30
|
-
this.register_hook('helo', 'init');
|
|
31
|
-
this.register_hook('ehlo', 'init');
|
|
32
|
-
|
|
33
|
-
for (const c of checks) {
|
|
34
|
-
if (!this.cfg.check[c]) continue; // disabled in config
|
|
35
|
-
this.register_hook('helo', c);
|
|
36
|
-
this.register_hook('ehlo', c);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Always emit a log entry
|
|
40
|
-
this.register_hook('helo', 'emit_log');
|
|
41
|
-
this.register_hook('ehlo', 'emit_log');
|
|
42
|
-
|
|
43
|
-
// IP literal that doesn't match remote IP
|
|
44
|
-
this.register_hook('helo', 'literal_mismatch');
|
|
45
|
-
this.register_hook('ehlo', 'literal_mismatch');
|
|
46
|
-
|
|
47
|
-
this.cfg.check.literal_mismatch = this.cfg.check.literal_mismatch ?? 2;
|
|
48
|
-
this.cfg.reject.literal_mismatch = this.cfg.reject.literal_mismatch ?? false;
|
|
49
|
-
|
|
50
|
-
if (this.cfg.check.match_re) {
|
|
51
|
-
const load_re_file = () => {
|
|
52
|
-
const regex_list = utils.valid_regexes(this.config.get('helo.checks.regexps', 'list', load_re_file));
|
|
53
|
-
// pre-compile the regexes
|
|
54
|
-
this.cfg.list_re = new RegExp(`^(${regex_list.join('|')})$`, 'i');
|
|
55
|
-
};
|
|
56
|
-
load_re_file();
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
exports.load_helo_checks_ini = function () {
|
|
61
|
-
|
|
62
|
-
const booleans = [
|
|
63
|
-
'+skip.private_ip',
|
|
64
|
-
'+skip.whitelist',
|
|
65
|
-
'+skip.relaying',
|
|
66
|
-
|
|
67
|
-
'+check.proto_mismatch',
|
|
68
|
-
'-reject.proto_mismatch',
|
|
69
|
-
];
|
|
70
|
-
|
|
71
|
-
checks.forEach(c => {
|
|
72
|
-
booleans.push(`+check.${c}`);
|
|
73
|
-
booleans.push(`-reject.${c}`);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
this.cfg = this.config.get('helo.checks.ini', { booleans },
|
|
77
|
-
() => {
|
|
78
|
-
this.load_helo_checks_ini();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
// backwards compatible with old config file
|
|
82
|
-
if (this.cfg.check_no_dot !== undefined) {
|
|
83
|
-
this.cfg.check.valid_hostname = !!this.cfg.check_no_dot;
|
|
84
|
-
}
|
|
85
|
-
if (this.cfg.check_dynamic !== undefined) {
|
|
86
|
-
this.cfg.check.dynamic = !!this.cfg.check_dynamic;
|
|
87
|
-
}
|
|
88
|
-
if (this.cfg.check_raw_ip !== undefined) {
|
|
89
|
-
this.cfg.check.bare_ip = !!this.cfg.check_raw_ip;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// non-default setting, so apply their localized setting
|
|
93
|
-
if (this.cfg.check.mismatch !== undefined && !this.cfg.check.mismatch) {
|
|
94
|
-
this.logerror('deprecated setting mismatch renamed to host_mismatch');
|
|
95
|
-
this.cfg.check.host_mismatch = this.cfg.check.mismatch;
|
|
96
|
-
}
|
|
97
|
-
if (this.cfg.reject.mismatch !== undefined && this.cfg.reject.mismatch) {
|
|
98
|
-
this.logerror('deprecated setting mismatch renamed to host_mismatch');
|
|
99
|
-
this.cfg.reject.host_mismatch = this.cfg.reject.mismatch;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
exports.init = function (next, connection, helo) {
|
|
104
|
-
if (!connection.results.has('helo.checks', 'helo_host', helo)) {
|
|
105
|
-
connection.results.add(this, {helo_host: helo});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
next();
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
exports.should_skip = function (connection, test_name) {
|
|
112
|
-
if (connection.results.has('helo.checks', '_skip_hooks', test_name)) {
|
|
113
|
-
this.logdebug(connection, `SKIPPING: ${test_name}`);
|
|
114
|
-
return true;
|
|
115
|
-
}
|
|
116
|
-
connection.results.push(this, {_skip_hooks: test_name});
|
|
117
|
-
|
|
118
|
-
if (this.cfg.skip.relaying && connection.relaying) {
|
|
119
|
-
connection.results.add(this, {skip: `${test_name}(relay)`});
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (this.cfg.skip.private_ip && connection.remote.is_private) {
|
|
124
|
-
connection.results.add(this, {skip: `${test_name}(private)`});
|
|
125
|
-
return true;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
exports.host_mismatch = function (next, connection, helo) {
|
|
132
|
-
if (this.should_skip(connection, 'host_mismatch')) return next();
|
|
133
|
-
|
|
134
|
-
const prev_helo = connection.results.get('helo.checks').helo_host;
|
|
135
|
-
if (!prev_helo) {
|
|
136
|
-
connection.results.add(this, {skip: 'host_mismatch(1st)'});
|
|
137
|
-
connection.notes.prev_helo = helo;
|
|
138
|
-
return next();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (prev_helo === helo) {
|
|
142
|
-
connection.results.add(this, {pass: 'host_mismatch'});
|
|
143
|
-
return next();
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const msg = `host_mismatch(${prev_helo} / ${helo})`;
|
|
147
|
-
connection.results.add(this, {fail: msg});
|
|
148
|
-
if (!this.cfg.reject.host_mismatch) return next();
|
|
149
|
-
|
|
150
|
-
next(DENY, `HELO host ${msg}`);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
exports.valid_hostname = function (next, connection, helo) {
|
|
154
|
-
|
|
155
|
-
if (this.should_skip(connection, 'valid_hostname')) return next();
|
|
156
|
-
|
|
157
|
-
if (net_utils.is_ip_literal(helo)) {
|
|
158
|
-
connection.results.add(this, {skip: 'valid_hostname(literal)'});
|
|
159
|
-
return next();
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (!/\./.test(helo)) {
|
|
163
|
-
connection.results.add(this, {fail: 'valid_hostname(no_dot)'});
|
|
164
|
-
if (this.cfg.reject.valid_hostname) {
|
|
165
|
-
return next(DENY, 'HELO host must be a FQDN or address literal (RFC 5321 2.3.5)');
|
|
166
|
-
}
|
|
167
|
-
return next();
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// this will fail if TLD is invalid or hostname is a public suffix
|
|
171
|
-
if (!tlds.get_organizational_domain(helo)) {
|
|
172
|
-
// Check for any excluded TLDs
|
|
173
|
-
const excludes = this.config.get('helo.checks.allow', 'list');
|
|
174
|
-
const tld = (helo.split(/\./).reverse())[0].toLowerCase();
|
|
175
|
-
// Exclude .local, .lan and .corp
|
|
176
|
-
if (tld === 'local' || tld === 'lan' || tld === 'corp' || excludes.includes(`.${tld}`)) {
|
|
177
|
-
return next();
|
|
178
|
-
}
|
|
179
|
-
connection.results.add(this, {fail: 'valid_hostname'});
|
|
180
|
-
if (this.cfg.reject.valid_hostname) {
|
|
181
|
-
return next(DENY, "HELO host name invalid");
|
|
182
|
-
}
|
|
183
|
-
return next();
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
connection.results.add(this, {pass: 'valid_hostname'});
|
|
187
|
-
next();
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
exports.match_re = function (next, connection, helo) {
|
|
191
|
-
|
|
192
|
-
if (this.should_skip(connection, 'match_re')) return next();
|
|
193
|
-
|
|
194
|
-
if (this.cfg.list_re?.test(helo)) {
|
|
195
|
-
connection.results.add(this, {fail: 'match_re'});
|
|
196
|
-
if (this.cfg.reject.match_re) {
|
|
197
|
-
return next(DENY, "That HELO not allowed here");
|
|
198
|
-
}
|
|
199
|
-
return next();
|
|
200
|
-
}
|
|
201
|
-
connection.results.add(this, {pass: 'match_re'});
|
|
202
|
-
next();
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
exports.rdns_match = function (next, connection, helo) {
|
|
206
|
-
|
|
207
|
-
if (this.should_skip(connection, 'rdns_match')) return next();
|
|
208
|
-
|
|
209
|
-
if (!helo) {
|
|
210
|
-
connection.results.add(this, {fail: 'rdns_match(empty)'});
|
|
211
|
-
return next();
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (net_utils.is_ip_literal(helo)) {
|
|
215
|
-
connection.results.add(this, {fail: 'rdns_match(literal)'});
|
|
216
|
-
return next();
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const r_host = connection.remote.host;
|
|
220
|
-
if (r_host && helo === r_host) {
|
|
221
|
-
connection.results.add(this, {pass: 'rdns_match'});
|
|
222
|
-
return next();
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (tlds.get_organizational_domain(r_host) ===
|
|
226
|
-
tlds.get_organizational_domain(helo)) {
|
|
227
|
-
connection.results.add(this, {pass: 'rdns_match(org_dom)'});
|
|
228
|
-
return next();
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
connection.results.add(this, {fail: 'rdns_match'});
|
|
232
|
-
if (this.cfg.reject.rdns_match) {
|
|
233
|
-
return next(DENY, 'HELO host does not match rDNS');
|
|
234
|
-
}
|
|
235
|
-
next();
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
exports.bare_ip = function (next, connection, helo) {
|
|
239
|
-
|
|
240
|
-
if (this.should_skip(connection, 'bare_ip')) return next();
|
|
241
|
-
|
|
242
|
-
// RFC 2821, 4.1.1.1 Address literals must be in brackets
|
|
243
|
-
// RAW IPs must be formatted: "[1.2.3.4]" not "1.2.3.4" in HELO
|
|
244
|
-
if (net_utils.get_ipany_re('^(?:IPv6:)?','$','').test(helo)) {
|
|
245
|
-
connection.results.add(this, {fail: 'bare_ip(invalid literal)'});
|
|
246
|
-
if (this.cfg.reject.bare_ip) {
|
|
247
|
-
return next(DENY, "Invalid address format in HELO");
|
|
248
|
-
}
|
|
249
|
-
return next();
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
connection.results.add(this, {pass: 'bare_ip'});
|
|
253
|
-
next();
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
exports.dynamic = function (next, connection, helo) {
|
|
257
|
-
|
|
258
|
-
if (this.should_skip(connection, 'dynamic')) return next();
|
|
259
|
-
|
|
260
|
-
// Skip if no dots or an IP literal or address
|
|
261
|
-
if (!/\./.test(helo)) {
|
|
262
|
-
connection.results.add(this, {skip: 'dynamic(no dots)'});
|
|
263
|
-
return next();
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
if (net_utils.get_ipany_re('^\\[?(?:IPv6:)?','\\]?$','').test(helo)) {
|
|
267
|
-
connection.results.add(this, {skip: 'dynamic(literal)'});
|
|
268
|
-
return next();
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
if (net_utils.is_ip_in_str(connection.remote.ip, helo)) {
|
|
272
|
-
connection.results.add(this, {fail: 'dynamic'});
|
|
273
|
-
if (this.cfg.reject.dynamic) {
|
|
274
|
-
return next(DENY, 'HELO is dynamic');
|
|
275
|
-
}
|
|
276
|
-
return next();
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
connection.results.add(this, {pass: 'dynamic'});
|
|
280
|
-
next();
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
exports.big_company = function (next, connection, helo) {
|
|
284
|
-
|
|
285
|
-
if (this.should_skip(connection, 'big_company')) return next();
|
|
286
|
-
|
|
287
|
-
if (net_utils.is_ip_literal(helo)) {
|
|
288
|
-
connection.results.add(this, {skip: 'big_co(literal)'});
|
|
289
|
-
return next();
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
if (!this.cfg.bigco) {
|
|
293
|
-
connection.results.add(this, {err: 'big_co(config missing)'});
|
|
294
|
-
return next();
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
if (!this.cfg.bigco[helo]) {
|
|
298
|
-
connection.results.add(this, {pass: 'big_co(not)'});
|
|
299
|
-
return next();
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
const rdns = connection.remote.host;
|
|
303
|
-
if (!rdns || rdns === 'Unknown' || rdns === 'DNSERROR') {
|
|
304
|
-
connection.results.add(this, {fail: 'big_co(rDNS)'});
|
|
305
|
-
if (this.cfg.reject.big_company) {
|
|
306
|
-
return next(DENY, "Big company w/o rDNS? Unlikely.");
|
|
307
|
-
}
|
|
308
|
-
return next();
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
const allowed_rdns = this.cfg.bigco[helo].split(/,/);
|
|
312
|
-
for (const allow of allowed_rdns) {
|
|
313
|
-
const re = new RegExp(`${allow.replace(/\./g, '\\.')}$`);
|
|
314
|
-
if (re.test(rdns)) {
|
|
315
|
-
connection.results.add(this, {pass: 'big_co'});
|
|
316
|
-
return next();
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
connection.results.add(this, {fail: 'big_co'});
|
|
321
|
-
if (this.cfg.reject.big_company) {
|
|
322
|
-
return next(DENY, "You are not who you say you are");
|
|
323
|
-
}
|
|
324
|
-
next();
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
exports.literal_mismatch = function (next, connection, helo) {
|
|
328
|
-
|
|
329
|
-
if (this.should_skip(connection, 'literal_mismatch')) return next();
|
|
330
|
-
|
|
331
|
-
const literal = net_utils.get_ipany_re('^\\[(?:IPv6:)?','\\]$','').exec(helo);
|
|
332
|
-
if (!literal) {
|
|
333
|
-
connection.results.add(this, {pass: 'literal_mismatch'});
|
|
334
|
-
return next();
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
const lmm_mode = parseInt(this.cfg.check.literal_mismatch, 10);
|
|
338
|
-
const helo_ip = literal[1];
|
|
339
|
-
if (lmm_mode > 2 && net_utils.is_private_ip(helo_ip)) {
|
|
340
|
-
connection.results.add(this, {pass: 'literal_mismatch(private)'});
|
|
341
|
-
return next();
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
if (lmm_mode > 1) {
|
|
345
|
-
if (net_utils.same_ipv4_network(connection.remote.ip, [helo_ip])) {
|
|
346
|
-
connection.results.add(this, {pass: 'literal_mismatch'});
|
|
347
|
-
return next();
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
connection.results.add(this, {fail: 'literal_mismatch'});
|
|
351
|
-
if (this.cfg.reject.literal_mismatch) {
|
|
352
|
-
return next(DENY, 'HELO IP literal not in the same /24 as your IP address');
|
|
353
|
-
}
|
|
354
|
-
return next();
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
if (helo_ip === connection.remote.ip) {
|
|
358
|
-
connection.results.add(this, {pass: 'literal_mismatch'});
|
|
359
|
-
return next();
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
connection.results.add(this, {fail: 'literal_mismatch'});
|
|
363
|
-
if (this.cfg.reject.literal_mismatch) {
|
|
364
|
-
return next(DENY, 'HELO IP literal does not match your IP address');
|
|
365
|
-
}
|
|
366
|
-
next();
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
exports.forward_dns = function (next, connection, helo) {
|
|
370
|
-
|
|
371
|
-
if (this.should_skip(connection, 'forward_dns')) return next();
|
|
372
|
-
if (!this.cfg.check.valid_hostname) {
|
|
373
|
-
connection.results.add(this, {err: 'forward_dns(valid_hostname disabled)'});
|
|
374
|
-
return next();
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
if (net_utils.is_ip_literal(helo)) {
|
|
378
|
-
connection.results.add(this, {skip: 'forward_dns(literal)'});
|
|
379
|
-
return next();
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
if (!connection.results.has('helo.checks', 'pass', /^valid_hostname/)) {
|
|
383
|
-
connection.results.add(this, {fail: 'forward_dns(invalid_hostname)'});
|
|
384
|
-
if (this.cfg.reject.forward_dns) {
|
|
385
|
-
return next(DENY, "Invalid HELO host cannot achieve forward DNS match");
|
|
386
|
-
}
|
|
387
|
-
return next();
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
this.get_a_records(helo)
|
|
391
|
-
.then(ips => {
|
|
392
|
-
|
|
393
|
-
if (!ips) {
|
|
394
|
-
connection.results.add(this, {err: 'forward_dns, no ips!'});
|
|
395
|
-
return next();
|
|
396
|
-
}
|
|
397
|
-
connection.results.add(this, {ips});
|
|
398
|
-
|
|
399
|
-
if (ips.includes(connection.remote.ip)) {
|
|
400
|
-
connection.results.add(this, {pass: 'forward_dns'});
|
|
401
|
-
return next();
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// some valid hosts (facebook.com, hotmail.com) use a generic HELO
|
|
405
|
-
// hostname that resolves but doesn't contain the IP that is
|
|
406
|
-
// connecting. If their rDNS passed, and their HELO hostname is in
|
|
407
|
-
// the same domain, consider it close enough.
|
|
408
|
-
if (connection.results.has('helo.checks', 'pass', /^rdns_match/)) {
|
|
409
|
-
const helo_od = tlds.get_organizational_domain(helo);
|
|
410
|
-
const rdns_od = tlds.get_organizational_domain(connection.remote.host);
|
|
411
|
-
if (helo_od && helo_od === rdns_od) {
|
|
412
|
-
connection.results.add(this, {pass: 'forward_dns(domain)'});
|
|
413
|
-
return next();
|
|
414
|
-
}
|
|
415
|
-
connection.results.add(this, {msg: `od miss: ${helo_od}, ${rdns_od}`});
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
connection.results.add(this, {fail: 'forward_dns(no IP match)'});
|
|
419
|
-
if (this.cfg.reject.forward_dns) {
|
|
420
|
-
return next(DENY, "HELO host has no forward DNS match");
|
|
421
|
-
}
|
|
422
|
-
next();
|
|
423
|
-
})
|
|
424
|
-
.catch(err => {
|
|
425
|
-
if (err.code === dns.NOTFOUND || err.code === dns.NODATA || err.code === dns.SERVFAIL) {
|
|
426
|
-
connection.results.add(this, {fail: `forward_dns(${err.code})`});
|
|
427
|
-
return next();
|
|
428
|
-
}
|
|
429
|
-
if (err.code === dns.TIMEOUT && this.cfg.reject.forward_dns) {
|
|
430
|
-
connection.results.add(this, {fail: `forward_dns(${err.code})`});
|
|
431
|
-
return next(DENYSOFT, "DNS timeout resolving your HELO hostname");
|
|
432
|
-
}
|
|
433
|
-
connection.results.add(this, {err: `forward_dns(${err})`, emit_log_level: 'warn'});
|
|
434
|
-
next();
|
|
435
|
-
})
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
exports.proto_mismatch = function (next, connection, helo, proto) {
|
|
439
|
-
|
|
440
|
-
if (this.should_skip(connection, 'proto_mismatch')) return next();
|
|
441
|
-
|
|
442
|
-
const r = connection.results.get('helo.checks');
|
|
443
|
-
if (!r || (r && !r.helo_host)) return next();
|
|
444
|
-
|
|
445
|
-
if ((connection.esmtp && proto === 'smtp') ||
|
|
446
|
-
(!connection.esmtp && proto === 'esmtp')) {
|
|
447
|
-
connection.results.add(this, {fail: `proto_mismatch(${proto})`});
|
|
448
|
-
if (this.cfg.reject.proto_mismatch) {
|
|
449
|
-
return next(DENY, `${proto === 'smtp' ? 'HELO' : 'EHLO'} protocol mismatch`);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
next();
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
exports.proto_mismatch_smtp = function (next, connection, helo) {
|
|
457
|
-
this.proto_mismatch(next, connection, helo, 'smtp');
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
exports.proto_mismatch_esmtp = function (next, connection, helo) {
|
|
461
|
-
this.proto_mismatch(next, connection, helo, 'esmtp');
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
exports.emit_log = function (next, connection, helo) {
|
|
465
|
-
// Spits out an INFO log entry. Default looks like this:
|
|
466
|
-
// [helo.checks] helo_host: [182.212.17.35], fail:big_co(rDNS) rdns_match(literal), pass:valid_hostname, match_re, bare_ip, literal_mismatch, mismatch, skip:dynamic(literal), valid_hostname(literal)
|
|
467
|
-
//
|
|
468
|
-
// Although sometimes useful, that's a bit verbose. I find that I'm rarely
|
|
469
|
-
// interested in the passes, the helo_host is already logged elsewhere,
|
|
470
|
-
// and so I set this in config/results.ini:
|
|
471
|
-
//
|
|
472
|
-
// [helo.checks]
|
|
473
|
-
// order=fail,pass,msg,err,skip
|
|
474
|
-
// hide=helo_host,multi,pass
|
|
475
|
-
//
|
|
476
|
-
// Thus set, my log entries look like this:
|
|
477
|
-
//
|
|
478
|
-
// [UUID] [helo.checks] fail:rdns_match
|
|
479
|
-
// [UUID] [helo.checks]
|
|
480
|
-
// [UUID] [helo.checks] fail:dynamic
|
|
481
|
-
connection.loginfo(this, connection.results.collate(this));
|
|
482
|
-
next();
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
exports.get_a_records = async function (host) {
|
|
486
|
-
|
|
487
|
-
if (!/\./.test(host)) {
|
|
488
|
-
// a single label is not a host name
|
|
489
|
-
const e = new Error("invalid hostname");
|
|
490
|
-
e.code = dns.NOTFOUND;
|
|
491
|
-
throw e;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
// Set-up timer
|
|
495
|
-
let timed_out = false;
|
|
496
|
-
const timer = setTimeout(() => {
|
|
497
|
-
timed_out = true;
|
|
498
|
-
const err = new Error(`timeout resolving: ${host}`);
|
|
499
|
-
err.code = dns.TIMEOUT;
|
|
500
|
-
this.logerror(err);
|
|
501
|
-
throw err;
|
|
502
|
-
}, (this.cfg.main.dns_timeout || 30) * 1000);
|
|
503
|
-
|
|
504
|
-
// fully qualify, to ignore any search options in /etc/resolv.conf
|
|
505
|
-
if (!/\.$/.test(host)) host = `${host}.`;
|
|
506
|
-
|
|
507
|
-
// do the queries
|
|
508
|
-
let ips
|
|
509
|
-
let err = '';
|
|
510
|
-
try {
|
|
511
|
-
ips = await net_utils.get_ips_by_host(host)
|
|
512
|
-
}
|
|
513
|
-
catch (errs) {
|
|
514
|
-
for (const error of errs) {
|
|
515
|
-
switch (error.code) {
|
|
516
|
-
case dns.NODATA:
|
|
517
|
-
case dns.NOTFOUND:
|
|
518
|
-
case dns.SERVFAIL:
|
|
519
|
-
continue;
|
|
520
|
-
default:
|
|
521
|
-
err = `${err}, ${error.message}`;
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
// results is now equals to: {queryA: 1, queryAAAA: 2}
|
|
527
|
-
if (timed_out) return;
|
|
528
|
-
if (timer) clearTimeout(timer);
|
|
529
|
-
if (!ips.length && err) throw err
|
|
530
|
-
// this.logdebug(this, host + ' => ' + ips);
|
|
531
|
-
// return the DNS results
|
|
532
|
-
return ips;
|
|
533
|
-
};
|