forked from smfreegard/Haraka
/
helo.checks.js
141 lines (123 loc) · 4.13 KB
/
helo.checks.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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Check various bits of the HELO string
var net_utils = require('./net_utils');
// Checks to implement:
// - HELO has no "dot"
// - List of regexps
// - HELO raw IP
// - HELO looks dynamic
// - Well known HELOs that must match rdns
// - IP literal that doesn't match connecting IP
exports.register = function () {
var plugin = this;
['helo_no_dot',
'helo_match_re',
'helo_raw_ip',
'helo_is_dynamic',
'helo_big_company',
'helo_literal_mismatch'
].forEach(function (hook) {
plugin.register_hook('helo', hook);
plugin.register_hook('ehlo', hook);
});
}
exports.helo_no_dot = function (next, connection, helo) {
var config = this.config.get('helo.checks.ini');
if (!config.main.check_no_dot ||
!config.main.require_valid_tld ||
(config.main.skip_private_ip &&
net_utils.is_rfc1918(connection.remote_ip)))
{
return next();
}
if (!/\./.test(helo)) {
return next(DENY, 'HELO must have a dot');
}
if (config.main.require_valid_tld) {
var tld = (helo.split(/\./).reverse())[0].toLowerCase();
if (!/^\[\d+\.\d+\.\d+\.\d+\]$/.test(helo) && !net_utils.top_level_tlds[tld]) {
return next(DENY, "HELO must have a valid TLD");
}
}
return next();
};
exports.helo_match_re = function (next, connection, helo) {
var regexps = this.config.get('helo.checks.regexps', 'list');
for (var i=0,l=regexps.length; i < l; i++) {
var re = new RegExp('^' + regexps[i] + '$');
if (re.test(helo)) {
return next(DENY, "BAD HELO");
}
}
return next();
};
exports.helo_raw_ip = function (next, connection, helo) {
var config = this.config.get('helo.checks.ini');
if (!config.main.check_raw_ip ||
(config.main.skip_private_ip &&
net_utils.is_rfc1918(connection.remote_ip)))
{
return next();
}
// RAW IPs must be formatted: "[1.2.3.4]" not "1.2.3.4" in HELOs
/^\d+\.\d+\.\d+\.\d+$/.test(helo) ?
next(DENY, "RAW IP HELOs must be correctly formatted")
: next();
};
exports.helo_is_dynamic = function (next, connection, helo) {
var config = this.config.get('helo.checks.ini');
if (!config.main.check_dynamic ||
(config.main.skip_private_ip &&
net_utils.is_rfc1918(connection.remote_ip)))
{
return next();
}
// Skip if no dots or an IP literal or address
if (!/\./.test(helo) || /^\[?\d+\.\d+\.\d+\.\d+\]?$/.test(helo)) {
return next();
}
(net_utils.is_ip_in_str(connection.remote_ip, helo)) ?
next(DENY, 'HELO is dynamic')
: next();
};
exports.helo_big_company = function (next, connection, helo) {
var rdns = connection.remote_host;
var big_co = this.config.get('helo.checks.ini').bigco;
if (big_co[helo]) {
var allowed_rdns = big_co[helo].split(/,/);
for (var i=0,l=allowed_rdns.length; i < l; i++) {
var re = new RegExp(allowed_rdns[i].replace(/\./g, '\\.') + '$');
if (re.test(rdns)) {
return next();
}
}
return next(DENY, "You are not who you say you are");
}
else {
return next();
}
};
exports.helo_literal_mismatch = function (next, connection, helo) {
var config = this.config.get('helo.checks.ini');
if (!config.main.check_literal_mismatch ||
(config.main.skip_private_ip &&
net_utils.is_rfc1918(connection.remote_ip)))
{
return next();
}
var literal = /^\[(\d+\.\d+\.\d+\.\d+)\]$/.exec(helo);
if (literal) {
if (parseInt(config.main.check_literal_mismatch) === 2) {
// Only match the /24
if (literal[1].split(/\./).splice(0,3).join('.') !==
connection.remote_ip.split(/\./).splice(0,3).join('.'))
{
return next(DENY, 'HELO IP literal not in the same /24 as your IP address');
}
return next();
}
if (literal[1] !== connection.remote_ip) {
return next(DENY, 'HELO IP literal does not match your IP address');
}
}
return next();
}