Haraka 3.1.0 → 3.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/.prettierignore +4 -0
  2. package/CONTRIBUTORS.md +5 -5
  3. package/Changes.md +69 -50
  4. package/Plugins.md +3 -1
  5. package/README.md +1 -1
  6. package/bin/haraka +475 -478
  7. package/config/outbound.ini +3 -0
  8. package/connection.js +1072 -1108
  9. package/docs/Connection.md +29 -30
  10. package/docs/CoreConfig.md +38 -39
  11. package/docs/CustomReturnCodes.md +0 -1
  12. package/docs/HAProxy.md +2 -2
  13. package/docs/Header.md +1 -1
  14. package/docs/Logging.md +29 -5
  15. package/docs/Outbound.md +93 -78
  16. package/docs/Plugins.md +103 -108
  17. package/docs/Transaction.md +49 -51
  18. package/docs/Tutorial.md +127 -143
  19. package/docs/deprecated/access.md +0 -1
  20. package/docs/deprecated/backscatterer.md +2 -3
  21. package/docs/deprecated/connect.rdns_access.md +18 -27
  22. package/docs/deprecated/data.headers.md +0 -1
  23. package/docs/deprecated/data.nomsgid.md +1 -2
  24. package/docs/deprecated/data.noreceived.md +1 -2
  25. package/docs/deprecated/data.rfc5322_header_checks.md +1 -2
  26. package/docs/deprecated/dkim_sign.md +13 -17
  27. package/docs/deprecated/dkim_verify.md +9 -17
  28. package/docs/deprecated/dnsbl.md +36 -38
  29. package/docs/deprecated/dnswl.md +41 -43
  30. package/docs/deprecated/lookup_rdns.strict.md +21 -34
  31. package/docs/deprecated/mail_from.access.md +17 -25
  32. package/docs/deprecated/mail_from.blocklist.md +9 -12
  33. package/docs/deprecated/mail_from.nobounces.md +1 -2
  34. package/docs/deprecated/rcpt_to.access.md +20 -27
  35. package/docs/deprecated/rcpt_to.blocklist.md +10 -13
  36. package/docs/deprecated/rcpt_to.routes.md +0 -1
  37. package/docs/deprecated/rdns.regexp.md +13 -15
  38. package/docs/plugins/aliases.md +89 -89
  39. package/docs/plugins/auth/auth_bridge.md +5 -7
  40. package/docs/plugins/auth/auth_ldap.md +11 -14
  41. package/docs/plugins/auth/auth_proxy.md +10 -12
  42. package/docs/plugins/auth/auth_vpopmaild.md +5 -6
  43. package/docs/plugins/auth/flat_file.md +4 -4
  44. package/docs/plugins/block_me.md +3 -3
  45. package/docs/plugins/data.signatures.md +1 -2
  46. package/docs/plugins/delay_deny.md +3 -4
  47. package/docs/plugins/max_unrecognized_commands.md +4 -4
  48. package/docs/plugins/prevent_credential_leaks.md +6 -6
  49. package/docs/plugins/process_title.md +18 -18
  50. package/docs/plugins/queue/deliver.md +2 -3
  51. package/docs/plugins/queue/discard.md +4 -4
  52. package/docs/plugins/queue/lmtp.md +1 -3
  53. package/docs/plugins/queue/qmail-queue.md +7 -9
  54. package/docs/plugins/queue/quarantine.md +16 -21
  55. package/docs/plugins/queue/rabbitmq.md +8 -11
  56. package/docs/plugins/queue/rabbitmq_amqplib.md +43 -39
  57. package/docs/plugins/queue/smtp_bridge.md +7 -10
  58. package/docs/plugins/queue/smtp_forward.md +42 -34
  59. package/docs/plugins/queue/smtp_proxy.md +30 -29
  60. package/docs/plugins/queue/test.md +1 -3
  61. package/docs/plugins/rcpt_to.in_host_list.md +6 -6
  62. package/docs/plugins/rcpt_to.max_count.md +1 -1
  63. package/docs/plugins/record_envelope_addresses.md +3 -3
  64. package/docs/plugins/reseed_rng.md +6 -6
  65. package/docs/plugins/status.md +9 -8
  66. package/docs/plugins/tarpit.md +7 -11
  67. package/docs/plugins/tls.md +12 -17
  68. package/docs/plugins/toobusy.md +4 -4
  69. package/docs/plugins/xclient.md +3 -3
  70. package/docs/tutorials/Migrating_from_v1_to_v2.md +19 -41
  71. package/docs/tutorials/SettingUpOutbound.md +6 -9
  72. package/endpoint.js +35 -38
  73. package/eslint.config.mjs +22 -19
  74. package/haraka.js +42 -47
  75. package/host_pool.js +75 -79
  76. package/http/html/404.html +45 -49
  77. package/http/html/index.html +39 -28
  78. package/http/package.json +2 -4
  79. package/line_socket.js +27 -28
  80. package/logger.js +182 -201
  81. package/outbound/client_pool.js +34 -27
  82. package/outbound/config.js +64 -59
  83. package/outbound/fsync_writestream.js +24 -25
  84. package/outbound/hmail.js +888 -835
  85. package/outbound/index.js +194 -187
  86. package/outbound/qfile.js +49 -52
  87. package/outbound/queue.js +197 -190
  88. package/outbound/timer_queue.js +41 -43
  89. package/outbound/tls.js +68 -61
  90. package/outbound/todo.js +11 -11
  91. package/package.json +38 -33
  92. package/plugins/.eslintrc.yaml +0 -1
  93. package/plugins/auth/auth_base.js +123 -127
  94. package/plugins/auth/auth_bridge.js +7 -7
  95. package/plugins/auth/auth_proxy.js +121 -126
  96. package/plugins/auth/auth_vpopmaild.js +84 -85
  97. package/plugins/auth/flat_file.js +18 -17
  98. package/plugins/block_me.js +31 -31
  99. package/plugins/data.signatures.js +13 -13
  100. package/plugins/delay_deny.js +65 -61
  101. package/plugins/prevent_credential_leaks.js +23 -23
  102. package/plugins/process_title.js +125 -128
  103. package/plugins/profile.js +5 -5
  104. package/plugins/queue/deliver.js +3 -3
  105. package/plugins/queue/discard.js +13 -14
  106. package/plugins/queue/lmtp.js +16 -17
  107. package/plugins/queue/qmail-queue.js +54 -55
  108. package/plugins/queue/quarantine.js +68 -70
  109. package/plugins/queue/rabbitmq.js +80 -87
  110. package/plugins/queue/rabbitmq_amqplib.js +75 -54
  111. package/plugins/queue/smtp_bridge.js +16 -16
  112. package/plugins/queue/smtp_forward.js +175 -179
  113. package/plugins/queue/smtp_proxy.js +69 -71
  114. package/plugins/queue/test.js +9 -9
  115. package/plugins/rcpt_to.host_list_base.js +30 -34
  116. package/plugins/rcpt_to.in_host_list.js +19 -19
  117. package/plugins/record_envelope_addresses.js +4 -4
  118. package/plugins/reseed_rng.js +4 -4
  119. package/plugins/status.js +90 -97
  120. package/plugins/tarpit.js +25 -14
  121. package/plugins/tls.js +68 -68
  122. package/plugins/toobusy.js +21 -23
  123. package/plugins/xclient.js +51 -53
  124. package/plugins.js +276 -293
  125. package/rfc1869.js +30 -35
  126. package/server.js +308 -299
  127. package/smtp_client.js +244 -228
  128. package/test/.eslintrc.yaml +0 -1
  129. package/test/connection.js +127 -134
  130. package/test/endpoint.js +53 -47
  131. package/test/fixtures/line_socket.js +12 -12
  132. package/test/fixtures/util_hmailitem.js +89 -85
  133. package/test/host_pool.js +90 -92
  134. package/test/installation/plugins/base_plugin.js +2 -2
  135. package/test/installation/plugins/folder_plugin/index.js +2 -3
  136. package/test/installation/plugins/inherits.js +3 -3
  137. package/test/installation/plugins/load_first.js +2 -3
  138. package/test/installation/plugins/plugin.js +1 -3
  139. package/test/installation/plugins/tls.js +2 -4
  140. package/test/logger.js +135 -116
  141. package/test/outbound/hmail.js +49 -35
  142. package/test/outbound/index.js +118 -101
  143. package/test/outbound/qfile.js +51 -53
  144. package/test/outbound_bounce_net_errors.js +84 -69
  145. package/test/outbound_bounce_rfc3464.js +235 -165
  146. package/test/plugins/auth/auth_base.js +420 -279
  147. package/test/plugins/auth/auth_vpopmaild.js +38 -39
  148. package/test/plugins/queue/smtp_forward.js +126 -104
  149. package/test/plugins/rcpt_to.host_list_base.js +85 -67
  150. package/test/plugins/rcpt_to.in_host_list.js +159 -112
  151. package/test/plugins/status.js +71 -64
  152. package/test/plugins/tls.js +37 -34
  153. package/test/plugins.js +97 -92
  154. package/test/rfc1869.js +19 -26
  155. package/test/server.js +293 -272
  156. package/test/smtp_client.js +180 -176
  157. package/test/tls_socket.js +62 -66
  158. package/test/transaction.js +159 -160
  159. package/tls_socket.js +331 -333
  160. package/transaction.js +129 -137
