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.
- package/.prettierignore +4 -0
- package/CONTRIBUTORS.md +5 -5
- package/Changes.md +69 -50
- package/Plugins.md +3 -1
- package/README.md +1 -1
- package/bin/haraka +475 -478
- package/config/outbound.ini +3 -0
- package/connection.js +1072 -1108
- package/docs/Connection.md +29 -30
- package/docs/CoreConfig.md +38 -39
- package/docs/CustomReturnCodes.md +0 -1
- package/docs/HAProxy.md +2 -2
- package/docs/Header.md +1 -1
- package/docs/Logging.md +29 -5
- package/docs/Outbound.md +93 -78
- package/docs/Plugins.md +103 -108
- package/docs/Transaction.md +49 -51
- package/docs/Tutorial.md +127 -143
- package/docs/deprecated/access.md +0 -1
- package/docs/deprecated/backscatterer.md +2 -3
- package/docs/deprecated/connect.rdns_access.md +18 -27
- package/docs/deprecated/data.headers.md +0 -1
- package/docs/deprecated/data.nomsgid.md +1 -2
- package/docs/deprecated/data.noreceived.md +1 -2
- package/docs/deprecated/data.rfc5322_header_checks.md +1 -2
- package/docs/deprecated/dkim_sign.md +13 -17
- package/docs/deprecated/dkim_verify.md +9 -17
- package/docs/deprecated/dnsbl.md +36 -38
- package/docs/deprecated/dnswl.md +41 -43
- package/docs/deprecated/lookup_rdns.strict.md +21 -34
- package/docs/deprecated/mail_from.access.md +17 -25
- package/docs/deprecated/mail_from.blocklist.md +9 -12
- package/docs/deprecated/mail_from.nobounces.md +1 -2
- package/docs/deprecated/rcpt_to.access.md +20 -27
- package/docs/deprecated/rcpt_to.blocklist.md +10 -13
- package/docs/deprecated/rcpt_to.routes.md +0 -1
- package/docs/deprecated/rdns.regexp.md +13 -15
- package/docs/plugins/aliases.md +89 -89
- package/docs/plugins/auth/auth_bridge.md +5 -7
- package/docs/plugins/auth/auth_ldap.md +11 -14
- package/docs/plugins/auth/auth_proxy.md +10 -12
- package/docs/plugins/auth/auth_vpopmaild.md +5 -6
- package/docs/plugins/auth/flat_file.md +4 -4
- package/docs/plugins/block_me.md +3 -3
- package/docs/plugins/data.signatures.md +1 -2
- package/docs/plugins/delay_deny.md +3 -4
- package/docs/plugins/max_unrecognized_commands.md +4 -4
- package/docs/plugins/prevent_credential_leaks.md +6 -6
- package/docs/plugins/process_title.md +18 -18
- package/docs/plugins/queue/deliver.md +2 -3
- package/docs/plugins/queue/discard.md +4 -4
- package/docs/plugins/queue/lmtp.md +1 -3
- package/docs/plugins/queue/qmail-queue.md +7 -9
- package/docs/plugins/queue/quarantine.md +16 -21
- package/docs/plugins/queue/rabbitmq.md +8 -11
- package/docs/plugins/queue/rabbitmq_amqplib.md +43 -39
- package/docs/plugins/queue/smtp_bridge.md +7 -10
- package/docs/plugins/queue/smtp_forward.md +42 -34
- package/docs/plugins/queue/smtp_proxy.md +30 -29
- package/docs/plugins/queue/test.md +1 -3
- package/docs/plugins/rcpt_to.in_host_list.md +6 -6
- package/docs/plugins/rcpt_to.max_count.md +1 -1
- package/docs/plugins/record_envelope_addresses.md +3 -3
- package/docs/plugins/reseed_rng.md +6 -6
- package/docs/plugins/status.md +9 -8
- package/docs/plugins/tarpit.md +7 -11
- package/docs/plugins/tls.md +12 -17
- package/docs/plugins/toobusy.md +4 -4
- package/docs/plugins/xclient.md +3 -3
- package/docs/tutorials/Migrating_from_v1_to_v2.md +19 -41
- package/docs/tutorials/SettingUpOutbound.md +6 -9
- package/endpoint.js +35 -38
- package/eslint.config.mjs +22 -19
- package/haraka.js +42 -47
- package/host_pool.js +75 -79
- package/http/html/404.html +45 -49
- package/http/html/index.html +39 -28
- package/http/package.json +2 -4
- package/line_socket.js +27 -28
- package/logger.js +182 -201
- package/outbound/client_pool.js +34 -27
- package/outbound/config.js +64 -59
- package/outbound/fsync_writestream.js +24 -25
- package/outbound/hmail.js +888 -835
- package/outbound/index.js +194 -187
- package/outbound/qfile.js +49 -52
- package/outbound/queue.js +197 -190
- package/outbound/timer_queue.js +41 -43
- package/outbound/tls.js +68 -61
- package/outbound/todo.js +11 -11
- package/package.json +38 -33
- package/plugins/.eslintrc.yaml +0 -1
- package/plugins/auth/auth_base.js +123 -127
- package/plugins/auth/auth_bridge.js +7 -7
- package/plugins/auth/auth_proxy.js +121 -126
- package/plugins/auth/auth_vpopmaild.js +84 -85
- package/plugins/auth/flat_file.js +18 -17
- package/plugins/block_me.js +31 -31
- package/plugins/data.signatures.js +13 -13
- package/plugins/delay_deny.js +65 -61
- package/plugins/prevent_credential_leaks.js +23 -23
- package/plugins/process_title.js +125 -128
- package/plugins/profile.js +5 -5
- package/plugins/queue/deliver.js +3 -3
- package/plugins/queue/discard.js +13 -14
- package/plugins/queue/lmtp.js +16 -17
- package/plugins/queue/qmail-queue.js +54 -55
- package/plugins/queue/quarantine.js +68 -70
- package/plugins/queue/rabbitmq.js +80 -87
- package/plugins/queue/rabbitmq_amqplib.js +75 -54
- package/plugins/queue/smtp_bridge.js +16 -16
- package/plugins/queue/smtp_forward.js +175 -179
- package/plugins/queue/smtp_proxy.js +69 -71
- package/plugins/queue/test.js +9 -9
- package/plugins/rcpt_to.host_list_base.js +30 -34
- package/plugins/rcpt_to.in_host_list.js +19 -19
- package/plugins/record_envelope_addresses.js +4 -4
- package/plugins/reseed_rng.js +4 -4
- package/plugins/status.js +90 -97
- package/plugins/tarpit.js +25 -14
- package/plugins/tls.js +68 -68
- package/plugins/toobusy.js +21 -23
- package/plugins/xclient.js +51 -53
- package/plugins.js +276 -293
- package/rfc1869.js +30 -35
- package/server.js +308 -299
- package/smtp_client.js +244 -228
- package/test/.eslintrc.yaml +0 -1
- package/test/connection.js +127 -134
- package/test/endpoint.js +53 -47
- package/test/fixtures/line_socket.js +12 -12
- package/test/fixtures/util_hmailitem.js +89 -85
- package/test/host_pool.js +90 -92
- package/test/installation/plugins/base_plugin.js +2 -2
- package/test/installation/plugins/folder_plugin/index.js +2 -3
- package/test/installation/plugins/inherits.js +3 -3
- package/test/installation/plugins/load_first.js +2 -3
- package/test/installation/plugins/plugin.js +1 -3
- package/test/installation/plugins/tls.js +2 -4
- package/test/logger.js +135 -116
- package/test/outbound/hmail.js +49 -35
- package/test/outbound/index.js +118 -101
- package/test/outbound/qfile.js +51 -53
- package/test/outbound_bounce_net_errors.js +84 -69
- package/test/outbound_bounce_rfc3464.js +235 -165
- package/test/plugins/auth/auth_base.js +420 -279
- package/test/plugins/auth/auth_vpopmaild.js +38 -39
- package/test/plugins/queue/smtp_forward.js +126 -104
- package/test/plugins/rcpt_to.host_list_base.js +85 -67
- package/test/plugins/rcpt_to.in_host_list.js +159 -112
- package/test/plugins/status.js +71 -64
- package/test/plugins/tls.js +37 -34
- package/test/plugins.js +97 -92
- package/test/rfc1869.js +19 -26
- package/test/server.js +293 -272
- package/test/smtp_client.js +180 -176
- package/test/tls_socket.js +62 -66
- package/test/transaction.js +159 -160
- package/tls_socket.js +331 -333
- 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
|
|
5
|
-
const fs
|
|
6
|
-
const os
|
|
7
|
-
const path
|
|
8
|
-
const 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
|
|
11
|
-
const constants
|
|
10
|
+
const daemon = require('daemon')
|
|
11
|
+
const constants = require('haraka-constants')
|
|
12
12
|
|
|
13
|
-
const tls_socket
|
|
14
|
-
const conn
|
|
15
|
-
const outbound
|
|
16
|
-
const 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
|
|
19
|
-
Server.logger
|
|
20
|
-
Server.config
|
|
21
|
-
Server.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 }
|
|
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(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
'-main.graceful_shutdown',
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
90
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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
|
-
|
|
239
|
-
process.send({cmd: command, params});
|
|
236
|
+
Server.receiveAsMaster(command, params)
|
|
237
|
+
} else {
|
|
238
|
+
process.send({ cmd: command, params })
|
|
240
239
|
}
|
|
241
|
-
}
|
|
242
|
-
|
|
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
|
|
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
|
|
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()
|
|
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(
|
|
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
|
-
|
|
383
|
-
server =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
459
|
-
|
|
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
|
|
478
|
-
Server.http.server = require('https').createServer(tlsOpts, app)
|
|
479
|
-
}
|
|
480
|
-
|
|
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${
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
539
|
-
|
|
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
|
|
558
|
+
function cluster_exit_listener(worker, code, signal) {
|
|
550
559
|
if (signal) {
|
|
551
|
-
Server.lognotice(`worker ${worker.id} killed by signal ${signal}`)
|
|
552
|
-
}
|
|
553
|
-
|
|
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',
|
|
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 ${
|
|
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
|
-
|
|
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 {
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
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(
|
|
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
|
}
|