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.
- package/.eslintrc.yaml +4 -9
- package/CONTRIBUTORS.md +11 -0
- package/Changes.md +1397 -1213
- package/Plugins.md +117 -105
- package/README.md +4 -13
- package/bin/haraka +198 -298
- package/config/auth_flat_file.ini +1 -0
- package/config/dhparams.pem +8 -0
- package/config/mail_from.is_resolvable.ini +4 -2
- package/config/me +1 -0
- package/config/outbound.ini +0 -2
- package/config/plugins +35 -36
- package/config/smtp.ini +1 -1
- package/config/smtp.json +17 -0
- package/config/tls.ini +2 -0
- package/config/tls_cert.pem +23 -0
- package/config/tls_key.pem +28 -0
- package/connection.js +46 -73
- package/contrib/bsd-rc.d/haraka +3 -1
- package/contrib/plugin2npm.sh +6 -36
- package/docs/CoreConfig.md +2 -2
- package/docs/Logging.md +7 -21
- package/docs/Outbound.md +104 -201
- package/docs/Plugins.md +2 -2
- package/docs/Transaction.md +59 -82
- package/docs/plugins/queue/smtp_proxy.md +5 -10
- package/docs/plugins/tls.md +37 -9
- package/endpoint.js +16 -13
- package/haraka.js +10 -14
- package/host_pool.js +5 -5
- package/http/html/index.html +6 -5
- package/line_socket.js +3 -4
- package/logger.js +44 -28
- package/outbound/client_pool.js +27 -23
- package/outbound/config.js +4 -6
- package/outbound/fsync_writestream.js +1 -1
- package/outbound/hmail.js +178 -218
- package/outbound/index.js +86 -99
- package/outbound/qfile.js +1 -1
- package/outbound/queue.js +51 -44
- package/outbound/timer_queue.js +3 -2
- package/outbound/tls.js +19 -7
- package/package.json +60 -51
- package/plugins/.eslintrc.yaml +0 -6
- package/plugins/auth/auth_base.js +4 -2
- package/plugins/auth/auth_proxy.js +14 -12
- package/plugins/auth/auth_vpopmaild.js +1 -1
- package/plugins/block_me.js +1 -1
- package/plugins/data.signatures.js +2 -4
- package/plugins/early_talker.js +2 -1
- package/plugins/mail_from.is_resolvable.js +65 -135
- package/plugins/queue/deliver.js +4 -5
- package/plugins/queue/lmtp.js +11 -12
- package/plugins/queue/qmail-queue.js +2 -2
- package/plugins/queue/quarantine.js +2 -2
- package/plugins/queue/rabbitmq.js +16 -17
- package/plugins/queue/smtp_forward.js +3 -3
- package/plugins/queue/smtp_proxy.js +10 -1
- package/plugins/queue/test.js +2 -2
- package/plugins/rcpt_to.host_list_base.js +5 -5
- package/plugins/rcpt_to.in_host_list.js +2 -2
- package/plugins/relay.js +6 -7
- package/plugins/reseed_rng.js +1 -1
- package/plugins/status.js +37 -33
- package/plugins/tls.js +2 -2
- package/plugins/xclient.js +3 -2
- package/plugins.js +50 -54
- package/run_tests +3 -30
- package/server.js +190 -190
- package/smtp_client.js +30 -23
- package/{tests → test}/config/plugins +0 -2
- package/{tests → test}/config/smtp.ini +3 -1
- package/test/config/tls/example.com/_.example.com.key +28 -0
- package/test/config/tls/example.com/example.com.crt +25 -0
- package/{tests/loud → test}/config/tls.ini +4 -2
- package/test/connection.js +302 -0
- package/test/endpoint.js +94 -0
- package/{tests → test}/fixtures/line_socket.js +1 -1
- package/{tests → test}/fixtures/util_hmailitem.js +19 -25
- package/{tests → test}/host_pool.js +42 -57
- package/test/logger.js +258 -0
- package/test/outbound/hmail.js +141 -0
- package/test/outbound/index.js +220 -0
- package/test/outbound/qfile.js +126 -0
- package/test/outbound_bounce_net_errors.js +142 -0
- package/{tests → test}/outbound_bounce_rfc3464.js +110 -122
- package/test/plugins/auth/auth_base.js +484 -0
- package/test/plugins/auth/auth_vpopmaild.js +83 -0
- package/test/plugins/early_talker.js +104 -0
- package/test/plugins/mail_from.is_resolvable.js +35 -0
- package/test/plugins/queue/smtp_forward.js +206 -0
- package/test/plugins/rcpt_to.host_list_base.js +122 -0
- package/test/plugins/rcpt_to.in_host_list.js +193 -0
- package/test/plugins/relay.js +303 -0
- package/test/plugins/status.js +130 -0
- package/test/plugins/tls.js +70 -0
- package/test/plugins.js +228 -0
- package/test/rfc1869.js +73 -0
- package/test/server.js +491 -0
- package/test/smtp_client.js +299 -0
- package/test/tls_socket.js +277 -0
- package/test/transaction.js +270 -0
- package/tls_socket.js +202 -252
- package/transaction.js +8 -23
- package/CONTRIBUTING.md +0 -1
- package/bin/dkimverify +0 -40
- package/config/access.domains +0 -13
- package/config/attachment.ctype.regex +0 -2
- package/config/attachment.filename.regex +0 -1
- package/config/avg.ini +0 -5
- package/config/bounce.ini +0 -15
- package/config/data.headers.ini +0 -61
- package/config/dkim/dkim_key_gen.sh +0 -78
- package/config/dkim_sign.ini +0 -4
- package/config/dkim_verify.ini +0 -7
- package/config/dnsbl.ini +0 -23
- package/config/greylist.ini +0 -43
- package/config/helo.checks.ini +0 -52
- package/config/messagesniffer.ini +0 -18
- package/config/spamassassin.ini +0 -56
- package/dkim.js +0 -614
- package/docs/plugins/avg.md +0 -35
- package/docs/plugins/bounce.md +0 -69
- package/docs/plugins/clamd.md +0 -147
- package/docs/plugins/esets.md +0 -8
- package/docs/plugins/greylist.md +0 -90
- package/docs/plugins/helo.checks.md +0 -135
- package/docs/plugins/messagesniffer.md +0 -163
- package/docs/plugins/spamassassin.md +0 -180
- package/outbound/mx_lookup.js +0 -70
- package/plugins/auth/auth_ldap.js +0 -3
- package/plugins/avg.js +0 -162
- package/plugins/backscatterer.js +0 -25
- package/plugins/bounce.js +0 -381
- package/plugins/clamd.js +0 -382
- package/plugins/data.uribl.js +0 -4
- package/plugins/dkim_sign.js +0 -395
- package/plugins/dkim_verify.js +0 -62
- package/plugins/dns_list_base.js +0 -221
- package/plugins/dnsbl.js +0 -146
- package/plugins/dnswl.js +0 -58
- package/plugins/esets.js +0 -71
- package/plugins/graph.js +0 -5
- package/plugins/greylist.js +0 -645
- package/plugins/helo.checks.js +0 -533
- package/plugins/messagesniffer.js +0 -381
- package/plugins/rcpt_to.ldap.js +0 -3
- package/plugins/rcpt_to.max_count.js +0 -24
- package/plugins/spamassassin.js +0 -384
- package/tests/config/dkim/example.com/dns +0 -29
- package/tests/config/dkim/example.com/private +0 -6
- package/tests/config/dkim/example.com/public +0 -4
- package/tests/config/dkim/example.com/selector +0 -1
- package/tests/config/dkim.private.key +0 -6
- package/tests/config/dkim_sign.ini +0 -4
- package/tests/config/helo.checks.ini +0 -52
- package/tests/connection.js +0 -327
- package/tests/endpoint.js +0 -128
- package/tests/fixtures/vm_harness.js +0 -59
- package/tests/logger.js +0 -327
- package/tests/outbound/hmail.js +0 -112
- package/tests/outbound/index.js +0 -324
- package/tests/outbound/qfile.js +0 -67
- package/tests/outbound_bounce_net_errors.js +0 -173
- package/tests/plugins/auth/auth_base.js +0 -463
- package/tests/plugins/auth/auth_vpopmaild.js +0 -91
- package/tests/plugins/bounce.js +0 -307
- package/tests/plugins/clamd.js +0 -224
- package/tests/plugins/deprecated/relay_acl.js +0 -140
- package/tests/plugins/deprecated/relay_all.js +0 -59
- package/tests/plugins/dkim_sign.js +0 -315
- package/tests/plugins/dkim_signer.js +0 -108
- package/tests/plugins/dns_list_base.js +0 -259
- package/tests/plugins/dnsbl.js +0 -101
- package/tests/plugins/early_talker.js +0 -115
- package/tests/plugins/greylist.js +0 -58
- package/tests/plugins/helo.checks.js +0 -525
- package/tests/plugins/mail_from.is_resolvable.js +0 -116
- package/tests/plugins/queue/smtp_forward.js +0 -221
- package/tests/plugins/rcpt_to.host_list_base.js +0 -132
- package/tests/plugins/rcpt_to.in_host_list.js +0 -218
- package/tests/plugins/relay.js +0 -339
- package/tests/plugins/spamassassin.js +0 -171
- package/tests/plugins/status.js +0 -138
- package/tests/plugins/tls.js +0 -84
- package/tests/plugins.js +0 -247
- package/tests/rfc1869.js +0 -61
- package/tests/server.js +0 -510
- package/tests/smtp_client/auth.js +0 -105
- package/tests/smtp_client/basic.js +0 -101
- package/tests/smtp_client.js +0 -80
- package/tests/tls_socket.js +0 -333
- package/tests/transaction.js +0 -284
- /package/docs/{plugins → deprecated}/dkim_sign.md +0 -0
- /package/docs/{plugins → deprecated}/dkim_verify.md +0 -0
- /package/docs/{plugins → deprecated}/dnsbl.md +0 -0
- /package/docs/{plugins → deprecated}/dnswl.md +0 -0
- /package/{tests → test}/.eslintrc.yaml +0 -0
- /package/{tests → test}/config/auth_flat_file.ini +0 -0
- /package/{tests → test}/config/dhparams.pem +0 -0
- /package/{tests → test}/config/host_list +0 -0
- /package/{tests → test}/config/outbound_tls_cert.pem +0 -0
- /package/{tests → test}/config/outbound_tls_key.pem +0 -0
- /package/{tests → test}/config/smtp_forward.ini +0 -0
- /package/{tests → test}/config/tls/ec.pem +0 -0
- /package/{tests → test}/config/tls/haraka.local.pem +0 -0
- /package/{tests → test}/config/tls/mismatched.pem +0 -0
- /package/{tests → test}/config/tls_cert.pem +0 -0
- /package/{tests → test}/config/tls_key.pem +0 -0
- /package/{tests → test}/fixtures/todo_qfile.txt +0 -0
- /package/{tests → test}/installation/config/test-plugin-flat +0 -0
- /package/{tests → test}/installation/config/test-plugin.ini +0 -0
- /package/{tests → test}/installation/config/tls.ini +0 -0
- /package/{tests → test}/installation/node_modules/load_first/index.js +0 -0
- /package/{tests → test}/installation/node_modules/load_first/package.json +0 -0
- /package/{tests → test}/installation/node_modules/test-plugin/config/test-plugin-flat +0 -0
- /package/{tests → test}/installation/node_modules/test-plugin/config/test-plugin.ini +0 -0
- /package/{tests → test}/installation/node_modules/test-plugin/package.json +0 -0
- /package/{tests → test}/installation/node_modules/test-plugin/test-plugin.js +0 -0
- /package/{tests → test}/installation/plugins/base_plugin.js +0 -0
- /package/{tests → test}/installation/plugins/folder_plugin/index.js +0 -0
- /package/{tests → test}/installation/plugins/folder_plugin/package.json +0 -0
- /package/{tests → test}/installation/plugins/inherits.js +0 -0
- /package/{tests → test}/installation/plugins/load_first.js +0 -0
- /package/{tests → test}/installation/plugins/plugin.js +0 -0
- /package/{tests → test}/installation/plugins/tls.js +0 -0
- /package/{tests → test}/loud/config/dhparams.pem +0 -0
- /package/{tests → test}/loud/config/tls/goobered.pem +0 -0
- /package/{tests → test/loud}/config/tls.ini +0 -0
- /package/{tests → test}/mail_specimen/base64-root-part.txt +0 -0
- /package/{tests → test}/mail_specimen/varied-fold-lengths-preserve-data.txt +0 -0
- /package/{tests → test}/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_fixed +0 -0
- /package/{tests → test}/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka +0 -0
- /package/{tests → test}/queue/1508269674999_1508269674999_0_34002_socVUF_1_haraka +0 -0
- /package/{tests → test}/queue/1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka +0 -0
- /package/{tests → test}/queue/multibyte +0 -0
- /package/{tests → test}/queue/plain +0 -0
- /package/{tests → test}/queue/zero-length +0 -0
- /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
|
|
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 }
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
132
|
-
Server.listeners
|
|
133
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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']
|
|
158
|
-
process.emit('message', {event: `${module
|
|
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 (
|
|
165
|
-
|
|
160
|
+
if (graceful_in_progress) {
|
|
161
|
+
Server.lognotice("Restart currently in progress - ignoring request");
|
|
166
162
|
return;
|
|
167
163
|
}
|
|
168
164
|
|
|
169
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
worker.
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
-
|
|
229
|
-
|
|
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
|
-
|
|
232
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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('*'
|
|
354
|
+
tls_socket.getSocketOpts('*').then(opts => {
|
|
345
355
|
done(opts);
|
|
346
|
-
})
|
|
356
|
+
})
|
|
347
357
|
}
|
|
348
358
|
|
|
349
|
-
Server.get_smtp_server = (ep, inactivity_timeout
|
|
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
|
-
|
|
371
|
-
tls_socket.getSocketOpts('*'
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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('*'
|
|
391
|
-
|
|
392
|
-
|
|
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
|
-
|
|
400
|
-
Server.get_listen_addrs(Server.cfg.main), // array of listeners
|
|
407
|
+
const errors = []
|
|
401
408
|
|
|
402
|
-
|
|
409
|
+
for (const listen_address of Server.get_listen_addrs(Server.cfg.main)) {
|
|
403
410
|
|
|
404
|
-
|
|
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
|
-
|
|
409
|
-
|
|
413
|
+
if (ep instanceof Error) {
|
|
414
|
+
Server.logerror(`Invalid "listen" format in smtp.ini: ${listen_address}`)
|
|
415
|
+
continue
|
|
416
|
+
}
|
|
410
417
|
|
|
411
|
-
|
|
412
|
-
|
|
418
|
+
const server = await Server.get_smtp_server(ep, inactivity_timeout)
|
|
419
|
+
if (!server) continue;
|
|
413
420
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
ep.
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
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
|
|
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
|
-
|
|
470
|
+
Server.loginfo('express loaded at Server.http.express');
|
|
458
471
|
}
|
|
459
472
|
catch (err) {
|
|
460
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
490
|
-
|
|
491
|
-
cb();
|
|
492
|
-
});
|
|
501
|
+
Server.lognotice(`Listening on ${endpoint(this.address())}`);
|
|
502
|
+
})
|
|
493
503
|
|
|
494
504
|
Server.http.server.on('error', e => {
|
|
495
|
-
|
|
496
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
565
|
+
Server.lognotice(`worker ${worker.id} killed by signal ${signal}`);
|
|
565
566
|
}
|
|
566
567
|
else if (code !== 0) {
|
|
567
|
-
|
|
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
|
-
|
|
622
|
+
Server.loginfo('init_http_respond');
|
|
622
623
|
|
|
623
624
|
let WebSocketServer;
|
|
624
625
|
try { WebSocketServer = require('ws').Server; }
|
|
625
626
|
catch (e) {
|
|
626
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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), '
|
|
650
|
-
|
|
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
|
|