Haraka 3.0.2 → 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 (267) hide show
  1. package/.eslintrc.yaml +5 -9
  2. package/.prettierrc.yml +1 -0
  3. package/CONTRIBUTORS.md +11 -0
  4. package/Changes.md +1393 -1211
  5. package/Dockerfile +3 -3
  6. package/Plugins.md +119 -106
  7. package/README.md +7 -16
  8. package/TODO +1 -24
  9. package/bin/haraka +197 -298
  10. package/config/auth_flat_file.ini +2 -0
  11. package/config/auth_vpopmaild.ini +4 -2
  12. package/config/dhparams.pem +8 -0
  13. package/config/mail_from.is_resolvable.ini +4 -2
  14. package/config/me +1 -0
  15. package/config/outbound.ini +0 -2
  16. package/config/plugins +36 -35
  17. package/config/rabbitmq_amqplib.ini +8 -1
  18. package/config/smtp.ini +0 -1
  19. package/config/smtp.json +17 -0
  20. package/config/tls_cert.pem +23 -0
  21. package/config/tls_key.pem +28 -0
  22. package/connection.js +46 -73
  23. package/contrib/bsd-rc.d/haraka +3 -1
  24. package/contrib/plugin2npm.sh +6 -36
  25. package/docs/Connection.md +1 -1
  26. package/docs/CoreConfig.md +2 -2
  27. package/docs/Logging.md +7 -21
  28. package/docs/Outbound.md +104 -210
  29. package/docs/Plugins.md +47 -40
  30. package/docs/Transaction.md +59 -82
  31. package/docs/{plugins → deprecated}/connect.rdns_access.md +1 -1
  32. package/docs/{plugins → deprecated}/mail_from.access.md +1 -1
  33. package/docs/{plugins → deprecated}/rcpt_to.access.md +1 -1
  34. package/docs/plugins/auth/auth_vpopmaild.md +15 -19
  35. package/docs/plugins/auth/flat_file.md +23 -30
  36. package/docs/plugins/queue/rabbitmq_amqplib.md +7 -0
  37. package/docs/plugins/queue/smtp_forward.md +1 -1
  38. package/docs/plugins/queue/smtp_proxy.md +5 -10
  39. package/docs/plugins/relay.md +2 -2
  40. package/docs/plugins/tls.md +29 -9
  41. package/endpoint.js +16 -13
  42. package/haraka.js +10 -14
  43. package/host_pool.js +5 -5
  44. package/line_socket.js +3 -4
  45. package/logger.js +44 -28
  46. package/outbound/client_pool.js +27 -23
  47. package/outbound/config.js +4 -6
  48. package/outbound/fsync_writestream.js +1 -1
  49. package/outbound/hmail.js +180 -220
  50. package/outbound/index.js +86 -99
  51. package/outbound/qfile.js +1 -1
  52. package/outbound/queue.js +55 -43
  53. package/outbound/timer_queue.js +3 -2
  54. package/outbound/tls.js +19 -7
  55. package/package.json +66 -55
  56. package/plugins/.eslintrc.yaml +0 -6
  57. package/plugins/auth/auth_base.js +30 -12
  58. package/plugins/auth/auth_proxy.js +14 -12
  59. package/plugins/auth/auth_vpopmaild.js +30 -20
  60. package/plugins/auth/flat_file.js +17 -12
  61. package/plugins/block_me.js +1 -1
  62. package/plugins/data.signatures.js +2 -4
  63. package/plugins/early_talker.js +2 -1
  64. package/plugins/mail_from.is_resolvable.js +65 -135
  65. package/plugins/queue/deliver.js +4 -5
  66. package/plugins/queue/lmtp.js +11 -14
  67. package/plugins/queue/qmail-queue.js +2 -2
  68. package/plugins/queue/quarantine.js +2 -2
  69. package/plugins/queue/rabbitmq.js +16 -17
  70. package/plugins/queue/rabbitmq_amqplib.js +1 -1
  71. package/plugins/queue/smtp_forward.js +6 -6
  72. package/plugins/queue/smtp_proxy.js +10 -1
  73. package/plugins/queue/test.js +2 -2
  74. package/plugins/rcpt_to.host_list_base.js +5 -5
  75. package/plugins/rcpt_to.in_host_list.js +2 -2
  76. package/plugins/relay.js +6 -7
  77. package/plugins/reseed_rng.js +1 -1
  78. package/plugins/status.js +37 -33
  79. package/plugins/tls.js +2 -2
  80. package/plugins/xclient.js +3 -2
  81. package/plugins.js +51 -54
  82. package/run_tests +3 -30
  83. package/server.js +190 -190
  84. package/smtp_client.js +30 -23
  85. package/{tests → test}/config/plugins +0 -2
  86. package/{tests → test}/config/smtp.ini +1 -1
  87. package/test/config/tls/example.com/_.example.com.key +28 -0
  88. package/test/config/tls/example.com/example.com.crt +25 -0
  89. package/test/connection.js +302 -0
  90. package/test/endpoint.js +94 -0
  91. package/{tests → test}/fixtures/line_socket.js +1 -1
  92. package/{tests → test}/fixtures/util_hmailitem.js +19 -25
  93. package/{tests → test}/host_pool.js +42 -57
  94. package/test/logger.js +258 -0
  95. package/test/outbound/hmail.js +141 -0
  96. package/test/outbound/index.js +220 -0
  97. package/test/outbound/qfile.js +126 -0
  98. package/test/outbound_bounce_net_errors.js +142 -0
  99. package/{tests → test}/outbound_bounce_rfc3464.js +110 -122
  100. package/test/plugins/auth/auth_base.js +484 -0
  101. package/test/plugins/auth/auth_vpopmaild.js +83 -0
  102. package/test/plugins/early_talker.js +104 -0
  103. package/test/plugins/mail_from.is_resolvable.js +35 -0
  104. package/test/plugins/queue/smtp_forward.js +206 -0
  105. package/test/plugins/rcpt_to.host_list_base.js +122 -0
  106. package/test/plugins/rcpt_to.in_host_list.js +193 -0
  107. package/test/plugins/relay.js +303 -0
  108. package/test/plugins/status.js +130 -0
  109. package/test/plugins/tls.js +70 -0
  110. package/test/plugins.js +228 -0
  111. package/{tests → test}/queue/multibyte +0 -0
  112. package/{tests → test}/queue/plain +0 -0
  113. package/test/rfc1869.js +73 -0
  114. package/test/server.js +491 -0
  115. package/test/smtp_client.js +299 -0
  116. package/test/tls_socket.js +273 -0
  117. package/test/transaction.js +270 -0
  118. package/tls_socket.js +202 -252
  119. package/transaction.js +9 -24
  120. package/CONTRIBUTING.md +0 -1
  121. package/bin/dkimverify +0 -40
  122. package/config/access.domains +0 -13
  123. package/config/attachment.ctype.regex +0 -2
  124. package/config/attachment.filename.regex +0 -1
  125. package/config/avg.ini +0 -5
  126. package/config/bounce.ini +0 -15
  127. package/config/data.headers.ini +0 -61
  128. package/config/dkim/dkim_key_gen.sh +0 -78
  129. package/config/dkim_sign.ini +0 -4
  130. package/config/dkim_verify.ini +0 -7
  131. package/config/dnsbl.ini +0 -23
  132. package/config/greylist.ini +0 -43
  133. package/config/helo.checks.ini +0 -52
  134. package/config/lookup_rdns.strict.ini +0 -12
  135. package/config/lookup_rdns.strict.timeout +0 -1
  136. package/config/lookup_rdns.strict.whitelist +0 -1
  137. package/config/lookup_rdns.strict.whitelist_regex +0 -5
  138. package/config/messagesniffer.ini +0 -18
  139. package/config/rcpt_to.blocklist +0 -1
  140. package/config/rdns.allow_regexps +0 -0
  141. package/config/rdns.deny_regexps +0 -0
  142. package/config/spamassassin.ini +0 -56
  143. package/config.js +0 -6
  144. package/dkim.js +0 -614
  145. package/docs/plugins/avg.md +0 -35
  146. package/docs/plugins/bounce.md +0 -69
  147. package/docs/plugins/clamd.md +0 -147
  148. package/docs/plugins/esets.md +0 -8
  149. package/docs/plugins/greylist.md +0 -90
  150. package/docs/plugins/helo.checks.md +0 -135
  151. package/docs/plugins/messagesniffer.md +0 -163
  152. package/docs/plugins/relay_acl.md +0 -29
  153. package/docs/plugins/relay_all.md +0 -15
  154. package/docs/plugins/relay_force_routing.md +0 -33
  155. package/docs/plugins/spamassassin.md +0 -180
  156. package/outbound/mx_lookup.js +0 -70
  157. package/plugins/auth/auth_ldap.js +0 -3
  158. package/plugins/avg.js +0 -162
  159. package/plugins/backscatterer.js +0 -25
  160. package/plugins/bounce.js +0 -381
  161. package/plugins/clamd.js +0 -381
  162. package/plugins/data.headers.js +0 -4
  163. package/plugins/data.uribl.js +0 -4
  164. package/plugins/dkim_sign.js +0 -395
  165. package/plugins/dkim_verify.js +0 -62
  166. package/plugins/dns_list_base.js +0 -221
  167. package/plugins/dnsbl.js +0 -146
  168. package/plugins/dnswl.js +0 -58
  169. package/plugins/esets.js +0 -71
  170. package/plugins/graph.js +0 -5
  171. package/plugins/greylist.js +0 -645
  172. package/plugins/helo.checks.js +0 -533
  173. package/plugins/messagesniffer.js +0 -381
  174. package/plugins/rcpt_to.ldap.js +0 -3
  175. package/plugins/rcpt_to.max_count.js +0 -24
  176. package/plugins/relay_all.js +0 -13
  177. package/plugins/spamassassin.js +0 -384
  178. package/tests/config/dkim/example.com/dns +0 -29
  179. package/tests/config/dkim/example.com/private +0 -6
  180. package/tests/config/dkim/example.com/public +0 -4
  181. package/tests/config/dkim/example.com/selector +0 -1
  182. package/tests/config/dkim.private.key +0 -6
  183. package/tests/config/dkim_sign.ini +0 -4
  184. package/tests/config/helo.checks.ini +0 -52
  185. package/tests/connection.js +0 -327
  186. package/tests/endpoint.js +0 -128
  187. package/tests/fixtures/vm_harness.js +0 -59
  188. package/tests/logger.js +0 -327
  189. package/tests/outbound/hmail.js +0 -112
  190. package/tests/outbound/index.js +0 -324
  191. package/tests/outbound/qfile.js +0 -67
  192. package/tests/outbound_bounce_net_errors.js +0 -173
  193. package/tests/plugins/auth/auth_base.js +0 -463
  194. package/tests/plugins/auth/auth_vpopmaild.js +0 -91
  195. package/tests/plugins/bounce.js +0 -307
  196. package/tests/plugins/clamd.js +0 -224
  197. package/tests/plugins/deprecated/relay_acl.js +0 -140
  198. package/tests/plugins/deprecated/relay_all.js +0 -59
  199. package/tests/plugins/dkim_sign.js +0 -315
  200. package/tests/plugins/dkim_signer.js +0 -108
  201. package/tests/plugins/dns_list_base.js +0 -259
  202. package/tests/plugins/dnsbl.js +0 -101
  203. package/tests/plugins/early_talker.js +0 -115
  204. package/tests/plugins/greylist.js +0 -58
  205. package/tests/plugins/helo.checks.js +0 -525
  206. package/tests/plugins/mail_from.is_resolvable.js +0 -116
  207. package/tests/plugins/queue/smtp_forward.js +0 -221
  208. package/tests/plugins/rcpt_to.host_list_base.js +0 -132
  209. package/tests/plugins/rcpt_to.in_host_list.js +0 -218
  210. package/tests/plugins/relay.js +0 -339
  211. package/tests/plugins/spamassassin.js +0 -171
  212. package/tests/plugins/status.js +0 -138
  213. package/tests/plugins/tls.js +0 -84
  214. package/tests/plugins.js +0 -247
  215. package/tests/rfc1869.js +0 -61
  216. package/tests/server.js +0 -510
  217. package/tests/smtp_client/auth.js +0 -105
  218. package/tests/smtp_client/basic.js +0 -101
  219. package/tests/smtp_client.js +0 -80
  220. package/tests/tls_socket.js +0 -333
  221. package/tests/transaction.js +0 -284
  222. /package/docs/{plugins → deprecated}/dkim_sign.md +0 -0
  223. /package/docs/{plugins → deprecated}/dkim_verify.md +0 -0
  224. /package/docs/{plugins → deprecated}/dnsbl.md +0 -0
  225. /package/docs/{plugins → deprecated}/dnswl.md +0 -0
  226. /package/docs/{plugins → deprecated}/rcpt_to.routes.md +0 -0
  227. /package/{tests → test}/.eslintrc.yaml +0 -0
  228. /package/{tests → test}/config/auth_flat_file.ini +0 -0
  229. /package/{tests → test}/config/dhparams.pem +0 -0
  230. /package/{tests → test}/config/host_list +0 -0
  231. /package/{tests → test}/config/outbound_tls_cert.pem +0 -0
  232. /package/{tests → test}/config/outbound_tls_key.pem +0 -0
  233. /package/{tests → test}/config/smtp_forward.ini +0 -0
  234. /package/{tests → test}/config/tls/ec.pem +0 -0
  235. /package/{tests → test}/config/tls/haraka.local.pem +0 -0
  236. /package/{tests → test}/config/tls/mismatched.pem +0 -0
  237. /package/{tests → test}/config/tls.ini +0 -0
  238. /package/{tests → test}/config/tls_cert.pem +0 -0
  239. /package/{tests → test}/config/tls_key.pem +0 -0
  240. /package/{tests → test}/fixtures/todo_qfile.txt +0 -0
  241. /package/{tests → test}/installation/config/test-plugin-flat +0 -0
  242. /package/{tests → test}/installation/config/test-plugin.ini +0 -0
  243. /package/{tests → test}/installation/config/tls.ini +0 -0
  244. /package/{tests → test}/installation/node_modules/load_first/index.js +0 -0
  245. /package/{tests → test}/installation/node_modules/load_first/package.json +0 -0
  246. /package/{tests → test}/installation/node_modules/test-plugin/config/test-plugin-flat +0 -0
  247. /package/{tests → test}/installation/node_modules/test-plugin/config/test-plugin.ini +0 -0
  248. /package/{tests → test}/installation/node_modules/test-plugin/package.json +0 -0
  249. /package/{tests → test}/installation/node_modules/test-plugin/test-plugin.js +0 -0
  250. /package/{tests → test}/installation/plugins/base_plugin.js +0 -0
  251. /package/{tests → test}/installation/plugins/folder_plugin/index.js +0 -0
  252. /package/{tests → test}/installation/plugins/folder_plugin/package.json +0 -0
  253. /package/{tests → test}/installation/plugins/inherits.js +0 -0
  254. /package/{tests → test}/installation/plugins/load_first.js +0 -0
  255. /package/{tests → test}/installation/plugins/plugin.js +0 -0
  256. /package/{tests → test}/installation/plugins/tls.js +0 -0
  257. /package/{tests → test}/loud/config/dhparams.pem +0 -0
  258. /package/{tests → test}/loud/config/tls/goobered.pem +0 -0
  259. /package/{tests → test}/loud/config/tls.ini +0 -0
  260. /package/{tests → test}/mail_specimen/base64-root-part.txt +0 -0
  261. /package/{tests → test}/mail_specimen/varied-fold-lengths-preserve-data.txt +0 -0
  262. /package/{tests → test}/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_fixed +0 -0
  263. /package/{tests → test}/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka +0 -0
  264. /package/{tests → test}/queue/1508269674999_1508269674999_0_34002_socVUF_1_haraka +0 -0
  265. /package/{tests → test}/queue/1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka +0 -0
  266. /package/{tests → test}/queue/zero-length +0 -0
  267. /package/{tests → test}/test-queue/delete-me +0 -0
