Haraka 3.0.3 → 3.0.4

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 (238) hide show
  1. package/.eslintrc.yaml +5 -9
  2. package/.prettierrc.yml +1 -0
  3. package/CONTRIBUTORS.md +11 -0
  4. package/Changes.md +1365 -1214
  5. package/Plugins.md +117 -105
  6. package/README.md +4 -13
  7. package/bin/haraka +197 -298
  8. package/config/auth_flat_file.ini +1 -0
  9. package/config/dhparams.pem +8 -0
  10. package/config/mail_from.is_resolvable.ini +4 -2
  11. package/config/me +1 -0
  12. package/config/outbound.ini +0 -2
  13. package/config/plugins +36 -35
  14. package/config/smtp.ini +0 -1
  15. package/config/smtp.json +17 -0
  16. package/config/tls_cert.pem +23 -0
  17. package/config/tls_key.pem +28 -0
  18. package/connection.js +46 -73
  19. package/contrib/bsd-rc.d/haraka +3 -1
  20. package/contrib/plugin2npm.sh +6 -36
  21. package/docs/CoreConfig.md +2 -2
  22. package/docs/Logging.md +7 -21
  23. package/docs/Outbound.md +104 -201
  24. package/docs/Plugins.md +2 -2
  25. package/docs/Transaction.md +59 -82
  26. package/docs/plugins/queue/smtp_proxy.md +5 -10
  27. package/docs/plugins/tls.md +29 -9
  28. package/endpoint.js +16 -13
  29. package/haraka.js +10 -14
  30. package/host_pool.js +5 -5
  31. package/line_socket.js +3 -4
  32. package/logger.js +44 -28
  33. package/outbound/client_pool.js +27 -23
  34. package/outbound/config.js +4 -6
  35. package/outbound/fsync_writestream.js +1 -1
  36. package/outbound/hmail.js +178 -218
  37. package/outbound/index.js +86 -99
  38. package/outbound/qfile.js +1 -1
  39. package/outbound/queue.js +51 -44
  40. package/outbound/timer_queue.js +3 -2
  41. package/outbound/tls.js +19 -7
  42. package/package.json +59 -48
  43. package/plugins/.eslintrc.yaml +0 -6
  44. package/plugins/auth/auth_base.js +4 -2
  45. package/plugins/auth/auth_proxy.js +14 -12
  46. package/plugins/auth/auth_vpopmaild.js +1 -1
  47. package/plugins/block_me.js +1 -1
  48. package/plugins/data.signatures.js +2 -4
  49. package/plugins/early_talker.js +2 -1
  50. package/plugins/mail_from.is_resolvable.js +65 -135
  51. package/plugins/queue/deliver.js +4 -5
  52. package/plugins/queue/lmtp.js +11 -14
  53. package/plugins/queue/qmail-queue.js +2 -2
  54. package/plugins/queue/quarantine.js +2 -2
  55. package/plugins/queue/rabbitmq.js +16 -17
  56. package/plugins/queue/smtp_forward.js +3 -3
  57. package/plugins/queue/smtp_proxy.js +10 -1
  58. package/plugins/queue/test.js +2 -2
  59. package/plugins/rcpt_to.host_list_base.js +5 -5
  60. package/plugins/rcpt_to.in_host_list.js +2 -2
  61. package/plugins/relay.js +6 -7
  62. package/plugins/reseed_rng.js +1 -1
  63. package/plugins/status.js +37 -33
  64. package/plugins/tls.js +2 -2
  65. package/plugins/xclient.js +3 -2
  66. package/plugins.js +50 -54
  67. package/run_tests +3 -30
  68. package/server.js +190 -190
  69. package/smtp_client.js +30 -23
  70. package/{tests → test}/config/plugins +0 -2
  71. package/{tests → test}/config/smtp.ini +1 -1
  72. package/test/config/tls/example.com/_.example.com.key +28 -0
  73. package/test/config/tls/example.com/example.com.crt +25 -0
  74. package/test/connection.js +302 -0
  75. package/test/endpoint.js +94 -0
  76. package/{tests → test}/fixtures/line_socket.js +1 -1
  77. package/{tests → test}/fixtures/util_hmailitem.js +19 -25
  78. package/{tests → test}/host_pool.js +42 -57
  79. package/test/logger.js +258 -0
  80. package/test/outbound/hmail.js +141 -0
  81. package/test/outbound/index.js +220 -0
  82. package/test/outbound/qfile.js +126 -0
  83. package/test/outbound_bounce_net_errors.js +142 -0
  84. package/{tests → test}/outbound_bounce_rfc3464.js +110 -122
  85. package/test/plugins/auth/auth_base.js +484 -0
  86. package/test/plugins/auth/auth_vpopmaild.js +83 -0
  87. package/test/plugins/early_talker.js +104 -0
  88. package/test/plugins/mail_from.is_resolvable.js +35 -0
  89. package/test/plugins/queue/smtp_forward.js +206 -0
  90. package/test/plugins/rcpt_to.host_list_base.js +122 -0
  91. package/test/plugins/rcpt_to.in_host_list.js +193 -0
  92. package/test/plugins/relay.js +303 -0
  93. package/test/plugins/status.js +130 -0
  94. package/test/plugins/tls.js +70 -0
  95. package/test/plugins.js +228 -0
  96. package/test/rfc1869.js +73 -0
  97. package/test/server.js +491 -0
  98. package/test/smtp_client.js +299 -0
  99. package/test/tls_socket.js +273 -0
  100. package/test/transaction.js +270 -0
  101. package/tls_socket.js +202 -252
  102. package/transaction.js +8 -23
  103. package/CONTRIBUTING.md +0 -1
  104. package/bin/dkimverify +0 -40
  105. package/config/access.domains +0 -13
  106. package/config/attachment.ctype.regex +0 -2
  107. package/config/attachment.filename.regex +0 -1
  108. package/config/avg.ini +0 -5
  109. package/config/bounce.ini +0 -15
  110. package/config/data.headers.ini +0 -61
  111. package/config/dkim/dkim_key_gen.sh +0 -78
  112. package/config/dkim_sign.ini +0 -4
  113. package/config/dkim_verify.ini +0 -7
  114. package/config/dnsbl.ini +0 -23
  115. package/config/greylist.ini +0 -43
  116. package/config/helo.checks.ini +0 -52
  117. package/config/messagesniffer.ini +0 -18
  118. package/config/spamassassin.ini +0 -56
  119. package/dkim.js +0 -614
  120. package/docs/plugins/avg.md +0 -35
  121. package/docs/plugins/bounce.md +0 -69
  122. package/docs/plugins/clamd.md +0 -147
  123. package/docs/plugins/esets.md +0 -8
  124. package/docs/plugins/greylist.md +0 -90
  125. package/docs/plugins/helo.checks.md +0 -135
  126. package/docs/plugins/messagesniffer.md +0 -163
  127. package/docs/plugins/spamassassin.md +0 -180
  128. package/outbound/mx_lookup.js +0 -70
  129. package/plugins/auth/auth_ldap.js +0 -3
  130. package/plugins/avg.js +0 -162
  131. package/plugins/backscatterer.js +0 -25
  132. package/plugins/bounce.js +0 -381
  133. package/plugins/clamd.js +0 -382
  134. package/plugins/data.uribl.js +0 -4
  135. package/plugins/dkim_sign.js +0 -395
  136. package/plugins/dkim_verify.js +0 -62
  137. package/plugins/dns_list_base.js +0 -221
  138. package/plugins/dnsbl.js +0 -146
  139. package/plugins/dnswl.js +0 -58
  140. package/plugins/esets.js +0 -71
  141. package/plugins/graph.js +0 -5
  142. package/plugins/greylist.js +0 -645
  143. package/plugins/helo.checks.js +0 -533
  144. package/plugins/messagesniffer.js +0 -381
  145. package/plugins/rcpt_to.ldap.js +0 -3
  146. package/plugins/rcpt_to.max_count.js +0 -24
  147. package/plugins/spamassassin.js +0 -384
  148. package/tests/config/dkim/example.com/dns +0 -29
  149. package/tests/config/dkim/example.com/private +0 -6
  150. package/tests/config/dkim/example.com/public +0 -4
  151. package/tests/config/dkim/example.com/selector +0 -1
  152. package/tests/config/dkim.private.key +0 -6
  153. package/tests/config/dkim_sign.ini +0 -4
  154. package/tests/config/helo.checks.ini +0 -52
  155. package/tests/connection.js +0 -327
  156. package/tests/endpoint.js +0 -128
  157. package/tests/fixtures/vm_harness.js +0 -59
  158. package/tests/logger.js +0 -327
  159. package/tests/outbound/hmail.js +0 -112
  160. package/tests/outbound/index.js +0 -324
  161. package/tests/outbound/qfile.js +0 -67
  162. package/tests/outbound_bounce_net_errors.js +0 -173
  163. package/tests/plugins/auth/auth_base.js +0 -463
  164. package/tests/plugins/auth/auth_vpopmaild.js +0 -91
  165. package/tests/plugins/bounce.js +0 -307
  166. package/tests/plugins/clamd.js +0 -224
  167. package/tests/plugins/deprecated/relay_acl.js +0 -140
  168. package/tests/plugins/deprecated/relay_all.js +0 -59
  169. package/tests/plugins/dkim_sign.js +0 -315
  170. package/tests/plugins/dkim_signer.js +0 -108
  171. package/tests/plugins/dns_list_base.js +0 -259
  172. package/tests/plugins/dnsbl.js +0 -101
  173. package/tests/plugins/early_talker.js +0 -115
  174. package/tests/plugins/greylist.js +0 -58
  175. package/tests/plugins/helo.checks.js +0 -525
  176. package/tests/plugins/mail_from.is_resolvable.js +0 -116
  177. package/tests/plugins/queue/smtp_forward.js +0 -221
  178. package/tests/plugins/rcpt_to.host_list_base.js +0 -132
  179. package/tests/plugins/rcpt_to.in_host_list.js +0 -218
  180. package/tests/plugins/relay.js +0 -339
  181. package/tests/plugins/spamassassin.js +0 -171
  182. package/tests/plugins/status.js +0 -138
  183. package/tests/plugins/tls.js +0 -84
  184. package/tests/plugins.js +0 -247
  185. package/tests/rfc1869.js +0 -61
  186. package/tests/server.js +0 -510
  187. package/tests/smtp_client/auth.js +0 -105
  188. package/tests/smtp_client/basic.js +0 -101
  189. package/tests/smtp_client.js +0 -80
  190. package/tests/tls_socket.js +0 -333
  191. package/tests/transaction.js +0 -284
  192. /package/docs/{plugins → deprecated}/dkim_sign.md +0 -0
  193. /package/docs/{plugins → deprecated}/dkim_verify.md +0 -0
  194. /package/docs/{plugins → deprecated}/dnsbl.md +0 -0
  195. /package/docs/{plugins → deprecated}/dnswl.md +0 -0
  196. /package/{tests → test}/.eslintrc.yaml +0 -0
  197. /package/{tests → test}/config/auth_flat_file.ini +0 -0
  198. /package/{tests → test}/config/dhparams.pem +0 -0
  199. /package/{tests → test}/config/host_list +0 -0
  200. /package/{tests → test}/config/outbound_tls_cert.pem +0 -0
  201. /package/{tests → test}/config/outbound_tls_key.pem +0 -0
  202. /package/{tests → test}/config/smtp_forward.ini +0 -0
  203. /package/{tests → test}/config/tls/ec.pem +0 -0
  204. /package/{tests → test}/config/tls/haraka.local.pem +0 -0
  205. /package/{tests → test}/config/tls/mismatched.pem +0 -0
  206. /package/{tests → test}/config/tls.ini +0 -0
  207. /package/{tests → test}/config/tls_cert.pem +0 -0
  208. /package/{tests → test}/config/tls_key.pem +0 -0
  209. /package/{tests → test}/fixtures/todo_qfile.txt +0 -0
  210. /package/{tests → test}/installation/config/test-plugin-flat +0 -0
  211. /package/{tests → test}/installation/config/test-plugin.ini +0 -0
  212. /package/{tests → test}/installation/config/tls.ini +0 -0
  213. /package/{tests → test}/installation/node_modules/load_first/index.js +0 -0
  214. /package/{tests → test}/installation/node_modules/load_first/package.json +0 -0
  215. /package/{tests → test}/installation/node_modules/test-plugin/config/test-plugin-flat +0 -0
  216. /package/{tests → test}/installation/node_modules/test-plugin/config/test-plugin.ini +0 -0
  217. /package/{tests → test}/installation/node_modules/test-plugin/package.json +0 -0
  218. /package/{tests → test}/installation/node_modules/test-plugin/test-plugin.js +0 -0
  219. /package/{tests → test}/installation/plugins/base_plugin.js +0 -0
  220. /package/{tests → test}/installation/plugins/folder_plugin/index.js +0 -0
  221. /package/{tests → test}/installation/plugins/folder_plugin/package.json +0 -0
  222. /package/{tests → test}/installation/plugins/inherits.js +0 -0
  223. /package/{tests → test}/installation/plugins/load_first.js +0 -0
  224. /package/{tests → test}/installation/plugins/plugin.js +0 -0
  225. /package/{tests → test}/installation/plugins/tls.js +0 -0
  226. /package/{tests → test}/loud/config/dhparams.pem +0 -0
  227. /package/{tests → test}/loud/config/tls/goobered.pem +0 -0
  228. /package/{tests → test}/loud/config/tls.ini +0 -0
  229. /package/{tests → test}/mail_specimen/base64-root-part.txt +0 -0
  230. /package/{tests → test}/mail_specimen/varied-fold-lengths-preserve-data.txt +0 -0
  231. /package/{tests → test}/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_fixed +0 -0
  232. /package/{tests → test}/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka +0 -0
  233. /package/{tests → test}/queue/1508269674999_1508269674999_0_34002_socVUF_1_haraka +0 -0
  234. /package/{tests → test}/queue/1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka +0 -0
  235. /package/{tests → test}/queue/multibyte +0 -0
  236. /package/{tests → test}/queue/plain +0 -0
  237. /package/{tests → test}/queue/zero-length +0 -0
  238. /package/{tests → test}/test-queue/delete-me +0 -0
