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
@@ -10,17 +10,16 @@ const util = require('util');
10
10
  const DSN = require('haraka-dsn');
11
11
  const tlds = require('haraka-tld');
12
12
  const net_utils = require('haraka-net-utils');
13
- const Address = require('address-rfc2821').Address;
13
+ const { Address } = require('address-rfc2821');
14
14
 
15
15
  // External NPM modules
16
16
  const ipaddr = require('ipaddr.js');
17
17
 
18
18
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
19
19
  exports.register = function (next) {
20
- const plugin = this;
21
- plugin.inherits('haraka-plugin-redis');
20
+ this.inherits('haraka-plugin-redis');
22
21
 
23
- plugin.load_config();
22
+ this.load_config();
24
23
 
25
24
  this.register_hook('init_master', 'init_redis_plugin');
26
25
  this.register_hook('init_child', 'init_redis_plugin');
@@ -31,19 +30,18 @@ exports.register = function (next) {
31
30
 
32
31
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
33
32
  exports.load_config = function () {
34
- const plugin = this;
35
33
 
36
- plugin.cfg = plugin.config.get('greylist.ini', {
34
+ this.cfg = this.config.get('greylist.ini', {
37
35
  booleans : [
38
36
  '+skip.dnswlorg',
39
37
  '-skip.mailspikewl'
40
38
  ]
41
39
  }, () => {
42
- plugin.load_config();
40
+ this.load_config();
43
41
  });
44
42
 
45
- plugin.merge_redis_ini();
46
- plugin.load_config_lists();
43
+ this.merge_redis_ini();
44
+ this.load_config_lists();
47
45
  }
48
46
 
49
47
  // Load various configuration lists
@@ -60,8 +58,8 @@ exports.load_config_lists = function () {
60
58
 
61
59
  // toLower when loading spends a fraction of a second at load time
62
60
  // to save millions of seconds during run time.
63
- for (let i = 0; i < list.length; i++) {
64
- plugin.whitelist[type][list[i].toLowerCase()] = true;
61
+ for (const element of list) {
62
+ plugin.whitelist[type][element.toLowerCase()] = true;
65
63
  }
66
64
  plugin.logdebug(`whitelist {${type}} loaded from ${file_name} with ${list.length} entries`);
67
65
  }
@@ -71,9 +69,9 @@ exports.load_config_lists = function () {
71
69
 
72
70
  const list = Object.keys(plugin.cfg[file_name]);
73
71
 
74
- for (let i = 0; i < list.length; i++) {
72
+ for (const element of list) {
75
73
  try {
76
- let addr = list[i];
74
+ let addr = element;
77
75
  if (addr.match(/\/\d+$/)) {
78
76
  addr = ipaddr.parseCIDR(addr);
79
77
  }
@@ -111,156 +109,140 @@ exports.shutdown = function () {
111
109
 
112
110
  // We check for IP and envelope whitelist
113
111
  exports.hook_mail = function (next, connection, params) {
114
- const plugin = this;
112
+ if (!connection.transaction) return next();
113
+
115
114
  const mail_from = params[0];
116
115
 
117
116
  // whitelist checks
118
- if (plugin.ip_in_list(connection.remote.ip)) { // check connecting IP
117
+ if (this.ip_in_list(connection.remote.ip)) { // check connecting IP
119
118
 
120
- plugin.loginfo(connection, 'Connecting IP was whitelisted via config');
121
- connection.transaction.results.add(plugin, {
122
- skip : 'config-whitelist(ip)'
123
- });
119
+ this.loginfo(connection, 'Connecting IP was whitelisted via config');
120
+ connection.transaction.results.add(this, { skip : 'config-whitelist(ip)' })
124
121
 
125
122
  }
126
- else if (plugin.addr_in_list('mail', mail_from.address().toLowerCase())) { // check envelope (email & domain)
123
+ else if (this.addr_in_list('mail', mail_from.address().toLowerCase())) { // check envelope (email & domain)
127
124
 
128
- plugin.loginfo(connection, 'Envelope was whitelisted via config');
129
- connection.transaction.results.add(plugin, {
130
- skip : 'config-whitelist(envelope)'
131
- });
125
+ this.loginfo(connection, 'Envelope was whitelisted via config');
126
+ connection.transaction.results.add(this, { skip : 'config-whitelist(envelope)' });
132
127
 
133
128
  }
134
129
  else {
135
- const why_skip = plugin.process_skip_rules(connection);
130
+ const why_skip = this.process_skip_rules(connection);
136
131
 
137
132
  if (why_skip) {
138
- plugin.loginfo(connection, `Requested to skip the GL because skip rule matched: ${why_skip}`);
139
- connection.transaction.results.add(plugin, {
140
- skip : `requested(${why_skip})`
141
- });
133
+ this.loginfo(connection, `Requested to skip the GL because skip rule matched: ${why_skip}`);
134
+ connection.transaction.results.add(this, { skip : `requested(${why_skip})` });
142
135
  }
143
136
  }
144
137
 
145
- return next();
138
+ next();
146
139
  }
147
140
 
148
141
  //
149
142
  exports.hook_rcpt_ok = function (next, connection, rcpt) {
150
- const plugin = this;
151
143
 
152
- if (plugin.should_skip_check(connection)) return next();
153
-
154
- if (plugin.was_whitelisted_in_session(connection)) {
155
- plugin.logdebug(connection, 'host already whitelisted in this session');
144
+ if (this.should_skip_check(connection)) return next();
145
+ if (this.was_whitelisted_in_session(connection)) {
146
+ this.logdebug(connection, 'host already whitelisted in this session');
156
147
  return next();
157
148
  }
158
149
 
159
- const ctr = connection.transaction.results;
160
- const mail_from = connection.transaction.mail_from;
150
+ const { transaction } = connection
151
+
152
+ const ctr = transaction.results;
153
+ const { mail_from } = transaction;
161
154
 
162
155
  // check rcpt in whitelist (email & domain)
163
- if (plugin.addr_in_list('rcpt', rcpt.address().toLowerCase())) {
164
- plugin.loginfo(connection, 'RCPT was whitelisted via config');
165
- ctr.add(plugin, {
166
- skip : 'config-whitelist(recipient)'
167
- });
156
+ if (this.addr_in_list('rcpt', rcpt.address().toLowerCase())) {
157
+ this.loginfo(connection, 'RCPT was whitelisted via config');
158
+ ctr.add(this, { skip : 'config-whitelist(recipient)' });
168
159
  return next();
169
160
  }
170
161
 
171
- plugin.check_and_update_white(connection, (err, white_rec) => {
162
+ this.check_and_update_white(connection, (err, white_rec) => {
172
163
  if (err) {
173
- plugin.logerror(connection, `Got error: ${util.inspect(err)}`);
164
+ this.logerror(connection, `Got error: ${util.inspect(err)}`);
174
165
  return next(DENYSOFT, DSN.sec_unspecified('Backend failure. Please, retry later or contact our support.'));
175
166
  }
176
167
  if (white_rec) {
177
- plugin.logdebug(connection, 'host in WHITE zone');
178
- ctr.add(plugin, {
179
- pass : 'whitelisted'
180
- });
181
- ctr.push(plugin, {
182
- stats : {
183
- rcpt : white_rec
184
- },
185
- stage : 'rcpt'
186
- });
168
+ this.logdebug(connection, 'host in WHITE zone');
169
+ ctr.add(this, { pass : 'whitelisted' });
170
+ ctr.push(this, { stats : { rcpt : white_rec }, stage : 'rcpt' });
187
171
 
188
172
  return next();
189
173
  }
190
- else {
191
-
192
- return plugin.process_tuple(connection, mail_from.address(), rcpt.address(), (err2, white_promo_rec) => {
193
- if (err2) {
194
- if (err2 instanceof Error && err2.notanerror) {
195
- plugin.logdebug(connection, 'host in GREY zone');
196
-
197
- ctr.add(plugin, {
198
- fail : 'greylisted'
199
- });
200
- ctr.push(plugin, {
201
- stats : {
202
- rcpt : err2.record
203
- },
204
- stage : 'rcpt'
205
- });
206
-
207
- return plugin.invoke_outcome_cb(next, false);
208
- }
209
-
210
- throw err2;
211
- }
212
174
 
213
- if (!white_promo_rec) {
214
- ctr.add(plugin, {
215
- fail : 'greylisted',
216
- stage : 'rcpt'
175
+ return this.process_tuple(connection, mail_from.address(), rcpt.address(), (err2, white_promo_rec) => {
176
+ if (err2) {
177
+ if (err2 instanceof Error && err2.notanerror) {
178
+ this.logdebug(connection, 'host in GREY zone');
179
+
180
+ ctr.add(this, {
181
+ fail : 'greylisted'
217
182
  });
218
- return plugin.invoke_outcome_cb(next, false);
219
- }
220
- else {
221
- plugin.loginfo(connection, 'host has been promoted to WHITE zone');
222
- ctr.add(plugin, {
223
- pass : 'whitelisted',
224
- stats : white_promo_rec,
183
+ ctr.push(this, {
184
+ stats : {
185
+ rcpt : err2.record
186
+ },
225
187
  stage : 'rcpt'
226
188
  });
227
- ctr.add(plugin, {
228
- pass : 'whitelisted'
229
- });
230
- return plugin.invoke_outcome_cb(next, true);
189
+
190
+ return this.invoke_outcome_cb(next, false);
231
191
  }
232
- });
233
- }
234
- });
192
+
193
+ throw err2;
194
+ }
195
+
196
+ if (!white_promo_rec) {
197
+ ctr.add(this, {
198
+ fail : 'greylisted',
199
+ stage : 'rcpt'
200
+ });
201
+ return this.invoke_outcome_cb(next, false);
202
+ }
203
+ else {
204
+ this.loginfo(connection, 'host has been promoted to WHITE zone');
205
+ ctr.add(this, {
206
+ pass : 'whitelisted',
207
+ stats : white_promo_rec,
208
+ stage : 'rcpt'
209
+ });
210
+ ctr.add(this, {
211
+ pass : 'whitelisted'
212
+ });
213
+ return this.invoke_outcome_cb(next, true);
214
+ }
215
+ })
216
+ })
235
217
  }
236
218
 
237
219
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
238
220
 
239
221
  // Main GL engine that accepts tuple and returns matched record or a rejection.
240
222
  exports.process_tuple = function (connection, sender, rcpt, cb) {
241
- const plugin = this;
242
223
 
243
- const key = plugin.craft_grey_key(connection, sender, rcpt);
224
+ const key = this.craft_grey_key(connection, sender, rcpt);
225
+ if (!key) return;
244
226
 
245
- return plugin.db_lookup(key, (err, record) => {
227
+ return this.db_lookup(key, (err, record) => {
246
228
  if (err) {
247
229
  if (err instanceof Error && err.what == 'db_error')
248
- plugin.logwarn(connection, `got err from DB: ${util.inspect(err)}`);
230
+ this.logwarn(connection, `got err from DB: ${util.inspect(err)}`);
249
231
  throw err;
250
232
  }
251
- plugin.logdebug(connection, `got record: ${util.inspect(record)}`);
233
+ this.logdebug(connection, `got record: ${util.inspect(record)}`);
252
234
 
253
235
  // { created: TS, updated: TS, lifetime: TTL, tried: Integer }
254
236
  const now = Date.now() / 1000;
255
237
 
256
238
  if (record &&
257
- (record.created + plugin.cfg.period.black < now) &&
239
+ (record.created + this.cfg.period.black < now) &&
258
240
  (record.created + record.lifetime >= now)) {
259
241
  // Host passed greylisting
260
- return plugin.promote_to_white(connection, record, cb);
242
+ return this.promote_to_white(connection, record, cb);
261
243
  }
262
244
 
263
- return plugin.update_grey(key, !record, (err2, created_record) => {
245
+ return this.update_grey(key, !record, (err2, created_record) => {
264
246
  const err3 = new Error('in black zone');
265
247
  err3.record = created_record || record;
266
248
  err3.notanerror = true;
@@ -271,22 +253,21 @@ exports.process_tuple = function (connection, sender, rcpt, cb) {
271
253
 
272
254
  // Checks if host is _white_. Updates stats if so.
273
255
  exports.check_and_update_white = function (connection, cb) {
274
- const plugin = this;
275
256
 
276
- const key = plugin.craft_white_key(connection);
257
+ const key = this.craft_white_key(connection);
277
258
 
278
- return plugin.db_lookup(key, (err, record) => {
259
+ return this.db_lookup(key, (err, record) => {
279
260
  if (err) {
280
- plugin.logwarn(connection, `got err from DB: ${util.inspect(err)}`);
261
+ this.logwarn(connection, `got err from DB: ${util.inspect(err)}`);
281
262
  throw err;
282
263
  }
283
264
  if (record) {
284
265
  if (record.updated + record.lifetime - 2 < Date.now() / 1000) { // race "prevention".
285
- plugin.logerror(connection, "Mischief! Race condition triggered.");
266
+ this.logerror(connection, "Mischief! Race condition triggered.");
286
267
  return cb(new Error('drunkard'));
287
268
  }
288
269
 
289
- return plugin.update_white_record(key, record, cb);
270
+ return this.update_white_record(key, record, cb);
290
271
  }
291
272
 
292
273
  return cb(null, false);
@@ -295,46 +276,45 @@ exports.check_and_update_white = function (connection, cb) {
295
276
 
296
277
  // invokes next() depending on outcome param
297
278
  exports.invoke_outcome_cb = function (next, is_whitelisted) {
298
- const plugin = this;
299
279
 
300
- if (is_whitelisted) {
301
- return next();
302
- }
303
- else {
304
- const text = plugin.cfg.main.text || '';
280
+ if (is_whitelisted) return next();
305
281
 
306
- return next(DENYSOFT, DSN.sec_unauthorized(text, '451'));
307
- }
282
+ const text = this.cfg.main.text || '';
283
+
284
+ return next(DENYSOFT, DSN.sec_unauthorized(text, '451'));
308
285
  }
309
286
 
310
287
  // Should we skip greylisting invokation altogether?
311
288
  exports.should_skip_check = function (connection) {
312
- const plugin = this;
313
- const ctr = connection.transaction && connection.transaction.results;
289
+ const { transaction, relaying, remote } = connection ?? {}
290
+
291
+ if (!transaction) return true;
314
292
 
315
- if (connection.relaying) {
316
- plugin.logdebug(connection, 'skipping GL for relaying host');
317
- ctr.add(plugin, {
293
+ const ctr = transaction.results;
294
+
295
+ if (relaying) {
296
+ this.logdebug(connection, 'skipping GL for relaying host');
297
+ ctr.add(this, {
318
298
  skip : 'relaying'
319
299
  });
320
300
  return true;
321
301
  }
322
302
 
323
- if (connection.remote.is_private) {
324
- connection.logdebug(plugin, `skipping private IP: ${connection.remote.ip}`);
325
- ctr.add(plugin, {
303
+ if (remote?.is_private) {
304
+ connection.logdebug(this, `skipping private IP: ${connection.remote.ip}`);
305
+ ctr.add(this, {
326
306
  skip : 'private-ip'
327
307
  });
328
308
  return true;
329
309
  }
330
310
 
331
311
  if (ctr) {
332
- if (ctr.has(plugin, 'skip', /^config-whitelist/)) {
333
- plugin.loginfo(connection, 'skipping GL for host whitelisted in config');
312
+ if (ctr.has(this, 'skip', /^config-whitelist/)) {
313
+ this.loginfo(connection, 'skipping GL for host whitelisted in config');
334
314
  return true;
335
315
  }
336
- if (ctr.has(plugin, 'skip', /^requested/)) {
337
- plugin.loginfo(connection, 'skipping GL because was asked to previously');
316
+ if (ctr.has(this, 'skip', /^requested/)) {
317
+ this.loginfo(connection, 'skipping GL because was asked to previously');
338
318
  return true;
339
319
  }
340
320
  }
@@ -344,14 +324,14 @@ exports.should_skip_check = function (connection) {
344
324
 
345
325
  // Was whitelisted previously in this session
346
326
  exports.was_whitelisted_in_session = function (connection) {
327
+ if (!connection?.transaction?.results) return false;
347
328
  return connection.transaction.results.has(this, 'pass', 'whitelisted');
348
329
  }
349
330
 
350
331
  exports.process_skip_rules = function (connection) {
351
- const plugin = this;
352
332
  const cr = connection.results;
353
333
 
354
- const skip_cfg = plugin.cfg.skip;
334
+ const skip_cfg = this.cfg.skip;
355
335
  if (skip_cfg) {
356
336
  if (skip_cfg.dnswlorg && cr.has('dnswl.org', 'pass', /^list\.dnswl\.org\([123]\)$/)) {
357
337
  return 'dnswl.org(MED)'
@@ -362,7 +342,7 @@ exports.process_skip_rules = function (connection) {
362
342
  }
363
343
  }
364
344
 
365
- return false;
345
+ return '';
366
346
  }
367
347
 
368
348
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -371,8 +351,11 @@ exports.process_skip_rules = function (connection) {
371
351
  // When _to_ is false, we craft +sender+ key
372
352
  // When _to_ is String, we craft +rcpt+ key
373
353
  exports.craft_grey_key = function (connection, from, to) {
374
- const plugin = this;
375
- let key = `grey:${plugin.craft_hostid(connection)}:${(from || '<>')}`;
354
+
355
+ const crafted_host_id = this.craft_hostid(connection)
356
+ if (!crafted_host_id) return null;
357
+
358
+ let key = `grey:${crafted_host_id}:${(from || '<>')}`;
376
359
  if (to != undefined) {
377
360
  key += `:${(to || '<>')}`;
378
361
  }
@@ -381,40 +364,39 @@ exports.craft_grey_key = function (connection, from, to) {
381
364
 
382
365
  // Build white DB key off supplied params.
383
366
  exports.craft_white_key = function (connection) {
384
- const plugin = this;
385
- return `white:${plugin.craft_hostid(connection)}`;
367
+ return `white:${this.craft_hostid(connection)}`;
386
368
  }
387
369
 
388
370
  // Return so-called +hostid+.
389
371
  exports.craft_hostid = function (connection) {
390
372
  const plugin = this;
391
- const trx = connection.transaction;
373
+ const { transaction, remote } = connection ?? {};
374
+ if (!transaction || !remote) return null;
392
375
 
393
- if (trx.notes.greylist && trx.notes.greylist.hostid)
394
- return trx.notes.greylist.hostid; // "caching"
395
-
396
- const ip = connection.remote.ip;
397
- let rdns = connection.remote.host;
376
+ if (transaction.notes?.greylist?.hostid) {
377
+ return transaction.notes.greylist.hostid; // "caching"
378
+ }
398
379
 
399
380
  function chsit (value, reason) { // cache the return value
400
381
  if (!value)
401
382
  plugin.logdebug(connection, `hostid set to IP: ${reason}`);
402
383
 
403
- trx.results.add(plugin, {
384
+ transaction.results.add(plugin, {
404
385
  hostid_type : value ? 'domain' : 'ip',
405
- rdns : (value || ip),
386
+ rdns : (value || remote.ip),
406
387
  msg : reason
407
388
  }); // !don't move me.
408
389
 
409
- value = value || ip;
390
+ value = value || remote.ip;
410
391
 
411
- return ((trx.notes.greylist = trx.notes.greylist || {}).hostid = value);
392
+ return ((transaction.notes.greylist = transaction.notes.greylist || {}).hostid = value);
412
393
  }
413
394
 
414
- if (!rdns || rdns === 'Unknown' || rdns === 'DNSERROR') // no rDNS . FIXME: use fcrdns results
395
+ // no rDNS . FIXME: use fcrdns results
396
+ if (!remote.host || [ 'Unknown' | 'DNSERROR' ].includes(remote.host))
415
397
  return chsit(null, 'no rDNS info for this host');
416
398
 
417
- rdns = rdns.replace(/\.$/, ''); // strip ending dot, just in case
399
+ remote.host = remote.host.replace(/\.$/, ''); // strip ending dot, just in case
418
400
 
419
401
  const fcrdns = connection.results.get('fcrdns');
420
402
  if (!fcrdns) {
@@ -435,22 +417,20 @@ exports.craft_hostid = function (connection) {
435
417
  return chsit(null, 'invalid org domain in rDNS');
436
418
 
437
419
  // strip first label up until the tld boundary.
438
- const decoupled = tlds.split_hostname(rdns, 3);
420
+ const decoupled = tlds.split_hostname(!remote.host, 3);
439
421
  const vardom = decoupled[0]; // "variable" portion of domain
440
422
  const dom = decoupled[1]; // "static" portion of domain
441
423
 
442
424
  // we check for special cases where rdns looks custom/static, but really is dynamic
443
- const special_case_info = plugin.check_rdns_for_special_cases(rdns, vardom);
444
- if (special_case_info) {
445
- return chsit(null, special_case_info.why);
446
- }
425
+ const special_case_info = plugin.check_rdns_for_special_cases(!remote.host, vardom);
426
+ if (special_case_info) return chsit(null, special_case_info.why);
447
427
 
448
428
  let stripped_dom = dom;
449
429
 
450
430
  if (vardom) {
451
431
 
452
432
  // check for decimal IP in rDNS
453
- if (vardom.match(String(net_utils.ip_to_long(ip))))
433
+ if (vardom.match(String(net_utils.ip_to_long(remote.ip))))
454
434
  return chsit(null, 'decimal IP');
455
435
 
456
436
  // craft the +hostid+
@@ -467,15 +447,14 @@ exports.craft_hostid = function (connection) {
467
447
  // Retrieve _grey_ record
468
448
  // not implemented
469
449
  exports.retrieve_grey = function (rcpt_key, sender_key, cb) {
470
- const plugin = this;
471
- const multi = plugin.db.multi();
450
+ const multi = this.db.multi();
472
451
 
473
452
  multi.hgetall(rcpt_key);
474
453
  multi.hgetall(sender_key);
475
454
 
476
455
  multi.exec((err, result) => {
477
456
  if (err) {
478
- plugin.lognotice(`DB error: ${util.inspect(err)}`);
457
+ this.lognotice(`DB error: ${util.inspect(err)}`);
479
458
  err.what = 'db_error';
480
459
  throw err;
481
460
  }
@@ -485,15 +464,13 @@ exports.retrieve_grey = function (rcpt_key, sender_key, cb) {
485
464
 
486
465
  // Update or create _grey_ record
487
466
  exports.update_grey = function (key, create, cb) {
488
- // { created: TS, updated: TS, lifetime: TTL, tried: Integer }
489
- const plugin = this;
490
- const multi = plugin.db.multi();
467
+ const multi = this.db.multi();
491
468
 
492
469
  const ts_now = Math.round(Date.now() / 1000);
493
470
  let new_record;
494
471
 
495
472
  if (create) {
496
- const lifetime = plugin.cfg.period.grey;
473
+ const lifetime = this.cfg.period.grey;
497
474
  new_record = {
498
475
  created : ts_now,
499
476
  updated : ts_now,
@@ -513,7 +490,7 @@ exports.update_grey = function (key, create, cb) {
513
490
 
514
491
  multi.exec((err, records) => {
515
492
  if (err) {
516
- plugin.lognotice(`DB error: ${util.inspect(err)}`);
493
+ this.lognotice(`DB error: ${util.inspect(err)}`);
517
494
  err.what = 'db_error';
518
495
  throw err;
519
496
  }
@@ -523,10 +500,9 @@ exports.update_grey = function (key, create, cb) {
523
500
 
524
501
  // Promote _grey_ record to _white_.
525
502
  exports.promote_to_white = function (connection, grey_rec, cb) {
526
- const plugin = this;
527
503
 
528
504
  const ts_now = Math.round(Date.now() / 1000);
529
- const white_ttl = plugin.cfg.period.white;
505
+ const white_ttl = this.cfg.period.white;
530
506
 
531
507
  // { first_connect: TS, whitelisted: TS, updated: TS, lifetime: TTL, tried: Integer, tried_when_greylisted: Integer }
532
508
  const white_rec = {
@@ -538,17 +514,18 @@ exports.promote_to_white = function (connection, grey_rec, cb) {
538
514
  tried : 1
539
515
  };
540
516
 
541
- const white_key = plugin.craft_white_key(connection);
517
+ const white_key = this.craft_white_key(connection);
518
+ if (!white_key) return;
542
519
 
543
- return plugin.db.hmset(white_key, white_rec, (err, result) => {
520
+ return this.db.hmset(white_key, white_rec, (err, result) => {
544
521
  if (err) {
545
- plugin.lognotice(`DB error: ${util.inspect(err)}`);
522
+ this.lognotice(`DB error: ${util.inspect(err)}`);
546
523
  err.what = 'db_error';
547
524
  throw err;
548
525
  }
549
- plugin.db.expire(white_key, white_ttl, (err2, result2) => {
526
+ this.db.expire(white_key, white_ttl, (err2, result2) => {
550
527
  if (err2) {
551
- plugin.lognotice(`DB error: ${util.inspect(err2)}`);
528
+ this.lognotice(`DB error: ${util.inspect(err2)}`);
552
529
  }
553
530
  return cb(err2, result2);
554
531
  });
@@ -557,9 +534,8 @@ exports.promote_to_white = function (connection, grey_rec, cb) {
557
534
 
558
535
  // Update _white_ record
559
536
  exports.update_white_record = function (key, record, cb) {
560
- const plugin = this;
561
537
 
562
- const multi = plugin.db.multi();
538
+ const multi = this.db.multi();
563
539
  const ts_now = Math.round(Date.now() / 1000);
564
540
 
565
541
  // { first_connect: TS, whitelisted: TS, updated: TS, lifetime: TTL, tried: Integer, tried_when_greylisted: Integer }
@@ -571,7 +547,7 @@ exports.update_white_record = function (key, record, cb) {
571
547
 
572
548
  return multi.exec((err2, record2) => {
573
549
  if (err2) {
574
- plugin.lognotice(`DB error: ${util.inspect(err2)}`);
550
+ this.lognotice(`DB error: ${util.inspect(err2)}`);
575
551
  err2.what = 'db_error';
576
552
  throw err2;
577
553
  }
@@ -582,11 +558,10 @@ exports.update_white_record = function (key, record, cb) {
582
558
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
583
559
 
584
560
  exports.db_lookup = function (key, cb) {
585
- const plugin = this;
586
561
 
587
- plugin.db.hgetall(key, (err, result) => {
562
+ this.db.hgetall(key, (err, result) => {
588
563
  if (err) {
589
- plugin.lognotice(`DB error: ${util.inspect(err)}`, key);
564
+ this.lognotice(`DB error: ${util.inspect(err)}`, key);
590
565
  }
591
566
  if (result && typeof result === 'object') { // groom known-to-be numeric values
592
567
  ['created', 'updated', 'lifetime', 'tried', 'first_connect', 'whitelisted', 'tried_when_greylisted'].forEach(kk => {
@@ -602,20 +577,19 @@ exports.db_lookup = function (key, cb) {
602
577
 
603
578
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
604
579
  exports.addr_in_list = function (type, address) {
605
- const plugin = this;
606
580
 
607
- if (!plugin.whitelist[type]) {
608
- plugin.logwarn(`List not defined: ${type}`);
581
+ if (!this.whitelist[type]) {
582
+ this.logwarn(`List not defined: ${type}`);
609
583
  return false;
610
584
  }
611
585
 
612
- if (plugin.whitelist[type][address]) {
586
+ if (this.whitelist[type][address]) {
613
587
  return true;
614
588
  }
615
589
 
616
590
  try {
617
591
  const addr = new Address(address);
618
- return !!plugin.whitelist[type][addr.host];
592
+ return !!this.whitelist[type][addr.host];
619
593
  }
620
594
  catch (err) {
621
595
  return false;
@@ -623,14 +597,13 @@ exports.addr_in_list = function (type, address) {
623
597
  }
624
598
 
625
599
  exports.ip_in_list = function (ip) {
626
- const plugin = this;
627
600
  const ipobj = ipaddr.parse(ip);
628
601
 
629
- const list = plugin.whitelist.ip;
602
+ const list = this.whitelist.ip;
630
603
 
631
- for (let i = 0; i < list.length; i++) {
604
+ for (const element of list) {
632
605
  try {
633
- if (ipobj.match(list[i])) {
606
+ if (ipobj.match(element)) {
634
607
  return true;
635
608
  }
636
609
  }
@@ -642,16 +615,15 @@ exports.ip_in_list = function (ip) {
642
615
 
643
616
  // Match patterns in the list against (end of) domain
644
617
  exports.domain_in_list = function (list_name, domain) {
645
- const plugin = this;
646
- const list = plugin.list[list_name];
618
+ const list = this.list[list_name];
647
619
 
648
620
  if (!list) {
649
- plugin.logwarn(`List not defined: ${list_name}`);
621
+ this.logwarn(`List not defined: ${list_name}`);
650
622
  return false;
651
623
  }
652
624
 
653
- for (let i = 0; i < list.length; i++) {
654
- if (domain.length - domain.lastIndexOf(list[i]) == list[i].length)
625
+ for (const element of list) {
626
+ if (domain.length - domain.lastIndexOf(element) == element.length)
655
627
  return true;
656
628
  }
657
629
 
@@ -661,10 +633,9 @@ exports.domain_in_list = function (list_name, domain) {
661
633
  // Check for special rDNS cases
662
634
  // @return {type: 'dynamic'} if rnds is dynamic (hostid should be IP)
663
635
  exports.check_rdns_for_special_cases = function (domain, label) {
664
- const plugin = this;
665
636
 
666
637
  // ptr for these is in fact dynamic
667
- if (plugin.domain_in_list('dyndom', domain))
638
+ if (this.domain_in_list('dyndom', domain))
668
639
  return {
669
640
  type : 'dynamic',
670
641
  why : 'rDNS considered dynamic: listed in dynamic.domains config list'