Haraka 3.0.3 → 3.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/.eslintrc.yaml +4 -9
  2. package/CONTRIBUTORS.md +11 -0
  3. package/Changes.md +1397 -1213
  4. package/Plugins.md +117 -105
  5. package/README.md +4 -13
  6. package/bin/haraka +198 -298
  7. package/config/auth_flat_file.ini +1 -0
  8. package/config/dhparams.pem +8 -0
  9. package/config/mail_from.is_resolvable.ini +4 -2
  10. package/config/me +1 -0
  11. package/config/outbound.ini +0 -2
  12. package/config/plugins +35 -36
  13. package/config/smtp.ini +1 -1
  14. package/config/smtp.json +17 -0
  15. package/config/tls.ini +2 -0
  16. package/config/tls_cert.pem +23 -0
  17. package/config/tls_key.pem +28 -0
  18. package/connection.js +46 -73
  19. package/contrib/bsd-rc.d/haraka +3 -1
  20. package/contrib/plugin2npm.sh +6 -36
  21. package/docs/CoreConfig.md +2 -2
  22. package/docs/Logging.md +7 -21
  23. package/docs/Outbound.md +104 -201
  24. package/docs/Plugins.md +2 -2
  25. package/docs/Transaction.md +59 -82
  26. package/docs/plugins/queue/smtp_proxy.md +5 -10
  27. package/docs/plugins/tls.md +37 -9
  28. package/endpoint.js +16 -13
  29. package/haraka.js +10 -14
  30. package/host_pool.js +5 -5
  31. package/http/html/index.html +6 -5
  32. package/line_socket.js +3 -4
  33. package/logger.js +44 -28
  34. package/outbound/client_pool.js +27 -23
  35. package/outbound/config.js +4 -6
  36. package/outbound/fsync_writestream.js +1 -1
  37. package/outbound/hmail.js +178 -218
  38. package/outbound/index.js +86 -99
  39. package/outbound/qfile.js +1 -1
  40. package/outbound/queue.js +51 -44
  41. package/outbound/timer_queue.js +3 -2
  42. package/outbound/tls.js +19 -7
  43. package/package.json +60 -51
  44. package/plugins/.eslintrc.yaml +0 -6
  45. package/plugins/auth/auth_base.js +4 -2
  46. package/plugins/auth/auth_proxy.js +14 -12
  47. package/plugins/auth/auth_vpopmaild.js +1 -1
  48. package/plugins/block_me.js +1 -1
  49. package/plugins/data.signatures.js +2 -4
  50. package/plugins/early_talker.js +2 -1
  51. package/plugins/mail_from.is_resolvable.js +65 -135
  52. package/plugins/queue/deliver.js +4 -5
  53. package/plugins/queue/lmtp.js +11 -12
  54. package/plugins/queue/qmail-queue.js +2 -2
  55. package/plugins/queue/quarantine.js +2 -2
  56. package/plugins/queue/rabbitmq.js +16 -17
  57. package/plugins/queue/smtp_forward.js +3 -3
  58. package/plugins/queue/smtp_proxy.js +10 -1
  59. package/plugins/queue/test.js +2 -2
  60. package/plugins/rcpt_to.host_list_base.js +5 -5
  61. package/plugins/rcpt_to.in_host_list.js +2 -2
  62. package/plugins/relay.js +6 -7
  63. package/plugins/reseed_rng.js +1 -1
  64. package/plugins/status.js +37 -33
  65. package/plugins/tls.js +2 -2
  66. package/plugins/xclient.js +3 -2
  67. package/plugins.js +50 -54
  68. package/run_tests +3 -30
  69. package/server.js +190 -190
  70. package/smtp_client.js +30 -23
  71. package/{tests → test}/config/plugins +0 -2
  72. package/{tests → test}/config/smtp.ini +3 -1
  73. package/test/config/tls/example.com/_.example.com.key +28 -0
  74. package/test/config/tls/example.com/example.com.crt +25 -0
  75. package/{tests/loud → test}/config/tls.ini +4 -2
  76. package/test/connection.js +302 -0
  77. package/test/endpoint.js +94 -0
  78. package/{tests → test}/fixtures/line_socket.js +1 -1
  79. package/{tests → test}/fixtures/util_hmailitem.js +19 -25
  80. package/{tests → test}/host_pool.js +42 -57
  81. package/test/logger.js +258 -0
  82. package/test/outbound/hmail.js +141 -0
  83. package/test/outbound/index.js +220 -0
  84. package/test/outbound/qfile.js +126 -0
  85. package/test/outbound_bounce_net_errors.js +142 -0
  86. package/{tests → test}/outbound_bounce_rfc3464.js +110 -122
  87. package/test/plugins/auth/auth_base.js +484 -0
  88. package/test/plugins/auth/auth_vpopmaild.js +83 -0
  89. package/test/plugins/early_talker.js +104 -0
  90. package/test/plugins/mail_from.is_resolvable.js +35 -0
  91. package/test/plugins/queue/smtp_forward.js +206 -0
  92. package/test/plugins/rcpt_to.host_list_base.js +122 -0
  93. package/test/plugins/rcpt_to.in_host_list.js +193 -0
  94. package/test/plugins/relay.js +303 -0
  95. package/test/plugins/status.js +130 -0
  96. package/test/plugins/tls.js +70 -0
  97. package/test/plugins.js +228 -0
  98. package/test/rfc1869.js +73 -0
  99. package/test/server.js +491 -0
  100. package/test/smtp_client.js +299 -0
  101. package/test/tls_socket.js +277 -0
  102. package/test/transaction.js +270 -0
  103. package/tls_socket.js +202 -252
  104. package/transaction.js +8 -23
  105. package/CONTRIBUTING.md +0 -1
  106. package/bin/dkimverify +0 -40
  107. package/config/access.domains +0 -13
  108. package/config/attachment.ctype.regex +0 -2
  109. package/config/attachment.filename.regex +0 -1
  110. package/config/avg.ini +0 -5
  111. package/config/bounce.ini +0 -15
  112. package/config/data.headers.ini +0 -61
  113. package/config/dkim/dkim_key_gen.sh +0 -78
  114. package/config/dkim_sign.ini +0 -4
  115. package/config/dkim_verify.ini +0 -7
  116. package/config/dnsbl.ini +0 -23
  117. package/config/greylist.ini +0 -43
  118. package/config/helo.checks.ini +0 -52
  119. package/config/messagesniffer.ini +0 -18
  120. package/config/spamassassin.ini +0 -56
  121. package/dkim.js +0 -614
  122. package/docs/plugins/avg.md +0 -35
  123. package/docs/plugins/bounce.md +0 -69
  124. package/docs/plugins/clamd.md +0 -147
  125. package/docs/plugins/esets.md +0 -8
  126. package/docs/plugins/greylist.md +0 -90
  127. package/docs/plugins/helo.checks.md +0 -135
  128. package/docs/plugins/messagesniffer.md +0 -163
  129. package/docs/plugins/spamassassin.md +0 -180
  130. package/outbound/mx_lookup.js +0 -70
  131. package/plugins/auth/auth_ldap.js +0 -3
  132. package/plugins/avg.js +0 -162
  133. package/plugins/backscatterer.js +0 -25
  134. package/plugins/bounce.js +0 -381
  135. package/plugins/clamd.js +0 -382
  136. package/plugins/data.uribl.js +0 -4
  137. package/plugins/dkim_sign.js +0 -395
  138. package/plugins/dkim_verify.js +0 -62
  139. package/plugins/dns_list_base.js +0 -221
  140. package/plugins/dnsbl.js +0 -146
  141. package/plugins/dnswl.js +0 -58
  142. package/plugins/esets.js +0 -71
  143. package/plugins/graph.js +0 -5
  144. package/plugins/greylist.js +0 -645
  145. package/plugins/helo.checks.js +0 -533
  146. package/plugins/messagesniffer.js +0 -381
  147. package/plugins/rcpt_to.ldap.js +0 -3
  148. package/plugins/rcpt_to.max_count.js +0 -24
  149. package/plugins/spamassassin.js +0 -384
  150. package/tests/config/dkim/example.com/dns +0 -29
  151. package/tests/config/dkim/example.com/private +0 -6
  152. package/tests/config/dkim/example.com/public +0 -4
  153. package/tests/config/dkim/example.com/selector +0 -1
  154. package/tests/config/dkim.private.key +0 -6
  155. package/tests/config/dkim_sign.ini +0 -4
  156. package/tests/config/helo.checks.ini +0 -52
  157. package/tests/connection.js +0 -327
  158. package/tests/endpoint.js +0 -128
  159. package/tests/fixtures/vm_harness.js +0 -59
  160. package/tests/logger.js +0 -327
  161. package/tests/outbound/hmail.js +0 -112
  162. package/tests/outbound/index.js +0 -324
  163. package/tests/outbound/qfile.js +0 -67
  164. package/tests/outbound_bounce_net_errors.js +0 -173
  165. package/tests/plugins/auth/auth_base.js +0 -463
  166. package/tests/plugins/auth/auth_vpopmaild.js +0 -91
  167. package/tests/plugins/bounce.js +0 -307
  168. package/tests/plugins/clamd.js +0 -224
  169. package/tests/plugins/deprecated/relay_acl.js +0 -140
  170. package/tests/plugins/deprecated/relay_all.js +0 -59
  171. package/tests/plugins/dkim_sign.js +0 -315
  172. package/tests/plugins/dkim_signer.js +0 -108
  173. package/tests/plugins/dns_list_base.js +0 -259
  174. package/tests/plugins/dnsbl.js +0 -101
  175. package/tests/plugins/early_talker.js +0 -115
  176. package/tests/plugins/greylist.js +0 -58
  177. package/tests/plugins/helo.checks.js +0 -525
  178. package/tests/plugins/mail_from.is_resolvable.js +0 -116
  179. package/tests/plugins/queue/smtp_forward.js +0 -221
  180. package/tests/plugins/rcpt_to.host_list_base.js +0 -132
  181. package/tests/plugins/rcpt_to.in_host_list.js +0 -218
  182. package/tests/plugins/relay.js +0 -339
  183. package/tests/plugins/spamassassin.js +0 -171
  184. package/tests/plugins/status.js +0 -138
  185. package/tests/plugins/tls.js +0 -84
  186. package/tests/plugins.js +0 -247
  187. package/tests/rfc1869.js +0 -61
  188. package/tests/server.js +0 -510
  189. package/tests/smtp_client/auth.js +0 -105
  190. package/tests/smtp_client/basic.js +0 -101
  191. package/tests/smtp_client.js +0 -80
  192. package/tests/tls_socket.js +0 -333
  193. package/tests/transaction.js +0 -284
  194. /package/docs/{plugins → deprecated}/dkim_sign.md +0 -0
  195. /package/docs/{plugins → deprecated}/dkim_verify.md +0 -0
  196. /package/docs/{plugins → deprecated}/dnsbl.md +0 -0
  197. /package/docs/{plugins → deprecated}/dnswl.md +0 -0
  198. /package/{tests → test}/.eslintrc.yaml +0 -0
  199. /package/{tests → test}/config/auth_flat_file.ini +0 -0
  200. /package/{tests → test}/config/dhparams.pem +0 -0
  201. /package/{tests → test}/config/host_list +0 -0
  202. /package/{tests → test}/config/outbound_tls_cert.pem +0 -0
  203. /package/{tests → test}/config/outbound_tls_key.pem +0 -0
  204. /package/{tests → test}/config/smtp_forward.ini +0 -0
  205. /package/{tests → test}/config/tls/ec.pem +0 -0
  206. /package/{tests → test}/config/tls/haraka.local.pem +0 -0
  207. /package/{tests → test}/config/tls/mismatched.pem +0 -0
  208. /package/{tests → test}/config/tls_cert.pem +0 -0
  209. /package/{tests → test}/config/tls_key.pem +0 -0
  210. /package/{tests → test}/fixtures/todo_qfile.txt +0 -0
  211. /package/{tests → test}/installation/config/test-plugin-flat +0 -0
  212. /package/{tests → test}/installation/config/test-plugin.ini +0 -0
  213. /package/{tests → test}/installation/config/tls.ini +0 -0
  214. /package/{tests → test}/installation/node_modules/load_first/index.js +0 -0
  215. /package/{tests → test}/installation/node_modules/load_first/package.json +0 -0
  216. /package/{tests → test}/installation/node_modules/test-plugin/config/test-plugin-flat +0 -0
  217. /package/{tests → test}/installation/node_modules/test-plugin/config/test-plugin.ini +0 -0
  218. /package/{tests → test}/installation/node_modules/test-plugin/package.json +0 -0
  219. /package/{tests → test}/installation/node_modules/test-plugin/test-plugin.js +0 -0
  220. /package/{tests → test}/installation/plugins/base_plugin.js +0 -0
  221. /package/{tests → test}/installation/plugins/folder_plugin/index.js +0 -0
  222. /package/{tests → test}/installation/plugins/folder_plugin/package.json +0 -0
  223. /package/{tests → test}/installation/plugins/inherits.js +0 -0
  224. /package/{tests → test}/installation/plugins/load_first.js +0 -0
  225. /package/{tests → test}/installation/plugins/plugin.js +0 -0
  226. /package/{tests → test}/installation/plugins/tls.js +0 -0
  227. /package/{tests → test}/loud/config/dhparams.pem +0 -0
  228. /package/{tests → test}/loud/config/tls/goobered.pem +0 -0
  229. /package/{tests → test/loud}/config/tls.ini +0 -0
  230. /package/{tests → test}/mail_specimen/base64-root-part.txt +0 -0
  231. /package/{tests → test}/mail_specimen/varied-fold-lengths-preserve-data.txt +0 -0
  232. /package/{tests → test}/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_fixed +0 -0
  233. /package/{tests → test}/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka +0 -0
  234. /package/{tests → test}/queue/1508269674999_1508269674999_0_34002_socVUF_1_haraka +0 -0
  235. /package/{tests → test}/queue/1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka +0 -0
  236. /package/{tests → test}/queue/multibyte +0 -0
  237. /package/{tests → test}/queue/plain +0 -0
  238. /package/{tests → test}/queue/zero-length +0 -0
  239. /package/{tests → test}/test-queue/delete-me +0 -0
