Haraka 3.1.0 → 3.1.2

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 (160) hide show
  1. package/.prettierignore +4 -0
  2. package/CONTRIBUTORS.md +5 -5
  3. package/Changes.md +69 -50
  4. package/Plugins.md +3 -1
  5. package/README.md +1 -1
  6. package/bin/haraka +475 -478
  7. package/config/outbound.ini +3 -0
  8. package/connection.js +1072 -1108
  9. package/docs/Connection.md +29 -30
  10. package/docs/CoreConfig.md +38 -39
  11. package/docs/CustomReturnCodes.md +0 -1
  12. package/docs/HAProxy.md +2 -2
  13. package/docs/Header.md +1 -1
  14. package/docs/Logging.md +29 -5
  15. package/docs/Outbound.md +93 -78
  16. package/docs/Plugins.md +103 -108
  17. package/docs/Transaction.md +49 -51
  18. package/docs/Tutorial.md +127 -143
  19. package/docs/deprecated/access.md +0 -1
  20. package/docs/deprecated/backscatterer.md +2 -3
  21. package/docs/deprecated/connect.rdns_access.md +18 -27
  22. package/docs/deprecated/data.headers.md +0 -1
  23. package/docs/deprecated/data.nomsgid.md +1 -2
  24. package/docs/deprecated/data.noreceived.md +1 -2
  25. package/docs/deprecated/data.rfc5322_header_checks.md +1 -2
  26. package/docs/deprecated/dkim_sign.md +13 -17
  27. package/docs/deprecated/dkim_verify.md +9 -17
  28. package/docs/deprecated/dnsbl.md +36 -38
  29. package/docs/deprecated/dnswl.md +41 -43
  30. package/docs/deprecated/lookup_rdns.strict.md +21 -34
  31. package/docs/deprecated/mail_from.access.md +17 -25
  32. package/docs/deprecated/mail_from.blocklist.md +9 -12
  33. package/docs/deprecated/mail_from.nobounces.md +1 -2
  34. package/docs/deprecated/rcpt_to.access.md +20 -27
  35. package/docs/deprecated/rcpt_to.blocklist.md +10 -13
  36. package/docs/deprecated/rcpt_to.routes.md +0 -1
  37. package/docs/deprecated/rdns.regexp.md +13 -15
  38. package/docs/plugins/aliases.md +89 -89
  39. package/docs/plugins/auth/auth_bridge.md +5 -7
  40. package/docs/plugins/auth/auth_ldap.md +11 -14
  41. package/docs/plugins/auth/auth_proxy.md +10 -12
  42. package/docs/plugins/auth/auth_vpopmaild.md +5 -6
  43. package/docs/plugins/auth/flat_file.md +4 -4
  44. package/docs/plugins/block_me.md +3 -3
  45. package/docs/plugins/data.signatures.md +1 -2
  46. package/docs/plugins/delay_deny.md +3 -4
  47. package/docs/plugins/max_unrecognized_commands.md +4 -4
  48. package/docs/plugins/prevent_credential_leaks.md +6 -6
  49. package/docs/plugins/process_title.md +18 -18
  50. package/docs/plugins/queue/deliver.md +2 -3
  51. package/docs/plugins/queue/discard.md +4 -4
  52. package/docs/plugins/queue/lmtp.md +1 -3
  53. package/docs/plugins/queue/qmail-queue.md +7 -9
  54. package/docs/plugins/queue/quarantine.md +16 -21
  55. package/docs/plugins/queue/rabbitmq.md +8 -11
  56. package/docs/plugins/queue/rabbitmq_amqplib.md +43 -39
  57. package/docs/plugins/queue/smtp_bridge.md +7 -10
  58. package/docs/plugins/queue/smtp_forward.md +42 -34
  59. package/docs/plugins/queue/smtp_proxy.md +30 -29
  60. package/docs/plugins/queue/test.md +1 -3
  61. package/docs/plugins/rcpt_to.in_host_list.md +6 -6
  62. package/docs/plugins/rcpt_to.max_count.md +1 -1
  63. package/docs/plugins/record_envelope_addresses.md +3 -3
  64. package/docs/plugins/reseed_rng.md +6 -6
  65. package/docs/plugins/status.md +9 -8
  66. package/docs/plugins/tarpit.md +7 -11
  67. package/docs/plugins/tls.md +12 -17
  68. package/docs/plugins/toobusy.md +4 -4
  69. package/docs/plugins/xclient.md +3 -3
  70. package/docs/tutorials/Migrating_from_v1_to_v2.md +19 -41
  71. package/docs/tutorials/SettingUpOutbound.md +6 -9
  72. package/endpoint.js +35 -38
  73. package/eslint.config.mjs +22 -19
  74. package/haraka.js +42 -47
  75. package/host_pool.js +75 -79
  76. package/http/html/404.html +45 -49
  77. package/http/html/index.html +39 -28
  78. package/http/package.json +2 -4
  79. package/line_socket.js +27 -28
  80. package/logger.js +182 -201
  81. package/outbound/client_pool.js +34 -27
  82. package/outbound/config.js +64 -59
  83. package/outbound/fsync_writestream.js +24 -25
  84. package/outbound/hmail.js +888 -835
  85. package/outbound/index.js +194 -187
  86. package/outbound/qfile.js +49 -52
  87. package/outbound/queue.js +197 -190
  88. package/outbound/timer_queue.js +41 -43
  89. package/outbound/tls.js +68 -61
  90. package/outbound/todo.js +11 -11
  91. package/package.json +38 -33
  92. package/plugins/.eslintrc.yaml +0 -1
  93. package/plugins/auth/auth_base.js +123 -127
  94. package/plugins/auth/auth_bridge.js +7 -7
  95. package/plugins/auth/auth_proxy.js +121 -126
  96. package/plugins/auth/auth_vpopmaild.js +84 -85
  97. package/plugins/auth/flat_file.js +18 -17
  98. package/plugins/block_me.js +31 -31
  99. package/plugins/data.signatures.js +13 -13
  100. package/plugins/delay_deny.js +65 -61
  101. package/plugins/prevent_credential_leaks.js +23 -23
  102. package/plugins/process_title.js +125 -128
  103. package/plugins/profile.js +5 -5
  104. package/plugins/queue/deliver.js +3 -3
  105. package/plugins/queue/discard.js +13 -14
  106. package/plugins/queue/lmtp.js +16 -17
  107. package/plugins/queue/qmail-queue.js +54 -55
  108. package/plugins/queue/quarantine.js +68 -70
  109. package/plugins/queue/rabbitmq.js +80 -87
  110. package/plugins/queue/rabbitmq_amqplib.js +75 -54
  111. package/plugins/queue/smtp_bridge.js +16 -16
  112. package/plugins/queue/smtp_forward.js +175 -179
  113. package/plugins/queue/smtp_proxy.js +69 -71
  114. package/plugins/queue/test.js +9 -9
  115. package/plugins/rcpt_to.host_list_base.js +30 -34
  116. package/plugins/rcpt_to.in_host_list.js +19 -19
  117. package/plugins/record_envelope_addresses.js +4 -4
  118. package/plugins/reseed_rng.js +4 -4
  119. package/plugins/status.js +90 -97
  120. package/plugins/tarpit.js +25 -14
  121. package/plugins/tls.js +68 -68
  122. package/plugins/toobusy.js +21 -23
  123. package/plugins/xclient.js +51 -53
  124. package/plugins.js +276 -293
  125. package/rfc1869.js +30 -35
  126. package/server.js +308 -299
  127. package/smtp_client.js +244 -228
  128. package/test/.eslintrc.yaml +0 -1
  129. package/test/connection.js +127 -134
  130. package/test/endpoint.js +53 -47
  131. package/test/fixtures/line_socket.js +12 -12
  132. package/test/fixtures/util_hmailitem.js +89 -85
  133. package/test/host_pool.js +90 -92
  134. package/test/installation/plugins/base_plugin.js +2 -2
  135. package/test/installation/plugins/folder_plugin/index.js +2 -3
  136. package/test/installation/plugins/inherits.js +3 -3
  137. package/test/installation/plugins/load_first.js +2 -3
  138. package/test/installation/plugins/plugin.js +1 -3
  139. package/test/installation/plugins/tls.js +2 -4
  140. package/test/logger.js +135 -116
  141. package/test/outbound/hmail.js +49 -35
  142. package/test/outbound/index.js +118 -101
  143. package/test/outbound/qfile.js +51 -53
  144. package/test/outbound_bounce_net_errors.js +84 -69
  145. package/test/outbound_bounce_rfc3464.js +235 -165
  146. package/test/plugins/auth/auth_base.js +420 -279
  147. package/test/plugins/auth/auth_vpopmaild.js +38 -39
  148. package/test/plugins/queue/smtp_forward.js +126 -104
  149. package/test/plugins/rcpt_to.host_list_base.js +85 -67
  150. package/test/plugins/rcpt_to.in_host_list.js +159 -112
  151. package/test/plugins/status.js +71 -64
  152. package/test/plugins/tls.js +37 -34
  153. package/test/plugins.js +97 -92
  154. package/test/rfc1869.js +19 -26
  155. package/test/server.js +293 -272
  156. package/test/smtp_client.js +180 -176
  157. package/test/tls_socket.js +62 -66
  158. package/test/transaction.js +159 -160
  159. package/tls_socket.js +331 -333
  160. package/transaction.js +129 -137
