Haraka 3.0.1 → 3.0.2
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/Changes.md +25 -0
- package/config/outbound.ini +1 -1
- package/connection.js +32 -10
- package/docs/plugins/clamd.md +1 -1
- package/docs/plugins/queue/smtp_forward.md +15 -37
- package/docs/plugins/queue/smtp_proxy.md +9 -11
- package/outbound/tls.js +1 -1
- package/package.json +13 -13
- package/plugins/dns_list_base.js +3 -3
- package/plugins/helo.checks.js +14 -6
- package/plugins/queue/smtp_forward.js +22 -16
- package/plugins/tls.js +1 -1
- package/tests/config/helo.checks.ini +52 -0
- package/tests/plugins/dns_list_base.js +41 -31
- package/tests/plugins/helo.checks.js +212 -239
- package/tests/plugins/queue/smtp_forward.js +36 -7
- package/coverage/lcov.info +0 -13863
- package/coverage/tmp/coverage-42958-1658373250585-0.json +0 -1
- package/coverage/tmp/coverage-42961-1658373250529-0.json +0 -1
package/Changes.md
CHANGED
|
@@ -2,6 +2,30 @@
|
|
|
2
2
|
### Unreleased
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
### [3.0.2] - 2023-06-12
|
|
6
|
+
|
|
7
|
+
#### Fixed
|
|
8
|
+
|
|
9
|
+
- feat(q_forward): add LMTP routing handling #3199
|
|
10
|
+
- chore(q_forward): tighten up queue.wants handling #3199
|
|
11
|
+
- doc(q_forward): improve markdown formatting #3199
|
|
12
|
+
- helo.checks: several fixes, #3191
|
|
13
|
+
- q/smtp_forward: correct path to next_hop #3186
|
|
14
|
+
- don't leak addr parsing errors into SMTP conversation #3185
|
|
15
|
+
- connection: handle dns.reverse invalid throws on node v20 #3184
|
|
16
|
+
- rename redis command setex to setEx #3181
|
|
17
|
+
|
|
18
|
+
#### Changed
|
|
19
|
+
|
|
20
|
+
- test(helo.checks): add regression tests for #3191 #3195
|
|
21
|
+
- connection: handle dns.reverse invalid throws on node v20
|
|
22
|
+
- build(deps): bump ipaddr.js from 2.0.1 to 2.1.0 #3194
|
|
23
|
+
- chore: bump a few dependency versions #3184
|
|
24
|
+
- dns_list_base: avoid test failure when public DNS used #3184
|
|
25
|
+
- doc(outbound.ini) update link #3159
|
|
26
|
+
- doc(clamd.md) fixed spelling error #3155
|
|
27
|
+
|
|
28
|
+
|
|
5
29
|
### [3.0.1] - 2023-01-19
|
|
6
30
|
|
|
7
31
|
#### Fixed
|
|
@@ -1345,3 +1369,4 @@
|
|
|
1345
1369
|
|
|
1346
1370
|
[3.0.0]: https://github.com/haraka/Haraka/releases/tag/3.0.0
|
|
1347
1371
|
[3.0.1]: https://github.com/haraka/Haraka/releases/tag/3.0.1
|
|
1372
|
+
[3.0.2]: https://github.com/haraka/Haraka/releases/tag/3.0.2
|
package/config/outbound.ini
CHANGED
package/connection.js
CHANGED
|
@@ -15,7 +15,7 @@ const constants = require('haraka-constants');
|
|
|
15
15
|
const net_utils = require('haraka-net-utils');
|
|
16
16
|
const Notes = require('haraka-notes');
|
|
17
17
|
const utils = require('haraka-utils');
|
|
18
|
-
const { Address }
|
|
18
|
+
const { Address } = require('address-rfc2821');
|
|
19
19
|
const ResultStore = require('haraka-results');
|
|
20
20
|
|
|
21
21
|
// Haraka libs
|
|
@@ -734,9 +734,16 @@ class Connection {
|
|
|
734
734
|
});
|
|
735
735
|
break;
|
|
736
736
|
default:
|
|
737
|
-
dns.reverse
|
|
738
|
-
|
|
739
|
-
|
|
737
|
+
// BUG: dns.reverse throws on invalid input (and sometimes valid
|
|
738
|
+
// input nodejs/node#47847). Also throws when empty results
|
|
739
|
+
try {
|
|
740
|
+
dns.reverse(this.remote.ip, (err, domains) => {
|
|
741
|
+
this.rdns_response(err, domains);
|
|
742
|
+
})
|
|
743
|
+
}
|
|
744
|
+
catch (err) {
|
|
745
|
+
this.rdns_response(err, []);
|
|
746
|
+
}
|
|
740
747
|
}
|
|
741
748
|
}
|
|
742
749
|
rdns_response (err, domains) {
|
|
@@ -1319,16 +1326,15 @@ class Connection {
|
|
|
1319
1326
|
this.errors++;
|
|
1320
1327
|
return this.respond(503, 'Use EHLO/HELO before MAIL');
|
|
1321
1328
|
}
|
|
1322
|
-
// Require authentication on
|
|
1329
|
+
// Require authentication on ports 587 & 465
|
|
1323
1330
|
if (!this.relaying && [587,465].includes(this.local.port)) {
|
|
1324
1331
|
this.errors++;
|
|
1325
1332
|
return this.respond(550, 'Authentication required');
|
|
1326
1333
|
}
|
|
1334
|
+
|
|
1327
1335
|
let results;
|
|
1328
|
-
let from;
|
|
1329
1336
|
try {
|
|
1330
1337
|
results = rfc1869.parse('mail', line, this.cfg.main.strict_rfc1869 && !this.relaying);
|
|
1331
|
-
from = new Address (results.shift());
|
|
1332
1338
|
}
|
|
1333
1339
|
catch (err) {
|
|
1334
1340
|
this.errors++;
|
|
@@ -1343,9 +1349,18 @@ class Connection {
|
|
|
1343
1349
|
return this.respond(452, 'Internal Server Error');
|
|
1344
1350
|
}
|
|
1345
1351
|
else {
|
|
1346
|
-
return this.respond(501, [
|
|
1352
|
+
return this.respond(501, ['Command parsing failed', err]);
|
|
1347
1353
|
}
|
|
1348
1354
|
}
|
|
1355
|
+
|
|
1356
|
+
let from;
|
|
1357
|
+
try {
|
|
1358
|
+
from = new Address(results.shift());
|
|
1359
|
+
}
|
|
1360
|
+
catch (err) {
|
|
1361
|
+
return this.respond(501, `Invalid MAIL FROM address`);
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1349
1364
|
// Get rest of key=value pairs
|
|
1350
1365
|
const params = {};
|
|
1351
1366
|
results.forEach(param => {
|
|
@@ -1382,10 +1397,8 @@ class Connection {
|
|
|
1382
1397
|
}
|
|
1383
1398
|
|
|
1384
1399
|
let results;
|
|
1385
|
-
let recip;
|
|
1386
1400
|
try {
|
|
1387
1401
|
results = rfc1869.parse('rcpt', line, this.cfg.main.strict_rfc1869 && !this.relaying);
|
|
1388
|
-
recip = new Address(results.shift());
|
|
1389
1402
|
}
|
|
1390
1403
|
catch (err) {
|
|
1391
1404
|
this.errors++;
|
|
@@ -1403,6 +1416,15 @@ class Connection {
|
|
|
1403
1416
|
return this.respond(501, ["Command parsing failed", err]);
|
|
1404
1417
|
}
|
|
1405
1418
|
}
|
|
1419
|
+
|
|
1420
|
+
let recip;
|
|
1421
|
+
try {
|
|
1422
|
+
recip = new Address(results.shift());
|
|
1423
|
+
}
|
|
1424
|
+
catch (err) {
|
|
1425
|
+
return this.respond(501, `Invalid RCPT TO address`);
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1406
1428
|
// Get rest of key=value pairs
|
|
1407
1429
|
const params = {};
|
|
1408
1430
|
results.forEach((param) => {
|
package/docs/plugins/clamd.md
CHANGED
|
@@ -133,7 +133,7 @@ meeting following criteria.
|
|
|
133
133
|
via regexp by enclosing the pattern in //.
|
|
134
134
|
|
|
135
135
|
To negate a match (e.g. reject if it matches), prefix the match with !.
|
|
136
|
-
Negative matches are always tested
|
|
136
|
+
Negative matches are always tested first.
|
|
137
137
|
|
|
138
138
|
Example:
|
|
139
139
|
|
|
@@ -1,27 +1,18 @@
|
|
|
1
|
-
queue/smtp\_forward
|
|
1
|
+
# queue/smtp\_forward
|
|
2
2
|
==================
|
|
3
3
|
|
|
4
|
-
This plugin delivers to another mail server. This is a common setup when you
|
|
5
|
-
want to have a mail server with a solid pedigree of outbound delivery to
|
|
6
|
-
other hosts, and inbound delivery to users.
|
|
4
|
+
This plugin delivers to another mail server. This is a common setup when you want to have a mail server with a solid pedigree of outbound delivery to other hosts, and inbound delivery to users.
|
|
7
5
|
|
|
8
|
-
In comparison to `queue/smtp_proxy`, this plugin waits until queue time to
|
|
9
|
-
attempt the ongoing connection. This can be a benefit in reducing connections
|
|
10
|
-
to your inbound mail server when you have content filtering (such as
|
|
11
|
-
spamassassin) enabled. A possible downside is that it also delays recipient
|
|
12
|
-
validation that the ongoing mail server may provide until queue time.
|
|
6
|
+
In comparison to `queue/smtp_proxy`, this plugin waits until queue time to attempt the ongoing connection. This can be a benefit in reducing connections to your inbound mail server when you have content filtering (such as spamassassin) enabled. A possible downside is that it also delays recipient validation that the ongoing mail server may provide until queue time.
|
|
13
7
|
|
|
14
|
-
Configuration
|
|
8
|
+
## Configuration
|
|
15
9
|
-------------
|
|
16
10
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
Configuration is stored in this file in the following keys:
|
|
11
|
+
Configuration is stored in smtp\_forward.ini in the following keys:
|
|
20
12
|
|
|
21
13
|
* enable\_outbound=[true]
|
|
22
14
|
|
|
23
|
-
SMTP forward outbound messages (set to false to enable Haraka's separate
|
|
24
|
-
Outbound mail routing (MX based delivery)).
|
|
15
|
+
SMTP forward outbound messages (set to false to enable Haraka's separate Outbound mail routing (MX based delivery)).
|
|
25
16
|
|
|
26
17
|
* host=HOST
|
|
27
18
|
|
|
@@ -33,14 +24,11 @@ Configuration
|
|
|
33
24
|
|
|
34
25
|
* connect\_timeout=SECONDS
|
|
35
26
|
|
|
36
|
-
The maximum amount of time to wait when creating a new connection
|
|
37
|
-
to the host. Default: 30 seconds.
|
|
27
|
+
The maximum amount of time to wait when creating a new connection to the host. Default: 30 seconds.
|
|
38
28
|
|
|
39
29
|
* timeout=SECONDS
|
|
40
30
|
|
|
41
|
-
The amount of seconds to let a backend connection live idle in the
|
|
42
|
-
connection pool. This should always be less than the global plugin
|
|
43
|
-
timeout, which should in turn be less than the connection timeout.
|
|
31
|
+
The amount of seconds to let a backend connection live idle in the connection pool. This should always be less than the global plugin timeout, which should in turn be less than the connection timeout.
|
|
44
32
|
|
|
45
33
|
* max\_connections=NUMBER
|
|
46
34
|
|
|
@@ -48,12 +36,9 @@ Configuration
|
|
|
48
36
|
|
|
49
37
|
* enable\_tls=[true]
|
|
50
38
|
|
|
51
|
-
Enable TLS with the forward host (if supported). TLS uses options
|
|
52
|
-
from the tls plugin. If key and cert are provided in the the outbound section of the tls plugin,
|
|
53
|
-
that certificate will be used as a TLS Client Certificate.
|
|
39
|
+
Enable TLS with the forward host (if supported). TLS uses options from the tls plugin. If key and cert are provided in the the outbound section of the tls plugin, that certificate will be used as a TLS Client Certificate.
|
|
54
40
|
|
|
55
|
-
This option controls the use of TLS via `STARTTLS`. This plugin does not work with
|
|
56
|
-
SMTP over TLS.
|
|
41
|
+
This option controls the use of TLS via `STARTTLS`. This plugin does not work with SMTP over TLS.
|
|
57
42
|
|
|
58
43
|
* auth\_type=[plain\|login]
|
|
59
44
|
|
|
@@ -69,9 +54,7 @@ Configuration
|
|
|
69
54
|
|
|
70
55
|
* queue
|
|
71
56
|
|
|
72
|
-
Which queue plugin to use. Default: undefined. The default bahavior is to
|
|
73
|
-
use smtp_forward for inbound connections and outbound for relaying
|
|
74
|
-
connections. This option is used for complex mail routes.
|
|
57
|
+
Which queue plugin to use. Default: undefined. The default bahavior is to use smtp_forward for inbound connections and outbound for relaying connections. This option is used for complex mail routes.
|
|
75
58
|
|
|
76
59
|
* check_sender=false
|
|
77
60
|
|
|
@@ -86,18 +69,13 @@ Configuration
|
|
|
86
69
|
|
|
87
70
|
# Per-Domain Configuration
|
|
88
71
|
|
|
89
|
-
More specific forward routes for domains can be defined. The domain is
|
|
90
|
-
chosen based on the value of the `domain_selector` config variable.
|
|
72
|
+
More specific forward routes for domains can be defined. The domain is chosen based on the value of the `domain_selector` config variable.
|
|
91
73
|
|
|
92
|
-
When `domain_selector` is set to `rcpt_to` (the default), more specific
|
|
93
|
-
routes are only honored for SMTP connections with a single recipient or SMTP
|
|
94
|
-
connections where every recipient host is identical.
|
|
74
|
+
When `domain_selector` is set to `rcpt_to` (the default), more specific routes are only honored for SMTP connections with a single recipient or SMTP connections where every recipient host is identical.
|
|
95
75
|
|
|
96
|
-
When `domain_selector` is set to `mail_from`, the domain of the MAIL FROM
|
|
97
|
-
address is used.
|
|
76
|
+
When `domain_selector` is set to `mail_from`, the domain of the MAIL FROM address is used.
|
|
98
77
|
|
|
99
|
-
enable\_outbound can be set or unset on a per-domain level to enable or disable
|
|
100
|
-
forwarding for specific domains.
|
|
78
|
+
enable\_outbound can be set or unset on a per-domain level to enable or disable forwarding for specific domains.
|
|
101
79
|
|
|
102
80
|
# default SMTP host
|
|
103
81
|
host=1.2.3.4
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
queue/smtp\_proxy
|
|
1
|
+
# queue/smtp\_proxy
|
|
2
2
|
================
|
|
3
3
|
|
|
4
4
|
This plugin delivers to another mail server. This is a common setup when you
|
|
@@ -12,14 +12,12 @@ particular one important facility to some setups is recipient filtering.
|
|
|
12
12
|
However be aware that other than connect and HELO-time filtering, you will
|
|
13
13
|
have as many connections to your ongoing SMTP server as you have to Haraka.
|
|
14
14
|
|
|
15
|
-
Configuration
|
|
15
|
+
## Configuration
|
|
16
16
|
-------------
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
Configuration is stored in this file in the following keys:
|
|
18
|
+
Configuration is stored in smtp\_proxy.ini in the following keys:
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
* enable\_outbound=[true]
|
|
23
21
|
|
|
24
22
|
SMTP proxy outbound messages (set to false to enable Haraka's
|
|
25
23
|
separate Outbound mail routing (MX based delivery)).
|
|
@@ -27,9 +25,9 @@ Configuration
|
|
|
27
25
|
* host=HOST
|
|
28
26
|
|
|
29
27
|
The host to connect to.
|
|
30
|
-
|
|
28
|
+
|
|
31
29
|
* port=PORT
|
|
32
|
-
|
|
30
|
+
|
|
33
31
|
The port to connect to.
|
|
34
32
|
|
|
35
33
|
* connect\_timeout=SECONDS
|
|
@@ -38,17 +36,17 @@ Configuration
|
|
|
38
36
|
to the host. Default if unspecified is 30 seconds.
|
|
39
37
|
|
|
40
38
|
* timeout=SECONDS
|
|
41
|
-
|
|
39
|
+
|
|
42
40
|
The amount of seconds to let a backend connection live idle in the
|
|
43
41
|
proxy pool. This should always be less than the global plugin timeout,
|
|
44
42
|
which should in turn be less than the connection timeout.
|
|
45
43
|
|
|
46
44
|
* max\_connections=NUMBER
|
|
47
|
-
|
|
45
|
+
|
|
48
46
|
Maximum number of connections to create at any given time.
|
|
49
47
|
|
|
50
48
|
* enable\_tls=[true|yes|1]
|
|
51
|
-
|
|
49
|
+
|
|
52
50
|
Enable TLS with the forward host (if supported). TLS uses options from
|
|
53
51
|
the tls plugin.
|
|
54
52
|
|
package/outbound/tls.js
CHANGED
|
@@ -95,7 +95,7 @@ class OutboundTLS {
|
|
|
95
95
|
|
|
96
96
|
logger.lognotice(this, `TLS connection failed. Marking ${host} as non-TLS for ${expiry} seconds`);
|
|
97
97
|
|
|
98
|
-
this.db.
|
|
98
|
+
this.db.setEx(dbkey, expiry, (new Date()).toISOString())
|
|
99
99
|
.then(cb)
|
|
100
100
|
.catch(err => {
|
|
101
101
|
logger.logerror(this, `Redis returned error: ${err}`);
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"server",
|
|
10
10
|
"email"
|
|
11
11
|
],
|
|
12
|
-
"version": "3.0.
|
|
12
|
+
"version": "3.0.2",
|
|
13
13
|
"homepage": "http://haraka.github.io",
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
"address-rfc2822": "^2.1.0",
|
|
25
25
|
"async": "^3.2.4",
|
|
26
26
|
"daemon": "~1.1.0",
|
|
27
|
-
"ipaddr.js": "~2.0
|
|
28
|
-
"node-gyp": "^9.
|
|
29
|
-
"nopt": "~7.
|
|
27
|
+
"ipaddr.js": "~2.1.0",
|
|
28
|
+
"node-gyp": "^9.4.0",
|
|
29
|
+
"nopt": "~7.2.0",
|
|
30
30
|
"npid": "~0.4.0",
|
|
31
|
-
"semver": "~7.
|
|
31
|
+
"semver": "~7.5.2",
|
|
32
32
|
"sprintf-js": "~1.1.2",
|
|
33
33
|
"haraka-config": "^1.1.0",
|
|
34
34
|
"haraka-constants": "^1.0.6",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"haraka-plugin-attachment": "^1.0.7",
|
|
41
41
|
"haraka-plugin-spf": "1.2.0",
|
|
42
42
|
"haraka-plugin-redis": "^2.0.5",
|
|
43
|
-
"haraka-results": "^2.2.
|
|
44
|
-
"haraka-tld": "^1.1.
|
|
43
|
+
"haraka-results": "^2.2.3",
|
|
44
|
+
"haraka-tld": "^1.1.1",
|
|
45
45
|
"haraka-utils": "^1.0.3",
|
|
46
46
|
"openssl-wrapper": "^0.3.4",
|
|
47
47
|
"sockaddr": "^1.0.1"
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"haraka-plugin-karma": "^2.1.0",
|
|
61
61
|
"haraka-plugin-limit": "^1.1.0",
|
|
62
62
|
"haraka-plugin-p0f": "^1.0.9",
|
|
63
|
-
"haraka-plugin-qmail-deliverable": "^1.
|
|
63
|
+
"haraka-plugin-qmail-deliverable": "^1.2.1",
|
|
64
64
|
"haraka-plugin-known-senders": "^1.0.8",
|
|
65
65
|
"haraka-plugin-rcpt-ldap": "^1.0.0",
|
|
66
66
|
"haraka-plugin-recipient-routes": "^1.0.4",
|
|
@@ -69,16 +69,16 @@
|
|
|
69
69
|
"haraka-plugin-uribl": "^1.0.6",
|
|
70
70
|
"haraka-plugin-watch": "^2.0.2",
|
|
71
71
|
"ocsp": "~1.2.0",
|
|
72
|
-
"redis": "
|
|
72
|
+
"redis": "^4.5.1",
|
|
73
73
|
"tmp": "~0.2.1"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
|
-
"nodeunit-x": "^0.
|
|
77
|
-
"haraka-test-fixtures": "^1.
|
|
76
|
+
"nodeunit-x": "^0.16.0",
|
|
77
|
+
"haraka-test-fixtures": "^1.3.0",
|
|
78
78
|
"mock-require": "^3.0.3",
|
|
79
|
-
"eslint": "^8.
|
|
79
|
+
"eslint": "^8.42.0",
|
|
80
80
|
"eslint-plugin-haraka": "^1.0.15",
|
|
81
|
-
"nodemailer": "^6.
|
|
81
|
+
"nodemailer": "^6.9.3"
|
|
82
82
|
},
|
|
83
83
|
"bugs": {
|
|
84
84
|
"mail": "haraka.mail@gmail.com",
|
package/plugins/dns_list_base.js
CHANGED
|
@@ -126,7 +126,7 @@ exports.multi = function (lookup, zones, cb) {
|
|
|
126
126
|
}
|
|
127
127
|
cb(err, zone, a, true);
|
|
128
128
|
done();
|
|
129
|
-
})
|
|
129
|
+
})
|
|
130
130
|
}
|
|
131
131
|
function zonesDone (err) {
|
|
132
132
|
cb(err, null, null, false);
|
|
@@ -148,8 +148,8 @@ exports.first = function (lookup, zones, cb, cb_each) {
|
|
|
148
148
|
|
|
149
149
|
// has pending queries OR this one is a positive result
|
|
150
150
|
ran_cb = true;
|
|
151
|
-
|
|
152
|
-
})
|
|
151
|
+
cb(err, zone, a);
|
|
152
|
+
})
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
exports.check_zones = function (interval) {
|
package/plugins/helo.checks.js
CHANGED
|
@@ -11,7 +11,6 @@ const checks = [
|
|
|
11
11
|
'bare_ip', // HELO is bare IP (vs required Address Literal)
|
|
12
12
|
'dynamic', // HELO hostname looks dynamic (dsl|dialup|etc...)
|
|
13
13
|
'big_company', // Well known HELOs that must match rdns
|
|
14
|
-
'literal_mismatch', // IP literal that doesn't match remote IP
|
|
15
14
|
'valid_hostname', // HELO hostname is a legal DNS name
|
|
16
15
|
'rdns_match', // HELO hostname matches rDNS
|
|
17
16
|
'forward_dns', // HELO hostname resolves to the connecting IP
|
|
@@ -31,7 +30,7 @@ exports.register = function () {
|
|
|
31
30
|
this.register_hook('helo', 'init');
|
|
32
31
|
this.register_hook('ehlo', 'init');
|
|
33
32
|
|
|
34
|
-
for (const c
|
|
33
|
+
for (const c of checks) {
|
|
35
34
|
if (!this.cfg.check[c]) continue; // disabled in config
|
|
36
35
|
this.register_hook('helo', c);
|
|
37
36
|
this.register_hook('ehlo', c);
|
|
@@ -41,6 +40,13 @@ exports.register = function () {
|
|
|
41
40
|
this.register_hook('helo', 'emit_log');
|
|
42
41
|
this.register_hook('ehlo', 'emit_log');
|
|
43
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
|
+
|
|
44
50
|
if (this.cfg.check.match_re) {
|
|
45
51
|
const load_re_file = () => {
|
|
46
52
|
const regex_list = utils.valid_regexes(this.config.get('helo.checks.regexps', 'list', load_re_file));
|
|
@@ -95,17 +101,19 @@ exports.load_helo_checks_ini = function () {
|
|
|
95
101
|
}
|
|
96
102
|
|
|
97
103
|
exports.init = function (next, connection, helo) {
|
|
98
|
-
|
|
99
|
-
const hc = connection.results.get('helo.checks');
|
|
100
|
-
if (!hc) { // first HELO result
|
|
104
|
+
if (!connection.results.has('helo.checks', 'helo_host', helo)) {
|
|
101
105
|
connection.results.add(this, {helo_host: helo});
|
|
102
|
-
return next();
|
|
103
106
|
}
|
|
104
107
|
|
|
105
108
|
next();
|
|
106
109
|
}
|
|
107
110
|
|
|
108
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});
|
|
109
117
|
|
|
110
118
|
if (this.cfg.skip.relaying && connection.relaying) {
|
|
111
119
|
connection.results.add(this, {skip: `${test_name}(relay)`});
|
|
@@ -26,10 +26,12 @@ exports.register = function () {
|
|
|
26
26
|
this.register_hook('queue', 'queue_forward');
|
|
27
27
|
|
|
28
28
|
if (this.cfg.main.enable_outbound) {
|
|
29
|
+
// deliver local message via smtp forward when relaying=true
|
|
29
30
|
this.register_hook('queue_outbound', 'queue_forward');
|
|
30
|
-
}
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
// may specify more specific routes for outbound
|
|
33
|
+
this.register_hook('get_mx', 'get_mx');
|
|
34
|
+
}
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
exports.load_smtp_forward_ini = function () {
|
|
@@ -75,7 +77,7 @@ exports.is_outbound_enabled = function (dom_cfg) {
|
|
|
75
77
|
if ('enable_outbound' in dom_cfg) return dom_cfg.enable_outbound; // per-domain flag
|
|
76
78
|
|
|
77
79
|
return this.cfg.main.enable_outbound; // follow the global configuration
|
|
78
|
-
}
|
|
80
|
+
}
|
|
79
81
|
|
|
80
82
|
exports.check_sender = function (next, connection, params) {
|
|
81
83
|
const txn = connection?.transaction;
|
|
@@ -99,7 +101,7 @@ exports.check_sender = function (next, connection, params) {
|
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
txn.results.add(this, {pass: 'mail_from'});
|
|
102
|
-
|
|
104
|
+
next();
|
|
103
105
|
}
|
|
104
106
|
|
|
105
107
|
exports.set_queue = function (connection, queue_wanted, domain) {
|
|
@@ -110,7 +112,6 @@ exports.set_queue = function (connection, queue_wanted, domain) {
|
|
|
110
112
|
if (!queue_wanted) queue_wanted = dom_cfg.queue || this.cfg.main.queue;
|
|
111
113
|
if (!queue_wanted) return true;
|
|
112
114
|
|
|
113
|
-
|
|
114
115
|
let dst_host = dom_cfg.host || this.cfg.main.host;
|
|
115
116
|
if (dst_host) dst_host = `smtp://${dst_host}`;
|
|
116
117
|
|
|
@@ -164,7 +165,7 @@ exports.check_recipient = function (next, connection, params) {
|
|
|
164
165
|
// the MAIL FROM domain is not local and neither is the RCPT TO
|
|
165
166
|
// Another RCPT plugin may vouch for this recipient.
|
|
166
167
|
txn.results.add(this, {msg: 'rcpt!local'});
|
|
167
|
-
|
|
168
|
+
next();
|
|
168
169
|
}
|
|
169
170
|
|
|
170
171
|
exports.auth = function (cfg, connection, smtp_client) {
|
|
@@ -303,20 +304,24 @@ exports.queue_forward = function (next, connection) {
|
|
|
303
304
|
|
|
304
305
|
smtp_client.on('bad_code', (code, msg) => {
|
|
305
306
|
if (dead_sender() || !txn) return;
|
|
306
|
-
smtp_client.call_next(((code && code[0] === '5') ? DENY : DENYSOFT),
|
|
307
|
-
msg);
|
|
307
|
+
smtp_client.call_next(((code && code[0] === '5') ? DENY : DENYSOFT), msg);
|
|
308
308
|
smtp_client.release();
|
|
309
309
|
});
|
|
310
310
|
});
|
|
311
311
|
}
|
|
312
312
|
|
|
313
313
|
exports.get_mx_next_hop = next_hop => {
|
|
314
|
-
|
|
314
|
+
// queue.wants && queue.next_hop are mechanisms for fine-grained MX routing.
|
|
315
|
+
// Plugins can specify a queue to perform the delivery as well as a route. A
|
|
316
|
+
// plugin that uses this is qmail-deliverable, which can direct email delivery
|
|
317
|
+
// via smtp_forward, outbound (SMTP), and outbound (LMTP).
|
|
318
|
+
const dest = new url.URL(next_hop);
|
|
315
319
|
const mx = {
|
|
316
320
|
priority: 0,
|
|
317
|
-
port: dest.port || 25,
|
|
321
|
+
port: dest.port || (dest.protocol === 'lmtp:' ? 24 : 25),
|
|
318
322
|
exchange: dest.hostname,
|
|
319
323
|
}
|
|
324
|
+
if (dest.protocol === 'lmtp:') mx.using_lmtp = true;
|
|
320
325
|
if (dest.auth) {
|
|
321
326
|
mx.auth_type = 'plain';
|
|
322
327
|
mx.auth_user = dest.auth.split(':')[0];
|
|
@@ -327,9 +332,11 @@ exports.get_mx_next_hop = next_hop => {
|
|
|
327
332
|
|
|
328
333
|
exports.get_mx = function (next, hmail, domain) {
|
|
329
334
|
|
|
330
|
-
|
|
331
|
-
if (
|
|
332
|
-
|
|
335
|
+
const qw = hmail.todo.notes.get('queue.wants')
|
|
336
|
+
if (qw && qw !== 'smtp_forward') return next()
|
|
337
|
+
|
|
338
|
+
if (qw === 'smtp_forward' && hmail.todo.notes.get('queue.next_hop')) {
|
|
339
|
+
return next(OK, this.get_mx_next_hop(hmail.todo.notes.get('queue.next_hop')));
|
|
333
340
|
}
|
|
334
341
|
|
|
335
342
|
const dom = this.cfg.main.domain_selector === 'mail_from' ? hmail.todo.mail_from.host.toLowerCase() : domain.toLowerCase();
|
|
@@ -341,8 +348,7 @@ exports.get_mx = function (next, hmail, domain) {
|
|
|
341
348
|
}
|
|
342
349
|
|
|
343
350
|
const mx_opts = [
|
|
344
|
-
'auth_type', 'auth_user', 'auth_pass', 'bind', 'bind_helo',
|
|
345
|
-
'using_lmtp'
|
|
351
|
+
'auth_type', 'auth_user', 'auth_pass', 'bind', 'bind_helo', 'using_lmtp'
|
|
346
352
|
]
|
|
347
353
|
|
|
348
354
|
const mx = {
|
|
@@ -357,5 +363,5 @@ exports.get_mx = function (next, hmail, domain) {
|
|
|
357
363
|
mx[o] = this.cfg[dom][o];
|
|
358
364
|
})
|
|
359
365
|
|
|
360
|
-
|
|
366
|
+
next(OK, mx);
|
|
361
367
|
}
|
package/plugins/tls.js
CHANGED
|
@@ -75,7 +75,7 @@ exports.set_notls = function (connection) {
|
|
|
75
75
|
|
|
76
76
|
this.lognotice(connection, `STARTTLS failed. Marking ${connection.remote.ip} as non-TLS host for ${expiry} seconds`);
|
|
77
77
|
|
|
78
|
-
server.notes.redis.
|
|
78
|
+
server.notes.redis.setEx(`no_tls|${connection.remote.ip}`, expiry, (new Date()).toISOString());
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
exports.upgrade_connection = function (next, connection, params) {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
; disable checks or reject for each test if you are worried about strictness
|
|
2
|
+
|
|
3
|
+
;dns_timeout=30
|
|
4
|
+
|
|
5
|
+
[check]
|
|
6
|
+
match_re=true
|
|
7
|
+
bare_ip=true
|
|
8
|
+
dynamic=true
|
|
9
|
+
big_company=true
|
|
10
|
+
; literal_mismatch: 1 = exact IP match, 2 = IP/24 match, 3 = /24 or RFC1918
|
|
11
|
+
literal_mismatch=2
|
|
12
|
+
valid_hostname=true
|
|
13
|
+
forward_dns=true
|
|
14
|
+
rdns_match=true
|
|
15
|
+
; host_mismatch: hostname differs between EHLO invocations
|
|
16
|
+
host_mismatch=true
|
|
17
|
+
proto_mismatch: host sent EHLO but then tries to sent HELO or vice-versa
|
|
18
|
+
proto_mismatch=true
|
|
19
|
+
|
|
20
|
+
[reject]
|
|
21
|
+
host_mismatch=true
|
|
22
|
+
proto_mismatch=true
|
|
23
|
+
rdns_match=true
|
|
24
|
+
dynamic=true
|
|
25
|
+
bare_ip=true
|
|
26
|
+
literal_mismatch=true
|
|
27
|
+
valid_hostname=true
|
|
28
|
+
forward_dns=true
|
|
29
|
+
big_company=true
|
|
30
|
+
|
|
31
|
+
[skip]
|
|
32
|
+
private_ip=true
|
|
33
|
+
relaying=true
|
|
34
|
+
whitelist=true
|
|
35
|
+
|
|
36
|
+
[bigco]
|
|
37
|
+
msn.com=msn.com
|
|
38
|
+
hotmail.com=hotmail.com
|
|
39
|
+
yahoo.com=yahoo.com,yahoo.co.jp
|
|
40
|
+
yahoo.co.jp=yahoo.com,yahoo.co.jp
|
|
41
|
+
yahoo.co.uk=yahoo.co.uk
|
|
42
|
+
excite.com=excite.com,excitenetwork.com
|
|
43
|
+
mailexcite.com=excite.com,excitenetwork.com
|
|
44
|
+
yahoo.co.jp=yahoo.com,yahoo.co.jp
|
|
45
|
+
mailexcite.com=excite.com,excitenetwork.com
|
|
46
|
+
aol.com=aol.com
|
|
47
|
+
compuserve.com=compuserve.com,adelphia.net
|
|
48
|
+
nortelnetworks.com=nortelnetworks.com,nortel.com
|
|
49
|
+
earthlink.net=earthlink.net
|
|
50
|
+
earthling.net=earthling.net
|
|
51
|
+
google.com=google.com
|
|
52
|
+
gmail.com=google.com,gmail.com
|