package/smtp_client.js CHANGED
@@ -7,18 +7,15 @@
7
7
  // than a bunch of connections to a single host from the configuration values
8
8
  // in "host" and "port" (see host_pool.js).
9
9
 
10
- // node.js builtins
11
- const events = require('events');
10
+ const events = require('node:events');
12
11
 
13
- // npm deps
14
- const ipaddr = require('ipaddr.js');
15
- const net_utils = require('haraka-net-utils');
16
- const utils = require('haraka-utils');
12
+ const ipaddr = require('ipaddr.js');
13
+ const net_utils = require('haraka-net-utils');
14
+ const utils = require('haraka-utils');
17
15
 
18
- // haraka libs
19
- const line_socket = require('./line_socket');
20
- const logger = require('./logger');
21
- const HostPool = require('./host_pool');
16
+ const tls_socket = require('./tls_socket')
17
+ const logger = require('./logger');
18
+ const HostPool = require('./host_pool');
22
19
 
23
20
  const smtp_regexp = /^(\d{3})([ -])(.*)/;
24
21
  const STATE = {
@@ -33,7 +30,7 @@ class SMTPClient extends events.EventEmitter {
33
30
  super();
34
31
  this.uuid = utils.uuid();
35
32
  this.connect_timeout = parseInt(opts.connect_timeout) || 30;
36
- this.socket = opts.socket || line_socket.connect(opts.port, opts.host);
33
+ this.socket = opts.socket || this.get_socket(opts)
37
34
  this.socket.setTimeout(this.connect_timeout * 1000);
38
35
  this.socket.setKeepAlive(true);
39
36
  this.state = STATE.IDLE;
@@ -66,7 +63,7 @@ class SMTPClient extends events.EventEmitter {
66
63
  if (cont !== ' ') return;
67
64
 
68
65
  if (client.command === 'auth' || client.authenticating) {
69
- logger.loginfo(`SERVER RESPONSE, CLIENT ${client.command}, authenticating=${client.authenticating},code=${code},cont=${cont},msg=${msg}`);
66
+ logger.info(`SERVER RESPONSE, CLIENT ${client.command}, authenticating=${client.authenticating},code=${code},cont=${cont},msg=${msg}`);
70
67
  if (/^3/.test(code) && (
71
68
  msg === 'VXNlcm5hbWU6' ||
72
69
  msg === 'dXNlcm5hbWU6' // Workaround ill-mannered SMTP servers (namely smtp.163.com)
@@ -79,7 +76,7 @@ class SMTPClient extends events.EventEmitter {
79
76
  return;
80
77
  }
81
78
  if (/^2/.test(code) && client.authenticating) {
82
- logger.loginfo('AUTHENTICATED');
79
+ logger.info('AUTHENTICATED');
83
80
  client.authenticating = false;
84
81
  client.authenticated = true;
85
82
  client.emit('auth');
@@ -155,7 +152,7 @@ class SMTPClient extends events.EventEmitter {
155
152
  client.socket.setTimeout((opts.idle_timeout || 300) * 1000);
156
153
  if (!client.socket.remoteAddress) {
157
154
  // "Value may be undefined if the socket is destroyed"
158
- logger.logdebug('socket.remoteAddress undefined');
155
+ logger.debug('socket.remoteAddress undefined');
159
156
  return;
160
157
  }
161
158
  client.remote_ip = ipaddr.process(client.socket.remoteAddress).toString();
@@ -184,7 +181,7 @@ class SMTPClient extends events.EventEmitter {
184
181
  default:
185
182
  }
186
183
 
187
- logger.logdebug(`[smtp_client] ${errMsg} (state=${client.state})`);
184
+ logger.debug(`[smtp_client] ${errMsg} (state=${client.state})`);
188
185
  }
189
186
  }
190
187
 
@@ -220,7 +217,7 @@ class SMTPClient extends events.EventEmitter {
220
217
 
221
218
  release () {
222
219
  if (this.state === STATE.DESTROYED) return;
223
- logger.logdebug(`[smtp_client] ${this.uuid} releasing, state=${this.state}`);
220
+ logger.debug(`[smtp_client] ${this.uuid} releasing, state=${this.state}`);
224
221
 
225
222
  [
226
223
  'auth', 'bad_code', 'capabilities', 'client_protocol', 'connection-error',
@@ -243,7 +240,7 @@ class SMTPClient extends events.EventEmitter {
243
240
  upgrade (tls_options) {
244
241
 
245
242
  this.socket.upgrade(tls_options, (verified, verifyError, cert, cipher) => {
246
- logger.loginfo(`secured:${
243
+ logger.info(`secured:${
247
244
 
248
245
  (cipher) ? ` cipher=${cipher.name} version=${cipher.version}` : ''
249
246
  } verified=${verified}${
@@ -263,6 +260,16 @@ class SMTPClient extends events.EventEmitter {
263
260
  this.release();
264
261
  return true;
265
262
  }
263
+
264
+ get_socket(opts) {
265
+ const socket = tls_socket.connect({
266
+ host: opts.host,
267
+ port: opts.port,
268
+ timeout: this.connect_timeout,
269
+ })
270
+ net_utils.add_line_processor(socket)
271
+ return socket
272
+ }
266
273
  }
267
274
 
268
275
  exports.smtp_client = SMTPClient;
@@ -271,7 +278,7 @@ exports.smtp_client = SMTPClient;
271
278
  // used only in testing
272
279
  exports.get_client = (server, callback, opts = {}) => {
273
280
  const smtp_client = new SMTPClient(opts)
274
- logger.logdebug(`[smtp_client] uuid=${smtp_client.uuid} host=${opts.host} port=${opts.port} created`)
281
+ logger.debug(`[smtp_client] uuid=${smtp_client.uuid} host=${opts.host} port=${opts.port} created`)
275
282
  callback(smtp_client)
276
283
  }
277
284
 
@@ -336,7 +343,7 @@ exports.get_client_plugin = (plugin, connection, c, callback) => {
336
343
 
337
344
  const hostport = get_hostport(connection, connection.server, c);
338
345
  const smtp_client = new SMTPClient(hostport)
339
- logger.loginfo(`[smtp_client] uuid=${smtp_client.uuid} host=${hostport.host} port=${hostport.port} created`);
346
+ logger.info(`[smtp_client] uuid=${smtp_client.uuid} host=${hostport.host} port=${hostport.port} created`);
340
347
 
341
348
  connection.logdebug(plugin, `Got smtp_client: ${smtp_client.uuid}`);
342
349
 
@@ -400,7 +407,7 @@ exports.get_client_plugin = (plugin, connection, c, callback) => {
400
407
  if (!c.auth.user || !c.auth.pass) {
401
408
  throw new Error("Must include auth.user and auth.pass for PLAIN auth.");
402
409
  }
403
- logger.logdebug(`[smtp_client] uuid=${smtp_client.uuid} authenticating as "${c.auth.user}"`);
410
+ logger.debug(`[smtp_client] uuid=${smtp_client.uuid} authenticating as "${c.auth.user}"`);
404
411
  smtp_client.send_command('AUTH', `PLAIN ${utils.base64(`${c.auth.user}\0${c.auth.user}\0${c.auth.pass}`)}`);
405
412
  break;
406
413
  case 'cram-md5':
@@ -429,7 +436,7 @@ exports.get_client_plugin = (plugin, connection, c, callback) => {
429
436
  // these are the errors thrown when the connection is dead
430
437
  smtp_client.on('connection-error', (error) => {
431
438
  // error contains e.g. "Error: connect ECONNREFUSE"
432
- logger.logerror(`backend failure: ${smtp_client.host}:${smtp_client.port} - ${error}`);
439
+ logger.error(`backend failure: ${smtp_client.host}:${smtp_client.port} - ${error}`);
433
440
  const { host_pool } = connection.server.notes;
434
441
  // only exists for if forwarding_host_pool is set in the config
435
442
  if (host_pool) {
@@ -465,12 +472,12 @@ function get_hostport (connection, server, cfg) {
465
472
  const host = server.notes.host_pool.get_host();
466
473
  if (host) return host; // { host: 1.2.3.4, port: 567 }
467
474
 
468
- logger.logerror('[smtp_client] no backend hosts in pool!');
475
+ logger.error('[smtp_client] no backend hosts in pool!');
469
476
  throw new Error("no backend hosts found in pool!");
470
477
  }
471
478
 
472
479
  if (cfg.host && cfg.port) return { host: cfg.host, port: cfg.port };
473
480
 
474
- logger.logwarn("[smtp_client] forwarding_host_pool or host and port were not found in config file");
481
+ logger.warn("[smtp_client] forwarding_host_pool or host and port were not found in config file");
475
482
  throw new Error("You must specify either forwarding_host_pool or host and port");
476
483
  }
@@ -4,6 +4,4 @@ auth/flat_file
4
4
 
5
5
  rcpt_to.in_host_list
6
6
 
7
- dkim_verify
8
-
9
7
  queue/discard
@@ -6,4 +6,4 @@ nodes=0
6
6
 
7
7
  daemonize=false
8
8
 
9
- spool_dir=tests/tmp/queue
9
+ spool_dir=test/tmp/queue
@@ -0,0 +1,28 @@
1
+ -----BEGIN PRIVATE KEY-----
2
+ MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCYpxuOx4M2s6sI
3
+ iOEInjet7AcJddg/3uVoqoKRW6iTwAUKnP4CsU1Xe/rMU3wr/UcCs8zzFm7L6/cq
4
+ nbj2Pg1dnEvd5arYFWN59XhWjI6vua2ubZkTwBEEGSgSvwnBGU/9qdrXKTeuq+8C
5
+ BgV+SJzeEAmHEyhCDDT0M9Z1p5TFx0ynkaChy2yjffC2dbiRtK7RAja0I97MmSZL
6
+ ym+bVpY0ucTBNNwSNNh8UhWvWZSvVP92WGd2L72txCyfT333i0u1+SoexnJI7Wvn
7
+ 7YUaMNA3HSJ5/JjTW0/rOIZGyrfnjFEs5UmULCs6RZSE0eeEuJVRL+Kd+ncYCMlC
8
+ pmsqeIlHAgMBAAECggEAO0n8LBJVZjOWJDR1opFA8u4PNZ9tpDEATQyctbQx32Df
9
+ FGYxSf5vGaFvoVhzi+pNYEFRQsDdu5okX4ruwcUMD+Wamc6P8mksP7wVRxhEev/U
10
+ 80BiCge5FCxpIg7MzRD1voHwG01I8TCaHeEU1R2Cv8TeznWkVzLChm5zxzKVV9Mc
11
+ 08eqSxOg6M7wF3RpFpn1sBKch2ZNypu0IU4sP+NlRwxsRs/GBZqnyE/dmqvjHeCg
12
+ izcaji/ev8UdiYqASNZ2zzti9SME8VQebAWb71T6MMic03S9cfA+p3FZbzoYrnbG
13
+ pWf1AEsXiyclf6jaxd7ZV1ho6JbcaGdIPXzHZ5VIsQKBgQDLqg9M2oUBrBjJ31LN
14
+ 6OtjvMj9SO5BA5eMS/xC2j5cbb9bn8PV3Lge4IDlRMafZW/FwpAt3S68f81lHi9Y
15
+ KwK7zid8J16XFIEh+E95d0O5WOtdm1EU3T19ky8MbfCEIwcmurUusRZoyCYV/9cX
16
+ 1n9pDA3hXZtN1OcoHgOaubc+WwKBgQC/4Uh/KIz11YDcWgSO3Z37vwjh8JTtkjLg
17
+ DHRY0zOR6lzrUh5dT9nNT7TjeNxRk8ypPW5BET7T7NBTsoblN53d/nI2lI/sikg0
18
+ nOzqauXAlAX7s6tI/5LSsGF9OHZaiw7R2+egquIv4zN2Keq3Eq4WbGtcV0jiWFsC
19
+ oaOCXyCshQKBgDh2YCGFX2R0SrcEs9ckIMYY23vk0TCzBzu9ASWjjbBgOLH1G/zZ
20
+ YS4mPXXSWGJuY8tmwkQE0uUtZUsIUEXYPrzETYwM+htWcupxBc998gebkDz2R0dK
21
+ grairGN8wzZO47eoAXz9WWIZQv3MXNxd+hqsXdjB88FjKeakU4l8vUGLAoGAG7/z
22
+ AiDVMgBsoHGMUzUN0giwuixW/Xy1St3CPc5dmO6x/X5k0c3oi97JJFSoWEvtv1QZ
23
+ C+P4mCGZh2E8TQ4cEKzpy6b0oZrmEmXXhZdsHsvJibtUPDxp+Xp0vu1ZgIK34/XP
24
+ q9bK224aVS5+uXdEIg4QAMzGx6VLlDfYM9SaHxkCgYBOXl6pRe6UNhRKCfcbKH5e
25
+ JxwHbIyv2d9cL7yqy2lsYlXPhp7S8ClXChoaP5c6v1wAmY2RwPxP/7a541dzGxG5
26
+ RtUY78HHzKQGyobH55YNTyJq+qEUrP/DH5nhLJNvyV1SO7cpMnIO1p3dvnYawVzW
27
+ q+4Yrrdi6tWFPXnjWTGSjw==
28
+ -----END PRIVATE KEY-----
@@ -0,0 +1,25 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIEMTCCAxmgAwIBAgIUel2F7YVWKevz0bTFV4mbjr6ZWbIwDQYJKoZIhvcNAQEL
3
+ BQAwgaYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQH
4
+ DAdTZWF0dGxlMR0wGwYDVQQKDBRFeGFtcGxlIFdpZGdldHMgSW5jLjETMBEGA1UE
5
+ CwwKRW1haWwgVGVhbTEWMBQGA1UEAwwNKi5leGFtcGxlLmNvbTEkMCIGCSqGSIb3
6
+ DQEJARYVaGFyYWthLm1haWxAZ21haWwuY29tMCAXDTI0MDUwMjE4MzMwMVoYDzIw
7
+ NTEwOTE3MTgzMzAxWjCBpjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0
8
+ b24xEDAOBgNVBAcMB1NlYXR0bGUxHTAbBgNVBAoMFEV4YW1wbGUgV2lkZ2V0cyBJ
9
+ bmMuMRMwEQYDVQQLDApFbWFpbCBUZWFtMRYwFAYDVQQDDA0qLmV4YW1wbGUuY29t
10
+ MSQwIgYJKoZIhvcNAQkBFhVoYXJha2EubWFpbEBnbWFpbC5jb20wggEiMA0GCSqG
11
+ SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYpxuOx4M2s6sIiOEInjet7AcJddg/3uVo
12
+ qoKRW6iTwAUKnP4CsU1Xe/rMU3wr/UcCs8zzFm7L6/cqnbj2Pg1dnEvd5arYFWN5
13
+ 9XhWjI6vua2ubZkTwBEEGSgSvwnBGU/9qdrXKTeuq+8CBgV+SJzeEAmHEyhCDDT0
14
+ M9Z1p5TFx0ynkaChy2yjffC2dbiRtK7RAja0I97MmSZLym+bVpY0ucTBNNwSNNh8
15
+ UhWvWZSvVP92WGd2L72txCyfT333i0u1+SoexnJI7Wvn7YUaMNA3HSJ5/JjTW0/r
16
+ OIZGyrfnjFEs5UmULCs6RZSE0eeEuJVRL+Kd+ncYCMlCpmsqeIlHAgMBAAGjUzBR
17
+ MB0GA1UdDgQWBBSuPzKGYEvrv9ZuBlSbpgUYG2/gSjAfBgNVHSMEGDAWgBSuPzKG
18
+ YEvrv9ZuBlSbpgUYG2/gSjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA
19
+ A4IBAQCCqek5MhytpmmEjVHxfSrbSBJQ63oDa1AllqpDduWCcZZE60zOZOfrGGRO
20
+ A/JUXlJL5otPuxxJX65dJU57Y+ONbGVp9sFOT2bGrL+uwsqS7L+XyUz281sfgO0o
21
+ wsVZq6NyBVaMvtD5ViXPTZslcIZWygcvDVaUEyQ4ri8wqXNQeCNZiZikfM4U03oH
22
+ 9kgW/4IuW28Utmnt9f8vPR5XTHURX20p3Q0xhM74mZRtmldwyO3grfe25S7ZOZ5W
23
+ TFuLfxuftJNl5t1w5CdTZre7BYzbWA+Jg34eNWMiHkHE3lVMCLMqZFb8FY0TOLxw
24
+ 7YZFDrLYrA2G/alivbHs7ipj6D7i
25
+ -----END CERTIFICATE-----
@@ -0,0 +1,302 @@
1
+
2
+ const assert = require('node:assert')
3
+
4
+ const constants = require('haraka-constants');
5
+ const DSN = require('haraka-dsn')
6
+
7
+ const connection = require('../connection');
8
+ const Server = require('../server');
9
+
10
+ // huge hack here, but plugin tests need constants
11
+ constants.import(global);
12
+
13
+ const _set_up = (done) => {
14
+ this.backup = {};
15
+ const client = {
16
+ remotePort: null,
17
+ remoteAddress: null,
18
+ destroy: () => { true; },
19
+ };
20
+ const server = {
21
+ ip_address: null,
22
+ address () {
23
+ return this.ip_address;
24
+ }
25
+ }
26
+ this.connection = connection.createConnection(client, server, Server.cfg);
27
+ done()
28
+ }
29
+
30
+ describe('connection', () => {
31
+
32
+ describe('connectionRaw', () => {
33
+ beforeEach(_set_up)
34
+
35
+ it('has remote object', () => {
36
+ assert.deepEqual(this.connection.remote, {
37
+ ip: null,
38
+ port: null,
39
+ host: null,
40
+ info: null,
41
+ closed: false,
42
+ is_private: false,
43
+ is_local: false
44
+ });
45
+ })
46
+
47
+ it('has local object', () => {
48
+ assert.equal(this.connection.local.ip, null);
49
+ assert.equal(this.connection.local.port, null);
50
+ assert.ok(this.connection.local.host, this.connection.local.host);
51
+ })
52
+
53
+ it('has tls object', () => {
54
+ assert.deepEqual(this.connection.tls, {
55
+ enabled: false,
56
+ advertised: false,
57
+ verified: false,
58
+ cipher: {},
59
+ });
60
+ })
61
+
62
+ it('get_capabilities', () => {
63
+ assert.deepEqual([], this.connection.get_capabilities());
64
+ })
65
+
66
+ it('queue_msg, defined', () => {
67
+ assert.equal(
68
+ 'test message',
69
+ this.connection.queue_msg(1, 'test message')
70
+ );
71
+ })
72
+
73
+ it('queue_msg, default deny', () => {
74
+ assert.equal(
75
+ 'Message denied',
76
+ this.connection.queue_msg(DENY)
77
+ );
78
+ assert.equal(
79
+ 'Message denied',
80
+ this.connection.queue_msg(DENYDISCONNECT)
81
+ );
82
+ })
83
+
84
+ it('queue_msg, default denysoft', () => {
85
+ assert.equal(
86
+ 'Message denied temporarily',
87
+ this.connection.queue_msg(DENYSOFT)
88
+ );
89
+ assert.equal(
90
+ 'Message denied temporarily',
91
+ this.connection.queue_msg(DENYSOFTDISCONNECT)
92
+ );
93
+ })
94
+
95
+ it('queue_msg, default else', () => {
96
+ assert.equal('', this.connection.queue_msg('hello'));
97
+ })
98
+
99
+ it('has normalized connection properties', () => {
100
+ this.connection.set('remote', 'ip', '172.16.15.1');
101
+ this.connection.set('hello', 'verb', 'EHLO');
102
+ this.connection.set('tls', 'enabled', true);
103
+
104
+ assert.equal('172.16.15.1', this.connection.remote.ip);
105
+ assert.equal(null, this.connection.remote.port);
106
+ assert.equal('EHLO', this.connection.hello.verb);
107
+ assert.equal(null, this.connection.hello.host);
108
+ assert.equal(true, this.connection.tls.enabled);
109
+ })
110
+
111
+ it('sets remote.is_private and remote.is_local', () => {
112
+ assert.equal(false, this.connection.remote.is_private);
113
+ assert.equal(false, this.connection.remote.is_local);
114
+ })
115
+
116
+ it('has normalized proxy properties, default', () => {
117
+ assert.equal(false, this.connection.proxy.allowed);
118
+ assert.equal(null, this.connection.proxy.ip);
119
+ assert.equal(null, this.connection.proxy.type);
120
+ assert.equal(null, this.connection.proxy.timer);
121
+ })
122
+
123
+ it('has normalized proxy properties, set', () => {
124
+ this.connection.set('proxy', 'ip', '172.16.15.1');
125
+ this.connection.set('proxy', 'type', 'haproxy');
126
+ this.connection.set('proxy', 'timer', setTimeout(() => {}, 1000));
127
+ this.connection.set('proxy', 'allowed', true);
128
+
129
+ assert.equal(true, this.connection.proxy.allowed);
130
+ assert.equal('172.16.15.1', this.connection.proxy.ip);
131
+ assert.ok(this.connection.proxy.timer);
132
+ assert.equal(this.connection.proxy.type, 'haproxy');
133
+ })
134
+ })
135
+
136
+ describe('connectionPrivate', () => {
137
+ beforeEach((done) => {
138
+ this.backup = {};
139
+ const client = {
140
+ remotePort: 2525,
141
+ remoteAddress: '172.16.15.1',
142
+ localPort: 25,
143
+ localAddress: '172.16.15.254',
144
+ destroy: () => { true; },
145
+ };
146
+ const server = {
147
+ ip_address: '172.16.15.254',
148
+ address () {
149
+ return this.ip_address;
150
+ }
151
+ }
152
+ this.connection = connection.createConnection(client, server, Server.cfg);
153
+ done()
154
+ })
155
+
156
+ it('sets remote.is_private and remote.is_local', () => {
157
+ assert.equal(true, this.connection.remote.is_private);
158
+ assert.equal(false, this.connection.remote.is_local);
159
+ assert.equal(2525, this.connection.remote.port);
160
+ })
161
+ })
162
+
163
+
164
+ describe('connectionLocal', () => {
165
+ beforeEach((done) => {
166
+ const client = {
167
+ remotePort: 2525,
168
+ remoteAddress: '127.0.0.2',
169
+ localPort: 25,
170
+ localAddress: '172.0.0.1',
171
+ destroy: () => { true; },
172
+ };
173
+ const server = {
174
+ ip_address: '127.0.0.1',
175
+ address () {
176
+ return this.ip_address;
177
+ }
178
+ };
179
+ this.connection = connection.createConnection(client, server, Server.cfg);
180
+ done();
181
+ })
182
+
183
+ it('sets remote.is_private and remote.is_local', () => {
184
+ assert.equal(true, this.connection.remote.is_private);
185
+ assert.equal(true, this.connection.remote.is_local);
186
+ assert.equal(2525, this.connection.remote.port);
187
+ })
188
+ })
189
+
190
+
191
+ describe('get_remote', () => {
192
+ beforeEach(_set_up)
193
+
194
+ it('valid hostname', () => {
195
+ this.connection.remote.host='a.host.tld'
196
+ this.connection.remote.ip='172.16.199.198'
197
+ assert.equal(this.connection.get_remote('host'), 'a.host.tld [172.16.199.198]');
198
+ })
199
+
200
+ it('no hostname', () => {
201
+ this.connection.remote.ip='172.16.199.198'
202
+ assert.equal(this.connection.get_remote('host'), '[172.16.199.198]');
203
+ })
204
+
205
+ it('DNSERROR', () => {
206
+ this.connection.remote.host='DNSERROR'
207
+ this.connection.remote.ip='172.16.199.198'
208
+ assert.equal(this.connection.get_remote('host'), '[172.16.199.198]');
209
+ })
210
+
211
+ it('NXDOMAIN', () => {
212
+ this.connection.remote.host='NXDOMAIN'
213
+ this.connection.remote.ip='172.16.199.198'
214
+ assert.equal(this.connection.get_remote('host'), '[172.16.199.198]');
215
+ })
216
+
217
+ })
218
+
219
+ describe('local_info', () => {
220
+ beforeEach(_set_up)
221
+
222
+ it('is Haraka/version', () => {
223
+ assert.ok(/Haraka\/\d.\d/.test(this.connection.local.info), this.connection.local.info);
224
+ })
225
+ })
226
+
227
+ describe('relaying', () => {
228
+ beforeEach(_set_up)
229
+
230
+ it('sets and gets', () => {
231
+ assert.equal(this.connection.relaying, false);
232
+ assert.ok(this.connection.relaying = 'alligators');
233
+ assert.equal(this.connection.relaying, 'alligators');
234
+ })
235
+
236
+ it('sets and gets in a transaction', () => {
237
+ assert.equal(this.connection.relaying, false);
238
+ this.connection.transaction = {};
239
+ assert.ok(this.connection.relaying = 'crocodiles');
240
+ assert.equal(this.connection.transaction._relaying, 'crocodiles');
241
+ assert.equal(this.connection.relaying, 'crocodiles');
242
+ })
243
+ })
244
+
245
+ describe('get_set', () => {
246
+ beforeEach(_set_up)
247
+
248
+ it('sets single level properties', () => {
249
+ this.connection.set('encoding', true);
250
+ assert.ok(this.connection.encoding);
251
+ assert.ok(this.connection.get('encoding'));
252
+ })
253
+
254
+ it('sets two level deep properties', () => {
255
+ this.connection.set('local.host', 'test');
256
+ assert.equal(this.connection.local.host, 'test');
257
+ assert.equal(this.connection.get('local.host'), 'test');
258
+ })
259
+
260
+ it('sets three level deep properties', () => {
261
+ this.connection.set('some.fine.example', true);
262
+ assert.ok(this.connection.some.fine.example);
263
+ assert.ok(this.connection.get('some.fine.example'));
264
+ })
265
+ })
266
+
267
+ describe('respond', () => {
268
+ beforeEach(_set_up)
269
+
270
+ it('disconnected returns undefined', () => {
271
+ this.connection.state = constants.connection.state.DISCONNECTED
272
+ assert.equal(this.connection.respond(200, 'your lucky day'), undefined);
273
+ assert.equal(this.connection.respond(550, 'you are jacked'), undefined);
274
+ })
275
+
276
+ it('state=command, 200', () => {
277
+ assert.equal(this.connection.respond(200, 'you may pass Go'), '200 you may pass Go\r\n');
278
+ })
279
+
280
+ it('DSN 200', () => {
281
+ assert.equal(
282
+ this.connection.respond(200, DSN.create(200, 'you may pass Go')),
283
+ '200 2.0.0 you may pass Go\r\n'
284
+ );
285
+ })
286
+
287
+ it('DSN 550 create', () => {
288
+ // note, the DSN code overrides the response code
289
+ assert.equal(
290
+ this.connection.respond(450, DSN.create(550, 'This domain is not in use and does not accept mail')),
291
+ '550 5.0.0 This domain is not in use and does not accept mail\r\n'
292
+ );
293
+ })
294
+
295
+ it('DSN 550 addr_bad_dest_system', () => {
296
+ assert.equal(
297
+ this.connection.respond(550, DSN.addr_bad_dest_system('This domain is not in use and does not accept mail', 550)),
298
+ '550 5.1.2 This domain is not in use and does not accept mail\r\n'
299
+ );
300
+ })
301
+ })
302
+ })
@@ -0,0 +1,94 @@
1
+ const assert = require('node:assert')
2
+
3
+ const mock = require('mock-require');
4
+ const endpoint = require('../endpoint');
5
+
6
+ describe('endpoint', () => {
7
+
8
+ it('toString()', () => {
9
+ assert.equal( endpoint(25), '[::0]:25' );
10
+ assert.equal( endpoint('10.0.0.3', 42), '10.0.0.3:42' );
11
+ assert.equal( endpoint('/foo/bar.sock'), '/foo/bar.sock' );
12
+ assert.equal( endpoint('/foo/bar.sock:770'), '/foo/bar.sock:770' );
13
+ assert.equal( endpoint({address: '::0', port: 80}), '[::0]:80' );
14
+ })
15
+
16
+ describe('parse', () => {
17
+ it('Number as port', () => {
18
+ assert.deepEqual( endpoint(25), {host:'::0', port:25} );
19
+ })
20
+
21
+ it('Default port if only host', () => {
22
+ assert.deepEqual( endpoint('10.0.0.3', 42), {host:'10.0.0.3', port:42} );
23
+ })
24
+
25
+ it('Unix socket', () => {
26
+ assert.deepEqual( endpoint('/foo/bar.sock'), {path:'/foo/bar.sock'} );
27
+ })
28
+
29
+ it('Unix socket w/mode', () => {
30
+ assert.deepEqual( endpoint('/foo/bar.sock:770'), {path:'/foo/bar.sock', mode:'770'} );
31
+ })
32
+ })
33
+
34
+ describe('bind()', () => {
35
+ beforeEach((done) => {
36
+ // Mock filesystem and log server + fs method calls
37
+ const modes = this.modes = {}
38
+ const log = this.log = []
39
+
40
+ this.server = {
41
+ listen (opts, cb) {
42
+ log.push(['listen', opts]);
43
+ if (cb) cb();
44
+ }
45
+ }
46
+
47
+ this.mockfs = {
48
+ chmod (path, mode, ...args) {
49
+ log.push(['chmod', path, mode, ...args]);
50
+ modes[path] = mode;
51
+ },
52
+ rm (path, ...args) {
53
+ log.push(['rm', path, ...args]);
54
+ },
55
+ };
56
+
57
+ mock('node:fs/promises', this.mockfs);
58
+ this.endpoint = mock.reRequire('../endpoint');
59
+ done();
60
+ })
61
+
62
+ afterEach((done) => {
63
+ mock.stop('node:fs/promises');
64
+ done();
65
+ })
66
+
67
+ it('IP socket', async () => {
68
+ await this.endpoint('10.0.0.3:42').bind(this.server, {backlog:19});
69
+ assert.deepEqual(
70
+ this.log, [
71
+ ['listen', {host: '10.0.0.3', port: 42, backlog: 19}],
72
+ ]);
73
+ })
74
+
75
+ it('Unix socket', async () => {
76
+ await this.endpoint('/foo/bar.sock').bind(this.server, {readableAll:true});
77
+ assert.deepEqual(
78
+ this.log, [
79
+ ['rm', '/foo/bar.sock', {force:true}],
80
+ ['listen', {path: '/foo/bar.sock', readableAll: true}],
81
+ ]);
82
+ })
83
+
84
+ it('Unix socket w/mode', async () => {
85
+ await this.endpoint('/foo/bar.sock:764').bind(this.server);
86
+ assert.deepEqual(
87
+ this.log, [
88
+ ['rm', '/foo/bar.sock', {force:true}],
89
+ ['listen', {path: '/foo/bar.sock'}],
90
+ ['chmod', '/foo/bar.sock', 0o764],
91
+ ]);
92
+ })
93
+ })
94
+ })
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const events = require('events');
3
+ const events = require('node:events');
4
4
  const fixtures = require('haraka-test-fixtures');
5
5
  const { stub } = fixtures.stub;
6
6