Haraka 3.1.1 → 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 +62 -50
  4. package/Plugins.md +3 -1
  5. package/README.md +1 -1
  6. package/bin/haraka +475 -479
  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 +33 -33
  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 +32 -32
  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/plugins/status.js CHANGED
@@ -1,50 +1,49 @@
1
- 'use strict';
1
+ 'use strict'
2
+ /* global server */
2
3
 
3
- const fs = require('node:fs');
4
- const path = require('node:path');
4
+ const fs = require('node:fs')
5
+ const path = require('node:path')
5
6
 
6
7
  exports.register = function () {
7
- this.outbound = require('../outbound');
8
- this.queue_dir = require('../outbound/queue').queue_dir;
8
+ this.outbound = require('../outbound')
9
+ this.queue_dir = require('../outbound/queue').queue_dir
9
10
  }
10
11
 
11
12
  exports.hook_capabilities = (next, connection) => {
12
-
13
13
  if (connection.remote.is_local) {
14
- connection.capabilities.push('STATUS');
14
+ connection.capabilities.push('STATUS')
15
15
  }
16
16
 
17
- next();
17
+ next()
18
18
  }
19
19
 
20
20
  exports.hook_unrecognized_command = function (next, connection, params) {
21
- if (params[0] !== 'STATUS') return next();
22
- if (!connection.remote.is_local) return next(DENY, 'STATUS not allowed remotely');
21
+ if (params[0] !== 'STATUS') return next()
22
+ if (!connection.remote.is_local) return next(DENY, 'STATUS not allowed remotely')
23
23
 
24
24
  this.run(params[1], (err, result) => {
25
- if (err) return next(DENY, err.message);
25
+ if (err) return next(DENY, err.message)
26
26
 
27
- connection.respond(211, result ? JSON.stringify(result) : 'null', () => next(OK));
28
- });
27
+ connection.respond(211, result ? JSON.stringify(result) : 'null', () => next(OK))
28
+ })
29
29
  }
30
30
 
31
31
  exports.run = function (cmd, cb) {
32
32
  if (server.cluster && !/^QUEUE LIST/.test(cmd)) {
33
- this.call_master(cmd, cb);
34
- }
35
- else {
36
- this.command_action(cmd, cb);
33
+ this.call_master(cmd, cb)
34
+ } else {
35
+ this.command_action(cmd, cb)
37
36
  }
38
37
  }
39
38
 
