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
package/server.js CHANGED
@@ -1,19 +1,18 @@
1
1
  'use strict';
2
2
  // smtp network server
3
3
 
4
+ const cluster = require('node:cluster');
5
+ const fs = require('node:fs');
6
+ const os = require('node:os');
7
+ const path = require('node:path');
8
+ const tls = require('node:tls');
9
+
4
10
  const daemon = require('daemon');
5
- const fs = require('fs');
6
- const os = require('os');
7
- const path = require('path');
8
- const tls = require('tls');
11
+ const constants = require('haraka-constants');
9
12
 
10
- // let log = require('why-is-node-running');
11
13
  const tls_socket = require('./tls_socket');
12
14
  const conn = require('./connection');
13
15
  const outbound = require('./outbound');
14
- const async = require('async');
15
- const cluster = require('cluster');
16
- const constants = require('haraka-constants');
17
16
  const endpoint = require('./endpoint');
18
17
 
19
18
  const Server = exports;
@@ -22,7 +21,7 @@ Server.config = require('haraka-config');
22
21
  Server.plugins = require('./plugins');
23
22
  Server.notes = {};
24
23
 
25
- const { logger } = Server;
24
+ const { logger } = Server;
26
25
 
27
26
  // Need these here so we can run hooks
28
27
  logger.add_log_methods(Server, 'server');
@@ -45,7 +44,7 @@ Server.load_smtp_ini = () => {
45
44
  });
46
45
 
47
46
  if (Server.cfg.main.nodes === undefined) {
48
- logger.logwarn(`smtp.ini.nodes unset, using 1, see https://github.com/haraka/Haraka/wiki/Performance-Tuning`)
47
+ Server.logwarn(`smtp.ini.nodes unset, using 1, see https://github.com/haraka/Haraka/wiki/Performance-Tuning`)
49
48
  }
50
49
 
