Haraka 2.8.28 → 3.0.1

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