40
39
  exports.command_action = function (cmd, cb) {
41
- const params = cmd.split(' ');
40
+ const params = cmd.split(' ')
42
41
 
43
42
  switch (params.shift()) {
44
43
  case 'POOL':
45
- return this.pool_action(params, cb);
44
+ return this.pool_action(params, cb)
46
45
  case 'QUEUE':
47
- return this.queue_action(params, cb);
46
+ return this.queue_action(params, cb)
48
47
  default:
49
48
  cb('unknown STATUS command')
50
49
  }
@@ -53,7 +52,7 @@ exports.command_action = function (cmd, cb) {
53
52
  exports.pool_action = function (params, cb) {
54
53
  switch (params.shift()) {
55
54
  case 'LIST':
56
- return this.pool_list(cb);
55
+ return this.pool_list(cb)
57
56
  default:
58
57
  cb('unknown POOL command')
59
58
  }
@@ -62,40 +61,40 @@ exports.pool_action = function (params, cb) {
62
61
  exports.queue_action = function (params, cb) {
63
62
  switch (params.shift()) {
64
63
  case 'LIST':
65
- return this.queue_list(cb);
64
+ return this.queue_list(cb)
66
65
  case 'STATS':
67
- return this.queue_stats(cb);
66
+ return this.queue_stats(cb)
68
67
  case 'INSPECT':
69
- return this.queue_inspect(cb);
68
+ return this.queue_inspect(cb)
70
69
  case 'DISCARD':
71
- return this.queue_discard(params.shift(), cb);
70
+ return this.queue_discard(params.shift(), cb)
72
71
  case 'PUSH':
73
- return this.queue_push(params.shift(), cb);
72
+ return this.queue_push(params.shift(), cb)
74
73
  default:
75
74
  cb('unknown QUEUE command')
76
75
  }
77
76
  }
78
77
 
79
- exports.pool_list = cb => {
80
- const result = {};
78
+ exports.pool_list = (cb) => {
79
+ const result = {}
81
80
 
82
81
  if (server.notes.pool) {
83
82
  for (const name of Object.keys(server.notes.pool)) {
84
- const instance = server.notes.pool[name];
83
+ const instance = server.notes.pool[name]
85
84
 
86
85
  result[name] = {
87
86
  inUse: instance.inUseObjectsCount(),
88
- size: instance.getPoolSize()
89
- };
87
+ size: instance.getPoolSize(),
88
+ }
90
89
  }
91
90
  }
92
91
 
93
- cb(null, result);
92
+ cb(null, result)
94
93
  }
95
94
 
96
95
  exports.queue_list = function (cb) {
97
96
  this.outbound.list_queue((err, qlist = []) => {
98
- const result = [];
97
+ const result = []
99
98
 
100
99
  for (const todo of qlist) {
101
100
  result.push({
@@ -104,120 +103,115 @@ exports.queue_list = function (cb) {
104
103
  queue_time: todo.queue_time,
105
104
  domain: todo.domain,
106
105
  from: todo.mail_from.toString(),
107
- to: todo.rcpt_to.map((r) => r.toString())
106
+ to: todo.rcpt_to.map((r) => r.toString()),
108
107
  })
109
108
  }
110
109
 
111
- cb(err, result);
110
+ cb(err, result)
112
111
  })
113
112
  }
114
113
 
115
114
  exports.queue_stats = function (cb) {
116
- cb(null, this.outbound.get_stats());
115
+ cb(null, this.outbound.get_stats())
117
116
  }
118
117
 
119
118
  exports.queue_inspect = function (cb) {
120
- const delivery_queue_items = this.outbound.delivery_queue._tasks.toArray();
121
- const fail_queue_items = this.outbound.temp_fail_queue.queue;
119
+ const delivery_queue_items = this.outbound.delivery_queue._tasks.toArray()
120
+ const fail_queue_items = this.outbound.temp_fail_queue.queue
122
121
 
123
122
  cb(null, {
124
123
  delivery_queue: delivery_queue_items.map((hmail) => ({
125
- id: hmail.file
124
+ id: hmail.file,
126
125
  })),
127
126
  temp_fail_queue: fail_queue_items.map((tqtimer) => ({
128
127
  id: tqtimer.id,
129
- fire_time: tqtimer.fire_time
130
- }))
131
- });
128
+ fire_time: tqtimer.fire_time,
129
+ })),
130
+ })
132
131
  }
133
132
 
134
133
  exports.queue_discard = function (file, cb) {
135
134
  try {
136
- this.outbound.temp_fail_queue.discard(file);
137
- }
138
- catch (e) {
135
+ this.outbound.temp_fail_queue.discard(file)
136
+ } catch (e) {
139
137
  // we ignore not found error
140
138
  }
141
139
 
142
140
  fs.unlink(path.join(this.queue_dir || '', file), () => {
143
- cb(null, 'OK');
144
- });
141
+ cb(null, 'OK')
142
+ })
145
143
  }
146
144
 
147
145
  exports.queue_push = function (file, cb) {
148
- const { queue } = this.outbound.temp_fail_queue;
146
+ const { queue } = this.outbound.temp_fail_queue
149
147
 
150
148
  for (let i = 0; i < queue.length; i++) {
151
- if (queue[i].id !== file) continue;
149
+ if (queue[i].id !== file) continue
152
150
 
153
- const item = queue.splice(i, 1)[0];
154
- item.cb();
151
+ const item = queue.splice(i, 1)[0]
152
+ item.cb()
155
153
 
156
- break;
154
+ break
157
155
  }
158
156
 
159
- cb(null, 'OK');
157
+ cb(null, 'OK')
160
158
  }
161
159
 
162
160
  // cluster IPC
163
161
 