@@ -1,8 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  // Check MAIL FROM domain is resolvable to an MX
4
- const dns = require('dns');
5
- const net = require('net');
4
+ const net = require('node:net');
6
5
 
7
6
  const net_utils = require('haraka-net-utils');
8
7
 
@@ -14,12 +13,17 @@ exports.load_ini = function () {
14
13
  this.cfg = this.config.get('mail_from.is_resolvable.ini', {
15
14
  booleans: [
16
15
  '-main.allow_mx_ip',
17
- '+main.reject_no_mx',
16
+ '+reject.no_mx',
18
17
  ],
19
18
  }, () => {
20
19
  this.load_ini();
21
20
  });
22
21
 
22
+ // compat. Sunset 4.0
23
+ if (this.cfg.main.reject_no_mx) {
24
+ this.cfg.reject.no_mx = this.cfg.main.reject_no_mx
25
+ }
26
+
23
27
  if (isNaN(this.cfg.main.timeout)) {
24
28
  this.cfg.main.timeout = 29;
25
29
  }
@@ -40,163 +44,89 @@ exports.hook_mail = function (next, connection, params) {
40
44
  const mail_from = params[0];
41
45
  const txn = connection?.transaction;
42
46
  if (!txn) return next();
43
- const { results } = txn;
47
+ const { results } = txn;
44
48
 
45
- // Check for MAIL FROM without an @ first - ignore those here
49
+ // ignore MAIL FROM without an @
46
50
  if (!mail_from.host) {
47
51
  results.add(plugin, {skip: 'null host'});
48
52
  return next();
49
53
  }
50
54
 
51
55
  let called_next = 0;
52
- const domain = mail_from.host;
53
- const c = plugin.cfg.main;
54
- const timeout_id = setTimeout(() => {
55
- // DNS answer didn't return (UDP)
56
- connection.loginfo(plugin, `timed out resolving MX for ${domain}`);
56
+ const domain = mail_from.host;
57
+ const timeout_id = setTimeout(() => {
58
+ connection.logdebug(plugin, `DNS timeout resolving MX for ${domain}`);
57
59
  called_next++;
58
60
  if (txn) results.add(plugin, {err: `timeout(${domain})`});
59
61
  next(DENYSOFT, 'Temporary resolver error (timeout)');
60
- }, c.timeout * 1000);
62
+ }, this.cfg.main.timeout * 1000);
61
63
 
62
64
  function mxDone (code, reply) {
63
65
  if (called_next) return;
64
66
  clearTimeout(timeout_id);
65
67
  called_next++;
66
- next(code, reply);
68
+ next(...arguments);
67
69
  }
68
70
 
69
- // IS: IPv6 compatible
70
- net_utils.get_mx(domain, (err, addresses) => {
71
- if (!txn) return;
72
- if (err && plugin.mxErr(connection, domain, 'MX', err, mxDone)) return;
71
+ function mxErr (err) {
72
+ if (!connection.transaction) return;
73
+ results.add(plugin, {err: `${domain}:${err.message}`});
74
+ mxDone(DENYSOFT, `Temp. resolver error (${err.code})`);
75
+ }
73
76
 
74
- if (!addresses || !addresses.length) {
75
- // Check for implicit MX 0 record
76
- return plugin.implicit_mx(connection, domain, mxDone);
77
- }
77
+ connection.logdebug(plugin, `resolving MX for domain ${domain}`)
78
78
 
79
- // Verify that the MX records resolve to valid addresses
80
- let records = {};
81
- let pending_queries = 0;
82
- function check_results () {
83
- if (pending_queries !== 0) return;
84
-
85
- records = Object.keys(records);
86
- if (records?.length) {
87
- connection.logdebug(plugin, `${domain}: ${records}`);
88
- results.add(plugin, {pass: 'has_fwd_dns'});
89
- return mxDone();
90
- }
91
- results.add(plugin, {fail: 'has_fwd_dns'});
92
- return mxDone(((c.reject_no_mx) ? DENY : DENYSOFT),
93
- 'MX without A/AAAA records');
94
- }
79
+ net_utils
80
+ .get_mx(domain)
81
+ .then((exchanges) => {
82
+ if (!txn) return;
95
83
 
96
- addresses.forEach(addr => {
97
- // Handle MX records that are IP addresses
98
- // This is invalid - but a lot of MTAs allow it.
99
- if (net_utils.get_ipany_re('^\\[','\\]$','').test(addr.exchange)) {
100
- connection.logwarn(plugin, `${domain}: invalid MX ${addr.exchange}`);
101
- if (c.allow_mx_ip) {
102
- records[addr.exchange] = 1;
103
- }
104
- return;
84
+ connection.logdebug(plugin, `${domain}: MX => ${JSON.stringify(exchanges)}`)
85
+
86
+ if (!exchanges || !exchanges.length) {
87
+ results.add(this, {fail: 'has_fwd_dns'});
88
+ return mxDone(
89
+ ((this.cfg.reject.no_mx) ? DENY : DENYSOFT),
90
+ 'No MX for your FROM address'
91
+ );
105
92
  }
106
- pending_queries++;
107
- net_utils.get_ips_by_host(addr.exchange, (err2, addresses2) => {
108
- pending_queries--;
109
- if (!txn) return;
110
- if (err2 && err2.length === 2) {
111
- results.add(plugin, {msg: err2[0].message});
112
- connection.logdebug(plugin, `${domain}: MX ${addr.priority} ${addr.exchange} => ${err2[0].message}`);
113
- check_results();
114
- return;
115
- }
116
- connection.logdebug(plugin, `${domain}: MX ${addr.priority} ${addr.exchange} => ${addresses2}`);
117
- for (const element of addresses2) {
118
- // Ignore anything obviously bogus
119
- if (net.isIPv4(element)){
120
- if (plugin.re_bogus_ip.test(element)) {
121
- connection.logdebug(plugin, `${addr.exchange}: discarding ${element}`);
122
- continue;
123
- }
93
+
94
+ if (this.cfg.main.allow_mx_ip) {
95
+ for (const mx of exchanges) {
96
+ if (net.isIPv4(mx.exchange) && !this.re_bogus_ip.test(mx.exchange)) {
97
+ txn.results.add(this, {pass: 'implicit_mx', emit: true});
98
+ return mxDone()
124
99
  }
125
- if (net.isIPv6(element)){
126
- if (net_utils.ipv6_bogus(element)) {
127
- connection.logdebug(plugin, `${addr.exchange}: discarding ${element}`);
128
- continue;
129
- }
100
+ if (net.isIPv6(mx.exchange) && !net_utils.ipv6_bogus(mx.exchange)) {
101
+ txn.results.add(this, {pass: 'implicit_mx', emit: true});
102
+ return mxDone()
130
103
  }
131
- records[element] = 1;
132
- }
133
- check_results();
134
- });
135
- });
136
- // In case we don't run any queries
137
- check_results();
138
- });
139
- }
140
-
141
- exports.mxErr = function (connection, domain, type, err, mxDone) {
142
-
143
- const txn = connection?.transaction;
144
- if (!txn) return;
145
-
146
- txn.results.add(this, {msg: `${domain}:${type}:${err.message}`});
147
- connection.logdebug(this, `${domain}:${type} => ${err.message}`);
148
- switch (err.code) {
149
- case dns.NXDOMAIN:
150
- case dns.NOTFOUND:
151
- case dns.NODATA:
152
- // Ignore
153
- break;
154
- default:
155
- mxDone(DENYSOFT, `Temp. resolver error (${err.code})`);
156
- return true;
157
- }
158
- return false;
159
- }
160
-
161
- // IS: IPv6 compatible
162
- exports.implicit_mx = function (connection, domain, mxDone) {
163
- const txn = connection?.transaction;
164
- if (!txn) return;
165
-
166
- net_utils.get_ips_by_host(domain, (err, addresses) => {
167
- if (!txn) return;
168
- if (!addresses || !addresses.length) {
169
- txn.results.add(this, {fail: 'has_fwd_dns'});
170
- return mxDone(((this.cfg.main.reject_no_mx) ? DENY : DENYSOFT),
171
- 'No MX for your FROM address');
172
- }
173
-
174
- connection.logdebug(this, `${domain}: A/AAAA => ${addresses}`);
175
- let records = {};
176
- for (const addr of addresses) {
177
- // Ignore anything obviously bogus
178
- if (net.isIPv4(addr)) {
179
- if (this.re_bogus_ip.test(addr)) {
180
- connection.logdebug(this, `${domain}: discarding ${addr}`);
181
- continue;
182
- }
183
- }
184
- if (net.isIPv6(addr)) {
185
- if (net_utils.ipv6_bogus(addr)) {
186
- connection.logdebug(this, `${domain}: discarding ${addr}`);
187
- continue;
188
104
  }
189
105
  }
190
- records[addr] = true;
191
- }
192
106
 
193
- records = Object.keys(records);
194
- if (records?.length) {
195
- txn.results.add(this, {pass: 'implicit_mx'});
196
- return mxDone();
197
- }
107
+ // filter out the implicit MX and resolve the MX hostnames
108
+ net_utils
109
+ .resolve_mx_hosts(exchanges.filter(a => !net.isIP(a.exchange)))
110
+ .then(resolved => {
111
+ connection.logdebug(plugin, `resolved MX => ${JSON.stringify(resolved)}`);
198
112
 
199
- txn.results.add(this, {fail: `implicit_mx(${domain})`});
200
- return mxDone();
201
- });
113
+ for (const mx of resolved) {
114
+ if (net.isIPv4(mx.exchange) && !this.re_bogus_ip.test(mx.exchange)) {
115
+ txn.results.add(this, {pass: 'has_fwd_dns', emit: true});
116
+ return mxDone()
117
+ }
118
+ if (net.isIPv6(mx.exchange) && !net_utils.ipv6_bogus(mx.exchange)) {
119
+ txn.results.add(this, {pass: 'has_fwd_dns', emit: true});
120
+ return mxDone()
121
+ }
122
+ }
123
+
124
+ mxDone(
125
+ ((this.cfg.main.reject_no_mx) ? DENY : DENYSOFT),
126
+ 'No valid MX for your FROM address'
127
+ );
128
+ })
129
+ .catch(mxErr)
130
+ })
131
+ .catch(mxErr)
202
132
  }
@@ -1,13 +1,12 @@
1
- // This plugin is now entirely redundant. The core will queue outbound mails
1
+ // This plugin is entirely redundant. The core will queue outbound mails
2
2
  // automatically just like this. It is kept here for backwards compatibility
3
3
  // purposes only.
4
4
 
5
5
  const outbound = require('./outbound');
6
6
 
7
7
  exports.hook_queue_outbound = (next, connection) => {
8
- if (!connection?.relaying) {
9
- return next(); // we're not relaying so don't deliver outbound
10
- }
8
+ // if not relaying, don't deliver outbound
9
+ if (!connection?.relaying) return next();
11
10
 
12
- outbound.send_email(connection?.transaction, next);
11
+ outbound.send_trans_email(connection?.transaction, next);
13
12
  }
@@ -19,29 +19,26 @@ exports.hook_get_mx = function (next, hmail, domain) {
19
19
 
20
20
  if (!hmail.todo.notes.using_lmtp) return next();
21
21
 
22
- const mx = { using_lmtp: true, priority: 0, exchange: '127.0.0.1' };
23
-
24
22
  const section = this.cfg[domain] || this.cfg.main;
25
- if (section.path) {
26
- Object.assign(mx, { path: section.path });
27
- return next(OK, mx);
28
- }
29
23
 
30
- Object.assign(mx, {
31
- exchange: section.host || '127.0.0.1',
32
- port: section.port || 24,
33
- });
24
+ const mx = {
25
+ using_lmtp: true,
26
+ priority: 0,
27
+ exchange: section.host ?? '127.0.0.1',
28
+ port: section.port ?? 24,
29
+ };
30
+
31
+ if (section.path) mx.path = section.path;
34
32
 
35
- return next(OK, mx);
33
+ next(OK, mx);
36
34
  }
37
35
 
38
36
  exports.hook_queue = (next, connection) => {
39
37
  const txn = connection?.transaction;
40
38
  if (!txn) return next();
41
39
 
42
- const q_wants = txn.notes.get('queue.wants');
43
- if (q_wants && q_wants !== 'lmtp') return next();
40
+ if (txn.notes.get('queue.wants') !== 'lmtp') return next();
44
41
 
45
42
  txn.notes.using_lmtp = true;
46
- outbound.send_email(txn, next);
43
+ outbound.send_trans_email(txn, next);
47
44
  }
@@ -1,7 +1,7 @@
1
1
  // Queue to qmail-queue
2
2
 
3
- const childproc = require('child_process');
4
- const fs = require('fs');
3
+ const childproc = require('node:child_process');
4
+ const fs = require('node:fs');
5
5
 
6
6
  exports.register = function () {
7
7
 
@@ -1,7 +1,7 @@
1
1
  // quarantine
2
2
 
3
- const fs = require('fs');
4
- const path = require('path');
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
5
 
6
6
  exports.register = function () {
7
7
 
@@ -1,5 +1,4 @@
1
1
  const amqp = require('amqp');
2
- const logger = require('./logger');
3
2
 
4
3
  let rabbitqueue;
5
4
  let exchangeName;
@@ -12,41 +11,41 @@ exports.exchangeMapping = {}
12
11
 
13
12
  //This method registers the hook and try to initialize the connection to rabbitmq server for later use.
14
13
  exports.register = function () {
15
- logger.logdebug("About to connect and initialize queue object");
14
+ this.logdebug("About to connect and initialize queue object");
16
15
  this.init_rabbitmq_server();
17
- logger.logdebug(`Finished initiating : ${exports.exchangeMapping[exchangeName + queueName]}`);
16
+ this.logdebug(`Finished initiating : ${exports.exchangeMapping[exchangeName + queueName]}`);
18
17
  }
19
18
 
20
19
 
21
20
  //Actual magic of publishing message to rabbit when email comes happen here.
22
- exports.hook_queue = (next, connection) => {
21
+ exports.hook_queue = function (next, connection) {
23
22
  if (!connection?.transaction) return next();
24
23
 
25
24
  //Calling the get_data method and when it gets the data on callback, publish the message to queue with routing key.
26
25
  connection.transaction.message_stream.get_data(buffere => {
27
26
  const exchangeData = exports.exchangeMapping[exchangeName + queueName]
28
- logger.logdebug(`Sending the data: ${ queueName} Routing : ${exchangeData} exchange :${connExchange_}`);
27
+ this.logdebug(`Sending the data: ${ queueName} Routing : ${exchangeData} exchange :${connExchange_}`);
29
28
  if (connExchange_ && routing_) {
30
29
  //This is publish function of rabbitmq amqp library, currently direct queue is configured and routing is fixed.
31
30
  //Needs to be changed.
32
31
  connExchange_.publish(routing_, buffere,{deliveryMode}, error => {
33
32
  if (error) {
34
33
  //There was some error while sending the email to queue.
35
- logger.logdebug("queueFailure: #{JSON.stringify(error)}");
34
+ this.logdebug("queueFailure: #{JSON.stringify(error)}");
36
35
  exports.init_rabbitmq_server();
37
- return next();
36
+ next();
38
37
  }
39
38
  else {
40
39
  //Queueing was successful, send ok as reply
41
- logger.logdebug( "queueSuccess");
42
- return next(OK,"Successfully Queued! in rabbitmq");
40
+ this.logdebug( "queueSuccess");
41
+ next(OK,"Successfully Queued! in rabbitmq");
43
42
  }
44
43
  });
45
44
  }
46
45
  else {
47
46
  //Seems like connExchange is not defined , lets create one for next call
48
47
  exports.init_rabbitmq_server();
49
- return next();
48
+ next();
50
49
  }
51
50
  });
52
51
  }
@@ -88,18 +87,18 @@ exports.init_rabbitmq_server = function () {
88
87
 
89
88
 
90
89
  //Create connection to the rabbitmq server
91
- logger.logdebug("About to Create connection with server");
90
+ this.logdebug("About to Create connection with server");
92
91
  rabbitqueue = amqp.createConnection(options);
93
92
 
94
93
 
95
94
  //Declaring listerner on error on connection.
96
95
  rabbitqueue.on('error', error => {
97
- logger.logdebug(`There was some error on the connection : ${error}`);
96
+ this.logerror(`There was some error on the connection : ${error}`);
98
97
  });
99
98
 
100
99
  //Declaring listerner on close on connection.
101
100
  rabbitqueue.on('close', close => {
102
- logger.logdebug(` Connection is beingclosed : ${close}`);
101
+ this.logdebug(` Connection is being closed : ${close}`);
103
102
  });
104
103
 
105
104
 
@@ -111,16 +110,16 @@ exports.init_rabbitmq_server = function () {
111
110
  */
112
111
 
113
112
  rabbitqueue.on('ready', () => {
114
- logger.logdebug("Connection is ready, will try making exchange");
113
+ this.logdebug("Connection is ready, will try making exchange");
115
114
  // Now connection is ready will try to open exchange with config data.
116
115
  rabbitqueue.exchange(exchangeName, { type: exchangeType, confirm, durable }, connExchange => {
117
116
 
118
117
 
119
- logger.logdebug(`connExchange with server ${connExchange} autoDelete : ${autoDelete}`);
118
+ this.logdebug(`connExchange with server ${connExchange} autoDelete : ${autoDelete}`);
120
119
 
121
120
  //Exchange is now open, will try to open queue.
122
121
  return rabbitqueue.queue(queueName,{autoDelete, durable }, connQueue => {
123
- logger.logdebug(`connQueue with server ${connQueue}`);
122
+ this.logdebug(`connQueue with server ${connQueue}`);
124
123
 
125
124
  //Creating the Routing key to bind the queue and exchange.
126
125
  const routing = `${queueName}Routing`;
@@ -142,7 +141,7 @@ exports.init_rabbitmq_server = function () {
142
141
  routing : routing_,
143
142
  queueName
144
143
  });
145
- logger.logdebug(`exchange: ${exchangeName}, queue: ${queueName} exchange : ${connExchange_} queue : ${connQueue_}` );
144
+ this.logdebug(`exchange: ${exchangeName}, queue: ${queueName} exchange : ${connExchange_} queue : ${connQueue_}` );
146
145
  });
147
146
  });
148
147
  });
@@ -58,7 +58,7 @@ exports.init_amqp_connection = function () {
58
58
  return conn.close();
59
59
  }
60
60
  ch.assertQueue(queueName,
61
- {durable, autoDelete},
61
+ {durable, autoDelete, arguments: this.config.get("rabbitmq.ini").queue_args},
62
62
  (err4, ok2) => {
63
63
  if (err4) {
64
64
  this.logerror(`Error asserting rabbitmq queue: ${err4}`);
@@ -4,7 +4,7 @@
4
4
  // and passes back any errors seen on the ongoing server to the
5
5
  // originating server.
6
6
 
7
- const url = require('url');
7
+ const url = require('node:url');
8
8
 
9
9
  const smtp_client_mod = require('./smtp_client');
10
10
 
@@ -28,10 +28,10 @@ exports.register = function () {
28
28
  if (this.cfg.main.enable_outbound) {
29
29
  // deliver local message via smtp forward when relaying=true
30
30
  this.register_hook('queue_outbound', 'queue_forward');
31
-
32
- // may specify more specific routes for outbound
33
- this.register_hook('get_mx', 'get_mx');
34
31
  }
32
+
33
+ // may specify more specific [per-domain] outbound routes
34
+ this.register_hook('get_mx', 'get_mx');
35
35
  }
36
36
 
37
37
  exports.load_smtp_forward_ini = function () {
@@ -230,6 +230,7 @@ exports.forward_enabled = function (conn, dom_cfg) {
230
230
 
231
231
  exports.queue_forward = function (next, connection) {
232
232
  const plugin = this;
233
+ if (connection.remote.closed) return
233
234
  const txn = connection?.transaction;
234
235
 
235
236
  const cfg = plugin.get_config(connection);
@@ -247,8 +248,7 @@ exports.queue_forward = function (next, connection) {
247
248
  );
248
249
 
249
250
  function get_rs () {
250
- if (txn) return txn.results;
251
- return connection.results;
251
+ return connection?.transaction?.results ? connection.transaction.results : connection.results
252
252
  }
253
253
 
254
254
  function dead_sender () {
@@ -79,6 +79,10 @@ exports.hook_mail = function (next, connection, params) {
79
79
  exports.hook_rcpt_ok = (next, connection, recipient) => {
80
80
  const { smtp_client } = connection.notes;
81
81
  if (!smtp_client) return next();
82
+ if (smtp_client.is_dead_sender(this, connection)) {
83
+ delete connection.notes.smtp_client;
84
+ return;
85
+ }
82
86
  smtp_client.next = next;
83
87
  smtp_client.send_command('RCPT', `TO:${recipient.format(!smtp_client.smtp_utf8)}`);
84
88
  }
@@ -86,6 +90,11 @@ exports.hook_rcpt_ok = (next, connection, recipient) => {
86
90
  exports.hook_data = (next, connection) => {
87
91
  const { smtp_client } = connection.notes;
88
92
  if (!smtp_client) return next();
93
+
94
+ if (smtp_client.is_dead_sender(this, connection)) {
95
+ delete connection.notes.smtp_client;
96
+ return;
97
+ }
89
98
  smtp_client.next = next;
90
99
  smtp_client.send_command("DATA");
91
100
  }
@@ -96,11 +105,11 @@ exports.hook_queue = function (next, connection) {
96
105
  const { smtp_client } = connection.notes;
97
106
  if (!smtp_client) return next();
98
107
 
99
- smtp_client.next = next;
100
108
  if (smtp_client.is_dead_sender(this, connection)) {
101
109
  delete connection.notes.smtp_client;
102
110
  return;
103
111
  }
112
+ smtp_client.next = next;
104
113
  smtp_client.start_data(connection.transaction.message_stream);
105
114
  }
106
115
 
@@ -1,5 +1,5 @@
1
- const fs = require('fs');
2
- const os = require('os');
1
+ const fs = require('node:fs');
2
+ const os = require('node:os');
3
3
 
4
4
  const tempDir = os.tmpdir();
5
5
 
@@ -40,7 +40,7 @@ exports.hook_mail = function (next, connection, params) {
40
40
 
41
41
  const anti_spoof = this.config.get('host_list.anti_spoof') || false;
42
42
 
43
- if (this.in_host_list(domain) || this.in_host_regex(domain)) {
43
+ if (this.in_host_list(domain, connection) || this.in_host_regex(domain, connection)) {
44
44
  if (anti_spoof && !connection.relaying) {
45
45
  txn.results.add(this, {fail: 'mail_from.anti_spoof'});
46
46
  return next(DENY, `Mail from domain '${domain}' is not allowed from your host`);
@@ -54,16 +54,16 @@ exports.hook_mail = function (next, connection, params) {
54
54
  return next();
55
55
  }
56
56
 
57
- exports.in_host_list = function (domain) {
58
- this.logdebug(`checking ${domain} in config/host_list`);
57
+ exports.in_host_list = function (domain, connection) {
58
+ this.logdebug(connection, `checking ${domain} in config/host_list`);
59
59
  return !!(this.host_list[domain]);
60
60
  }
61
61
 
62
- exports.in_host_regex = function (domain) {
62
+ exports.in_host_regex = function (domain, connection) {
63
63
  if (!this.host_list_regex) return false;
64
64
  if (!this.host_list_regex.length) return false;
65
65
 
66
- this.logdebug(`checking ${domain} against config/host_list_regex `);
66
+ this.logdebug(connection, `checking ${domain} against config/host_list_regex `);
67
67
 
68
68
  return !!(this.hl_re.test(domain));
69
69
  }
@@ -32,12 +32,12 @@ exports.hook_rcpt = function (next, connection, params) {
32
32
 
33
33
  const domain = rcpt.host.toLowerCase();
34
34
 
35
- if (this.in_host_list(domain)) {
35
+ if (this.in_host_list(domain, connection)) {
36
36
  txn.results.add(this, {pass: 'rcpt_to'});
37
37
  return next(OK);
38
38
  }
39
39
 
40
- if (this.in_host_regex(domain)) {
40
+ if (this.in_host_regex(domain, connection)) {
41
41
  txn.results.add(this, {pass: 'rcpt_to'});
42
42
  return next(OK);
43
43
  }
package/plugins/relay.js CHANGED
@@ -2,8 +2,9 @@
2
2
  //
3
3
  // documentation via: haraka -h relay
4
4
 
5
+ const net = require('node:net');
6
+
5
7
  const ipaddr = require('ipaddr.js');
6
- const net = require('net');
7
8
 
8
9
  exports.register = function () {
9
10
 
@@ -89,11 +90,9 @@ exports.acl = function (next, connection) {
89
90
  }
90
91
 
91
92
  exports.pass_relaying = (next, connection) => {
92
- if (connection.relaying) {
93
- return next(OK);
94
- }
93
+ if (connection.relaying) return next(OK);
95
94
 
96
- return next();
95
+ next();
97
96
  }
98
97
 
99
98
  exports.is_acl_allowed = function (connection) {
@@ -172,7 +171,7 @@ exports.dest_domains = function (next, connection, params) {
172
171
  }
173
172
 
174
173
  transaction.results.add(this, {fail: 'relay_dest_domain'});
175
- return next(DENY, "Mail for that recipient is not accepted here.");
174
+ next(DENY, "Mail for that recipient is not accepted here.");
176
175
  }
177
176
 
178
177
  exports.force_routing = function (next, hmail, domain) {
@@ -196,7 +195,7 @@ exports.force_routing = function (next, hmail, domain) {
196
195
  }
197
196
 
198
197
  this.logdebug(this, `using ${nexthop} for: ${domain}`);
199
- return next(OK, nexthop);
198
+ next(OK, nexthop);
200
199
  }
201
200
 
202
201
  exports.all = function (next, connection, params) {
@@ -1,4 +1,4 @@
1
- const crypto = require('crypto');
1
+ const crypto = require('node:crypto');
2
2
 
3
3
  exports.hook_init_child = function (next) {
4
4
  Math.seedrandom(crypto.randomBytes(256).toString('hex'));