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
package/plugins/spf.js DELETED
@@ -1,327 +0,0 @@
1
- // spf
2
-
3
- const SPF = require('./spf').SPF;
4
- const net_utils = require('haraka-net-utils');
5
- const DSN = require('haraka-dsn');
6
-
7
- exports.register = function () {
8
- const plugin = this;
9
-
10
- // Override logging in SPF module
11
- SPF.prototype.log_debug = str => plugin.logdebug(str);
12
-
13
- plugin.load_spf_ini();
14
-
15
- plugin.register_hook('helo', 'helo_spf');
16
- plugin.register_hook('ehlo', 'helo_spf');
17
- }
18
-
19
- exports.load_spf_ini = function () {
20
- const plugin = this;
21
- plugin.nu = net_utils; // so tests can set public_ip
22
- plugin.SPF = SPF;
23
-
24
- plugin.cfg = plugin.config.get('spf.ini', {
25
- booleans: [
26
- '-defer.helo_temperror',
27
- '-defer.mfrom_temperror',
28
-
29
- '-defer_relay.helo_temperror',
30
- '-defer_relay.mfrom_temperror',
31
-
32
- '-deny.helo_none',
33
- '-deny.helo_softfail',
34
- '-deny.helo_fail',
35
- '-deny.helo_permerror',
36
- '-deny.openspf_text',
37
-
38
- '-deny.mfrom_none',
39
- '-deny.mfrom_softfail',
40
- '-deny.mfrom_fail',
41
- '-deny.mfrom_permerror',
42
-
43
- '-deny_relay.helo_none',
44
- '-deny_relay.helo_softfail',
45
- '-deny_relay.helo_fail',
46
- '-deny_relay.helo_permerror',
47
-
48
- '-deny_relay.mfrom_none',
49
- '-deny_relay.mfrom_softfail',
50
- '-deny_relay.mfrom_fail',
51
- '-deny_relay.mfrom_permerror',
52
- '-deny_relay.openspf_text',
53
-
54
- '-skip.relaying',
55
- '-skip.auth',
56
- ]
57
- },
58
- () => { plugin.load_spf_ini(); }
59
- );
60
-
61
- // when set, preserve legacy config settings
62
- ['helo','mail'].forEach(phase => {
63
- if (plugin.cfg.main[`${phase}_softfail_reject`]) {
64
- plugin.cfg.deny[`${phase}_softfail`] = true;
65
- }
66
- if (plugin.cfg.main[`${phase}_fail_reject`]) {
67
- plugin.cfg.deny[`${phase}_fail`] = true;
68
- }
69
- if (plugin.cfg.main[`${phase}_temperror_defer`]) {
70
- plugin.cfg.defer[`${phase}_temperror`] = true;
71
- }
72
- if (plugin.cfg.main[`${phase}_permerror_reject`]) {
73
- plugin.cfg.deny[`${phase}_permerror`] = true;
74
- }
75
- });
76
-
77
- if (!plugin.cfg.relay) {
78
- plugin.cfg.relay = { context: 'sender' }; // default/legacy
79
- }
80
-
81
- plugin.cfg.lookup_timeout = plugin.cfg.main.lookup_timeout || plugin.timeout - 1;
82
- }
83
-
84
- exports.helo_spf = function (next, connection, helo) {
85
- const plugin = this;
86
-
87
- // bypass auth'ed or relay'ing hosts if told to
88
- const skip_reason = exports.skip_hosts(connection);
89
- if (skip_reason) {
90
- connection.results.add(plugin, {skip: `helo(${skip_reason})`});
91
- return next();
92
- }
93
-
94
- // Bypass private IPs
95
- if (connection.remote.is_private) {
96
- connection.results.add(plugin, {skip: 'helo(private_ip)'});
97
- return next();
98
- }
99
-
100
- // RFC 4408, 2.1: "SPF clients must be prepared for the "HELO"
101
- // identity to be malformed or an IP address literal.
102
- if (net_utils.is_ip_literal(helo)) {
103
- connection.results.add(plugin, {skip: 'helo(ip_literal)'});
104
- return next();
105
- }
106
-
107
- // avoid 2nd EHLO evaluation if EHLO host is identical
108
- const results = connection.results.get(plugin);
109
- if (results && results.domain === helo) return next();
110
-
111
- let timeout = false;
112
- const spf = new SPF();
113
- const timer = setTimeout(() => {
114
- timeout = true;
115
- connection.loginfo(plugin, 'timeout');
116
- return next();
117
- }, plugin.cfg.lookup_timeout * 1000);
118
-
119
- spf.check_host(connection.remote.ip, helo, null, (err, result) => {
120
- if (timer) clearTimeout(timer);
121
- if (timeout) return;
122
- if (err) {
123
- connection.logerror(plugin, err);
124
- return next();
125
- }
126
- const host = connection.hello.host;
127
- plugin.log_result(connection, 'helo', host, `postmaster@${host}`, spf.result(result));
128
-
129
- connection.notes.spf_helo = result; // used between hooks
130
- connection.results.add(plugin, {
131
- scope: 'helo',
132
- result: spf.result(result),
133
- domain: host,
134
- emit: true,
135
- });
136
- if (spf.result(result) === 'Pass') connection.results.add(plugin, { pass: host });
137
- next();
138
- });
139
- }
140
-
141
- exports.hook_mail = function (next, connection, params) {
142
- const plugin = this;
143
-
144
- const txn = connection.transaction;
145
- if (!txn) return next();
146
-
147
- // bypass auth'ed or relay'ing hosts if told to
148
- const skip_reason = exports.skip_hosts(connection);
149
- if (skip_reason) {
150
- txn.results.add(plugin, {skip: `host(${skip_reason})`});
151
- return next(CONT, `skipped because host(${skip_reason})`);
152
- }
153
-
154
- // For messages from private IP space...
155
- if (connection.remote.is_private) {
156
- if (!connection.relaying) return next();
157
- if (connection.relaying && plugin.cfg.relay.context !== 'myself') {
158
- txn.results.add(plugin, {skip: 'host(private_ip)'});
159
- return next(CONT, 'envelope from private IP space');
160
- }
161
- }
162
-
163
- const mfrom = params[0].address();
164
- const host = params[0].host;
165
- let spf = new SPF();
166
- let auth_result;
167
-
168
- if (connection.notes.spf_helo) {
169
- const h_result = connection.notes.spf_helo;
170
- const h_host = connection.hello.host;
171
- plugin.save_to_header(connection, spf, h_result, mfrom, h_host, 'helo');
172
- if (!host) { // Use results from HELO if the return-path is null
173
- auth_result = spf.result(h_result).toLowerCase();
174
- connection.auth_results(`spf=${auth_result} smtp.helo=${h_host}`);
175
-
176
- const sender = `<> via ${h_host}`;
177
- return plugin.return_results(next, connection, spf, 'helo', h_result, sender);
178
- }
179
- }
180
-
181
- if (!host) return next(); // null-sender
182
-
183
- let timeout = false;
184
- const timer = setTimeout(() => {
185
- timeout = true;
186
- connection.loginfo(plugin, 'timeout');
187
- next();
188
- }, plugin.cfg.lookup_timeout * 1000);
189
-
190
- spf.helo = connection.hello.host;
191
-
192
- function ch_cb (err, result, ip) {
193
- if (timer) clearTimeout(timer);
194
- if (timeout) return;
195
- if (err) {
196
- connection.logerror(plugin, err);
197
- return next();
198
- }
199
- plugin.log_result(connection, 'mfrom', host, mfrom, spf.result(result), (ip ? ip : connection.remote.ip));
200
- plugin.save_to_header(connection, spf, result, mfrom, host, 'mailfrom', (ip ? ip : connection.remote.ip));
201
-
202
- auth_result = spf.result(result).toLowerCase();
203
- connection.auth_results(`spf=${auth_result} smtp.mailfrom=${host}`);
204
-
205
- txn.notes.spf_mail_result = spf.result(result);
206
- txn.notes.spf_mail_record = spf.spf_record;
207
- txn.results.add(plugin, {
208
- scope: 'mfrom',
209
- result: spf.result(result),
210
- domain: host,
211
- emit: true,
212
- });
213
- if (spf.result(result) === 'Pass') connection.results.add(plugin, { pass: host });
214
- plugin.return_results(next, connection, spf, 'mfrom', result, mfrom);
215
- }
216
-
217
- // typical inbound (!relay)
218
- if (!connection.relaying) {
219
- return spf.check_host(connection.remote.ip, host, mfrom, ch_cb);
220
- }
221
-
222
- // outbound (relaying), context=sender
223
- if (plugin.cfg.relay.context === 'sender') {
224
- return spf.check_host(connection.remote.ip, host, mfrom, ch_cb);
225
- }
226
-
227
- // outbound (relaying), context=myself
228
- net_utils.get_public_ip((e, my_public_ip) => {
229
- // We always check the client IP first, because a relay
230
- // could be sending inbound mail from a non-local domain
231
- // which could case an incorrect SPF Fail result if we
232
- // check the public IP first, so we only check the public
233
- // IP if the client IP returns a result other than 'Pass'.
234
- spf.check_host(connection.remote.ip, host, mfrom, (err, result) => {
235
- let spf_result;
236
- if (result) {
237
- spf_result = spf.result(result).toLowerCase();
238
- }
239
- if (err || (spf_result && spf_result !== 'pass')) {
240
- if (e) return ch_cb(e); // Error looking up public IP
241
-
242
- if (!my_public_ip) {
243
- return ch_cb(new Error(`failed to discover public IP`));
244
- }
245
- spf = new SPF();
246
- spf.check_host(my_public_ip, host, mfrom, (er, r) => {
247
- ch_cb(er, r, my_public_ip);
248
- });
249
- return;
250
- }
251
- ch_cb(err, result, connection.remote.ip);
252
- });
253
- });
254
- }
255
-
256
- exports.log_result = function (connection, scope, host, mfrom, result, ip) {
257
- const show_ip=ip ? ip : connection.remote.ip;
258
- connection.loginfo(this, `identity=${scope} ip=${show_ip} domain="${host}" mfrom=<${mfrom}> result=${result}`);
259
- }
260
-
261
- exports.return_results = function (next, connection, spf, scope, result, sender) {
262
- const plugin = this;
263
- const msgpre = (scope === 'helo') ? `sender ${sender}` : `sender <${sender}>`;
264
- const deny = connection.relaying ? 'deny_relay' : 'deny';
265
- const defer = connection.relaying ? 'defer_relay' : 'defer';
266
- const sender_id = (scope === 'helo') ? connection.hello_host : sender;
267
- let text = DSN.sec_unauthorized(`http://www.openspf.org/Why?s=${scope}&id=${sender_id}&ip=${connection.remote.ip}`);
268
-
269
- switch (result) {
270
- case spf.SPF_NONE:
271
- if (plugin.cfg[deny][`${scope}_none`]) {
272
- text = plugin.cfg[deny].openspf_text ? text : `${msgpre} SPF record not found`;
273
- return next(DENY, text);
274
- }
275
- return next();
276
- case spf.SPF_NEUTRAL:
277
- case spf.SPF_PASS:
278
- return next();
279
- case spf.SPF_SOFTFAIL:
280
- if (plugin.cfg[deny][`${scope}_softfail`]) {
281
- text = plugin.cfg[deny].openspf_text ? text : `${msgpre} SPF SoftFail`;
282
- return next(DENY, text);
283
- }
284
- return next();
285
- case spf.SPF_FAIL:
286
- if (plugin.cfg[deny][`${scope}_fail`]) {
287
- text = plugin.cfg[deny].openspf_text ? text : `${msgpre} SPF Fail`;
288
- return next(DENY, text);
289
- }
290
- return next();
291
- case spf.SPF_TEMPERROR:
292
- if (plugin.cfg[defer][`${scope}_temperror`]) {
293
- return next(DENYSOFT, `${msgpre} SPF Temporary Error`);
294
- }
295
- return next();
296
- case spf.SPF_PERMERROR:
297
- if (plugin.cfg[deny][`${scope}_permerror`]) {
298
- return next(DENY, `${msgpre} SPF Permanent Error`);
299
- }
300
- return next();
301
- default:
302
- // Unknown result
303
- connection.logerror(plugin, `unknown result code=${result}`);
304
- return next();
305
- }
306
- }
307
-
308
- exports.save_to_header = (connection, spf, result, mfrom, host, id, ip) => {
309
- // Add a trace header
310
- if (!connection) return;
311
- if (!connection.transaction) return;
312
- const des = result === spf.SPF_PASS ? 'designates' : 'does not designate';
313
- const identity = `identity=${id}; client-ip=${ip ? ip : connection.remote.ip}`;
314
- connection.transaction.add_leading_header('Received-SPF',
315
- `${spf.result(result)} (${connection.local.host}: domain of ${host} ${des} ${connection.remote.ip} as permitted sender) receiver=${connection.local.host}; ${identity} helo=${connection.hello.host}; envelope-from=<${mfrom}>`
316
- );
317
- }
318
-
319
- exports.skip_hosts = function (connection) {
320
- const plugin = this;
321
-
322
- const skip = plugin.cfg.skip;
323
- if (skip) {
324
- if (skip.relaying && connection.relaying) return 'relay';
325
- if (skip.auth && connection.notes.auth_user) return 'auth';
326
- }
327
- }