package/server.js CHANGED
@@ -1,42 +1,43 @@
1
- 'use strict';
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');
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
9
 
10
- const daemon = require('daemon');
11
- const constants = require('haraka-constants');
10
+ const daemon = require('daemon')
11
+ const constants = require('haraka-constants')
12
12
 
13
- const tls_socket = require('./tls_socket');
14
- const conn = require('./connection');
15
- const outbound = require('./outbound');
16
- const endpoint = require('./endpoint');
13
+ const tls_socket = require('./tls_socket')
14
+ const conn = require('./connection')
15
+ const outbound = require('./outbound')
16
+ const endpoint = require('./endpoint')
17
17
 
18
- const Server = exports;
19
- Server.logger = require('./logger');
20
- Server.config = require('haraka-config');
21
- Server.plugins = require('./plugins');
22
- Server.notes = {};
18
+ const Server = exports
19
+ Server.logger = require('./logger')
20
+ Server.config = require('haraka-config')
21
+ Server.plugins = require('./plugins')
22
+ Server.notes = {}
23
23
 
24
- const { logger } = Server;
24
+ const { logger } = Server
25
25
 
26
26
  // Need these here so we can run hooks
27
- logger.add_log_methods(Server, 'server');
27
+ logger.add_log_methods(Server, 'server')
28
28
 
29
- Server.listeners = [];
29
+ Server.listeners = []
30
30
 
31
31
  Server.load_smtp_ini = () => {
32
- Server.cfg = Server.config.get('smtp.ini', {
33
- booleans: [
34
- '-main.daemonize',
35
- '-main.graceful_shutdown',
36
- ],
37
- }, () => {
38
- Server.load_smtp_ini();
39
- });
32
+ Server.cfg = Server.config.get(
33
+ 'smtp.ini',
34
+ {
35
+ booleans: ['-main.daemonize', '-main.graceful_shutdown'],
36
+ },
37
+ () => {
38
+ Server.load_smtp_ini()
39
+ },
40
+ )
40
41
 
41
42
  if (Server.cfg.main.nodes === undefined) {
42
43
  Server.logwarn(`smtp.ini.nodes unset, using 1, see https://github.com/haraka/Haraka/wiki/Performance-Tuning`)
@@ -49,162 +50,161 @@ Server.load_smtp_ini = () => {
49
50
  force_shutdown_timeout: 30,
50
51
  smtps_port: 465,
51
52
  nodes: 1,
52
- };
53
+ }
53
54
 
54
55
  for (const key in defaults) {
55
- if (Server.cfg.main[key] !== undefined) continue;
56
- Server.cfg.main[key] = defaults[key];
56
+ if (Server.cfg.main[key] !== undefined) continue
57
+ Server.cfg.main[key] = defaults[key]
57
58
  }
58
59
  }