164
162
  exports.hook_init_master = function (next) {
165
- const plugin = this;
163
+ const plugin = this
166
164
 
167
- if (!server.cluster) return next();
165
+ if (!server.cluster) return next()
168
166
 
169
- function message_handler (sender, msg) {
170
- if (msg.event !== 'status.request') return;
167
+ function message_handler(sender, msg) {
168
+ if (msg.event !== 'status.request') return
171
169
 
172
170
  plugin.call_workers(msg, (response) => {
173
- msg.result = response.filter((el) => el != null);
174
- msg.event = 'status.result';
175
- sender.send(msg);
176
- });
171
+ msg.result = response.filter((el) => el != null)
172
+ msg.event = 'status.result'
173
+ sender.send(msg)
174
+ })
177
175
  }
178
176
 
179
- server.cluster.on('message', message_handler);
180
- next();
177
+ server.cluster.on('message', message_handler)
178
+ next()
181
179
  }
182
180
 
183
181
  exports.hook_init_child = function (next) {
184
- const self = this;
182
+ const self = this
185
183
 
186
- function message_handler (msg) {
187
- if (msg.event !== 'status.request') return;
184
+ function message_handler(msg) {
185
+ if (msg.event !== 'status.request') return
188
186
 
189
187
  self.command_action(msg.params, (err, result) => {
190
- msg.event = 'status.response';
191
- msg.result = result;
192
- process.send(msg);
193
- });
188
+ msg.event = 'status.response'
189
+ msg.result = result
190
+ process.send(msg)
191
+ })
194
192
  }
195
193
 
196
- process.on('message', message_handler);
197
- next();
194
+ process.on('message', message_handler)
195
+ next()
198
196
  }
199
197
 