51
50
  const defaults = {
@@ -62,13 +61,10 @@ Server.load_smtp_ini = () => {
62
61
 
63
62
  const strict_ext = Server.config.get('strict_rfc1869');
64
63
  if (Server.cfg.main.strict_rfc1869 === false && strict_ext) {
65
- logger.logwarn(`legacy config config/strict_rfc1869 is overriding smtp.ini`)
64
+ Server.logwarn(`legacy config config/strict_rfc1869 is overriding smtp.ini`)
66
65
  Server.cfg.main.strict_rfc1869 = strict_ext;
67
66
  }
68
67
 
69
- const hhv = Server.config.get('header_hide_version') // backwards compat
70
- if (hhv !== null && !hhv) Server.cfg.headers.show_version = false;
71
-
72
68
  for (const key in defaults) {
73
69
  if (Server.cfg.main[key] !== undefined) continue;
74
70
  Server.cfg.main[key] = defaults[key];
@@ -93,7 +89,7 @@ Server.daemonize = function () {
93
89
  // Remove process.on('exit') listeners otherwise
94
90
  // we get a spurious 'Exiting' log entry.
95
91
  process.removeAllListeners('exit');
96
- logger.lognotice('Daemonizing...');
92
+ Server.lognotice('Daemonizing...');
97
93
  }
98
94
 
99
95
  const log_fd = fs.openSync(c.daemon_log_file, 'a');
@@ -105,7 +101,7 @@ Server.daemonize = function () {
105
101
  npid.create(c.daemon_pid_file).removeOnExit();
106
102
  }
107
103
  catch (err) {
108
- logger.logerror(err.message);
104
+ Server.logerror(err.message);
109
105
  logger.dump_and_exit(1);
110
106
  }
111
107
  }
@@ -121,17 +117,17 @@ Server.flushQueue = domain => {
121
117
  }
122
118
  }
123
119
 
124
- let gracefull_in_progress = false;
120
+ let graceful_in_progress = false;
125
121
 
126
122
  Server.gracefulRestart = () => {
127
123
  Server._graceful();
128
124
  }
129
125
 
130
126
  Server.stopListeners = () => {
131
- logger.loginfo('Shutting down listeners');
132
- Server.listeners.forEach(server => {
133
- server.close();
134
- });
127
+ Server.loginfo('Shutting down listeners');
128
+ for (const l of Server.listeners) {
129
+ l.close();
130
+ }
135
131
  Server.listeners = [];
136
132
  }
137
133
 
@@ -139,7 +135,7 @@ Server.performShutdown = () => {
139
135
  if (Server.cfg.main.graceful_shutdown) {
140
136
  return Server.gracefulShutdown();
141
137
  }
142
- logger.loginfo("Shutting down.");
138
+ Server.loginfo("Shutting down.");
143
139
  process.exit(0);
144
140
  }
145
141
 
@@ -147,90 +143,104 @@ Server.gracefulShutdown = () => {
147
143
  Server.stopListeners();
148
144
  Server._graceful(() => {
149
145
  // log();
150
- logger.loginfo("Failed to shutdown naturally. Exiting.");
146
+ Server.loginfo("Failed to shutdown naturally. Exiting.");
151
147
  process.exit(0);
152
148
  });
153
149
  }
154
150
 
155
- Server._graceful = shutdown => {
151
+ Server._graceful = async (shutdown) => {
156
152
  if (!Server.cluster && shutdown) {
157
- ['outbound', 'cfreader', 'plugins'].forEach(module => {
158
- process.emit('message', {event: `${module }.shutdown`});
159
- });
153
+ for (const module of ['outbound', 'cfreader', 'plugins']) {
154
+ process.emit('message', {event: `${module}.shutdown`});
155
+ }
160
156
  const t = setTimeout(shutdown, Server.cfg.main.force_shutdown_timeout * 1000);
161
157
  return t.unref();
162
158
  }
163
159
 
164
- if (gracefull_in_progress) {
165
- logger.lognotice("Restart currently in progress - ignoring request");
160
+ if (graceful_in_progress) {
161
+ Server.lognotice("Restart currently in progress - ignoring request");
166
162
  return;
167
163
  }
168
164
 
169
- gracefull_in_progress = true;
165
+ graceful_in_progress = true;
170
166
  // TODO: Make these configurable
171
167
  const disconnect_timeout = 30;
172
168
  const exit_timeout = 30;
173
169
  cluster.removeAllListeners('exit');
170
+
174
171
  // we reload using eachLimit where limit = num_workers - 1
175
172
  // this kills all-but-one workers in parallel, leaving one running
176
173
  // for new connections, and then restarts that one last worker.
174
+
177
175
  const worker_ids = Object.keys(cluster.workers);
178
176
  let limit = worker_ids.length - 1;
179
177
  if (limit < 2) limit = 1;
180
- async.eachLimit(worker_ids, limit, (id, cb) => {
181
- logger.lognotice(`Killing node: ${id}`);
182
- const worker = cluster.workers[id];
183
- ['outbound', 'cfreader', 'plugins'].forEach(module => {
184
- worker.send({event: `${module }.shutdown`});
185
- })
186
- worker.disconnect();
187
- let disconnect_received = false;
188
- const disconnect_timer = setTimeout(() => {
189
- if (!disconnect_received) {
190
- logger.logcrit("Disconnect never received by worker. Killing.");
191
- worker.kill();
192
- }
193
- }, disconnect_timeout * 1000);
194
- worker.once("disconnect", () => {
195
- clearTimeout(disconnect_timer);
196
- disconnect_received = true;
197
- logger.lognotice("Disconnect complete");
198
- let dead = false;
199
- const timer = setTimeout(() => {
200
- if (!dead) {
201
- logger.logcrit(`Worker ${id} failed to shutdown. Killing.`);
202
- worker.kill();
178
+
179
+ const todo = []
180
+
181
+ for (const id of Object.keys(cluster.workers)) {
182
+ todo.push((id) => {
183
+ return new Promise((resolve) => {
184
+ Server.lognotice(`Killing worker: ${id}`);
185
+ const worker = cluster.workers[id];
186
+ for (const module of ['outbound', 'cfreader', 'plugins']) {
187
+ worker.send({event: `${module }.shutdown`});
188
+ }
189
+ worker.disconnect();
190
+ let disconnect_received = false;
191
+ const disconnect_timer = setTimeout(() => {
192
+ if (!disconnect_received) {
193
+ Server.logcrit("Disconnect never received by worker. Killing.");
194
+ worker.kill();
195
+ }
196
+ }, disconnect_timeout * 1000);
197
+
198
+ worker.once('disconnect', () => {
199
+ clearTimeout(disconnect_timer);
200
+ disconnect_received = true;
201
+ Server.lognotice('Disconnect complete');
202
+ let dead = false;
203
+ const timer = setTimeout(() => {
204
+ if (!dead) {
205
+ Server.logcrit(`Worker ${id} failed to shutdown. Killing.`);
206
+ worker.kill();
207
+ }
208
+ }, exit_timeout * 1000);
209
+ worker.once('exit', () => {
210
+ dead = true;
211
+ clearTimeout(timer);
212
+ if (shutdown) resolve()
213
+ })
214
+ })
215
+ if (!shutdown) {
216
+ const newWorker = cluster.fork();
217
+ newWorker.once('listening', () => {
218
+ Server.lognotice('Replacement worker online.');
219
+ newWorker.on('exit', (code, signal) => {
220
+ cluster_exit_listener(newWorker, code, signal);
221
+ });
222
+ resolve()
223
+ })
203
224
  }
204
- }, exit_timeout * 1000);
205
- worker.once("exit", () => {
206
- dead = true;
207
- clearTimeout(timer);
208
- if (shutdown) cb();
209
- });
210
- });
211
- if (shutdown) return;
212
- const newWorker = cluster.fork();
213
- newWorker.once("listening", () => {
214
- logger.lognotice("Replacement worker online.");
215
- newWorker.on('exit', (code, signal) => {
216
- cluster_exit_listener(newWorker, code, signal);
217
- });
218
- cb();
219
- });
220
- }, err => {
221
- // err can basically never happen, but fuckit...
222
- if (err) logger.logerror(err);
223
- if (shutdown) {
224
- logger.loginfo("Workers closed. Shutting down master process subsystems");
225
- ['outbound', 'cfreader', 'plugins'].forEach(module => {
226
- process.emit('message', {event: `${module }.shutdown`});
227
225
  })
228
- const t2 = setTimeout(shutdown, Server.cfg.main.force_shutdown_timeout * 1000);
229
- return t2.unref();
226
+ })
227
+ }
228
+
229
+ while (todo.length) {
230
+ // process batches of workers
231
+ await Promise.all(todo.splice(0, limit))
232
+ }
233
+
234
+ if (shutdown) {
235
+ Server.loginfo("Workers closed. Shutting down master process subsystems");
236
+ for (const module of ['outbound', 'cfreader', 'plugins']) {
237
+ process.emit('message', {event: `${module}.shutdown`});
230
238
  }
231
- gracefull_in_progress = false;
232
- logger.lognotice(`Reload complete, workers: ${JSON.stringify(Object.keys(cluster.workers))}`);
233
- });
239
+ const t2 = setTimeout(shutdown, Server.cfg.main.force_shutdown_timeout * 1000);
240
+ return t2.unref();
241
+ }
242
+ graceful_in_progress = false;
243
+ Server.lognotice(`Reload complete, workers: ${JSON.stringify(Object.keys(cluster.workers))}`);
234
244
  }
235
245
 
236
246
  Server.sendToMaster = (command, params) => {
@@ -250,7 +260,7 @@ Server.sendToMaster = (command, params) => {
250
260
 
251
261
  Server.receiveAsMaster = (command, params) => {
252
262
  if (!Server[command]) {
253
- logger.logerror(`Invalid command: ${command}`);
263
+ Server.logerror(`Invalid command: ${command}`);
254
264
  return;
255
265
  }
256
266
  Server[command].apply(Server, params);
@@ -338,15 +348,15 @@ Server.createServer = params => {
338
348
  Server.load_default_tls_config = done => {
339
349
  // this fn exists solely for testing
340
350
  if (Server.config.root_path != tls_socket.config.root_path) {
341
- logger.loginfo(`resetting tls_config.config path to ${Server.config.root_path}`);
351
+ Server.loginfo(`resetting tls_config.config path to ${Server.config.root_path}`);
342
352
  tls_socket.config = tls_socket.config.module_config(path.dirname(Server.config.root_path));
343
353
  }
344
- tls_socket.getSocketOpts('*', (opts) => {
354
+ tls_socket.getSocketOpts('*').then(opts => {
345
355
  done(opts);
346
- });
356
+ })
347
357
  }
348
358
 
349
- Server.get_smtp_server = (ep, inactivity_timeout, done) => {
359
+ Server.get_smtp_server = async (ep, inactivity_timeout) => {
350
360
  let server;
351
361
 
352
362
  function onConnect (client) {
@@ -367,116 +377,118 @@ Server.get_smtp_server = (ep, inactivity_timeout, done) => {
367
377
  }
368
378
 
369
379
  if (ep.port === parseInt(Server.cfg.main.smtps_port, 10)) {
370
- logger.loginfo('getting SocketOpts for SMTPS server');
371
- tls_socket.getSocketOpts('*', opts => {
372
- logger.loginfo(`Creating TLS server on ${ep}`);
373
-
374
- opts.rejectUnauthorized = tls_socket.get_rejectUnauthorized(opts.rejectUnauthorized, ep.port, tls_socket.cfg.main.requireAuthorized)
375
-
376
- server = tls.createServer(opts, onConnect);
377
- tls_socket.addOCSP(server);
378
- server.has_tls=true;
379
- server.on('resumeSession', (id, rsDone) => {
380
- logger.loginfo('client requested TLS resumeSession');
381
- rsDone(null, null);
382
- })
383
- Server.listeners.push(server);
384
- done(server);
380
+ Server.loginfo('getting SocketOpts for SMTPS server');
381
+ const opts = await tls_socket.getSocketOpts('*')
382
+ Server.loginfo(`Creating TLS server on ${ep}`);
383
+
384
+ opts.rejectUnauthorized = tls_socket.get_rejectUnauthorized(opts.rejectUnauthorized, ep.port, tls_socket.cfg.main.requireAuthorized)
385
+
386
+ server = tls.createServer(opts, onConnect);
387
+ tls_socket.addOCSP(server);
388
+ server.has_tls=true;
389
+ server.on('resumeSession', (id, rsDone) => {
390
+ Server.loginfo('client requested TLS resumeSession');
391
+ rsDone(null, null);
385
392
  })
393
+ Server.listeners.push(server);
394
+ return server
386
395
  }
387
396
  else {
388
397
  server = tls_socket.createServer(onConnect);
389
398
  server.has_tls = false;
390
- tls_socket.getSocketOpts('*', opts => {
391
- Server.listeners.push(server);
392
- done(server);
393
- })
399
+ const opts = await tls_socket.getSocketOpts('*')
400
+ Server.listeners.push(server);
401
+ return server
394
402
  }
395
403
  }
396
404
 
397
- Server.setup_smtp_listeners = (plugins2, type, inactivity_timeout) => {
405
+ Server.setup_smtp_listeners = async (plugins2, type, inactivity_timeout) => {
398
406
 
399
- async.each(
400
- Server.get_listen_addrs(Server.cfg.main), // array of listeners
407
+ const errors = []
401
408
 
402
- function setupListener (listen_address, listenerDone) {
409
+ for (const listen_address of Server.get_listen_addrs(Server.cfg.main)) {
403
410
 
404
- const ep = endpoint(listen_address, 25);
405
- if (ep instanceof Error) return listenerDone(
406
- new Error(`Invalid "listen" format in smtp.ini: ${listen_address}`));
411
+ const ep = endpoint(listen_address, 25);
407
412
 
408
- Server.get_smtp_server(ep, inactivity_timeout, (server) => {
409
- if (!server) return listenerDone();
413
+ if (ep instanceof Error) {
414
+ Server.logerror(`Invalid "listen" format in smtp.ini: ${listen_address}`)
415
+ continue
416
+ }
410
417
 
411
- server.notes = Server.notes;
412
- if (Server.cluster) server.cluster = Server.cluster;
418
+ const server = await Server.get_smtp_server(ep, inactivity_timeout)
419
+ if (!server) continue;
413
420
 
414
- server
415
- .on('listening', function () {
416
- const addr = this.address();
417
- logger.lognotice(`Listening on ${endpoint(addr)}`);
418
- listenerDone();
419
- })
420
- .on('close', () => {
421
- logger.loginfo(`Listener ${ep} stopped`);
422
- })
423
- .on('error', e => {
424
- if (e.code !== 'EAFNOSUPPORT') return listenerDone(e);
425
- // Fallback from IPv6 to IPv4 if not supported
426
- // But only if we supplied the default of [::0]:25
427
- if (/^::0/.test(ep.host) && Server.default_host) {
428
- server.listen(ep.port, '0.0.0.0', 0);
429
- return;
430
- }
431
- // Pass error to callback
432
- listenerDone(e);
433
- });
434
- ep.bind(server, {backlog: 0});
435
- });
436
- },
437
- function runInitHooks (err) {
438
- if (err) {
439
- logger.logerror(`Failed to setup listeners: ${err.message}`);
440
- return logger.dump_and_exit(-1);
441
- }
442
- Server.listening();
443
- plugins2.run_hooks(`init_${type}`, Server);
421
+ server.notes = Server.notes;
422
+ if (Server.cluster) server.cluster = Server.cluster;
423
+
424
+ server
425
+ .on('listening', function () {
426
+ const addr = this.address();
427
+ Server.lognotice(`Listening on ${endpoint(addr)}`);
428
+ })
429
+ .on('close', () => {
430
+ Server.loginfo(`Listener ${ep} stopped`);
431
+ })
432
+ .on('error', e => {
433
+ errors.push(e)
434
+ Server.logerror(`Failed to setup listeners: ${e.message}`);
435
+ if (e.code !== 'EAFNOSUPPORT') {
436
+ Server.logerror(e)
437
+ return
438
+ }
439
+ // Fallback from IPv6 to IPv4 if not supported
440
+ // But only if we supplied the default of [::0]:25
441
+ if (/^::0/.test(ep.host) && Server.default_host) {
442
+ server.listen(ep.port, '0.0.0.0', 0);
443
+ return;
444
+ }
445
+ // Pass error to callback
446
+ Server.logerror(e)
447
+ })
448
+
449
+ await ep.bind(server, {backlog: 0});
450
+ }
451
+
452
+ if (errors.length) {
453
+ for (const e of errors) {
454
+ Server.logerror(`Failed to setup listeners: ${e.message}`);
444
455
  }
445
- );
456
+ return logger.dump_and_exit(-1);
457
+ }
458
+ Server.listening();
459
+ plugins2.run_hooks(`init_${type}`, Server);
446
460
  }
447
461
 
448
- Server.setup_http_listeners = () => {
449
- if (!Server.http.cfg) return;
450
- if (!Server.http.cfg.listen) return;
462
+ Server.setup_http_listeners = async () => {
463
+ if (!Server.http?.cfg?.listen) return;
451
464
 
452
465
  const listeners = Server.get_listen_addrs(Server.http.cfg, 80);
453
466
  if (!listeners.length) return;
454
467
 
455
468
  try {
456
469
  Server.http.express = require('express');
457
- logger.loginfo('express loaded at Server.http.express');
470
+ Server.loginfo('express loaded at Server.http.express');
458
471
  }
459
472
  catch (err) {
460
- logger.logerror('express failed to load. No http server. ' +
461
- ' Try installing express with: npm install -g express');
473
+ Server.logerror('express failed to load. No http server. Install express with: npm install -g express');
462
474
  return;
463
475
  }
464
476
 
465
477
  const app = Server.http.express();
466
478
  Server.http.app = app;
467
- logger.loginfo('express app is at Server.http.app');
479
+ Server.loginfo('express app is at Server.http.app');
480
+
481
+ for (const listen_address of listeners) {
468
482
 
469
- function setupListener (listen_address, cb) {
470
483
  const ep = endpoint(listen_address, 80);
471
484
  if (ep instanceof Error) {
472
- return cb(new Error(`Invalid format for listen in http.ini: ${listen_address}`));
485
+ Server.logerror(`Invalid format for listen in http.ini: ${listen_address}`)
486
+ continue
473
487
  }
474
488
 
475
489
  if (443 == ep.port) {
476
- // clone the default TLS opts
477
- const tlsOpts = Object.assign({}, tls_socket.certsByHost['*']);
490
+ const tlsOpts = { ...tls_socket.certsByHost['*'] }
478
491
  tlsOpts.requestCert = false; // not appropriate for HTTPS
479
- // console.log(tlsOpts);
480
492
  Server.http.server = require('https').createServer(tlsOpts, app);
481
493
  }
482
494
  else {
@@ -486,30 +498,19 @@ Server.setup_http_listeners = () => {
486
498
  Server.listeners.push(Server.http.server);
487
499
 
488
500
  Server.http.server.on('listening', function () {
489
- const addr = this.address();
490
- logger.lognotice(`Listening on ${endpoint(addr)}`);
491
- cb();
492
- });
501
+ Server.lognotice(`Listening on ${endpoint(this.address())}`);
502
+ })
493
503
 
494
504
  Server.http.server.on('error', e => {
495
- logger.logerror(e);
496
- cb(e);
497
- });
498
-
499
- ep.bind(Server.http.server, {backlog: 0});
500
- }
501
-
502
- function registerRoutes (err) {
503
- if (err) {
504
- logger.logerror(`Failed to setup http routes: ${err.message}`);
505
- }
505
+ Server.logerror(e);
506
+ })
506
507
 
507
- Server.plugins.run_hooks('init_http', Server);
508
- app.use(Server.http.express.static(Server.get_http_docroot()));
509
- app.use(Server.handle404);
508
+ await ep.bind(Server.http.server, {backlog: 0});
510
509
  }
511
510
 
512
- async.each(listeners, setupListener, registerRoutes);
511
+ Server.plugins.run_hooks('init_http', Server);
512
+ app.use(Server.http.express.static(Server.get_http_docroot()));
513
+ app.use(Server.handle404);
513
514
  }
514
515
 
515
516
  Server.init_master_respond = (retval, msg) => {
@@ -547,13 +548,13 @@ Server.init_master_respond = (retval, msg) => {
547
548
  .send({event: 'outbound.load_pid_queue', data: pids[j]});
548
549
  }
549
550
  cluster.on('online', worker => {
550
- logger.lognotice(
551
+ Server.lognotice(
551
552
  'worker started',
552
553
  { worker: worker.id, pid: worker.process.pid }
553
554
  );
554
555
  });
555
556
  cluster.on('listening', (worker, address) => {
556
- logger.lognotice(`worker ${worker.id} listening on ${endpoint(address)}`);
557
+ Server.lognotice(`worker ${worker.id} listening on ${endpoint(address)}`);
557
558
  });
558
559
  cluster.on('exit', cluster_exit_listener);
559
560
  });
@@ -561,10 +562,10 @@ Server.init_master_respond = (retval, msg) => {
561
562
 
562
563
  function cluster_exit_listener (worker, code, signal) {
563
564
  if (signal) {
564
- logger.lognotice(`worker ${worker.id} killed by signal ${signal}`);
565
+ Server.lognotice(`worker ${worker.id} killed by signal ${signal}`);
565
566
  }
566
567
  else if (code !== 0) {
567
- logger.lognotice(`worker ${worker.id} exited with error code: ${code}`);
568
+ Server.lognotice(`worker ${worker.id} exited with error code: ${code}`);
568
569
  }
569
570
  if (signal || code !== 0) {
570
571
  // Restart worker
@@ -618,36 +619,35 @@ Server.listening = () => {
618
619
  }
619
620
 
620
621
  Server.init_http_respond = () => {
621
- logger.loginfo('init_http_respond');
622
+ Server.loginfo('init_http_respond');
622
623
 
623
624
  let WebSocketServer;
624
625
  try { WebSocketServer = require('ws').Server; }
625
626
  catch (e) {
626
- logger.logerror(`unable to load ws.\n did you: npm install -g ws?`);
627
+ Server.logerror(`unable to load ws.\n did you: npm install -g ws?`);
627
628
  return;
628
629
  }
629
630
 
630
631
  if (!WebSocketServer) {
631
- logger.logerror('ws failed to load');
632
+ Server.logerror('ws failed to load');
632
633
  return;
633
634
  }
634
635
 
635
636
  Server.http.wss = new WebSocketServer({ server: Server.http.server });
636
- logger.loginfo('Server.http.wss loaded');
637
+ Server.loginfo('Server.http.wss loaded');
637
638
 
638
639
  Server.plugins.run_hooks('init_wss', Server);
639
640
  }
640
641
 
641
642
  Server.init_wss_respond = () => {
642
- logger.loginfo('init_wss_respond');
643
- // logger.logdebug(arguments);
643
+ Server.loginfo('init_wss_respond');
644
644
  }
645
645
 
646
646
  Server.get_http_docroot = () => {
647
647
  if (Server.http.cfg.docroot) return Server.http.cfg.docroot;
648
648
 
649
- Server.http.cfg.docroot = path.join( (process.env.HARAKA || __dirname), '/html');
650
- logger.loginfo(`using html docroot: ${Server.http.cfg.docroot}`);
649
+ Server.http.cfg.docroot = path.join( (process.env.HARAKA || __dirname), 'http', 'html');
650
+ Server.loginfo(`using html docroot: ${Server.http.cfg.docroot}`);
651
651
  return Server.http.cfg.docroot;
652
652
  }
653
653