59
60
 
60
61
  Server.load_http_ini = () => {
61
- Server.http = {};
62
+ Server.http = {}
62
63
  Server.http.cfg = Server.config.get('http.ini', () => {
63
- Server.load_http_ini();
64
- }).main;
64
+ Server.load_http_ini()
65
+ }).main
65
66
  }
66
67
 
67
- Server.load_smtp_ini();
68
- Server.load_http_ini();
68
+ Server.load_smtp_ini()
69
+ Server.load_http_ini()
69
70
 
70
71
  Server.daemonize = function () {
71
- const c = this.cfg.main;
72
- if (!c.daemonize) return;
72
+ const c = this.cfg.main
73
+ if (!c.daemonize) return
73
74
 
74
75
  if (!process.env.__daemon) {
75
76
  // Remove process.on('exit') listeners otherwise
76
77
  // we get a spurious 'Exiting' log entry.
77
- process.removeAllListeners('exit');
78
- Server.lognotice('Daemonizing...');
78
+ process.removeAllListeners('exit')
79
+ Server.lognotice('Daemonizing...')
79
80
  }
80
81
 
81
- const log_fd = fs.openSync(c.daemon_log_file, 'a');
82
- daemon({ cwd: process.cwd(), stdout: log_fd });
82
+ const log_fd = fs.openSync(c.daemon_log_file, 'a')
83
+ daemon({ cwd: process.cwd(), stdout: log_fd })
83
84
 
84
85
  // We are the daemon from here on...
85
- const npid = require('npid');
86
+ const npid = require('npid')
86
87
  try {
87
- npid.create(c.daemon_pid_file).removeOnExit();
88
- }
89
- catch (err) {
90
- Server.logerror(err.message);
91
- logger.dump_and_exit(1);
88
+ npid.create(c.daemon_pid_file).removeOnExit()
89
+ } catch (err) {
90
+ Server.logerror(err.message)
91
+ logger.dump_and_exit(1)
92
92
  }
93
93
  }
94
94
 
95
- Server.flushQueue = domain => {
95
+ Server.flushQueue = (domain) => {
96
96
  if (!Server.cluster) {
97
- outbound.flush_queue(domain);
98
- return;
97
+ outbound.flush_queue(domain)
98
+ return
99
99
  }
100
100
 
101
101
  for (const id in cluster.workers) {
102
- cluster.workers[id].send({event: 'outbound.flush_queue', domain});
102
+ cluster.workers[id].send({ event: 'outbound.flush_queue', domain })
103
103
  }
104
104
  }
105
105
 
106
- let graceful_in_progress = false;
106
+ let graceful_in_progress = false
107
107
 
108
108
  Server.gracefulRestart = () => {
109
- Server._graceful();
109
+ Server._graceful()
110
110
  }
111
111
 
112
112
  Server.stopListeners = () => {
113
- Server.loginfo('Shutting down listeners');
113
+ Server.loginfo('Shutting down listeners')
114
114
  for (const l of Server.listeners) {
115
- l.close();
115
+ l.close()
116
116
  }
117
- Server.listeners = [];
117
+ Server.listeners = []
118
118
  }
119
119
 
120
120
  Server.performShutdown = () => {
121
121
  if (Server.cfg.main.graceful_shutdown) {
122
- return Server.gracefulShutdown();
122
+ return Server.gracefulShutdown()
123
123
  }
124
- Server.loginfo("Shutting down.");
125
- process.exit(0);
124
+ Server.loginfo('Shutting down.')
125
+ process.exit(0)
126
126
  }
127
127
 
128
128
  Server.gracefulShutdown = () => {
129
- Server.stopListeners();
129
+ Server.stopListeners()
130
130
  Server._graceful(() => {
131
131
  // log();
132
- Server.loginfo("Failed to shutdown naturally. Exiting.");
133
- process.exit(0);
134
- });
132
+ Server.loginfo('Failed to shutdown naturally. Exiting.')
133
+ process.exit(0)
134
+ })
135
135
  }
136
136
 