package/smtp_client.js CHANGED
@@ -1,4 +1,4 @@
1
- 'use strict';
1
+ 'use strict'
2
2
  // SMTP client object and class. This allows every part of the client
3
3
  // protocol to be hooked for different levels of control, such as
4
4
  // smtp_forward and smtp_proxy queue plugins.
@@ -7,17 +7,17 @@
7
7
  // than a bunch of connections to a single host from the configuration values
8
8
  // in "host" and "port" (see host_pool.js).
9
9
 
10
- const events = require('node:events');
10
+ const events = require('node:events')
11
11
 
12
- const ipaddr = require('ipaddr.js');
13
- const net_utils = require('haraka-net-utils');
14
- const utils = require('haraka-utils');
12
+ const ipaddr = require('ipaddr.js')
13
+ const net_utils = require('haraka-net-utils')
14
+ const utils = require('haraka-utils')
15
15
 
16
16
  const tls_socket = require('./tls_socket')
17
- const logger = require('./logger');
18
- const HostPool = require('./host_pool');
17
+ const logger = require('./logger')
18
+ const HostPool = require('./host_pool')
19
19
 
20
- const smtp_regexp = /^(\d{3})([ -])(.*)/;
20
+ const smtp_regexp = /^(\d{3})([ -])(.*)/
21
21
  const STATE = {
22
22
  IDLE: 1,
23
23
  ACTIVE: 2,
@@ -26,109 +26,110 @@ const STATE = {
26
26
  }
27
27
 
28
28
  class SMTPClient extends events.EventEmitter {
29
- constructor (opts = {}) {
30
- super();
31
- this.uuid = utils.uuid();
32
- this.connect_timeout = parseInt(opts.connect_timeout) || 30;
29
+ constructor(opts = {}) {
30
+ super()
31
+ this.uuid = utils.uuid()
32
+ this.connect_timeout = parseInt(opts.connect_timeout) || 30
33
33
  this.socket = opts.socket || this.get_socket(opts)
34
- this.socket.setTimeout(this.connect_timeout * 1000);
35
- this.socket.setKeepAlive(true);
36
- this.state = STATE.IDLE;
37
- this.command = 'greeting';
38
- this.response = [];
39
- this.connected = false;
40
- this.authenticating= false;
41
- this.authenticated = false;
42
- this.auth_capabilities = [];
43
- this.host = opts.host;
44
- this.port = opts.port;
45
- this.smtputf8 = false;
46
-
47
- const client = this;
34
+ this.socket.setTimeout(this.connect_timeout * 1000)
35
+ this.socket.setKeepAlive(true)
36
+ this.state = STATE.IDLE
37
+ this.command = 'greeting'
38
+ this.response = []
39
+ this.connected = false
40
+ this.authenticating = false
41
+ this.authenticated = false
42
+ this.auth_capabilities = []
43
+ this.host = opts.host
44
+ this.port = opts.port
45
+ this.smtputf8 = false
46
+
47
+ const client = this
48
48
 
49
49
  client.socket.on('line', (line) => {
50
- client.emit('server_protocol', line);
51
- const matches = smtp_regexp.exec(line);
50
+ client.emit('server_protocol', line)
51
+ const matches = smtp_regexp.exec(line)
52
52
  if (!matches) {
53
- client.emit('error', `${client.uuid}: Unrecognized response from upstream server: ${line}`);
54
- client.destroy();
55
- return;
53
+ client.emit('error', `${client.uuid}: Unrecognized response from upstream server: ${line}`)
54
+ client.destroy()
55
+ return
56
56
  }
57
57
 
58
- const code = matches[1];
59
- const cont = matches[2];
60
- const msg = matches[3];
58
+ const code = matches[1]
59
+ const cont = matches[2]
60
+ const msg = matches[3]
61
61
 
62
- client.response.push(msg);
63
- if (cont !== ' ') return;
62
+ client.response.push(msg)
63
+ if (cont !== ' ') return
64
64
 
65
65
  if (client.command === 'auth' || client.authenticating) {
66
- logger.info(`SERVER RESPONSE, CLIENT ${client.command}, authenticating=${client.authenticating},code=${code},cont=${cont},msg=${msg}`);
67
- if (/^3/.test(code) && (
68
- msg === 'VXNlcm5hbWU6' ||
69
- msg === 'dXNlcm5hbWU6' // Workaround ill-mannered SMTP servers (namely smtp.163.com)
70
- )) {
71
- client.emit('auth_username');
72
- return;
66
+ logger.info(
67
+ `SERVER RESPONSE, CLIENT ${client.command}, authenticating=${client.authenticating},code=${code},cont=${cont},msg=${msg}`,
68
+ )
69
+ if (
70
+ /^3/.test(code) &&
71
+ (msg === 'VXNlcm5hbWU6' || msg === 'dXNlcm5hbWU6') // Workaround ill-mannered SMTP servers (namely smtp.163.com)
72
+ ) {
73
+ client.emit('auth_username')
74
+ return
73
75
  }
74
76
  if (/^3/.test(code) && msg === 'UGFzc3dvcmQ6') {
75
- client.emit('auth_password');
76
- return;
77
+ client.emit('auth_password')
78
+ return
77
79
  }
78
80
  if (/^2/.test(code) && client.authenticating) {
79
- logger.info('AUTHENTICATED');
80
- client.authenticating = false;
81
- client.authenticated = true;
82
- client.emit('auth');
83
- return;
81
+ logger.info('AUTHENTICATED')
82
+ client.authenticating = false
83
+ client.authenticated = true
84
+ client.emit('auth')
85
+ return
84
86
  }
85
87
  }
86
88
 
87
89
  if (client.command === 'ehlo') {
88
90
  if (code.match(/^5/)) {
89
91
  // Handle fallback to HELO if EHLO is rejected
90
- client.emit('greeting', 'HELO');
91
- return;
92
+ client.emit('greeting', 'HELO')
93
+ return
92
94
  }
93
- client.emit('capabilities');
95
+ client.emit('capabilities')
94
96
  if (client.command !== 'ehlo') {
95
- return;
97
+ return
96
98
  }
97
99
  }
98
100
 
99
101
  if (client.command === 'xclient' && /^5/.test(code)) {
100
102
  // XCLIENT command was rejected (no permission?)
101
103
  // Carry on without XCLIENT
102
- client.command = 'helo';
103
- }
104
- else if (/^[45]/.test(code)) {
105
- client.emit('bad_code', code, client.response.join(' '));
104
+ client.command = 'helo'
105
+ } else if (/^[45]/.test(code)) {
106
+ client.emit('bad_code', code, client.response.join(' '))
106
107
  if (client.state !== STATE.ACTIVE) {
107
- return;
108
+ return
108
109
  }
109
110
  }
110
111
 
111
112
  if (/^441/.test(code)) {
112
113
  if (/Connection timed out/i.test(msg)) {
113
- client.destroy();
114
+ client.destroy()
114
115
  }
115
116
  }
116
117
 
117
118
  switch (client.command) {
118
119
  case 'xclient':
119
- client.xclient = true;
120
- client.emit('xclient', 'EHLO');
121
- break;
120
+ client.xclient = true
121
+ client.emit('xclient', 'EHLO')
122
+ break
122
123
  case 'starttls':
123
- client.upgrade(client.tls_options);
124
- break;
124
+ client.upgrade(client.tls_options)
125
+ break
125
126
  case 'greeting':
126
- client.connected = true;
127
- client.emit('greeting', 'EHLO');
128
- break;
127
+ client.connected = true
128
+ client.emit('greeting', 'EHLO')
129
+ break
129
130
  case 'ehlo':
130
- client.emit('helo');
131
- break;
131
+ client.emit('helo')
132
+ break
132
133
  case 'helo':
133
134
  case 'mail':
134
135
  case 'rcpt':
@@ -136,129 +137,144 @@ class SMTPClient extends events.EventEmitter {
136
137
  case 'dot':
137
138
  case 'rset':
138
139
  case 'auth':
139
- client.emit(client.command);
140
- break;
140
+ client.emit(client.command)
141
+ break
141
142
  case 'quit':
142
- client.emit('quit');
143
- client.destroy();
144
- break;
143
+ client.emit('quit')
144
+ client.destroy()
145
+ break
145
146
  default:
146
- throw new Error(`Unknown command: ${client.command}`);
147
+ throw new Error(`Unknown command: ${client.command}`)
147
148
  }
148
- });
149
+ })
149
150
 
150
151
  client.socket.on('connect', () => {
151
152
  // Replace connection timeout with idle timeout
152
- client.socket.setTimeout((opts.idle_timeout || 300) * 1000);
153
+ client.socket.setTimeout((opts.idle_timeout || 300) * 1000)
153
154
  if (!client.socket.remoteAddress) {
154
155
  // "Value may be undefined if the socket is destroyed"
155
- logger.debug('socket.remoteAddress undefined');
156
- return;
156
+ logger.debug('socket.remoteAddress undefined')
157
+ return
157
158
  }
158
- client.remote_ip = ipaddr.process(client.socket.remoteAddress).toString();
159
+ client.remote_ip = ipaddr.process(client.socket.remoteAddress).toString()
159
160
  })
160
161
 
161
- function closed (msg) {
162
- return error => {
163
- if (!error) error = '';
162
+ function closed(msg) {
163
+ return (error) => {
164
+ if (!error) error = ''
164
165
 
165
166
  // error is e.g. "Error: connect ECONNREFUSED"
166
- const errMsg = `${client.uuid}: [${client.host}:${client.port}] SMTP connection ${msg} ${error}`;
167
+ const errMsg = `${client.uuid}: [${client.host}:${client.port}] SMTP connection ${msg} ${error}`
167
168
 
168
169
  /* eslint-disable no-fallthrough */
169
170
  switch (client.state) {
170
171
  case STATE.ACTIVE:
171
- client.emit('error', errMsg);
172
+ client.emit('error', errMsg)
172
173
  case STATE.IDLE:
173
174
  case STATE.RELEASED:
174
- client.destroy();
175
- break;
175
+ client.destroy()
176
+ break
176
177
  case STATE.DESTROYED:
177
178
  if (msg === 'errored' || msg === 'timed out') {
178
- client.emit('connection-error', errMsg);
179
+ client.emit('connection-error', errMsg)
179
180
  }
180
181
  break
181
182
  default:
182
183
  }
183
184
 
184
- logger.debug(`[smtp_client] ${errMsg} (state=${client.state})`);
185
+ logger.debug(`[smtp_client] ${errMsg} (state=${client.state})`)
185
186
  }
186
187
  }
187
188
 
188
- client.socket.on('error', closed('errored'));
189
- client.socket.on('timeout', closed('timed out'));
190
- client.socket.on('close', closed('closed'));
191
- client.socket.on('end', closed('ended'));
189
+ client.socket.on('error', closed('errored'))
190
+ client.socket.on('timeout', closed('timed out'))
191
+ client.socket.on('close', closed('closed'))
192
+ client.socket.on('end', closed('ended'))
192
193
  }
193
194
 
194
- load_tls_config (opts) {
195
-
196
- const tls_options = { servername: this.host };
195
+ load_tls_config(opts) {
196
+ const tls_options = { servername: this.host }
197
197
  if (opts) {
198
- Object.assign(tls_options, opts);
198
+ Object.assign(tls_options, opts)
199
199
  }
200
200
 
201
- this.tls_options = tls_options;
201
+ this.tls_options = tls_options
202
202
  }
203
203
 
204
- send_command (command, data) {
205
- const line = (command === 'dot') ? '.' : command + (data ? (` ${data}`) : '');
206
- this.emit('client_protocol', line);
207
- this.command = command.toLowerCase();
208
- this.response = [];
209
- this.socket.write(`${line}\r\n`);
204
+ send_command(command, data) {
205
+ const line = command === 'dot' ? '.' : command + (data ? ` ${data}` : '')
206
+ this.emit('client_protocol', line)
207
+ this.command = command.toLowerCase()
208
+ this.response = []
209
+ this.socket.write(`${line}\r\n`)
210
210
  }
211
211
 
212
- start_data (data) {
213
- this.response = [];
214
- this.command = 'dot';
215
- data.pipe(this.socket, { dot_stuffing: true, ending_dot: true, end: false });
212
+ start_data(data) {
213
+ this.response = []
214
+ this.command = 'dot'
215
+ // SUNSET: dot_stuffing was renamed to dot_stuffed, remove it after 2026-01
216
+ data.pipe(this.socket, {
217
+ dot_stuffed: false,
218
+ dot_stuffing: true,
219
+ ending_dot: true,
220
+ end: false,
221
+ })
216
222
  }
217
223
 
218
- release () {
219
- if (this.state === STATE.DESTROYED) return;
220
- logger.debug(`[smtp_client] ${this.uuid} releasing, state=${this.state}`);
221
-
222
- [
223
- 'auth', 'bad_code', 'capabilities', 'client_protocol', 'connection-error',
224
- 'data', 'dot', 'error', 'greeting', 'helo',
225
- 'mail', 'rcpt', 'rset', 'server_protocol', 'xclient',
226
- ].forEach(l => {
227
- this.removeAllListeners(l);
224
+ release() {
225
+ if (this.state === STATE.DESTROYED) return
226
+ logger.debug(`[smtp_client] ${this.uuid} releasing, state=${this.state}`)
227
+ ;[
228
+ 'auth',
229
+ 'bad_code',
230
+ 'capabilities',
231
+ 'client_protocol',
232
+ 'connection-error',
233
+ 'data',
234
+ 'dot',
235
+ 'error',
236
+ 'greeting',
237
+ 'helo',
238
+ 'mail',
239
+ 'rcpt',
240
+ 'rset',
241
+ 'server_protocol',
242
+ 'xclient',
243
+ ].forEach((l) => {
244
+ this.removeAllListeners(l)
228
245
  })
229
246
 
230
- if (this.connected) this.send_command('QUIT');
247
+ if (this.connected) this.send_command('QUIT')
231
248
  this.destroy()
232
249
  }
233
250
 
234
- destroy () {
251
+ destroy() {
235
252
  if (this.state === STATE.DESTROYED) return
236
- this.state = STATE.DESTROYED;
237
- this.socket.destroy();
253
+ this.state = STATE.DESTROYED
254
+ this.socket.destroy()
238
255
  }
239
256
 
240
- upgrade (tls_options) {
241
-
257
+ upgrade(tls_options) {
242
258
  this.socket.upgrade(tls_options, (verified, verifyError, cert, cipher) => {
243
- logger.info(`secured:${
244
-
245
- (cipher) ? ` cipher=${cipher.name} version=${cipher.version}` : ''
246
- } verified=${verified}${
247
- (verifyError) ? ` error="${verifyError}"` : ''
248
- }${(cert?.subject) ? ` cn="${cert.subject.CN}" organization="${cert.subject.O}"` : ''
249
- }${(cert?.issuer) ? ` issuer="${cert.issuer.O}"` : ''
250
- }${(cert?.valid_to) ? ` expires="${cert.valid_to}"` : ''
251
- }${(cert?.fingerprint) ? ` fingerprint=${cert.fingerprint}` : ''}`);
252
- });
259
+ logger.info(
260
+ `secured:${
261
+ cipher ? ` cipher=${cipher.name} version=${cipher.version}` : ''
262
+ } verified=${verified}${verifyError ? ` error="${verifyError}"` : ''}${
263
+ cert?.subject ? ` cn="${cert.subject.CN}" organization="${cert.subject.O}"` : ''
264
+ }${cert?.issuer ? ` issuer="${cert.issuer.O}"` : ''}${
265
+ cert?.valid_to ? ` expires="${cert.valid_to}"` : ''
266
+ }${cert?.fingerprint ? ` fingerprint=${cert.fingerprint}` : ''}`,
267
+ )
268
+ })
253
269
  }
254
270
 
255
- is_dead_sender (plugin, connection) {
256
- if (connection?.transaction) return false;
271
+ is_dead_sender(plugin, connection) {
272
+ if (connection?.transaction) return false
257
273
 
258
274
  // This likely means the sender went away on us, cleanup.
259
- connection.logwarn(plugin, "transaction went away, releasing smtp_client");
260
- this.release();
261
- return true;
275
+ connection.logwarn(plugin, 'transaction went away, releasing smtp_client')
276
+ this.release()
277
+ return true
262
278
  }
263
279
 
264
280
  get_socket(opts) {
@@ -272,7 +288,7 @@ class SMTPClient extends events.EventEmitter {
272
288
  }
273
289
  }
274
290
 
275
- exports.smtp_client = SMTPClient;
291
+ exports.smtp_client = SMTPClient
276
292
 
277
293
  // Get a smtp_client for the given attributes.
278
294
  // used only in testing
@@ -286,41 +302,40 @@ exports.onCapabilitiesOutbound = (smtp_client, secured, connection, config, on_s
286
302
  for (const line in smtp_client.response) {
287
303
  if (/^XCLIENT/.test(smtp_client.response[line])) {
288
304
  if (!smtp_client.xclient) {
289
- smtp_client.send_command('XCLIENT', `ADDR=${connection.remote.ip}`);
290
- return;
305
+ smtp_client.send_command('XCLIENT', `ADDR=${connection.remote.ip}`)
306
+ return
291
307
  }
292
308
  }
293
309
 
294
310
  if (/^SMTPUTF8/.test(smtp_client.response[line])) {
295
- smtp_client.smtputf8 = true;
311
+ smtp_client.smtputf8 = true
296
312
  }
297
313
 
298
314
  if (/^STARTTLS/.test(smtp_client.response[line]) && !secured) {
299
-
300
315
  let hostBanned = false
301
316
  let serverBanned = false
302
317
 
303
318
  // Check if there are any banned TLS hosts
304
319
  if (smtp_client.tls_options.no_tls_hosts) {
305
320
  // If there are check if these hosts are in the blacklist
306
- hostBanned = net_utils.ip_in_list(smtp_client.tls_config.no_tls_hosts, config.host);
307
- serverBanned = net_utils.ip_in_list(smtp_client.tls_config.no_tls_hosts, smtp_client.remote_ip);
321
+ hostBanned = net_utils.ip_in_list(smtp_client.tls_config.no_tls_hosts, config.host)
322
+ serverBanned = net_utils.ip_in_list(smtp_client.tls_config.no_tls_hosts, smtp_client.remote_ip)
308
323
  }
309
324
 
310
325
  if (!hostBanned && !serverBanned && config.enable_tls) {
311
- smtp_client.socket.on('secure', on_secured);
312
- smtp_client.secured = false; // have to wait in forward plugin before we can do auth, even if capabilities are there on first EHLO
313
- smtp_client.send_command('STARTTLS');
314
- return;
326
+ smtp_client.socket.on('secure', on_secured)
327
+ smtp_client.secured = false // have to wait in forward plugin before we can do auth, even if capabilities are there on first EHLO
328
+ smtp_client.send_command('STARTTLS')
329
+ return
315
330
  }
316
331
  }
317
332
 
318
- let auth_matches = smtp_client.response[line].match(/^AUTH (.*)$/);
333
+ let auth_matches = smtp_client.response[line].match(/^AUTH (.*)$/)
319
334
  if (auth_matches) {
320
- smtp_client.auth_capabilities = [];
321
- auth_matches = auth_matches[1].split(' ');
335
+ smtp_client.auth_capabilities = []
336
+ auth_matches = auth_matches[1].split(' ')
322
337
  for (const authMatch of auth_matches) {
323
- smtp_client.auth_capabilities.push(authMatch.toLowerCase());
338
+ smtp_client.auth_capabilities.push(authMatch.toLowerCase())
324
339
  }
325
340
  }
326
341
  }
@@ -337,147 +352,148 @@ exports.get_client_plugin = (plugin, connection, c, callback) => {
337
352
  c.auth = {
338
353
  type: c.auth_type,
339
354
  user: c.auth_user,
340
- pass: c.auth_pass
355
+ pass: c.auth_pass,
341
356
  }
342
357
  }
343
358
 
344
- const hostport = get_hostport(connection, connection.server, c);
359
+ const hostport = get_hostport(connection, connection.server, c)
345
360
  const smtp_client = new SMTPClient(hostport)
346
- logger.info(`[smtp_client] uuid=${smtp_client.uuid} host=${hostport.host} port=${hostport.port} created`);
361
+ logger.info(`[smtp_client] uuid=${smtp_client.uuid} host=${hostport.host} port=${hostport.port} created`)
347
362
 
348
- connection.logdebug(plugin, `Got smtp_client: ${smtp_client.uuid}`);
363
+ connection.logdebug(plugin, `Got smtp_client: ${smtp_client.uuid}`)
349
364
 
350
- let secured = false;
365
+ let secured = false
351
366
 
352
- smtp_client.load_tls_config(plugin.tls_options);
367
+ smtp_client.load_tls_config(plugin.tls_options)
353
368
 
354
369
  smtp_client.call_next = function (retval, msg) {
355
370
  if (this.next) {
356
- const { next } = this;
357
- delete this.next;
358
- next(retval, msg);
371
+ const { next } = this
372
+ delete this.next
373
+ next(retval, msg)
359
374
  }
360
375
  }
361
376
 
362
377
  smtp_client.on('client_protocol', (line) => {
363
- connection.logprotocol(plugin, `C: ${line}`);
378
+ connection.logprotocol(plugin, `C: ${line}`)
364
379
  })
365
380
 
366
381
  smtp_client.on('server_protocol', (line) => {
367
- connection.logprotocol(plugin, `S: ${line}`);
382
+ connection.logprotocol(plugin, `S: ${line}`)
368
383
  })
369
384
 
370
- function helo (command) {
385
+ function helo(command) {
371
386
  if (smtp_client.xclient) {
372
- smtp_client.send_command(command, connection.hello.host);
373
- }
374
- else {
375
- smtp_client.send_command(command, connection.local.host);
387
+ smtp_client.send_command(command, connection.hello.host)
388
+ } else {
389
+ smtp_client.send_command(command, connection.local.host)
376
390
  }
377
391
  }
378
- smtp_client.on('greeting', helo);
379
- smtp_client.on('xclient', helo);
380
-
381
- function on_secured () {
382
- if (secured) return;
383
- secured = true;
384
- smtp_client.secured = true;
385
- smtp_client.emit('greeting', 'EHLO');
392
+ smtp_client.on('greeting', helo)
393
+ smtp_client.on('xclient', helo)
394
+
395
+ function on_secured() {
396
+ if (secured) return
397
+ secured = true
398
+ smtp_client.secured = true
399
+ smtp_client.emit('greeting', 'EHLO')
386
400
  }
387
401
 
388
402
  smtp_client.on('capabilities', () => {
389
- exports.onCapabilitiesOutbound(smtp_client, secured, connection, c, on_secured);
390
- });
403
+ exports.onCapabilitiesOutbound(smtp_client, secured, connection, c, on_secured)
404
+ })
391
405
 
392
406
  smtp_client.on('helo', () => {
393
407
  if (!c.auth || smtp_client.authenticated) {
394
- if (smtp_client.is_dead_sender(plugin, connection)) return;
408
+ if (smtp_client.is_dead_sender(plugin, connection)) return
395
409
 
396
- smtp_client.send_command('MAIL', `FROM:${connection.transaction.mail_from.format(!smtp_client.smtp_utf8)}`);
397
- return;
410
+ smtp_client.send_command('MAIL', `FROM:${connection.transaction.mail_from.format(!smtp_client.smtp_utf8)}`)
411
+ return
398
412
  }
399
413
 
400
- if (c.auth.type === null || typeof (c.auth.type) === 'undefined') return; // Ignore blank
401
- const auth_type = c.auth.type.toLowerCase();
414
+ if (c.auth.type === null || typeof c.auth.type === 'undefined') return // Ignore blank
415
+ const auth_type = c.auth.type.toLowerCase()
402
416
  if (!smtp_client.auth_capabilities.includes(auth_type)) {
403
- throw new Error(`Auth type "${auth_type}" not supported by server (supports: ${smtp_client.auth_capabilities.join(',')})`);
417
+ throw new Error(
418
+ `Auth type "${auth_type}" not supported by server (supports: ${smtp_client.auth_capabilities.join(',')})`,
419
+ )
404
420
  }
405
421
  switch (auth_type) {
406
422
  case 'plain':
407
423
  if (!c.auth.user || !c.auth.pass) {
408
- throw new Error("Must include auth.user and auth.pass for PLAIN auth.");
424
+ throw new Error('Must include auth.user and auth.pass for PLAIN auth.')
409
425
  }
410
- logger.debug(`[smtp_client] uuid=${smtp_client.uuid} authenticating as "${c.auth.user}"`);
411
- smtp_client.send_command('AUTH', `PLAIN ${utils.base64(`${c.auth.user}\0${c.auth.user}\0${c.auth.pass}`)}`);
412
- break;
426
+ logger.debug(`[smtp_client] uuid=${smtp_client.uuid} authenticating as "${c.auth.user}"`)
427
+ smtp_client.send_command(
428
+ 'AUTH',
429
+ `PLAIN ${utils.base64(`${c.auth.user}\0${c.auth.user}\0${c.auth.pass}`)}`,
430
+ )
431
+ break
413
432
  case 'cram-md5':
414
- throw new Error("Not implemented");
433
+ throw new Error('Not implemented')
415
434
  default:
416
- throw new Error(`Unknown AUTH type: ${auth_type}`);
435
+ throw new Error(`Unknown AUTH type: ${auth_type}`)
417
436
  }
418
- });
437
+ })
419
438
 
420
439
  smtp_client.on('auth', () => {
421
440
  // if authentication has been handled by plugin(s)
422
- if (smtp_client.authenticating) return;
441
+ if (smtp_client.authenticating) return
423
442
 
424
- if (smtp_client.is_dead_sender(plugin, connection)) return;
443
+ if (smtp_client.is_dead_sender(plugin, connection)) return
425
444
 
426
- smtp_client.authenticated = true;
427
- smtp_client.send_command('MAIL', `FROM:${connection.transaction.mail_from.format(!smtp_client.smtp_utf8)}`);
428
- });
445
+ smtp_client.authenticated = true
446
+ smtp_client.send_command('MAIL', `FROM:${connection.transaction.mail_from.format(!smtp_client.smtp_utf8)}`)
447
+ })
429
448
 
430
449
  // these errors only get thrown when the connection is still active
431
450
  smtp_client.on('error', (msg) => {
432
- connection.logwarn(plugin, msg);
433
- smtp_client.call_next();
434
- });
451
+ connection.logwarn(plugin, msg)
452
+ smtp_client.call_next()
453
+ })
435
454
 
436
455
  // these are the errors thrown when the connection is dead
437
456
  smtp_client.on('connection-error', (error) => {
438
457
  // error contains e.g. "Error: connect ECONNREFUSE"
439
- logger.error(`backend failure: ${smtp_client.host}:${smtp_client.port} - ${error}`);
440
- const { host_pool } = connection.server.notes;
458
+ logger.error(`backend failure: ${smtp_client.host}:${smtp_client.port} - ${error}`)
459
+ const { host_pool } = connection.server.notes
441
460
  // only exists for if forwarding_host_pool is set in the config
442
461
  if (host_pool) {
443
- host_pool.failed(smtp_client.host, smtp_client.port);
462
+ host_pool.failed(smtp_client.host, smtp_client.port)
444
463
  }
445
- smtp_client.call_next();
446
- });
464
+ smtp_client.call_next()
465
+ })
447
466
 
448
467
  if (smtp_client.connected) {
449
468
  if (smtp_client.xclient) {
450
- smtp_client.send_command('XCLIENT', `ADDR=${connection.remote.ip}`);
451
- }
452
- else {
453
- smtp_client.emit('helo');
469
+ smtp_client.send_command('XCLIENT', `ADDR=${connection.remote.ip}`)
470
+ } else {
471
+ smtp_client.emit('helo')
454
472
  }
455
473
  }
456
474
 
457
- callback(null, smtp_client);
475
+ callback(null, smtp_client)
458
476
  }
459
477
 
460
- function get_hostport (connection, server, cfg) {
461
-
478
+ function get_hostport(connection, server, cfg) {
462
479
  if (cfg.forwarding_host_pool) {
463
- if (! server.notes.host_pool) {
464
- connection.logwarn(`creating host_pool from ${cfg.forwarding_host_pool}`);
465
- server.notes.host_pool =
466
- new HostPool(
467
- cfg.forwarding_host_pool, // 1.2.3.4:420, 5.6.7.8:420
468
- cfg.dead_forwarding_host_retry_secs
469
- );
480
+ if (!server.notes.host_pool) {
481
+ connection.logwarn(`creating host_pool from ${cfg.forwarding_host_pool}`)
482
+ server.notes.host_pool = new HostPool(
483
+ cfg.forwarding_host_pool, // 1.2.3.4:420, 5.6.7.8:420
484
+ cfg.dead_forwarding_host_retry_secs,
485
+ )
470
486
  }
471
487
 
472
- const host = server.notes.host_pool.get_host();
473
- if (host) return host; // { host: 1.2.3.4, port: 567 }
488
+ const host = server.notes.host_pool.get_host()
489
+ if (host) return host // { host: 1.2.3.4, port: 567 }
474
490
 
475
- logger.error('[smtp_client] no backend hosts in pool!');
476
- throw new Error("no backend hosts found in pool!");
491
+ logger.error('[smtp_client] no backend hosts in pool!')
492
+ throw new Error('no backend hosts found in pool!')
477
493
  }
478
494
 
479
- if (cfg.host && cfg.port) return { host: cfg.host, port: cfg.port };
495
+ if (cfg.host && cfg.port) return { host: cfg.host, port: cfg.port }
480
496
 
481
- logger.warn("[smtp_client] forwarding_host_pool or host and port were not found in config file");
482
- throw new Error("You must specify either forwarding_host_pool or host and port");
497
+ logger.warn('[smtp_client] forwarding_host_pool or host and port were not found in config file')
498
+ throw new Error('You must specify either forwarding_host_pool or host and port')
483
499
  }