@@ -0,0 +1,141 @@
1
+
2
+ const assert = require('node:assert')
3
+ const fs = require('node:fs')
4
+ const path = require('node:path')
5
+
6
+ const Hmail = require('../../outbound/hmail');
7
+ const outbound = require('../../outbound/index');
8
+
9
+
10
+ describe('outbound/hmail', () => {
11
+ beforeEach((done) => {
12
+ this.hmail = new Hmail('1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka', 'test/queue/1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka', {});
13
+ done()
14
+ })
15
+
16
+ it('sort_mx', (done) => {
17
+ const sorted = this.hmail.sort_mx([
18
+ { exchange: 'mx2.example.com', priority: 5 },
19
+ { exchange: 'mx1.example.com', priority: 6 },
20
+ ])
21
+ assert.equal(sorted[0].exchange, 'mx2.example.com')
22
+ done()
23
+ })
24
+ it('sort_mx, shuffled', (done) => {
25
+ const sorted = this.hmail.sort_mx([
26
+ { exchange: 'mx2.example.com', priority: 5 },
27
+ { exchange: 'mx1.example.com', priority: 6 },
28
+ { exchange: 'mx3.example.com', priority: 6 },
29
+ ])
30
+ assert.equal(sorted[0].exchange, 'mx2.example.com')
31
+ assert.ok(sorted[1].exchange == 'mx3.example.com' || sorted[1].exchange == 'mx1.example.com')
32
+ done()
33
+ })
34
+ it('force_tls', (done) => {
35
+ this.hmail.todo = { domain: 'miss.example.com' }
36
+ this.hmail.obtls.cfg = { force_tls_hosts: ['1.2.3.4', 'hit.example.com'] }
37
+ assert.equal(this.hmail.get_force_tls({ exchange: '1.2.3.4' }), true)
38
+ assert.equal(this.hmail.get_force_tls({ exchange: '1.2.3.5' }), false)
39
+ this.hmail.todo = { domain: 'hit.example.com' }
40
+ assert.equal(this.hmail.get_force_tls({ exchange: '1.2.3.5' }), true)
41
+ done()
42
+ })
43
+ })
44
+
45
+ describe('outbound/hmail.HMailItem', () => {
46
+ it('normal queue file', (done) => {
47
+ this.hmail = new Hmail('1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka', 'test/queue/1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka', {});
48
+ this.hmail.on('ready', () => {
49
+ // console.log(this.hmail);
50
+ assert.ok(this.hmail)
51
+ done()
52
+ })
53
+ this.hmail.on('error', err => {
54
+ console.log(err)
55
+ assert.equal(err, undefined)
56
+ done()
57
+ })
58
+ })
59
+ it('normal TODO w/multibyte chars loads w/o error', (done) => {
60
+ this.hmail = new Hmail('1507509981169_1507509981169_0_61403_e0Y0Ym_1_qfile', 'test/fixtures/todo_qfile.txt', {});
61
+ this.hmail.on('ready', () => {
62
+ // console.log(this.hmail);
63
+ assert.ok(this.hmail)
64
+ done()
65
+ })
66
+ this.hmail.on('error', err => {
67
+ console.log(err)
68
+ assert.equal(err, undefined)
69
+ done()
70
+ })
71
+ })
72
+ it('too short TODO length declared', (done) => {
73
+ this.hmail = new Hmail('1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka', 'test/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka', {});
74
+ this.hmail.on('ready', () => {
75
+ // console.log(this.hmail);
76
+ assert.ok(this.hmail)
77
+ done();
78
+ })
79
+ this.hmail.on('error', (err) => {
80
+ console.log(err);
81
+ assert.ok(err);
82
+ done();
83
+ })
84
+ })
85
+ it('too long TODO length declared', (done) => {
86
+ this.hmail = new Hmail('1508269674999_1508269674999_0_34002_socVUF_1_haraka', 'test/queue/1508269674999_1508269674999_0_34002_socVUF_1_haraka', {});
87
+ this.hmail.on('ready', () => {
88
+ // console.log(this.hmail);
89
+ assert.ok(this.hmail)
90
+ done();
91
+ })
92
+ this.hmail.on('error', (err) => {
93
+ console.log(err);
94
+ assert.ok(err);
95
+ done();
96
+ })
97
+ })
98
+ it('zero-length file load skip w/o crash', (done) => {
99
+ this.hmail = new Hmail('1507509981169_1507509981169_0_61403_e0Y0Ym_2_zero', 'test/queue/zero-length', {});
100
+ this.hmail.on('ready', () => {
101
+ assert.ok(this.hmail)
102
+ done();
103
+ })
104
+ this.hmail.on('error', (err) => {
105
+ console.error(err);
106
+ assert.ok(err);
107
+ done();
108
+ })
109
+ })
110
+ it('lifecycle, reads and writes a haraka queue file', (done) => {
111
+
112
+ this.hmail = new Hmail('1507509981169_1507509981169_0_61403_e0Y0Ym_2_qfile', 'test/fixtures/todo_qfile.txt', {});
113
+
114
+ this.hmail.on('error', (err) => {
115
+ // console.log(err);
116
+ assert.equals(err, undefined);
117
+ done();
118
+ })
119
+
120
+ this.hmail.on('ready', () => {
121
+
122
+ const tmpfile = path.resolve('test', 'test-queue', 'delete-me');
123
+ const ws = new fs.createWriteStream(tmpfile)
124
+
125
+ outbound.build_todo(this.hmail.todo, ws, () => {
126
+ // console.log('returned from build_todo, piping')
127
+ // console.log(this.hmail.todo)
128
+ // assert.equals(this.hmail.todo.message_stream.headers.length, 22);
129
+
130
+ const ds = this.hmail.data_stream()
131
+ ds.pipe(ws, { dot_stuffing: true });
132
+
133
+ ws.on('close', () => {
134
+ // console.log(this.hmail.todo)
135
+ assert.equal(fs.statSync(tmpfile).size, 4204);
136
+ done();
137
+ })
138
+ })
139
+ })
140
+ })
141
+ })
@@ -0,0 +1,220 @@
1
+ 'use strict';
2
+
3
+ const assert = require('node:assert')
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ const constants = require('haraka-constants');
8
+ const logger = require('../../logger');
9
+
10
+ const lines = [
11
+ 'From: John Johnson <john@example.com>',
12
+ 'To: Jane Johnson <jane@example.com>',
13
+ "Subject: What's for dinner?",
14
+ '',
15
+ "I'm hungry.",
16
+ '',
17
+ ];
18
+
19
+ describe('outbound', () => {
20
+
21
+ it('converts \\n and \\r\\n line endings to \\r\\n' , () => {
22
+
23
+ for (const ending of ['\n', '\r\n']) {
24
+ let contents = lines.join(ending);
25
+ let result = '';
26
+
27
+ // Set data_lines to lines in contents
28
+ let match;
29
+ const re = /^([^\n]*\n?)/;
30
+ while ((match = re.exec(contents))) {
31
+ let line = match[1];
32
+ line = line.replace(/\r?\n?$/, '\r\n'); // assure \r\n ending
33
+ result += line;
34
+ contents = contents.substr(match[1].length);
35
+ if (contents.length === 0) {
36
+ break;
37
+ }
38
+ }
39
+
40
+ assert.deepEqual(lines.join('\r\n'), result);
41
+ }
42
+ })
43
+
44
+ it('log_methods added', () => {
45
+ const levels = ['DATA','PROTOCOL','DEBUG','INFO','NOTICE','WARN','ERROR','CRIT','ALERT','EMERG']
46
+
47
+ const HMailItem = require('../../outbound/hmail');
48
+
49
+ for (const level of levels) {
50
+ assert.ok(HMailItem.prototype[`log${level.toLowerCase()}`], `Log method for level: ${level}`);
51
+ }
52
+ })
53
+
54
+ it('set_temp_fail_intervals coverage', () => {
55
+
56
+ const config = require('../../outbound/config');
57
+ // Test default configuration
58
+ assert.deepEqual(config.cfg.temp_fail_intervals, [64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072]);
59
+ // Test a simple configuration
60
+ config.cfg.temp_fail_intervals = '10s, 1m*2';
61
+ config.set_temp_fail_intervals();
62
+ assert.deepEqual(config.cfg.temp_fail_intervals, [10, 60, 60]);
63
+ // Test a complex configuration
64
+ config.cfg.temp_fail_intervals = '30s, 1m, 5m, 9m, 15m*3, 30m*2, 1h*3, 2h*3, 1d';
65
+ config.set_temp_fail_intervals();
66
+ assert.deepEqual(config.cfg.temp_fail_intervals, [30,60,300,540,900,900,900,1800,1800,3600,3600,3600,7200,7200,7200,86400]);
67
+ // Test the "none" configuration
68
+ config.cfg.temp_fail_intervals = 'none';
69
+ config.set_temp_fail_intervals();
70
+ assert.deepEqual(config.cfg.temp_fail_intervals, []);
71
+ // Test bad config (should revert to default)
72
+ config.cfg.temp_fail_intervals = '60 min';
73
+ config.set_temp_fail_intervals();
74
+ assert.deepEqual(config.cfg.temp_fail_intervals, [64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072]);
75
+ })
76
+
77
+ describe('get_tls_options', () => {
78
+ beforeEach((done) => {
79
+ process.env.HARAKA_TEST_DIR=path.resolve('test');
80
+ this.outbound = require('../../outbound');
81
+ this.obtls = require('../../outbound/tls');
82
+ const tls_socket = require('../../tls_socket');
83
+
84
+ // reset config to load from tests directory
85
+ const testDir = path.resolve('test');
86
+ this.outbound.config = this.outbound.config.module_config(testDir);
87
+ this.obtls.test_config(tls_socket.config.module_config(testDir), this.outbound.config);
88
+ this.obtls.init(done)
89
+ })
90
+
91
+ afterEach((done) => {
92
+ delete process.env.HARAKA_TEST_DIR;
93
+ done();
94
+ })
95
+
96
+ it('gets TLS properties from tls.ini.outbound', () => {
97
+ const tls_config = this.obtls.get_tls_options(
98
+ { exchange: 'mail.example.com'}
99
+ );
100
+
101
+ assert.deepEqual(tls_config, {
102
+ servername: 'mail.example.com',
103
+ key: fs.readFileSync(path.resolve('test','config','outbound_tls_key.pem')),
104
+ cert: fs.readFileSync(path.resolve('test','config','outbound_tls_cert.pem')),
105
+ dhparam: fs.readFileSync(path.resolve('test','config','dhparams.pem')),
106
+ ciphers: 'ECDHE-RSA-AES256-GCM-SHA384',
107
+ minVersion: 'TLSv1',
108
+ rejectUnauthorized: false,
109
+ requestCert: false,
110
+ honorCipherOrder: false,
111
+ redis: { disable_for_failed_hosts: false },
112
+ no_tls_hosts: ['127.0.0.2', '192.168.31.1/24'],
113
+ force_tls_hosts: ['first.example.com', 'second.example.net']
114
+ })
115
+ })
116
+ })
117
+
118
+ describe('build_todo', () => {
119
+ beforeEach((done) => {
120
+ this.outbound = require('../../outbound');
121
+ try {
122
+ fs.unlinkSync('test/queue/multibyte');
123
+ fs.unlinkSync('test/queue/plain');
124
+ }
125
+ catch (ignore) {}
126
+ done();
127
+ })
128
+
129
+ it('saves a file', () => {
130
+ const todo = JSON.parse('{"queue_time":1507509981169,"domain":"redacteed.com","rcpt_to":[{"original":"<postmaster@redacteed.com>","original_host":"redacteed.com","host":"redacteed.com","user":"postmaster"}],"mail_from":{"original":"<matt@tnpi.net>","original_host":"tnpi.net","host":"tnpi.net","user":"matt"},"notes":{"authentication_results":["spf=pass smtp.mailfrom=tnpi.net"],"spf_mail_result":"Pass","spf_mail_record":"v=spf1 a mx include:mx.theartfarm.com ?include:forwards._spf.tnpi.net include:lists._spf.tnpi.net -all","attachment_count":0,"attachments":[{"ctype":"application/pdf","filename":"FileWithoutAccent Chars.pdf","extension":".pdf","md5":"6c1d5f5c047cff3f6320b1210970bdf6"}],"attachment_ctypes":["application/pdf","multipart/mixed","text/plain","application/pdf"],"attachment_files":["FileWithoutaccent Chars.pdf"],"attachment_archive_files":[]},"uuid":"1D5483B0-3E00-4280-A961-3AFD2017B4FC.1"}');
131
+ const fd = fs.openSync('test/queue/plain', 'w');
132
+ const ws = new fs.createWriteStream('test/queue/plain', { fd, flags: constants.WRITE_EXCL });
133
+ ws.on('close', () => {
134
+ // console.log(arguments);
135
+ assert.ok(1);
136
+ })
137
+ ws.on('error', (e) => {
138
+ console.error(e);
139
+ })
140
+ this.outbound.build_todo(todo, ws, () => {
141
+ ws.write(Buffer.from('This is the message body'));
142
+ fs.fsync(fd, () => { ws.close(); })
143
+ })
144
+ })
145
+
146
+ it('saves a file with multibyte chars', () => {
147
+ const todo = JSON.parse('{"queue_time":1507509981169,"domain":"redacteed.com","rcpt_to":[{"original":"<postmaster@redacteed.com>","original_host":"redacteed.com","host":"redacteed.com","user":"postmaster"}],"mail_from":{"original":"<matt@tnpi.net>","original_host":"tnpi.net","host":"tnpi.net","user":"matt"},"notes":{"authentication_results":["spf=pass smtp.mailfrom=tnpi.net"],"spf_mail_result":"Pass","spf_mail_record":"v=spf1 a mx include:mx.theartfarm.com ?include:forwards._spf.tnpi.net include:lists._spf.tnpi.net -all","attachment_count":0,"attachments":[{"ctype":"application/pdf","filename":"FileWîthÁccent Chars.pdf","extension":".pdf","md5":"6c1d5f5c047cff3f6320b1210970bdf6"}],"attachment_ctypes":["application/pdf","multipart/mixed","text/plain","application/pdf"],"attachment_files":["FileWîthÁccent Chars.pdf"],"attachment_archive_files":[]},"uuid":"1D5483B0-3E00-4280-A961-3AFD2017B4FC.1"}');
148
+ const fd = fs.openSync('test/queue/multibyte', 'w');
149
+ const ws = new fs.WriteStream('test/queue/multibyte', { fd, flags: constants.WRITE_EXCL });
150
+ ws.on('close', () => {
151
+ assert.ok(1);
152
+ })
153
+ ws.on('error', (e) => {
154
+ console.error(e);
155
+ })
156
+ this.outbound.build_todo(todo, ws, () => {
157
+ ws.write(Buffer.from('This is the message body'));
158
+ fs.fsync(fd, () => { ws.close(); })
159
+ })
160
+ })
161
+ })
162
+
163
+ describe('timer_queue', () => {
164
+ beforeEach((done) => {
165
+ process.env.HARAKA_TEST_DIR=path.resolve('test');
166
+ this.outbound = require('../../outbound');
167
+ const TimerQueue = require('../../outbound/timer_queue');
168
+ this.ob_timer_queue = new TimerQueue(500);
169
+ done();
170
+ })
171
+
172
+ afterEach((done) => {
173
+ delete process.env.HARAKA_TEST_DIR;
174
+ this.ob_timer_queue.shutdown();
175
+ done()
176
+ })
177
+
178
+ it('has initial length of 0', () => {
179
+ assert.equal(this.ob_timer_queue.length(), 0);
180
+ })
181
+
182
+ it('can add items', () => {
183
+ this.ob_timer_queue.add("1", 1000);
184
+ this.ob_timer_queue.add("2", 2000);
185
+
186
+ assert.equal(this.ob_timer_queue.length(), 2);
187
+ })
188
+
189
+ it('can drain items', () => {
190
+
191
+ this.ob_timer_queue.add("1", 1000);
192
+ this.ob_timer_queue.add("2", 2000);
193
+
194
+ let tq_length = this.ob_timer_queue.length();
195
+
196
+ assert.equal(tq_length, 2);
197
+
198
+ this.ob_timer_queue.drain();
199
+ tq_length = this.ob_timer_queue.length();
200
+
201
+ assert.equal(tq_length, 0);
202
+ })
203
+
204
+ it('can discard items by id', () => {
205
+
206
+ this.ob_timer_queue.add("1", 1000);
207
+ this.ob_timer_queue.add("2", 2000);
208
+
209
+ let tq_length = this.ob_timer_queue.length();
210
+
211
+ assert.equal(tq_length, 2);
212
+
213
+ this.ob_timer_queue.discard("2");
214
+ tq_length = this.ob_timer_queue.length();
215
+
216
+ assert.equal(tq_length, 1);
217
+ assert.equal(this.ob_timer_queue.queue[0].id, "1");
218
+ })
219
+ })
220
+ })
@@ -0,0 +1,126 @@
1
+ const assert = require('node:assert')
2
+ const os = require('node:os');
3
+
4
+ describe('qfile', () => {
5
+
6
+ describe('qfile', () => {
7
+ beforeEach((done) => {
8
+ this.qfile = require('../../outbound/qfile')
9
+ done();
10
+ })
11
+
12
+ it('name() basic functions', () => {
13
+ const name = this.qfile.name();
14
+ const split = name.split('_');
15
+ assert.equal(split.length, 7);
16
+ assert.equal(split[2], 0);
17
+ assert.equal(split[3], process.pid);
18
+ })
19
+
20
+ it('name() with overrides', () => {
21
+ const overrides = {
22
+ arrival : 12345,
23
+ next_attempt : 12345,
24
+ attempts : 15,
25
+ pid : process.pid,
26
+ uid : 'XXYYZZ',
27
+ host : os.hostname(),
28
+ };
29
+ const name = this.qfile.name(overrides);
30
+ const split = name.split('_');
31
+ assert.equal(split.length, 7);
32
+ assert.equal(split[0], overrides.arrival);
33
+ assert.equal(split[1], overrides.next_attempt);
34
+ assert.equal(split[2], overrides.attempts);
35
+ assert.equal(split[3], overrides.pid);
36
+ assert.equal(split[4], overrides.uid);
37
+ assert.equal(split[6], overrides.host);
38
+ })
39
+
40
+ it('rnd_unique() is unique-ish', () => {
41
+ const repeats = 1000;
42
+ const u = this.qfile.rnd_unique();
43
+ for (let i = 0; i < repeats; i++){
44
+ assert.notEqual(u, this.qfile.rnd_unique());
45
+ }
46
+ })
47
+ })
48
+
49
+ describe('parts', () => {
50
+
51
+ it('parts() updates previous queue filenames', () => {
52
+ // $nextattempt_$attempts_$pid_$uniq.$host
53
+ const name = "1111_0_2222_3333.foo.example.com"
54
+ const parts = this.qfile.parts(name);
55
+ assert.equal(parts.next_attempt, 1111);
56
+ assert.equal(parts.attempts, 0);
57
+ assert.equal(parts.pid, 2222);
58
+ assert.equal(parts.host, 'foo.example.com');
59
+ })
60
+
61
+ it('parts() handles standard queue filenames', () => {
62
+ const overrides = {
63
+ arrival : 12345,
64
+ next_attempt : 12345,
65
+ attempts : 15,
66
+ pid : process.pid,
67
+ uid : 'XXYYZZ',
68
+ host : os.hostname(),
69
+ };
70
+ const name = this.qfile.name(overrides);
71
+ const parts = this.qfile.parts(name);
72
+ assert.equal(parts.arrival, overrides.arrival);
73
+ assert.equal(parts.next_attempt, overrides.next_attempt);
74
+ assert.equal(parts.attempts, overrides.attempts);
75
+ assert.equal(parts.pid, overrides.pid);
76
+ assert.equal(parts.uid, overrides.uid);
77
+ assert.equal(parts.host, overrides.host);
78
+ })
79
+
80
+ it('handles 4', () => {
81
+ const r = this.qfile.parts('1484878079415_0_12345_8888.mta1.example.com')
82
+ delete r.arrival
83
+ delete r.uid
84
+ delete r.counter
85
+ assert.deepEqual(r, {
86
+ next_attempt: 1484878079415,
87
+ attempts: 0,
88
+ pid: 12345,
89
+ host: 'mta1.example.com',
90
+ age: 0
91
+ })
92
+ })
93
+
94
+ it('handles 7', () => {
95
+ const r = this.qfile.parts('1516650518128_1516667073032_8_29538_TkPZWz_1_haraka')
96
+ delete r.age;
97
+ assert.deepEqual(r, {
98
+ arrival: 1516650518128,
99
+ next_attempt: 1516667073032,
100
+ attempts: 8,
101
+ pid: 29538,
102
+ uid: 'TkPZWz',
103
+ counter: 1,
104
+ host: 'haraka',
105
+ })
106
+ })
107
+
108
+ it('punts on 5', () => {
109
+ assert.deepEqual(this.qfile.parts('1516650518128_1516667073032_8_29538_TkPZWz'), null)
110
+ })
111
+ })
112
+
113
+ describe('hostname', () => {
114
+ it('hostname, defaults to os.hostname()', () => {
115
+ assert.deepEqual(this.qfile.hostname(), require('os').hostname())
116
+ })
117
+
118
+ it('hostname, replaces \\ char', () => {
119
+ assert.deepEqual(this.qfile.hostname('mt\\a1.exam\\ple.com'), 'mt\\057a1.exam\\057ple.com')
120
+ })
121
+
122
+ it('hostname, replaces _ char', () => {
123
+ assert.deepEqual(this.qfile.hostname('mt_a1.exam_ple.com'), 'mt\\137a1.exam\\137ple.com')
124
+ })
125
+ })
126
+ })
@@ -0,0 +1,142 @@
1
+ 'use strict';
2
+
3
+ // Testing bounce email contents related to errors occuring during SMTP dialog
4
+
5
+ // About running the tests:
6
+ // - Making a folder for queuing files
7
+ // - Creating a HMailItem instance using fixtures/util_hmailitem
8
+ // - Talk some STMP in the playbook
9
+ // - Test the outcome by replacing trigger functions with our testing code (outbound.send_email, HMailItem.temp_fail, ...)
10
+
11
+ const assert = require('node:assert')
12
+ const dns = require('node:dns');
13
+ const fs = require('node:fs');
14
+ const path = require('node:path');
15
+
16
+ const constants = require('haraka-constants');
17
+ const util_hmailitem = require('./fixtures/util_hmailitem');
18
+ const TODOItem = require('../outbound/todo');
19
+ const HMailItem = require('../outbound/hmail');
20
+ const outbound = require('../outbound');
21
+
22
+ const outbound_context = {
23
+ TODOItem,
24
+ exports: outbound
25
+ }
26
+
27
+ const queue_dir = path.resolve(__dirname, 'test-queue');
28
+
29
+ describe('outbound_bounce_net_errors', () => {
30
+ beforeEach((done) => {
31
+ fs.exists(queue_dir, exists => {
32
+ if (exists) {
33
+ done();
34
+ }
35
+ else {
36
+ fs.mkdir(queue_dir, done)
37
+ }
38
+ });
39
+ })
40
+
41
+ afterEach((done) => {
42
+ fs.exists(queue_dir, (exists) => {
43
+ if (exists) {
44
+ for (const file of fs.readdirSync(queue_dir)) {
45
+ const curPath = path.resolve(queue_dir, file);
46
+ if (fs.lstatSync(curPath).isDirectory()) {
47
+ console.error(`did not expect an sub folder here ("${curPath}")! cancel`)
48
+ }
49
+ fs.unlinkSync(curPath);
50
+ }
51
+ }
52
+ done()
53
+ })
54
+ })
55
+
56
+ it('test get-mx-deny triggers bounce(...)', (done) => {
57
+ util_hmailitem.newMockHMailItem(outbound_context, done, {}, mock_hmail => {
58
+ const orig_bounce = HMailItem.prototype.bounce;
59
+ HMailItem.prototype.bounce = function (err, opts) {
60
+ assert.ok(true, 'get_mx=DENY: bounce function called');
61
+ /* dsn_code: 550,
62
+ dsn_status: '5.1.2',
63
+ dsn_action: 'failed' */
64
+ assert.equal('5.1.2', this.todo.rcpt_to[0].dsn_status, 'get_mx=DENY dsn status = 5.1.2');
65
+ done()
66
+ };
67
+ mock_hmail.domain = mock_hmail.todo.domain;
68
+ HMailItem.prototype.get_mx_respond.apply(mock_hmail, [constants.deny, {}]);
69
+ HMailItem.prototype.bounce = orig_bounce;
70
+ })
71
+ })
72
+
73
+ it('test get-mx-denysoft triggers temp_fail(...)', (done) => {
74
+ util_hmailitem.newMockHMailItem(outbound_context, done, {}, mock_hmail => {
75
+ const orig_temp_fail = HMailItem.prototype.temp_fail;
76
+ HMailItem.prototype.temp_fail = function (err, opts) {
77
+ assert.ok(true, 'get_mx-DENYSOFT: temp_fail function called');
78
+ /*dsn_code: 450,
79
+ dsn_status: '4.1.2',
80
+ dsn_action: 'delayed' */
81
+ assert.equal('4.1.2', this.todo.rcpt_to[0].dsn_status, 'get_mx=DENYSOFT dsn status = 4.1.2');
82
+ done()
83
+ };
84
+ mock_hmail.domain = mock_hmail.todo.domain;
85
+ HMailItem.prototype.get_mx_respond.apply(mock_hmail, [constants.denysoft, {}]);
86
+ HMailItem.prototype.temp_fail = orig_temp_fail;
87
+ })
88
+ })
89
+
90
+ it('test found_mx({code:dns.NXDOMAIN}) triggers bounce(...)', (done) => {
91
+ util_hmailitem.newMockHMailItem(outbound_context, done, {}, mock_hmail => {
92
+ const orig_bounce = HMailItem.prototype.bounce;
93
+ HMailItem.prototype.bounce = function (err, opts) {
94
+ assert.ok(true, 'get_mx_error({code: dns.NXDOMAIN}): bounce function called');
95
+ assert.equal('5.1.2', this.todo.rcpt_to[0].dsn_status, 'get_mx_error({code: dns.NXDOMAIN}: dsn status = 5.1.2');
96
+ done()
97
+ };
98
+ HMailItem.prototype.get_mx_error.apply(mock_hmail, [{code: dns.NXDOMAIN}]);
99
+ HMailItem.prototype.bounce = orig_bounce;
100
+ });
101
+ })
102
+
103
+ it('test get_mx_error({code:\'SOME-OTHER-ERR\'}) triggers temp_fail(...)', (done) => {
104
+ util_hmailitem.newMockHMailItem(outbound_context, done, {}, mock_hmail => {
105
+ const orig_temp_fail = HMailItem.prototype.temp_fail;
106
+ HMailItem.prototype.temp_fail = function (err, opts) {
107
+ assert.ok(true, 'get_mx_error({code: "SOME-OTHER-ERR"}): temp_fail function called');
108
+ assert.equal('4.1.0', this.todo.rcpt_to[0].dsn_status, 'get_mx_error({code: "SOME-OTHER-ERR"}: dsn status = 4.1.0');
109
+ done()
110
+ };
111
+ HMailItem.prototype.get_mx_error.apply(mock_hmail, [{code: 'SOME-OTHER-ERR'}, {}]);
112
+ HMailItem.prototype.temp_fail = orig_temp_fail;
113
+ });
114
+ })
115
+
116
+ it('test found_mx(null, [{priority:0,exchange:\'\'}]) triggers bounce(...)', (done) => {
117
+ util_hmailitem.newMockHMailItem(outbound_context, done, {}, mock_hmail => {
118
+ const orig_bounce = HMailItem.prototype.bounce;
119
+ HMailItem.prototype.bounce = function (err, opts) {
120
+ assert.ok(true, 'found_mx(null, [{priority:0,exchange:""}]): bounce function called');
121
+ assert.equal('5.1.2', this.todo.rcpt_to[0].dsn_status, 'found_mx(null, [{priority:0,exchange:""}]): dsn status = 5.1.2');
122
+ done()
123
+ };
124
+ HMailItem.prototype.found_mx.apply(mock_hmail, [[{priority:0,exchange:''}]]);
125
+ HMailItem.prototype.bounce = orig_bounce;
126
+ });
127
+ })
128
+
129
+ it('test try_deliver while hmail.mxlist=[] triggers bounce(...)', (done) => {
130
+ util_hmailitem.newMockHMailItem(outbound_context, done, {}, mock_hmail => {
131
+ mock_hmail.mxlist = [];
132
+ const orig_temp_fail = HMailItem.prototype.temp_fail;
133
+ HMailItem.prototype.temp_fail = function (err, opts) {
134
+ assert.ok(true, 'try_deliver while hmail.mxlist=[]: temp_fail function called');
135
+ assert.equal('5.1.2', this.todo.rcpt_to[0].dsn_status, 'try_deliver while hmail.mxlist=[]: dsn status = 5.1.2');
136
+ done()
137
+ };
138
+ HMailItem.prototype.try_deliver.apply(mock_hmail, []);
139
+ HMailItem.prototype.temp_fail = orig_temp_fail;
140
+ });
141
+ })
142
+ })