137
137
  Server._graceful = async (shutdown) => {
138
138
  if (!Server.cluster && shutdown) {
139
139
  for (const module of ['outbound', 'cfreader', 'plugins']) {
140
- process.emit('message', {event: `${module}.shutdown`});
140
+ process.emit('message', { event: `${module}.shutdown` })
141
141
  }
142
- const t = setTimeout(shutdown, Server.cfg.main.force_shutdown_timeout * 1000);
143
- return t.unref();
142
+ const t = setTimeout(shutdown, Server.cfg.main.force_shutdown_timeout * 1000)
143
+ return t.unref()
144
144
  }
145
145
 
146
146
  if (graceful_in_progress) {
147
- Server.lognotice("Restart currently in progress - ignoring request");
148
- return;
147
+ Server.lognotice('Restart currently in progress - ignoring request')
148
+ return
149
149
  }
150
150
 
151
- graceful_in_progress = true;
151
+ graceful_in_progress = true
152
152
  // TODO: Make these configurable
153
- const disconnect_timeout = 30;
154
- const exit_timeout = 30;
155
- cluster.removeAllListeners('exit');
153
+ const disconnect_timeout = 30
154
+ const exit_timeout = 30
155
+ cluster.removeAllListeners('exit')
156
156
 
157
157
  // we reload using eachLimit where limit = num_workers - 1
158
158
  // this kills all-but-one workers in parallel, leaving one running
159
159
  // for new connections, and then restarts that one last worker.
160
160
 
161
- const worker_ids = Object.keys(cluster.workers);
162
- let limit = worker_ids.length - 1;
163
- if (limit < 2) limit = 1;
161
+ const worker_ids = Object.keys(cluster.workers)
162
+ let limit = worker_ids.length - 1
163
+ if (limit < 2) limit = 1
164
164
 
165
165
  const todo = []
166
166
 
167
167
  for (const id of Object.keys(cluster.workers)) {
168
168
  todo.push((id) => {
169
169
  return new Promise((resolve) => {
170
- Server.lognotice(`Killing worker: ${id}`);
171
- const worker = cluster.workers[id];
170
+ Server.lognotice(`Killing worker: ${id}`)
171
+ const worker = cluster.workers[id]
172
172
  for (const module of ['outbound', 'cfreader', 'plugins']) {
173
- worker.send({event: `${module }.shutdown`});
173
+ worker.send({ event: `${module}.shutdown` })
174
174
  }
175
- worker.disconnect();
176
- let disconnect_received = false;
175
+ worker.disconnect()
176
+ let disconnect_received = false
177
177
  const disconnect_timer = setTimeout(() => {
178
178
  if (!disconnect_received) {
179
- Server.logcrit("Disconnect never received by worker. Killing.");
180
- worker.kill();
179
+ Server.logcrit('Disconnect never received by worker. Killing.')
180
+ worker.kill()
181
181
  }
182
- }, disconnect_timeout * 1000);
182
+ }, disconnect_timeout * 1000)
183
183
 
184
184
  worker.once('disconnect', () => {
185
- clearTimeout(disconnect_timer);
186
- disconnect_received = true;
187
- Server.lognotice('Disconnect complete');
188
- let dead = false;
185
+ clearTimeout(disconnect_timer)
186
+ disconnect_received = true
187
+ Server.lognotice('Disconnect complete')
188
+ let dead = false
189
189
  const timer = setTimeout(() => {
190
190
  if (!dead) {
191
- Server.logcrit(`Worker ${id} failed to shutdown. Killing.`);
192
- worker.kill();
191
+ Server.logcrit(`Worker ${id} failed to shutdown. Killing.`)
192
+ worker.kill()
193
193
  }
194
- }, exit_timeout * 1000);
194
+ }, exit_timeout * 1000)
195
195
  worker.once('exit', () => {
196
- dead = true;
197
- clearTimeout(timer);
196
+ dead = true
197
+ clearTimeout(timer)
198
198
  if (shutdown) resolve()
199
199
  })
200
200
  })
201
201
  if (!shutdown) {
202
- const newWorker = cluster.fork();
202
+ const newWorker = cluster.fork()
203
203
  newWorker.once('listening', () => {
204
- Server.lognotice('Replacement worker online.');
204
+ Server.lognotice('Replacement worker online.')
205
205
  newWorker.on('exit', (code, signal) => {
206
- cluster_exit_listener(newWorker, code, signal);
207
- });
206
+ cluster_exit_listener(newWorker, code, signal)
207
+ })
208
208
  resolve()
209
209
  })
210
210
  }
@@ -218,183 +218,193 @@ Server._graceful = async (shutdown) => {
218
218
  }
219
219
 
220
220
  if (shutdown) {
221
- Server.loginfo("Workers closed. Shutting down master process subsystems");
221
+ Server.loginfo('Workers closed. Shutting down master process subsystems')
222
222
  for (const module of ['outbound', 'cfreader', 'plugins']) {
223
- process.emit('message', {event: `${module}.shutdown`});
223
+ process.emit('message', { event: `${module}.shutdown` })
224
224
  }
225
- const t2 = setTimeout(shutdown, Server.cfg.main.force_shutdown_timeout * 1000);
226
- return t2.unref();
225
+ const t2 = setTimeout(shutdown, Server.cfg.main.force_shutdown_timeout * 1000)
226
+ return t2.unref()
227
227
  }
228
- graceful_in_progress = false;
229
- Server.lognotice(`Reload complete, workers: ${JSON.stringify(Object.keys(cluster.workers))}`);
228
+ graceful_in_progress = false
229
+ Server.lognotice(`Reload complete, workers: ${JSON.stringify(Object.keys(cluster.workers))}`)
230
230
  }
231
231
 
232
232
  Server.sendToMaster = (command, params) => {
233
233
  // console.log("Send to master: ", command);
234
234
  if (Server.cluster) {
235
235
  if (Server.cluster.isMaster) {
236
- Server.receiveAsMaster(command, params);
237
- }
238
- else {
239
- process.send({cmd: command, params});
236
+ Server.receiveAsMaster(command, params)
237
+ } else {
238
+ process.send({ cmd: command, params })
240
239
  }
241
- }
242
- else {
243
- Server.receiveAsMaster(command, params);
240
+ } else {
241
+ Server.receiveAsMaster(command, params)
244
242
  }
245
243
  }
246
244
 
247
245
  Server.receiveAsMaster = (command, params) => {
248
246
  if (!Server[command]) {
249
- Server.logerror(`Invalid command: ${command}`);
250
- return;
247
+ Server.logerror(`Invalid command: ${command}`)
248
+ return
251
249
  }
252
- Server[command].apply(Server, params);
250
+ Server[command].apply(Server, params)
253
251
  }
254
252
 
