generated from haraka/haraka-plugin-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
108 lines (93 loc) · 2.89 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
'use strict'
// Check MAIL FROM domain is resolvable to an MX
const net = require('node:net')
const net_utils = require('haraka-net-utils')
exports.register = function () {
this.load_ini()
}
exports.load_ini = function () {
this.cfg = this.config.get(
'mail_from.is_resolvable.ini',
{
booleans: ['-main.allow_mx_ip', '+reject.no_mx'],
},
() => {
this.load_ini()
},
)
// compat. Sunset 4.0
if (this.cfg.main.reject_no_mx) {
this.cfg.reject.no_mx = this.cfg.main.reject_no_mx
}
this.re_bogus_ip = new RegExp(
this.cfg.main.re_bogus_ip ||
'^(?:0\\.0\\.0\\.0|255\\.255\\.255\\.255|127\\.)',
)
}
exports.hook_mail = async function (next, connection, params) {
const mail_from = params[0]
const { results } = connection.transaction
// ignore MAIL FROM without an @
if (!mail_from.host) {
results.add(this, { skip: 'null host' })
return next()
}
const domain = mail_from.host
connection.logdebug(this, `resolving MX for domain ${domain}`)
let exchanges
try {
exchanges = await net_utils.get_mx(domain)
} catch (err) {
results.add(this, { err: err.message })
return next(DENYSOFT, `Temp. resolver error (${err.code})`)
}
connection.logdebug(this, `${domain}: MX => ${JSON.stringify(exchanges)}`)
if (!exchanges || !exchanges.length) {
results.add(this, { fail: 'has_fwd_dns', emit: true })
return next(
this.cfg.reject.no_mx ? DENY : DENYSOFT,
'No MX for your FROM address',
)
}
if (this.cfg.main.allow_mx_ip) {
for (const mx of exchanges) {
if (
(net.isIPv4(mx.exchange) && !this.re_bogus_ip.test(mx.exchange)) ||
(net.isIPv6(mx.exchange) && !net_utils.ipv6_bogus(mx.exchange))
) {
results.add(this, { pass: 'implicit_mx', emit: true })
return next()
}
}
}
// filter out the implicit MX and resolve the remaining MX hostnames
const mx_hostnames = exchanges.filter(
(a) => a.exchange && !net.isIP(a.exchange),
)
if (mx_hostnames.length) {
try {
const resolved = await net_utils.resolve_mx_hosts(mx_hostnames)
connection.logdebug(this, `resolved MX => ${JSON.stringify(resolved)}`)
if (resolved.length) {
for (const mx of resolved) {
if (
(net.isIPv4(mx.exchange) && !this.re_bogus_ip.test(mx.exchange)) ||
(net.isIPv6(mx.exchange) && !net_utils.ipv6_bogus(mx.exchange))
) {
results.add(this, { pass: 'has_fwd_dns', emit: true })
return next()
}
}
}
} catch (err) {
// resolve_mx_hosts ignores errors so this is unlikely to happen
results.add(this, { err: err.message })
return next(DENYSOFT, `Temp. resolver error (${err.code})`)
}
}
results.add(this, { fail: 'has_fwd_dns', emit: true })
return next(
this.cfg.reject.no_mx ? DENY : DENYSOFT,
'No valid MX for your FROM address',
)
}