Haraka 3.0.3 → 3.0.5

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.
Files changed (239) hide show
  1. package/.eslintrc.yaml +4 -9
  2. package/CONTRIBUTORS.md +11 -0
  3. package/Changes.md +1397 -1213
  4. package/Plugins.md +117 -105
  5. package/README.md +4 -13
  6. package/bin/haraka +198 -298
  7. package/config/auth_flat_file.ini +1 -0
  8. package/config/dhparams.pem +8 -0
  9. package/config/mail_from.is_resolvable.ini +4 -2
  10. package/config/me +1 -0
  11. package/config/outbound.ini +0 -2
  12. package/config/plugins +35 -36
  13. package/config/smtp.ini +1 -1
  14. package/config/smtp.json +17 -0
  15. package/config/tls.ini +2 -0
  16. package/config/tls_cert.pem +23 -0
  17. package/config/tls_key.pem +28 -0
  18. package/connection.js +46 -73
  19. package/contrib/bsd-rc.d/haraka +3 -1
  20. package/contrib/plugin2npm.sh +6 -36
  21. package/docs/CoreConfig.md +2 -2
  22. package/docs/Logging.md +7 -21
  23. package/docs/Outbound.md +104 -201
  24. package/docs/Plugins.md +2 -2
  25. package/docs/Transaction.md +59 -82
  26. package/docs/plugins/queue/smtp_proxy.md +5 -10
  27. package/docs/plugins/tls.md +37 -9
  28. package/endpoint.js +16 -13
  29. package/haraka.js +10 -14
  30. package/host_pool.js +5 -5
  31. package/http/html/index.html +6 -5
  32. package/line_socket.js +3 -4
  33. package/logger.js +44 -28
  34. package/outbound/client_pool.js +27 -23
  35. package/outbound/config.js +4 -6
  36. package/outbound/fsync_writestream.js +1 -1
  37. package/outbound/hmail.js +178 -218
  38. package/outbound/index.js +86 -99
  39. package/outbound/qfile.js +1 -1
  40. package/outbound/queue.js +51 -44
  41. package/outbound/timer_queue.js +3 -2
  42. package/outbound/tls.js +19 -7
  43. package/package.json +60 -51
  44. package/plugins/.eslintrc.yaml +0 -6
  45. package/plugins/auth/auth_base.js +4 -2
  46. package/plugins/auth/auth_proxy.js +14 -12
  47. package/plugins/auth/auth_vpopmaild.js +1 -1
  48. package/plugins/block_me.js +1 -1
  49. package/plugins/data.signatures.js +2 -4
  50. package/plugins/early_talker.js +2 -1
  51. package/plugins/mail_from.is_resolvable.js +65 -135
  52. package/plugins/queue/deliver.js +4 -5
  53. package/plugins/queue/lmtp.js +11 -12
  54. package/plugins/queue/qmail-queue.js +2 -2
  55. package/plugins/queue/quarantine.js +2 -2
  56. package/plugins/queue/rabbitmq.js +16 -17
  57. package/plugins/queue/smtp_forward.js +3 -3
  58. package/plugins/queue/smtp_proxy.js +10 -1
  59. package/plugins/queue/test.js +2 -2
  60. package/plugins/rcpt_to.host_list_base.js +5 -5
  61. package/plugins/rcpt_to.in_host_list.js +2 -2
  62. package/plugins/relay.js +6 -7
  63. package/plugins/reseed_rng.js +1 -1
  64. package/plugins/status.js +37 -33
  65. package/plugins/tls.js +2 -2
  66. package/plugins/xclient.js +3 -2
  67. package/plugins.js +50 -54
  68. package/run_tests +3 -30
  69. package/server.js +190 -190
  70. package/smtp_client.js +30 -23
  71. package/{tests → test}/config/plugins +0 -2
  72. package/{tests → test}/config/smtp.ini +3 -1
  73. package/test/config/tls/example.com/_.example.com.key +28 -0
  74. package/test/config/tls/example.com/example.com.crt +25 -0
  75. package/{tests/loud → test}/config/tls.ini +4 -2
  76. package/test/connection.js +302 -0
  77. package/test/endpoint.js +94 -0
  78. package/{tests → test}/fixtures/line_socket.js +1 -1
  79. package/{tests → test}/fixtures/util_hmailitem.js +19 -25
  80. package/{tests → test}/host_pool.js +42 -57
  81. package/test/logger.js +258 -0
  82. package/test/outbound/hmail.js +141 -0
  83. package/test/outbound/index.js +220 -0
  84. package/test/outbound/qfile.js +126 -0
  85. package/test/outbound_bounce_net_errors.js +142 -0
  86. package/{tests → test}/outbound_bounce_rfc3464.js +110 -122
  87. package/test/plugins/auth/auth_base.js +484 -0
  88. package/test/plugins/auth/auth_vpopmaild.js +83 -0
  89. package/test/plugins/early_talker.js +104 -0
  90. package/test/plugins/mail_from.is_resolvable.js +35 -0
  91. package/test/plugins/queue/smtp_forward.js +206 -0
  92. package/test/plugins/rcpt_to.host_list_base.js +122 -0
  93. package/test/plugins/rcpt_to.in_host_list.js +193 -0
  94. package/test/plugins/relay.js +303 -0
  95. package/test/plugins/status.js +130 -0
  96. package/test/plugins/tls.js +70 -0
  97. package/test/plugins.js +228 -0
  98. package/test/rfc1869.js +73 -0
  99. package/test/server.js +491 -0
  100. package/test/smtp_client.js +299 -0
  101. package/test/tls_socket.js +277 -0
  102. package/test/transaction.js +270 -0
  103. package/tls_socket.js +202 -252
  104. package/transaction.js +8 -23
  105. package/CONTRIBUTING.md +0 -1
  106. package/bin/dkimverify +0 -40
  107. package/config/access.domains +0 -13
  108. package/config/attachment.ctype.regex +0 -2
  109. package/config/attachment.filename.regex +0 -1
  110. package/config/avg.ini +0 -5
  111. package/config/bounce.ini +0 -15
  112. package/config/data.headers.ini +0 -61
  113. package/config/dkim/dkim_key_gen.sh +0 -78
  114. package/config/dkim_sign.ini +0 -4
  115. package/config/dkim_verify.ini +0 -7
  116. package/config/dnsbl.ini +0 -23
  117. package/config/greylist.ini +0 -43
  118. package/config/helo.checks.ini +0 -52
  119. package/config/messagesniffer.ini +0 -18
  120. package/config/spamassassin.ini +0 -56
  121. package/dkim.js +0 -614
  122. package/docs/plugins/avg.md +0 -35
  123. package/docs/plugins/bounce.md +0 -69
  124. package/docs/plugins/clamd.md +0 -147
  125. package/docs/plugins/esets.md +0 -8
  126. package/docs/plugins/greylist.md +0 -90
  127. package/docs/plugins/helo.checks.md +0 -135
  128. package/docs/plugins/messagesniffer.md +0 -163
  129. package/docs/plugins/spamassassin.md +0 -180
  130. package/outbound/mx_lookup.js +0 -70
  131. package/plugins/auth/auth_ldap.js +0 -3
  132. package/plugins/avg.js +0 -162
  133. package/plugins/backscatterer.js +0 -25
  134. package/plugins/bounce.js +0 -381
  135. package/plugins/clamd.js +0 -382
  136. package/plugins/data.uribl.js +0 -4
  137. package/plugins/dkim_sign.js +0 -395
  138. package/plugins/dkim_verify.js +0 -62
  139. package/plugins/dns_list_base.js +0 -221
  140. package/plugins/dnsbl.js +0 -146
  141. package/plugins/dnswl.js +0 -58
  142. package/plugins/esets.js +0 -71
  143. package/plugins/graph.js +0 -5
  144. package/plugins/greylist.js +0 -645
  145. package/plugins/helo.checks.js +0 -533
  146. package/plugins/messagesniffer.js +0 -381
  147. package/plugins/rcpt_to.ldap.js +0 -3
  148. package/plugins/rcpt_to.max_count.js +0 -24
  149. package/plugins/spamassassin.js +0 -384
  150. package/tests/config/dkim/example.com/dns +0 -29
  151. package/tests/config/dkim/example.com/private +0 -6
  152. package/tests/config/dkim/example.com/public +0 -4
  153. package/tests/config/dkim/example.com/selector +0 -1
  154. package/tests/config/dkim.private.key +0 -6
  155. package/tests/config/dkim_sign.ini +0 -4
  156. package/tests/config/helo.checks.ini +0 -52
  157. package/tests/connection.js +0 -327
  158. package/tests/endpoint.js +0 -128
  159. package/tests/fixtures/vm_harness.js +0 -59
  160. package/tests/logger.js +0 -327
  161. package/tests/outbound/hmail.js +0 -112
  162. package/tests/outbound/index.js +0 -324
  163. package/tests/outbound/qfile.js +0 -67
  164. package/tests/outbound_bounce_net_errors.js +0 -173
  165. package/tests/plugins/auth/auth_base.js +0 -463
  166. package/tests/plugins/auth/auth_vpopmaild.js +0 -91
  167. package/tests/plugins/bounce.js +0 -307
  168. package/tests/plugins/clamd.js +0 -224
  169. package/tests/plugins/deprecated/relay_acl.js +0 -140
  170. package/tests/plugins/deprecated/relay_all.js +0 -59
  171. package/tests/plugins/dkim_sign.js +0 -315
  172. package/tests/plugins/dkim_signer.js +0 -108
  173. package/tests/plugins/dns_list_base.js +0 -259
  174. package/tests/plugins/dnsbl.js +0 -101
  175. package/tests/plugins/early_talker.js +0 -115
  176. package/tests/plugins/greylist.js +0 -58
  177. package/tests/plugins/helo.checks.js +0 -525
  178. package/tests/plugins/mail_from.is_resolvable.js +0 -116
  179. package/tests/plugins/queue/smtp_forward.js +0 -221
  180. package/tests/plugins/rcpt_to.host_list_base.js +0 -132
  181. package/tests/plugins/rcpt_to.in_host_list.js +0 -218
  182. package/tests/plugins/relay.js +0 -339
  183. package/tests/plugins/spamassassin.js +0 -171
  184. package/tests/plugins/status.js +0 -138
  185. package/tests/plugins/tls.js +0 -84
  186. package/tests/plugins.js +0 -247
  187. package/tests/rfc1869.js +0 -61
  188. package/tests/server.js +0 -510
  189. package/tests/smtp_client/auth.js +0 -105
  190. package/tests/smtp_client/basic.js +0 -101
  191. package/tests/smtp_client.js +0 -80
  192. package/tests/tls_socket.js +0 -333
  193. package/tests/transaction.js +0 -284
  194. /package/docs/{plugins → deprecated}/dkim_sign.md +0 -0
  195. /package/docs/{plugins → deprecated}/dkim_verify.md +0 -0
  196. /package/docs/{plugins → deprecated}/dnsbl.md +0 -0
  197. /package/docs/{plugins → deprecated}/dnswl.md +0 -0
  198. /package/{tests → test}/.eslintrc.yaml +0 -0
  199. /package/{tests → test}/config/auth_flat_file.ini +0 -0
  200. /package/{tests → test}/config/dhparams.pem +0 -0
  201. /package/{tests → test}/config/host_list +0 -0
  202. /package/{tests → test}/config/outbound_tls_cert.pem +0 -0
  203. /package/{tests → test}/config/outbound_tls_key.pem +0 -0
  204. /package/{tests → test}/config/smtp_forward.ini +0 -0
  205. /package/{tests → test}/config/tls/ec.pem +0 -0
  206. /package/{tests → test}/config/tls/haraka.local.pem +0 -0
  207. /package/{tests → test}/config/tls/mismatched.pem +0 -0
  208. /package/{tests → test}/config/tls_cert.pem +0 -0
  209. /package/{tests → test}/config/tls_key.pem +0 -0
  210. /package/{tests → test}/fixtures/todo_qfile.txt +0 -0
  211. /package/{tests → test}/installation/config/test-plugin-flat +0 -0
  212. /package/{tests → test}/installation/config/test-plugin.ini +0 -0
  213. /package/{tests → test}/installation/config/tls.ini +0 -0
  214. /package/{tests → test}/installation/node_modules/load_first/index.js +0 -0
  215. /package/{tests → test}/installation/node_modules/load_first/package.json +0 -0
  216. /package/{tests → test}/installation/node_modules/test-plugin/config/test-plugin-flat +0 -0
  217. /package/{tests → test}/installation/node_modules/test-plugin/config/test-plugin.ini +0 -0
  218. /package/{tests → test}/installation/node_modules/test-plugin/package.json +0 -0
  219. /package/{tests → test}/installation/node_modules/test-plugin/test-plugin.js +0 -0
  220. /package/{tests → test}/installation/plugins/base_plugin.js +0 -0
  221. /package/{tests → test}/installation/plugins/folder_plugin/index.js +0 -0
  222. /package/{tests → test}/installation/plugins/folder_plugin/package.json +0 -0
  223. /package/{tests → test}/installation/plugins/inherits.js +0 -0
  224. /package/{tests → test}/installation/plugins/load_first.js +0 -0
  225. /package/{tests → test}/installation/plugins/plugin.js +0 -0
  226. /package/{tests → test}/installation/plugins/tls.js +0 -0
  227. /package/{tests → test}/loud/config/dhparams.pem +0 -0
  228. /package/{tests → test}/loud/config/tls/goobered.pem +0 -0
  229. /package/{tests → test/loud}/config/tls.ini +0 -0
  230. /package/{tests → test}/mail_specimen/base64-root-part.txt +0 -0
  231. /package/{tests → test}/mail_specimen/varied-fold-lengths-preserve-data.txt +0 -0
  232. /package/{tests → test}/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_fixed +0 -0
  233. /package/{tests → test}/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka +0 -0
  234. /package/{tests → test}/queue/1508269674999_1508269674999_0_34002_socVUF_1_haraka +0 -0
  235. /package/{tests → test}/queue/1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka +0 -0
  236. /package/{tests → test}/queue/multibyte +0 -0
  237. /package/{tests → test}/queue/plain +0 -0
  238. /package/{tests → test}/queue/zero-length +0 -0
  239. /package/{tests → test}/test-queue/delete-me +0 -0
@@ -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 || 28) * 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
- };