255
- function messageHandler (worker, msg, handle) {
253
+ function messageHandler(worker, msg, handle) {
256
254
  // sunset Haraka v3 (Node < 6)
257
255
  if (arguments.length === 2) {
258
- handle = msg;
259
- msg = worker;
260
- worker = undefined;
256
+ handle = msg
257
+ msg = worker
258
+ worker = undefined
261
259
  }
262
260
  // console.log("received cmd: ", msg);
263
261
  if (msg?.cmd) {
264
- Server.receiveAsMaster(msg.cmd, msg.params);
262
+ Server.receiveAsMaster(msg.cmd, msg.params)
265
263
  }
266
264
  }
267
265
 
268
266
  Server.get_listen_addrs = (cfg, port) => {
269
- if (!port) port = 25;
270
- let listeners = [];
267
+ if (!port) port = 25
268
+ let listeners = []
271
269
  if (cfg?.listen) {
272
- listeners = cfg.listen.split(/\s*,\s*/);
273
- if (listeners[0] === '') listeners = [];
274
- for (let i=0; i < listeners.length; i++) {
275
- const ep = endpoint(listeners[i], port);
270
+ listeners = cfg.listen.split(/\s*,\s*/)
271
+ if (listeners[0] === '') listeners = []
272
+ for (let i = 0; i < listeners.length; i++) {
273
+ const ep = endpoint(listeners[i], port)
276
274
  if (ep instanceof Error) continue
277
- listeners[i] = ep.toString();
275
+ listeners[i] = ep.toString()
278
276
  }
279
277
  }
280
278
  if (cfg.port) {
281
- let host = cfg.listen_host;
279
+ let host = cfg.listen_host
282
280
  if (!host) {
283
- host = '[::0]';
284
- Server.default_host = true;
281
+ host = '[::0]'
282
+ Server.default_host = true
285
283
  }
286
- listeners.unshift(`${host}:${cfg.port}`);
284
+ listeners.unshift(`${host}:${cfg.port}`)
287
285
  }
288
- if (listeners.length) return listeners;
286
+ if (listeners.length) return listeners
289
287
 
290
- Server.default_host = true;
291
- listeners.push(`[::0]:${port}`);
288
+ Server.default_host = true
289
+ listeners.push(`[::0]:${port}`)
292
290
 
293
- return listeners;
291
+ return listeners
294
292
  }
295
293
 
296
- Server.createServer = params => {
297
- const c = Server.cfg.main;
294
+ Server.createServer = (params) => {
295
+ const c = Server.cfg.main
298
296
  for (const key in params) {
299
- if (typeof params[key] === 'function') continue;
300
- c[key] = params[key];
297
+ if (typeof params[key] === 'function') continue
298
+ c[key] = params[key]
301
299
  }
302
300
 
303
- Server.notes = {};
304
- Server.plugins.server = Server;
305
- Server.plugins.load_plugins();
301
+ Server.notes = {}
302
+ Server.plugins.server = Server
303
+ Server.plugins.load_plugins()
306
304
 
307
- const inactivity_timeout = (c.inactivity_timeout || 300) * 1000;
305
+ const inactivity_timeout = (c.inactivity_timeout || 300) * 1000
308
306
 
309
307
  if (!cluster || !c.nodes) {
310
- Server.daemonize(c);
311
- Server.setup_smtp_listeners(Server.plugins, 'master', inactivity_timeout);
312
- return;
308
+ Server.daemonize(c)
309
+ Server.setup_smtp_listeners(Server.plugins, 'master', inactivity_timeout)
310
+ return
313
311
  }
314
312
 
315
313
  // Cluster
316
- Server.cluster = cluster;
314
+ Server.cluster = cluster
317
315
 
318
316
  // Cluster Workers
319
317
  if (!cluster.isMaster) {
320
- Server.setup_smtp_listeners(Server.plugins, 'child', inactivity_timeout);
321
- return;
322
- }
323
- else {
318
+ Server.setup_smtp_listeners(Server.plugins, 'child', inactivity_timeout)
319
+ return
320
+ } else {
324
321
  // console.log("Setting up message handler");
325
- cluster.on('message', messageHandler);
322
+ cluster.on('message', messageHandler)
326
323
  }
327
324
 
328
325
  // Cluster Master
329
326
  // We fork workers in init_master_respond so that plugins
330
327
  // can put handlers on cluster events before they are emitted.
331
- Server.plugins.run_hooks('init_master', Server);
328
+ Server.plugins.run_hooks('init_master', Server)
332
329
  }
333
330
 
334
- Server.load_default_tls_config = done => {
331
+ Server.load_default_tls_config = (done) => {
335
332
  // this fn exists solely for testing
336
333
  if (Server.config.root_path != tls_socket.config.root_path) {
337
- Server.loginfo(`resetting tls_config.config path to ${Server.config.root_path}`);
338
- tls_socket.config = tls_socket.config.module_config(path.dirname(Server.config.root_path));
334
+ Server.loginfo(`resetting tls_config.config path to ${Server.config.root_path}`)
335
+ tls_socket.config = tls_socket.config.module_config(path.dirname(Server.config.root_path))
339
336
  }
340
- tls_socket.getSocketOpts('*').then(opts => {
341
- done(opts);
337
+ tls_socket.getSocketOpts('*').then((opts) => {
338
+ done(opts)
342
339
  })
343
340
  }
344
341
 
345
342
  Server.get_smtp_server = async (ep, inactivity_timeout) => {
346
- let server;
343
+ let server
347
344
 
348
- function onConnect (client) {
349
- client.setTimeout(inactivity_timeout);
350
- const connection = conn.createConnection(client, server, Server.cfg);
345
+ function onConnect(client) {
346
+ client.setTimeout(inactivity_timeout)
347
+ const connection = conn.createConnection(client, server, Server.cfg)
351
348
 
352
- if (!server.has_tls) return;
349
+ if (!server.has_tls) return
353
350
 
354
- const cipher = client.getCipher();
355
- cipher.version = client.getProtocol(); // replace min with actual
351
+ const cipher = client.getCipher()
352
+ cipher.version = client.getProtocol() // replace min with actual
356
353
 
357
354
  connection.setTLS({
358
355
  cipher,
359
356
  verified: client.authorized,
360
357
  verifyError: client.authorizationError,
361
358
  peerCertificate: client.getPeerCertificate(),
362
- });
359
+ })
363
360
  }
364
361
 
365
362
  if (ep.port === parseInt(Server.cfg.main.smtps_port, 10)) {
366
- Server.loginfo('getting SocketOpts for SMTPS server');
363
+ Server.loginfo('getting SocketOpts for SMTPS server')
367
364
  const opts = await tls_socket.getSocketOpts('*')
368
- Server.loginfo(`Creating TLS server on ${ep}`);
365
+ Server.loginfo(`Creating TLS server on ${ep}`)
369
366
 
370
- opts.rejectUnauthorized = tls_socket.get_rejectUnauthorized(opts.rejectUnauthorized, ep.port, tls_socket.cfg.main.requireAuthorized)
367
+ opts.rejectUnauthorized = tls_socket.get_rejectUnauthorized(
368
+ opts.rejectUnauthorized,
369
+ ep.port,
370
+ tls_socket.cfg.main.requireAuthorized,
371
+ )
371
372
 
372
- server = tls.createServer(opts, onConnect);
373
- tls_socket.addOCSP(server);
374
- server.has_tls=true;
373
+ server = tls.createServer(opts, onConnect)
374
+ tls_socket.addOCSP(server)
375
+ server.has_tls = true
375
376
  server.on('resumeSession', (id, rsDone) => {
376
- Server.loginfo('client requested TLS resumeSession');
377
- rsDone(null, null);
377
+ Server.loginfo('client requested TLS resumeSession')
378
+ rsDone(null, null)
378
379
  })
379
- Server.listeners.push(server);
380
+ Server.listeners.push(server)
380
381
  return server
381
- }
382
- else {
383
- server = tls_socket.createServer(onConnect);
384
- server.has_tls = false;
382
+ } else {
383
+ server = tls_socket.createServer(onConnect)
384
+ server.has_tls = false
385
385
  const opts = await tls_socket.getSocketOpts('*')
386
- Server.listeners.push(server);
386
+ Server.listeners.push(server)
387
387
  return server
388
388
  }
389
389
  }
390
390
 
391
391
  Server.setup_smtp_listeners = async (plugins2, type, inactivity_timeout) => {
392
-
393
392
  const errors = []
394
393
 
395
- for (const listen_address of Server.get_listen_addrs(Server.cfg.main)) {
394
+ for (const [ifName, ifObj] of Object.entries(os.networkInterfaces())) {
395
+ for (const addr of ifObj) {
396
+ if (addr.family === 'IPv6') {
397
+ if (!Server.notes.IPv6) Server.notes.IPv6 = true
398
+ } else if (addr.family === 'IPv4') {
399
+ if (!Server.notes.IPv4) Server.notes.IPv4 = true
400
+ } else {
401
+ console.error(addr)
402
+ }
403
+ }
404
+ }
396
405
 
397
- const ep = endpoint(listen_address, 25);
406
+ for (const listen_address of Server.get_listen_addrs(Server.cfg.main)) {
407
+ const ep = endpoint(listen_address, 25)
398
408
 
399
409
  if (ep instanceof Error) {
400
410
  Server.logerror(`Invalid "listen" format in smtp.ini: ${listen_address}`)
@@ -402,22 +412,22 @@ Server.setup_smtp_listeners = async (plugins2, type, inactivity_timeout) => {
402
412
  }
403
413
 
404
414
  const server = await Server.get_smtp_server(ep, inactivity_timeout)
405
- if (!server) continue;
415
+ if (!server) continue
406
416
 
407
- server.notes = Server.notes;
408
- if (Server.cluster) server.cluster = Server.cluster;
417
+ server.notes = Server.notes
418
+ if (Server.cluster) server.cluster = Server.cluster
409
419
 
410
420
  server
411
421
  .on('listening', function () {
412
- const addr = this.address();
413
- Server.lognotice(`Listening on ${endpoint(addr)}`);
422
+ const addr = this.address()
423
+ Server.lognotice(`Listening on ${endpoint(addr)}`)
414
424
  })
415
425
  .on('close', () => {
416
- Server.loginfo(`Listener ${ep} stopped`);
426
+ Server.loginfo(`Listener ${ep} stopped`)
417
427
  })
418
- .on('error', e => {
428
+ .on('error', (e) => {
419
429
  errors.push(e)
420
- Server.logerror(`Failed to setup listeners: ${e.message}`);
430
+ Server.logerror(`Failed to setup listeners: ${e.message}`)
421
431
  if (e.code !== 'EAFNOSUPPORT') {
422
432
  Server.logerror(e)
423
433
  return
@@ -425,48 +435,46 @@ Server.setup_smtp_listeners = async (plugins2, type, inactivity_timeout) => {
425
435
  // Fallback from IPv6 to IPv4 if not supported
426
436
  // But only if we supplied the default of [::0]:25
427
437
  if (/^::0/.test(ep.host) && Server.default_host) {
428
- server.listen(ep.port, '0.0.0.0', 0);
429
- return;
438
+ server.listen(ep.port, '0.0.0.0', 0)
439
+ return
430
440
  }
431
441
  // Pass error to callback
432
442
  Server.logerror(e)
433
443
  })
434
444
 
435
- await ep.bind(server, {backlog: 0});
445
+ await ep.bind(server, { backlog: 0 })
436
446
  }
437
447
 
438
448
  if (errors.length) {
439
449
  for (const e of errors) {
440
- Server.logerror(`Failed to setup listeners: ${e.message}`);
450
+ Server.logerror(`Failed to setup listeners: ${e.message}`)
441
451
  }
442
- return logger.dump_and_exit(-1);
452
+ return logger.dump_and_exit(-1)
443
453
  }
444
- Server.listening();
445
- plugins2.run_hooks(`init_${type}`, Server);
454
+ Server.listening()
455
+ plugins2.run_hooks(`init_${type}`, Server)
446
456
  }
447
457
 
448
458
  Server.setup_http_listeners = async () => {
449
- if (!Server.http?.cfg?.listen) return;
459
+ if (!Server.http?.cfg?.listen) return
450
460
 
451
- const listeners = Server.get_listen_addrs(Server.http.cfg, 80);
452
- if (!listeners.length) return;
461
+ const listeners = Server.get_listen_addrs(Server.http.cfg, 80)
462
+ if (!listeners.length) return
453
463
 
454
464
  try {
455
- Server.http.express = require('express');
456
- Server.loginfo('express loaded at Server.http.express');
457
- }
458
- catch (err) {
459
- Server.logerror('express failed to load. No http server. Install express with: npm install -g express');
460
- return;
465
+ Server.http.express = require('express')
466
+ Server.loginfo('express loaded at Server.http.express')
467
+ } catch (err) {
468
+ Server.logerror('express failed to load. No http server. Install express with: npm install -g express')
469
+ return
461
470
  }
462
471
 
463
- const app = Server.http.express();
464
- Server.http.app = app;
465
- Server.loginfo('express app is at Server.http.app');
472
+ const app = Server.http.express()
473
+ Server.http.app = app
474
+ Server.loginfo('express app is at Server.http.app')
466
475
 
467
476
  for (const listen_address of listeners) {
468
-
469
- const ep = endpoint(listen_address, 80);
477
+ const ep = endpoint(listen_address, 80)
470
478
  if (ep instanceof Error) {
471
479
  Server.logerror(`Invalid format for listen in http.ini: ${listen_address}`)
472
480
  continue
@@ -474,93 +482,94 @@ Server.setup_http_listeners = async () => {
474
482
 
475
483
  if (443 == ep.port) {
476
484
  const tlsOpts = { ...tls_socket.certsByHost['*'] }
477
- tlsOpts.requestCert = false; // not appropriate for HTTPS
478
- Server.http.server = require('https').createServer(tlsOpts, app);
479
- }
480
- else {
481
- Server.http.server = require('http').createServer(app);
485
+ tlsOpts.requestCert = false // not appropriate for HTTPS
486
+ Server.http.server = require('https').createServer(tlsOpts, app)
487
+ } else {
488
+ Server.http.server = require('http').createServer(app)
482
489
  }
483
490
 
484
- Server.listeners.push(Server.http.server);
491
+ Server.listeners.push(Server.http.server)
485
492
 
486
493
  Server.http.server.on('listening', function () {
487
- Server.lognotice(`Listening on ${endpoint(this.address())}`);
494
+ Server.lognotice(`Listening on ${endpoint(this.address())}`)
488
495
  })
489
496
 
490
- Server.http.server.on('error', e => {
491
- Server.logerror(e);
497
+ Server.http.server.on('error', (e) => {
498
+ Server.logerror(e)
492
499
  })
493
500
 
494
- await ep.bind(Server.http.server, {backlog: 0});
501
+ await ep.bind(Server.http.server, { backlog: 0 })
495
502
  }
496
503
 
497
- Server.plugins.run_hooks('init_http', Server);
498
- app.use(Server.http.express.static(Server.get_http_docroot()));
499
- app.use(Server.handle404);
504
+ Server.plugins.run_hooks('init_http', Server)
505
+ app.use(Server.http.express.static(Server.get_http_docroot()))
506
+ app.use(Server.handle404)
500
507
  }
501
508
 
502
509
  Server.init_master_respond = (retval, msg) => {
503
510
  if (!(retval === constants.ok || retval === constants.cont)) {
504
- Server.logerror(`init_master returned error${((msg) ? `: ${msg}` : '')}`);
505
- return logger.dump_and_exit(1);
511
+ Server.logerror(`init_master returned error${msg ? `: ${msg}` : ''}`)
512
+ return logger.dump_and_exit(1)
506
513
  }
507
514
 
508
- const c = Server.cfg.main;
509
- Server.ready = 1;
515
+ const c = Server.cfg.main
516
+ Server.ready = 1
510
517
 
511
518
  // Load the queue if we're just one process
512
519
  if (!(cluster && c.nodes)) {
513
- outbound.load_queue();
514
- Server.setup_http_listeners();
515
- return;
520
+ outbound.load_queue()
521
+ Server.setup_http_listeners()
522
+ return
516
523
  }
517
524
 
518
525
  // Running under cluster, fork children here, so that
519
526
  // cluster events can be registered in init_master hooks.
520
527
  outbound.scan_queue_pids((err, pids) => {
521
528
  if (err) {
522
- Server.logcrit("Scanning queue failed. Shutting down.");
523
- return logger.dump_and_exit(1);
529
+ Server.logcrit('Scanning queue failed. Shutting down.')
530
+ return logger.dump_and_exit(1)
524
531
  }
525
- Server.daemonize();
532
+ Server.daemonize()
526
533
  // Fork workers
527
- const workers = (c.nodes === 'cpus') ? os.cpus().length : c.nodes;
528
- const new_workers = [];
529
- for (let i=0; i<workers; i++) {
530
- new_workers.push(cluster.fork({ CLUSTER_MASTER_PID: process.pid }));
534
+ const workers = c.nodes === 'cpus' ? os.cpus().length : c.nodes
535
+ const new_workers = []
536
+ for (let i = 0; i < workers; i++) {
537
+ new_workers.push(cluster.fork({ CLUSTER_MASTER_PID: process.pid }))
531
538
  }
532
- for (let j=0; j<pids.length; j++) {
533
- new_workers[j % new_workers.length]
534
- .send({event: 'outbound.load_pid_queue', data: pids[j]});
539
+ for (let j = 0; j < pids.length; j++) {
540
+ new_workers[j % new_workers.length].send({
541
+ event: 'outbound.load_pid_queue',
542
+ data: pids[j],
543
+ })
535
544
  }
536
- cluster.on('online', worker => {
537
- Server.lognotice(
538
- 'worker started',
539
- { worker: worker.id, pid: worker.process.pid }
540
- );
541
- });
545
+ cluster.on('online', (worker) => {
546
+ Server.lognotice('worker started', {
547
+ worker: worker.id,
548
+ pid: worker.process.pid,
549
+ })
550
+ })
542
551
  cluster.on('listening', (worker, address) => {
543
- Server.lognotice(`worker ${worker.id} listening on ${endpoint(address)}`);
544
- });
545
- cluster.on('exit', cluster_exit_listener);
546
- });
552
+ Server.lognotice(`worker ${worker.id} listening on ${endpoint(address)}`)
553
+ })
554
+ cluster.on('exit', cluster_exit_listener)
555
+ })
547
556
  }
548
557
 
549
- function cluster_exit_listener (worker, code, signal) {
558
+ function cluster_exit_listener(worker, code, signal) {
550
559
  if (signal) {
551
- Server.lognotice(`worker ${worker.id} killed by signal ${signal}`);
552
- }
553
- else if (code !== 0) {
554
- Server.lognotice(`worker ${worker.id} exited with error code: ${code}`);
560
+ Server.lognotice(`worker ${worker.id} killed by signal ${signal}`)
561
+ } else if (code !== 0) {
562
+ Server.lognotice(`worker ${worker.id} exited with error code: ${code}`)
555
563
  }
556
564
  if (signal || code !== 0) {
557
565
  // Restart worker
558
566
  const new_worker = cluster.fork({
559
- CLUSTER_MASTER_PID: process.pid
560
- });
567
+ CLUSTER_MASTER_PID: process.pid,
568
+ })
561
569
  new_worker.send({
562
- event: 'outbound.load_pid_queue', data: worker.process.pid,
563
- });
570
+ event: 'outbound.load_pid_queue',
571
+ data: worker.process.pid,
572
+ })
564
573
  }
565
574
  }
566
575
 
@@ -568,90 +577,90 @@ Server.init_child_respond = (retval, msg) => {
568
577
  switch (retval) {
569
578
  case constants.ok:
570
579
  case constants.cont:
571
- Server.setup_http_listeners();
572
- return;
580
+ Server.setup_http_listeners()
581
+ return
573
582
  }
574
583
 
575
- const pid = process.env.CLUSTER_MASTER_PID;
576
- Server.logerror(`init_child returned error ${((msg) ? `: ${msg}` : '')}`);
584
+ const pid = process.env.CLUSTER_MASTER_PID
585
+ Server.logerror(`init_child returned error ${msg ? `: ${msg}` : ''}`)
577
586
  try {
578
587
  if (pid) {
579
- process.kill(pid);
580
- Server.logerror(`Killing master (pid=${pid})`);
588
+ process.kill(pid)
589
+ Server.logerror(`Killing master (pid=${pid})`)
581
590
  }
591
+ } catch (err) {
592
+ Server.logerror('Terminating child')
582
593
  }
583
- catch (err) {
584
- Server.logerror('Terminating child');
585
- }
586
- logger.dump_and_exit(1);
594
+ logger.dump_and_exit(1)
587
595
  }
588
596
 
589
597
  Server.listening = () => {
590
- const c = Server.cfg.main;
598
+ const c = Server.cfg.main
591
599
 
592
600
  // Drop privileges
593
601
  if (c.group) {
594
- Server.lognotice(`Switching from current gid: ${process.getgid()}`);
595
- process.setgid(c.group);
596
- Server.lognotice(`New gid: ${process.getgid()}`);
602
+ Server.lognotice(`Switching from current gid: ${process.getgid()}`)
603
+ process.setgid(c.group)
604
+ Server.lognotice(`New gid: ${process.getgid()}`)
597
605
  }
598
606
  if (c.user) {
599
- Server.lognotice(`Switching from current uid: ${process.getuid()}`);
600
- process.setuid(c.user);
601
- Server.lognotice(`New uid: ${process.getuid()}`);
607
+ Server.lognotice(`Switching from current uid: ${process.getuid()}`)
608
+ process.setuid(c.user)
609
+ Server.lognotice(`New uid: ${process.getuid()}`)
602
610
  }
603
611
 
604
- Server.ready = 1;
612
+ Server.ready = 1
605
613
  }
606
614
 
607
615
  Server.init_http_respond = () => {
608
- Server.loginfo('init_http_respond');
616
+ Server.loginfo('init_http_respond')
609
617
 
610
- let WebSocketServer;
611
- try { WebSocketServer = require('ws').Server; }
612
- catch (e) {
613
- Server.logerror(`unable to load ws.\n did you: npm install -g ws?`);
614
- return;
618
+ let WebSocketServer
619
+ try {
620
+ WebSocketServer = require('ws').Server
621
+ } catch (e) {
622
+ Server.logerror(`unable to load ws.\n did you: npm install -g ws?`)
623
+ return
615
624
  }
616
625
 
617
626
  if (!WebSocketServer) {
618
- Server.logerror('ws failed to load');
619
- return;
627
+ Server.logerror('ws failed to load')
628
+ return
620
629
  }
621
630
 
622
- Server.http.wss = new WebSocketServer({ server: Server.http.server });
623
- Server.loginfo('Server.http.wss loaded');
631
+ Server.http.wss = new WebSocketServer({ server: Server.http.server })
632
+ Server.loginfo('Server.http.wss loaded')
624
633
 
625
- Server.plugins.run_hooks('init_wss', Server);
634
+ Server.plugins.run_hooks('init_wss', Server)
626
635
  }
627
636
 
628
637
  Server.init_wss_respond = () => {
629
- Server.loginfo('init_wss_respond');
638
+ Server.loginfo('init_wss_respond')
630
639
  }
631
640
 
632
641
  Server.get_http_docroot = () => {
633
- if (Server.http.cfg.docroot) return Server.http.cfg.docroot;
642
+ if (Server.http.cfg.docroot) return Server.http.cfg.docroot
634
643
 
635
- Server.http.cfg.docroot = path.join( (process.env.HARAKA || __dirname), 'http', 'html');
636
- Server.loginfo(`using html docroot: ${Server.http.cfg.docroot}`);
637
- return Server.http.cfg.docroot;
644
+ Server.http.cfg.docroot = path.join(process.env.HARAKA || __dirname, 'http', 'html')
645
+ Server.loginfo(`using html docroot: ${Server.http.cfg.docroot}`)
646
+ return Server.http.cfg.docroot
638
647
  }
639
648
 
640
649
  Server.handle404 = (req, res) => {
641
650
  // abandon all hope, serve up a 404
642
- const docroot = Server.get_http_docroot();
651
+ const docroot = Server.get_http_docroot()
643
652
 
644
653
  // respond with html page
645
654
  if (req.accepts('html')) {
646
- res.status(404).sendFile('404.html', { root: docroot });
647
- return;
655
+ res.status(404).sendFile('404.html', { root: docroot })
656
+ return
648
657
  }
649
658
 
650
659
  // respond with json
651
660
  if (req.accepts('json')) {
652
- res.status(404).send({ err: 'Not found' });
653
- return;
661
+ res.status(404).send({ err: 'Not found' })
662
+ return
654
663
  }
655
664
 
656
- res.status(404).send('Not found!');
665
+ res.status(404).send('Not found!')
657
666
  }