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
@@ -6,7 +6,7 @@ const async = require('async');
6
6
  const crypto = require('crypto');
7
7
  const fs = require('fs');
8
8
  const path = require('path');
9
- const Stream = require('stream').Stream;
9
+ const { Stream } = require('stream');
10
10
 
11
11
  const utils = require('haraka-utils');
12
12
 
@@ -106,22 +106,26 @@ class DKIMSignStream extends Stream {
106
106
  */
107
107
 
108
108
  const headers = [];
109
- for (let i=0; i < this.headers_to_sign.length; i++) {
110
- let head = this.header.get(this.headers_to_sign[i]);
109
+ for (const element of this.headers_to_sign) {
110
+ let head = this.header.get(element);
111
111
  if (head) {
112
112
  head = head.replace(/\r?\n/gm, '');
113
113
  head = head.replace(/\s+/gm, ' ');
114
114
  head = head.replace(/\s+$/gm, '');
115
- this.signer.update(`${this.headers_to_sign[i]}:${head}\r\n`);
116
- headers.push(this.headers_to_sign[i]);
115
+ this.signer.update(`${element}:${head}\r\n`);
116
+ headers.push(element);
117
117
  }
118
118
  }
119
119
 
120
120
  // Create DKIM header
121
- let dkim_header = `v=1;a=rsa-sha256;bh=${bodyhash};c=relaxed/simple;d=${this.domain_name};h=${headers.join(':')};s=${this.selector};b=`;
121
+ let dkim_header = `v=1; a=rsa-sha256; c=relaxed/simple; d=${this.domain_name}; s=${this.selector}; h=${headers.join(':')}; bh=${bodyhash}; b=`;
122
122
  this.signer.update(`dkim-signature:${dkim_header}`);
123
123
  const signature = this.signer.sign(this.private_key, 'base64');
124
- dkim_header += signature;
124
+ dkim_header = `v=1; a=rsa-sha256; c=relaxed/simple;\r\n\td=${this.domain_name}; s=${this.selector};\r\n\th=${headers.join(':')};\r\n\tbh=${bodyhash};\r\n\tb=`;
125
+ dkim_header += signature.substring(0,74);
126
+ for (let i=74; i<signature.length; i+=76) {
127
+ dkim_header += `\r\n\t${signature.substring(i, i+76)}`;
128
+ }
125
129
 
126
130
  if (this.end_callback) this.end_callback(null, dkim_header);
127
131
  this.end_callback = null;
@@ -139,28 +143,25 @@ class DKIMSignStream extends Stream {
139
143
  exports.DKIMSignStream = DKIMSignStream;
140
144
 
141
145
  exports.register = function () {
142
- const plugin = this;
143
- plugin.load_dkim_sign_ini();
144
- plugin.load_dkim_default_key();
146
+ this.load_dkim_sign_ini();
147
+ this.load_dkim_default_key();
145
148
  }
146
149
 
147
150
  exports.load_dkim_sign_ini = function () {
148
- const plugin = this;
149
- plugin.cfg = plugin.config.get('dkim_sign.ini', {
151
+ this.cfg = this.config.get('dkim_sign.ini', {
150
152
  booleans: [
151
153
  '-disabled',
152
154
  ]
153
155
  },
154
- () => { plugin.load_dkim_sign_ini(); }
156
+ () => { this.load_dkim_sign_ini(); }
155
157
  );
156
158
 
157
- plugin.cfg.headers_to_sign = plugin.get_headers_to_sign();
159
+ this.cfg.headers_to_sign = this.get_headers_to_sign();
158
160
  }
159
161
 
160
162
  exports.load_dkim_default_key = function () {
161
- const plugin = this;
162
- plugin.private_key = plugin.config.get('dkim.private.key', 'data', () => {
163
- plugin.load_dkim_default_key();
163
+ this.private_key = this.config.get('dkim.private.key', 'data', () => {
164
+ this.load_dkim_default_key();
164
165
  }).join('\n');
165
166
  }
166
167
 
@@ -169,37 +170,35 @@ exports.load_key = function (file) {
169
170
  }
170
171
 
171
172
  exports.hook_queue_outbound = exports.hook_pre_send_trans_email = function (next, connection) {
172
- const plugin = this;
173
- if (plugin.cfg.main.disabled) return next();
173
+ if (this.cfg.main.disabled) return next();
174
+ if (!connection?.transaction) return next();
174
175
 
175
- if (connection.transaction.notes.dkim_signed) {
176
- connection.logdebug(plugin, 'already signed');
176
+ if (connection.transaction.notes?.dkim_signed) {
177
+ connection.logdebug(this, 'already signed');
177
178
  return next();
178
179
  }
179
180
 
180
181
  exports.get_sign_properties(connection, (err, props) => {
182
+ if (!connection?.transaction) return next();
181
183
  // props: selector, domain, & private_key
182
- if (err) connection.logerror(plugin, `${err.message}`);
184
+ if (err) connection.logerror(this, `${err.message}`);
183
185
 
184
- if (!plugin.has_key_data(connection, props)) {
185
- connection.logerror(`missing key data for ${props.selector}.${props.domain}`)
186
- return next();
187
- }
186
+ if (!this.has_key_data(connection, props)) return next();
188
187
 
189
- connection.logdebug(plugin, `domain: ${props.domain}`);
188
+ connection.logdebug(this, `domain: ${props.domain}`);
190
189
 
191
190
  const txn = connection.transaction;
192
- props.headers = plugin.cfg.headers_to_sign;
191
+ props.headers = this.cfg.headers_to_sign;
193
192
 
194
193
  txn.message_stream.pipe(
195
194
  new DKIMSignStream(props, txn.header, (err2, dkim_header) => {
196
195
  if (err2) {
197
- txn.results.add(plugin, {err: err2.message});
196
+ txn.results.add(this, {err: err2.message});
198
197
  return next(err2);
199
198
  }
200
199
 
201
- connection.loginfo(plugin, `signed for ${props.domain}`);
202
- txn.results.add(plugin, {pass: dkim_header});
200
+ connection.loginfo(this, `signed for ${props.domain}`);
201
+ txn.results.add(this, {pass: dkim_header});
203
202
  txn.add_header('DKIM-Signature', dkim_header);
204
203
 
205
204
  connection.transaction.notes.dkim_signed = true;
@@ -210,34 +209,36 @@ exports.hook_queue_outbound = exports.hook_pre_send_trans_email = function (next
210
209
  }
211
210
 
212
211
  exports.get_sign_properties = function (connection, done) {
213
- const plugin = this;
212
+ if (!connection.transaction) return;
214
213
 
215
- const domain = plugin.get_sender_domain(connection);
214
+ const domain = this.get_sender_domain(connection);
216
215
 
217
216
  if (!domain) {
218
- connection.transaction.results.add(plugin, {msg: 'sending domain not detected', emit: true });
217
+ connection.transaction.results.add(this, {msg: 'sending domain not detected', emit: true });
219
218
  }
220
219
 
221
220
  const props = { domain }
222
221
 
223
- plugin.get_key_dir(connection, props, (err, keydir) => {
222
+ this.get_key_dir(connection, props, (err, keydir) => {
224
223
  if (err) {
225
224
  console.error(`err: ${err}`);
226
- connection.logerror(plugin, err);
225
+ connection.logerror(this, err);
227
226
  return done(new Error(`Error getting DKIM key_dir for ${domain}: ${err}`), props)
228
227
  }
229
228
 
229
+ if (!connection.transaction) return done(null, props);
230
+
230
231
  // a directory for ${domain} exists
231
232
  if (keydir) {
232
233
  props.domain = path.basename(keydir); // keydir might be apex (vs sub)domain
233
- props.private_key = plugin.load_key(path.join('dkim', props.domain, 'private'));
234
- props.selector = plugin.load_key(path.join('dkim', props.domain, 'selector')).trim();
234
+ props.private_key = this.load_key(path.join('dkim', props.domain, 'private'));
235
+ props.selector = this.load_key(path.join('dkim', props.domain, 'selector')).trim();
235
236
 
236
237
  if (!props.selector) {
237
- connection.transaction.results.add(plugin, {err: `missing selector for domain ${domain}`});
238
+ connection.transaction.results.add(this, {err: `missing selector for domain ${domain}`});
238
239
  }
239
240
  if (!props.private_key) {
240
- connection.transaction.results.add(plugin, {err: `missing dkim private_key for domain ${domain}`});
241
+ connection.transaction.results.add(this, {err: `missing dkim private_key for domain ${domain}`});
241
242
  }
242
243
 
243
244
  if (props.selector && props.private_key ) { // AND has correct files
@@ -246,13 +247,13 @@ exports.get_sign_properties = function (connection, done) {
246
247
  }
247
248
 
248
249
  // try [default / single domain] configuration
249
- if (plugin.cfg.main.domain && plugin.cfg.main.selector && plugin.private_key) {
250
+ if (this.cfg.main.domain && this.cfg.main.selector && this.private_key) {
250
251
 
251
- connection.transaction.results.add(plugin, {msg: 'using default key', emit: true });
252
+ connection.transaction.results.add(this, {msg: 'using default key', emit: true });
252
253
 
253
- props.domain = plugin.cfg.main.domain;
254
- props.private_key = plugin.private_key;
255
- props.selector = plugin.cfg.main.selector;
254
+ props.domain = this.cfg.main.domain;
255
+ props.private_key = this.private_key;
256
+ props.selector = this.cfg.main.selector;
256
257
 
257
258
  return done(null, props)
258
259
  }
@@ -263,7 +264,6 @@ exports.get_sign_properties = function (connection, done) {
263
264
  }
264
265
 
265
266
  exports.get_key_dir = function (connection, props, done) {
266
- const plugin = this;
267
267
 
268
268
  if (!props.domain) return done();
269
269
 
@@ -285,13 +285,12 @@ exports.get_key_dir = function (connection, props, done) {
285
285
  });
286
286
  },
287
287
  (err, results) => {
288
- connection.logdebug(plugin, results);
288
+ connection.logdebug(this, results);
289
289
  done(err, results);
290
290
  });
291
291
  }
292
292
 
293
293
  exports.has_key_data = function (conn, props) {
294
- const plugin = this;
295
294
 
296
295
  let missing = undefined;
297
296
 
@@ -308,22 +307,21 @@ exports.has_key_data = function (conn, props) {
308
307
 
309
308
  if (missing) {
310
309
  if (props.domain) {
311
- conn.lognotice(plugin, `skipped: no ${missing} for ${props.domain}`);
310
+ conn.lognotice(this, `skipped: no ${missing} for ${props.domain}`);
312
311
  }
313
312
  else {
314
- conn.lognotice(plugin, `skipped: no ${missing}`);
313
+ conn.lognotice(this, `skipped: no ${missing}`);
315
314
  }
316
315
  return false;
317
316
  }
318
317
 
319
- conn.logprotocol(plugin, `using selector: ${props.selector} at domain ${props.domain}`);
318
+ conn.logprotocol(this, `using selector: ${props.selector} at domain ${props.domain}`);
320
319
  return true;
321
320
  }
322
321
 
323
322
  exports.get_headers_to_sign = function (cfg) {
324
- const plugin = this;
325
323
 
326
- if (!cfg) cfg = plugin.cfg;
324
+ if (!cfg) cfg = this.cfg;
327
325
  if (!cfg.main.headers_to_sign) return [ 'from' ];
328
326
 
329
327
  const headers = cfg.main.headers_to_sign
@@ -338,20 +336,16 @@ exports.get_headers_to_sign = function (cfg) {
338
336
  }
339
337
 
340
338
  exports.get_sender_domain = function (connection) {
341
- const plugin = this;
342
- if (!connection.transaction) {
343
- connection.logerror(plugin, 'no transaction!')
344
- return;
345
- }
346
339
 
347
- const txn = connection.transaction;
340
+ const txn = connection?.transaction;
341
+ if (!txn) return;
348
342
 
349
343
  // fallback: use Envelope FROM when header parsing fails
350
344
  let domain;
351
345
  if (txn.mail_from.host) {
352
346
  try { domain = txn.mail_from.host.toLowerCase(); }
353
347
  catch (e) {
354
- connection.logerror(plugin, e);
348
+ connection.logerror(this, e);
355
349
  }
356
350
  }
357
351
 
@@ -371,7 +365,7 @@ exports.get_sender_domain = function (connection) {
371
365
  addrs = addrparser.parse(from_hdr);
372
366
  }
373
367
  catch (e) {
374
- connection.logerror(plugin, `address-rfc2822 failed to parse From header: ${from_hdr}`)
368
+ connection.logerror(this, `address-rfc2822 failed to parse From header: ${from_hdr}`)
375
369
  return domain;
376
370
  }
377
371
  if (!addrs || ! addrs.length) return domain;
@@ -394,7 +388,7 @@ exports.get_sender_domain = function (connection) {
394
388
  domain = (addrparser.parse(sender))[0].host().toLowerCase();
395
389
  }
396
390
  catch (e) {
397
- connection.logerror(plugin, e);
391
+ connection.logerror(this, e);
398
392
  }
399
393
  }
400
394
  return domain;
@@ -1,7 +1,7 @@
1
1
 
2
2
  const dkim = require('./dkim');
3
3
 
4
- const DKIMVerifyStream = dkim.DKIMVerifyStream;
4
+ const { DKIMVerifyStream } = dkim;
5
5
 
6
6
  const plugin = exports;
7
7
 
@@ -26,32 +26,33 @@ exports.load_config = function () {
26
26
  }
27
27
 
28
28
  exports.hook_data_post = function (next, connection) {
29
- const self = this;
30
- const txn = connection.transaction;
29
+ const txn = connection?.transaction;
30
+ if (!txn) return next();
31
+
31
32
  const verifier = new DKIMVerifyStream(this.cfg, (err, result, results) => {
32
33
  if (err) {
33
- txn.results.add(self, { err });
34
+ txn.results.add(this, { err });
34
35
  return next();
35
36
  }
36
37
  if (!results || results.length === 0) {
37
- txn.results.add(self, { skip: 'no/bad dkim signature' });
38
+ txn.results.add(this, { skip: 'no/bad dkim signature' });
38
39
  return next(CONT, 'no/bad signature')
39
40
  }
40
41
  results.forEach((res) => {
41
42
  let res_err = '';
42
43
  if (res.error) res_err = ` (${res.error})`;
43
44
  connection.auth_results(`dkim=${res.result}${res_err} header.i=${res.identity} header.d=${res.domain} header.s=${res.selector}`);
44
- connection.loginfo(self, `identity="${res.identity}" domain="${res.domain}" selector="${res.selector}" result=${res.result} ${res_err}`);
45
+ connection.loginfo(this, `identity="${res.identity}" domain="${res.domain}" selector="${res.selector}" result=${res.result} ${res_err}`);
45
46
 
46
47
  // save to ResultStore
47
48
  const rs_obj = JSON.parse(JSON.stringify(res));
48
49
  if (res.result === 'pass') { rs_obj.pass = res.domain; }
49
50
  else if (res.result === 'fail') { rs_obj.fail = res.domain + res_err; }
50
51
  else { rs_obj.err = res.domain + res_err; }
51
- txn.results.add(self, rs_obj);
52
+ txn.results.add(this, rs_obj);
52
53
  });
53
54
 
54
- connection.logdebug(self, JSON.stringify(results));
55
+ connection.logdebug(this, JSON.stringify(results));
55
56
  // Store results for other plugins
56
57
  txn.notes.dkim_results = results;
57
58
  next();
@@ -10,7 +10,6 @@ exports.redis_host = '127.0.0.1:6379';
10
10
  let redis_client;
11
11
 
12
12
  exports.lookup = function (lookup, zone, cb) {
13
- const self = this;
14
13
 
15
14
  if (!lookup || !zone) {
16
15
  return setImmediate(() => cb(new Error('missing data')));
@@ -33,26 +32,32 @@ exports.lookup = function (lookup, zone, cb) {
33
32
 
34
33
  // Build the query, adding the root dot if missing
35
34
  let query = [lookup, zone].join('.');
36
- if (query[query.length - 1] !== '.') {
35
+ if (!query.endsWith('.')) {
37
36
  query += '.';
38
37
  }
39
38
  this.logdebug(`looking up: ${query}`);
40
39
  // IS: IPv6 compatible (maybe; only if BL return IPv4 answers)
41
40
  dns.resolve(query, 'A', (err, a) => {
42
- self.stats_incr_zone(err, zone, start); // Statistics
41
+ this.stats_incr_zone(err, zone, start); // Statistics
43
42
 
44
43
  // Check for a result of 127.0.0.1 or outside 127/8
45
44
  // This should *never* happen on a proper DNS list
46
- if (a && ((!self.lookback_is_rejected && a.includes('127.0.0.1')) ||
45
+ if (a && ((!this.lookback_is_rejected && a.includes('127.0.0.1')) ||
47
46
  a.find((rec) => { return rec.split('.')[0] !== '127' }))
48
47
  ) {
49
- self.disable_zone(zone, a);
48
+ this.disable_zone(zone, a);
49
+ return cb(err, null); // Return a null A record
50
+ }
51
+
52
+ // <https://www.spamhaus.org/news/article/807/using-our-public-mirrors-check-your-return-codes-now>
53
+ if (a?.includes('127.255.255.')) {
54
+ this.disable_zone(zone, a);
50
55
  return cb(err, null); // Return a null A record
51
56
  }
52
57
 
53
58
  if (err) {
54
59
  if (err.code === dns.TIMEOUT) { // list timed out
55
- self.disable_zone(zone, err.code); // disable it
60
+ this.disable_zone(zone, err.code); // disable it
56
61
  }
57
62
  if (err.code === dns.NOTFOUND) { // unlisted
58
63
  return cb(null, a); // not an error for a DNSBL
@@ -63,38 +68,37 @@ exports.lookup = function (lookup, zone, cb) {
63
68
  }
64
69
 
65
70
  exports.stats_incr_zone = function (err, zone, start) {
66
- const plugin = this;
67
- if (!plugin.enable_stats) return;
71
+ if (!this.enable_stats) return;
68
72
 
69
73
  const rkey = `dns-list-stat:${zone}`;
70
74
  const elapsed = new Date().getTime() - start;
71
- redis_client.hincrby(rkey, 'TOTAL', 1);
75
+ redis_client.hIncrBy(rkey, 'TOTAL', 1);
72
76
  const foo = (err) ? err.code : 'LISTED';
73
- redis_client.hincrby(rkey, foo, 1);
74
- redis_client.hget(rkey, 'AVG_RT', (err2, rt) => {
75
- if (err2) return;
77
+ redis_client.hIncrBy(rkey, foo, 1);
78
+ redis_client.hGet(rkey, 'AVG_RT').then(rt => {
76
79
  const avg = parseInt(rt) ? (parseInt(elapsed) + parseInt(rt))/2
77
80
  : parseInt(elapsed);
78
- redis_client.hset(rkey, 'AVG_RT', avg);
81
+ redis_client.hSet(rkey, 'AVG_RT', avg);
79
82
  });
80
83
  }
81
84
 
82
85
  exports.init_redis = function () {
83
- const plugin = this;
84
86
  if (redis_client) { return; }
85
87
 
86
88
  const redis = require('redis');
87
- const host_port = plugin.redis_host.split(':');
89
+ const host_port = this.redis_host.split(':');
88
90
  const host = host_port[0] || '127.0.0.1';
89
91
  const port = parseInt(host_port[1], 10) || 6379;
90
92
 
91
93
  redis_client = redis.createClient(port, host);
92
- redis_client.on('error', err => {
93
- plugin.logerror(`Redis error: ${err}`);
94
- redis_client.quit();
95
- redis_client = null; // should force a reconnect
96
- // not sure if that's the right thing but better than nothing...
97
- });
94
+ redis_client.connect().then(() => {
95
+ redis_client.on('error', err => {
96
+ this.logerror(`Redis error: ${err}`);
97
+ redis_client.quit();
98
+ redis_client = null; // should force a reconnect
99
+ // not sure if that's the right thing but better than nothing...
100
+ })
101
+ })
98
102
  }
99
103
 
100
104
  exports.multi = function (lookup, zones, cb) {
@@ -108,9 +112,9 @@ exports.multi = function (lookup, zones, cb) {
108
112
  if (!self.enable_stats) return;
109
113
 
110
114
  // Statistics: check hit overlap
111
- for (let i=0; i < listed.length; i++) {
112
- const foo = (listed[i] === zone) ? 'TOTAL' : listed[i];
113
- redis_client.hincrby(`dns-list-overlap:${zone}`, foo, 1);
115
+ for (const element of listed) {
116
+ const foo = (element === zone) ? 'TOTAL' : element;
117
+ redis_client.hIncrBy(`dns-list-overlap:${zone}`, foo, 1);
114
118
  }
115
119
  }
116
120
 
@@ -149,14 +153,13 @@ exports.first = function (lookup, zones, cb, cb_each) {
149
153
  }
150
154
 
151
155
  exports.check_zones = function (interval) {
152
- const self = this;
153
156
  this.disable_allowed = true;
154
157
  if (interval) interval = parseInt(interval);
155
- if ((this.zones && this.zones.length) ||
156
- (this.disabled_zones && this.disabled_zones.length)) {
158
+ if ((this.zones?.length) ||
159
+ (this.disabled_zones?.length)) {
157
160
  let zones = [];
158
- if (this.zones && this.zones.length) zones = zones.concat(this.zones);
159
- if (this.disabled_zones && this.disabled_zones.length) {
161
+ if (this.zones?.length) zones = zones.concat(this.zones);
162
+ if (this.disabled_zones?.length) {
160
163
  zones = zones.concat(this.disabled_zones);
161
164
  }
162
165
 
@@ -165,20 +168,20 @@ exports.check_zones = function (interval) {
165
168
  this.multi('127.0.0.1', zones, (err, zone, a, pending) => {
166
169
  if (!zone) return;
167
170
 
168
- if ((!self.lookback_is_rejected && a) || (err && err.code === 'ETIMEOUT')) {
169
- return self.disable_zone(zone, ((a) ? a : err.code));
171
+ if ((!this.lookback_is_rejected && a) || (err && err.code === 'ETIMEOUT')) {
172
+ return this.disable_zone(zone, ((a) ? a : err.code));
170
173
  }
171
174
 
172
175
  // Try the test point
173
- self.lookup('127.0.0.2', zone, (err2, a2) => {
176
+ this.lookup('127.0.0.2', zone, (err2, a2) => {
174
177
  if (!a2) {
175
- self.logwarn(`zone '${zone}' did not respond to test point (${err2})`);
176
- return self.disable_zone(zone, a2);
178
+ this.logwarn(`zone '${zone}' did not respond to test point (${err2})`);
179
+ return this.disable_zone(zone, a2);
177
180
  }
178
181
  // Was this zone previously disabled?
179
- if (!self.zones.includes(zone)) {
180
- self.loginfo(`re-enabling zone ${zone}`);
181
- self.zones.push(zone);
182
+ if (!this.zones.includes(zone)) {
183
+ this.loginfo(`re-enabling zone ${zone}`);
184
+ this.zones.push(zone);
182
185
  }
183
186
  });
184
187
  });
@@ -187,16 +190,14 @@ exports.check_zones = function (interval) {
187
190
  if (interval && interval >= 5 && !this._interval) {
188
191
  this.logdebug(`will re-test list zones every ${interval} minutes`);
189
192
  this._interval = setInterval(() => {
190
- self.check_zones();
193
+ this.check_zones();
191
194
  }, (interval * 60) * 1000);
192
195
  }
193
196
  }
194
197
 
195
198
  exports.shutdown = function () {
196
199
  clearInterval(this._interval);
197
- if (redis_client) {
198
- redis_client.quit();
199
- }
200
+ if (redis_client) redis_client.quit();
200
201
  }
201
202
 
202
203
  exports.disable_zone = function (zone, result) {
@@ -209,7 +210,7 @@ exports.disable_zone = function (zone, result) {
209
210
  if (idx === -1) return false; // not enabled
210
211
 
211
212
  this.zones.splice(idx, 1);
212
- if (!(this.disabled_zones && this.disabled_zones.length)) {
213
+ if (!(this.disabled_zones?.length)) {
213
214
  this.disabled_zones = [];
214
215
  }
215
216
  if (!this.disabled_zones.includes(zone)) {