200
198
  exports.call_master = (cmd, cb) => {
199
+ function message_handler(msg) {
200
+ if (msg.event !== 'status.result') return
201
201
 
202
- function message_handler (msg) {
203
- if (msg.event !== 'status.result') return;
204
-
205
- process.removeListener('message', message_handler);
206
- cb(null, msg.result);
202
+ process.removeListener('message', message_handler)
203
+ cb(null, msg.result)
207
204
  }
208
205
 
209
- process.on('message', message_handler);
210
- process.send({event: 'status.request', params: cmd});
206
+ process.on('message', message_handler)
207
+ process.send({ event: 'status.request', params: cmd })
211
208
  }
212
209
 
213
210
  exports.call_workers = function (cmd, cb) {
214
- Promise.allSettled(
215
- Object.values(server.cluster.workers).map(w => this.call_worker(w, cmd))
216
- )
217
- .then(r => {
211
+ Promise.allSettled(Object.values(server.cluster.workers).map((w) => this.call_worker(w, cmd))).then((r) => {
218
212
  cb(
219
213
  // r.filter(s => s.status === 'rejected').flatMap(s => s.reason),
220
- r.filter(s => s.status === 'fulfilled').flatMap(s => s.value),
214
+ r.filter((s) => s.status === 'fulfilled').flatMap((s) => s.value),
221
215
  )
222
216
  })
223
217
  }
@@ -225,25 +219,24 @@ exports.call_workers = function (cmd, cb) {
225
219
  // sends command to worker and then wait for response or timeout
226
220
  exports.call_worker = (worker, cmd) => {
227
221
  return new Promise((resolve) => {
228
- let timeout;
222
+ let timeout
229
223
 
230
- function message_handler (sender, msg) {
231
- if (sender.id !== worker.id) return;
232
- if (msg.event !== 'status.response') return;
224
+ function message_handler(sender, msg) {
225
+ if (sender.id !== worker.id) return
226
+ if (msg.event !== 'status.response') return
233
227
 
234
- clearTimeout(timeout);
235
- server.cluster.removeListener('message', message_handler);
228
+ clearTimeout(timeout)
229
+ server.cluster.removeListener('message', message_handler)
236
230
 
237
- resolve(msg.result);
231
+ resolve(msg.result)
238
232
  }
239
233
 
240
234
  timeout = setTimeout(() => {
241
- server.cluster.removeListener('message', message_handler);
242
- resolve();
243
- }, 1000);
244
-
235
+ server.cluster.removeListener('message', message_handler)
236
+ resolve()
237
+ }, 1000)
245
238
 
246
- server.cluster.on('message', message_handler);
247
- worker.send(cmd);
239
+ server.cluster.on('message', message_handler)
240
+ worker.send(cmd)
248
241
  })
249
242
  }
package/plugins/tarpit.js CHANGED
@@ -1,34 +1,45 @@
1
1
  // tarpit
2
2
 
3
3
  let hooks_to_delay = [
4
- 'connect', 'helo', 'ehlo', 'mail', 'rcpt', 'rcpt_ok',
5
- 'data', 'data_post', 'queue', 'unrecognized_command', 'vrfy', 'noop',
6
- 'rset', 'quit'
7
- ];
4
+ 'connect',
5
+ 'helo',
6
+ 'ehlo',
7
+ 'mail',
8
+ 'rcpt',
9
+ 'rcpt_ok',
10
+ 'data',
11
+ 'data_post',
12
+ 'queue',
13
+ 'unrecognized_command',
14
+ 'vrfy',
15
+ 'noop',
16
+ 'rset',
17
+ 'quit',
18
+ ]
8
19
 
9
20
  exports.register = function () {
10
21
  // Register tarpit function last
11
22
 
12
- const cfg = this.config.get('tarpit.ini');
23
+ const cfg = this.config.get('tarpit.ini')
13
24
  if (cfg?.main.hooks_to_delay) {
14
- hooks_to_delay = cfg.main.hooks_to_delay.split(/[\s,;]+/);
25
+ hooks_to_delay = cfg.main.hooks_to_delay.split(/[\s,;]+/)
15
26
  }
16
27
 
17
28
  for (const hook of hooks_to_delay) {
18
- this.register_hook(hook, 'tarpit');
29
+ this.register_hook(hook, 'tarpit')
19
30
  }
20
31
  }
21
32
 
22
33
  exports.tarpit = function (next, connection) {
23
- const { transaction } = connection;
24
- if (!transaction) return next();
34
+ const { transaction } = connection
35
+ if (!transaction) return next()
25
36
 
26
- let delay = connection.notes.tarpit;
37
+ let delay = connection.notes.tarpit
27
38
 
28
- if (!delay) delay = transaction.notes.tarpit;
39
+ if (!delay) delay = transaction.notes.tarpit
29
40
 
30
- if (!delay) return next();
41
+ if (!delay) return next()
31
42
 
32
- connection.loginfo(this, `tarpitting response for ${delay}s`);
33
- setTimeout(() => next(), delay * 1000);
43
+ connection.loginfo(this, `tarpitting response for ${delay}s`)
44
+ setTimeout(() => next(), delay * 1000)
34
45
  }
package/plugins/tls.js CHANGED
@@ -1,156 +1,156 @@
1
- 'use strict';
1
+ 'use strict'
2
2
  // TLS is built into Haraka. This plugin conditionally advertises STARTTLS.
3
3
  // see 'haraka -h tls' for help
4
4
 
5
- const tls_socket = require('./tls_socket');
5
+ /* global server */
6
+
7
+ const tls_socket = require('./tls_socket')
6
8
 
7
9
  // exported so tests can override config dir
8
- exports.net_utils = require('haraka-net-utils');
10
+ exports.net_utils = require('haraka-net-utils')
9
11
 
10
12
  exports.register = function () {
11
13
  tls_socket.load_tls_ini()
12
14
 
13
15
  if (tls_socket.cfg.redis.disable_for_failed_hosts) this.logdebug('Will disable STARTTLS for failing TLS hosts')
14
16
 
15
- this.register_hook('capabilities', 'advertise_starttls')
17
+ this.register_hook('capabilities', 'advertise_starttls')
16
18
  this.register_hook('unrecognized_command', 'upgrade_connection')
17
19
  }
18
20
 
19
21
  exports.shutdown = () => {
20
- if (tls_socket.shutdown) tls_socket.shutdown();
22
+ if (tls_socket.shutdown) tls_socket.shutdown()
21
23
  }
22
24
 
23
25
  exports.advertise_starttls = function (next, connection) {
24
-
25
26
  // if no TLS setup incomplete/invalid, don't advertise
26
27
  if (!tls_socket.tls_valid) {
27
- this.logerror('no valid TLS config');
28
- return next();
28
+ this.logerror('no valid TLS config')
29
+ return next()
29
30
  }
30
31
 
31
32
  /* Caution: do not advertise STARTTLS if already TLS upgraded */
32
- if (connection.tls.enabled) return next();
33
+ if (connection.tls.enabled) return next()
33
34
 
34
35
  if (this.net_utils.ip_in_list(tls_socket.cfg.no_tls_hosts, connection.remote.ip)) {
35
- return next();
36
+ return next()
36
37
  }
37
38
 
38
- function enable_tls () {
39
- connection.capabilities.push('STARTTLS');
40
- connection.tls.advertised = true;
41
- next();
39
+ function enable_tls() {
40
+ connection.capabilities.push('STARTTLS')
41
+ connection.tls.advertised = true
42
+ next()
42
43
  }
43
44
 
44
45
  // check if local port is excluded from starttls advertisement
45
- if (tls_socket.cfg.main.no_starttls_ports.includes(connection.local.port)) return next();
46
+ if (tls_socket.cfg.main.no_starttls_ports.includes(connection.local.port)) return next()
46
47
 
47
48
  // exclude port 587 from NO-GO
48
- if (connection.local.port === 587) return enable_tls();
49
+ if (connection.local.port === 587) return enable_tls()
49
50
 
50
51
  if (!tls_socket.cfg.redis || !server.notes.redis) {
51
- return enable_tls();
52
+ return enable_tls()
52
53
  }
53
54
 
54
- const { redis } = server.notes;
55
- const dbkey = `no_tls|${connection.remote.ip}`;
55
+ const { redis } = server.notes
56
+ const dbkey = `no_tls|${connection.remote.ip}`
56
57
 
57
- redis.get(dbkey)
58
- .then(dbr => {
59
- if (!dbr) return enable_tls();
60
- connection.results.add(this, { msg: 'no_tls'});
58
+ redis
59
+ .get(dbkey)
60
+ .then((dbr) => {
61
+ if (!dbr) return enable_tls()
62
+ connection.results.add(this, { msg: 'no_tls' })
61
63
  next(CONT, 'STARTTLS disabled because previous attempt failed')
62
64
  })
63
- .catch(err => {
64
- connection.results.add(this, {err});
65
- enable_tls();
65
+ .catch((err) => {
66
+ connection.results.add(this, { err })
67
+ enable_tls()
66
68
  })
67
69
  }
68
70
 
69
71
  exports.set_notls = function (connection) {
72
+ if (!tls_socket.cfg.redis.disable_for_failed_hosts) return
73
+ if (!server.notes.redis) return
70
74
 
71
- if (!tls_socket.cfg.redis.disable_for_failed_hosts) return;
72
- if (!server.notes.redis) return;
75
+ const expiry = tls_socket.cfg.redis.disable_inbound_expiry || 3600
73
76
 
74
- const expiry = tls_socket.cfg.redis.disable_inbound_expiry || 3600;
77
+ this.lognotice(connection, `STARTTLS failed. Marking ${connection.remote.ip} as non-TLS host for ${expiry} seconds`)
75
78
 
76
- this.lognotice(connection, `STARTTLS failed. Marking ${connection.remote.ip} as non-TLS host for ${expiry} seconds`);
77
-
78
- server.notes.redis.setEx(`no_tls|${connection.remote.ip}`, expiry, (new Date()).toISOString());
79
+ server.notes.redis.setEx(`no_tls|${connection.remote.ip}`, expiry, new Date().toISOString())
79
80
  }
80
81
 
81
82
  exports.upgrade_connection = function (next, connection, params) {
82
- if (!connection.tls.advertised) return next();
83
+ if (!connection.tls.advertised) return next()
83
84
 
84
85
  /* Watch for STARTTLS directive from client. */
85
- if (params[0].toUpperCase() !== 'STARTTLS') return next();
86
+ if (params[0].toUpperCase() !== 'STARTTLS') return next()
86
87
 
87
88
  /* Respond to STARTTLS command. */
88
- connection.respond(220, "Go ahead.");
89
+ connection.respond(220, 'Go ahead.')
89
90
 
90
- const plugin = this;
91
- let called_next = false;
91
+ const plugin = this
92
+ let called_next = false
92
93
  // adjust plugin.timeout like so: echo '45' > config/tls.timeout
93
- const timeout = plugin.timeout - 1;
94
-
95
- function nextOnce (disconnected) {
96
- if (called_next) return;
97
- called_next = true;
98
- clearTimeout(connection.notes.tls_timer);
99
- if (!disconnected) connection.lognotice(plugin, 'timeout setting up TLS');
100
- plugin.set_notls(connection);
101
- return next(DENYSOFTDISCONNECT);
94
+ const timeout = plugin.timeout - 1
95
+
96
+ function nextOnce(disconnected) {
97
+ if (called_next) return
98
+ called_next = true
99
+ clearTimeout(connection.notes.tls_timer)
100
+ if (!disconnected) connection.lognotice(plugin, 'timeout setting up TLS')
101
+ plugin.set_notls(connection)
102
+ return next(DENYSOFTDISCONNECT)
102
103
  }
103
104
 
104
105
  if (timeout && timeout > 0) {
105
- connection.notes.tls_timer = setTimeout(nextOnce, timeout * 1000);
106
+ connection.notes.tls_timer = setTimeout(nextOnce, timeout * 1000)
106
107
  }
107
108
 
108
- connection.notes.cleanUpDisconnect = nextOnce;
109
+ connection.notes.cleanUpDisconnect = nextOnce
109
110
 
110
111
  /* Upgrade the connection to TLS. */
111
112
  connection.client.upgrade((verified, verifyErr, cert, cipher) => {
112
- if (called_next) return;
113
- clearTimeout(connection.notes.tls_timer);
114
- called_next = true;
113
+ if (called_next) return
114
+ clearTimeout(connection.notes.tls_timer)
115
+ called_next = true
115
116
  connection.reset_transaction(() => {
116
-
117
117
  connection.setTLS({
118
118
  cipher,
119
119
  verified,
120
120
  authorizationError: verifyErr,
121
121
  peerCertificate: cert,
122
- });
122
+ })
123
123
 
124
- connection.results.add(plugin, connection.tls);
125
- plugin.emit_upgrade_msg(connection, verified, verifyErr, cert, cipher);
126
- next(OK);
124
+ connection.results.add(plugin, connection.tls)
125
+ plugin.emit_upgrade_msg(connection, verified, verifyErr, cert, cipher)
126
+ next(OK)
127
127
  })
128
128
  })
129
129
  }
130
130
 
131
131
  exports.hook_disconnect = (next, connection) => {
132
132
  if (connection.notes.cleanUpDisconnect) {
133
- connection.notes.cleanUpDisconnect(true);
133
+ connection.notes.cleanUpDisconnect(true)
134
134
  }
135
- next();
135
+ next()
136
136
  }
137
137
 
138
138
  exports.emit_upgrade_msg = function (conn, verified, verifyErr, cert, cipher) {
139
- let msg = 'secured:';
139
+ let msg = 'secured:'
140
140
  if (cipher) {
141
- msg += ` cipher=${cipher.name} version=${cipher.version}`;
141
+ msg += ` cipher=${cipher.name} version=${cipher.version}`
142
142
  }
143
- msg += ` verified=${verified}`;
144
- if (verifyErr) msg += ` error="${verifyErr}"`;
143
+ msg += ` verified=${verified}`
144
+ if (verifyErr) msg += ` error="${verifyErr}"`
145
145
  if (cert) {
146
146
  if (cert.subject) {
147
- msg += ` cn="${cert.subject.CN}" organization="${cert.subject.O}"`;
147
+ msg += ` cn="${cert.subject.CN}" organization="${cert.subject.O}"`
148
148
  }
149
- if (cert.issuer) msg += ` issuer="${cert.issuer.O}"`;
150
- if (cert.valid_to) msg += ` expires="${cert.valid_to}"`;
151
- if (cert.fingerprint) msg += ` fingerprint=${cert.fingerprint}`;
149
+ if (cert.issuer) msg += ` issuer="${cert.issuer.O}"`
150
+ if (cert.valid_to) msg += ` expires="${cert.valid_to}"`
151
+ if (cert.fingerprint) msg += ` fingerprint=${cert.fingerprint}`
152
152
  }
153
153
 
154
- conn.loginfo(this, msg);
155
- return msg;
154
+ conn.loginfo(this, msg)
155
+ return msg
156
156
  }