Haraka 3.1.4 → 3.1.6

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 (55) hide show
  1. package/.prettierignore +1 -1
  2. package/{Changes.md → CHANGELOG.md} +34 -0
  3. package/CONTRIBUTORS.md +26 -26
  4. package/README.md +68 -93
  5. package/SECURITY.md +178 -0
  6. package/bin/haraka +7 -14
  7. package/config/plugins +0 -3
  8. package/docs/Connection.md +126 -39
  9. package/docs/CoreConfig.md +92 -74
  10. package/docs/HAProxy.md +41 -25
  11. package/docs/Logging.md +68 -38
  12. package/docs/Outbound.md +124 -179
  13. package/docs/Plugins.md +38 -59
  14. package/docs/Transaction.md +78 -83
  15. package/docs/Tutorial.md +122 -209
  16. package/docs/plugins/aliases.md +1 -141
  17. package/docs/plugins/auth/auth_ldap.md +2 -39
  18. package/docs/plugins/max_unrecognized_commands.md +4 -18
  19. package/docs/plugins/process_title.md +3 -3
  20. package/docs/plugins/reseed_rng.md +11 -13
  21. package/docs/plugins/tls.md +7 -7
  22. package/docs/plugins/toobusy.md +10 -4
  23. package/docs/tutorials/SettingUpOutbound.md +40 -48
  24. package/endpoint.js +32 -2
  25. package/outbound/hmail.js +3 -2
  26. package/outbound/index.js +3 -0
  27. package/package.json +21 -34
  28. package/plugins/queue/smtp_forward.js +4 -4
  29. package/run_tests +3 -15
  30. package/server.js +17 -7
  31. package/smtp_client.js +8 -6
  32. package/test/connection.js +234 -0
  33. package/test/endpoint.js +32 -4
  34. package/test/host_pool.js +57 -31
  35. package/test/logger.js +75 -135
  36. package/test/outbound/bounce_net_errors.js +87 -131
  37. package/test/outbound/bounce_rfc3464.js +177 -254
  38. package/test/outbound/hmail.js +19 -0
  39. package/test/outbound/index.js +189 -0
  40. package/test/outbound/queue.js +92 -0
  41. package/test/plugins/auth/auth_base.js +39 -44
  42. package/test/plugins/auth/auth_vpopmaild.js +8 -9
  43. package/test/plugins/queue/smtp_forward.js +953 -183
  44. package/test/plugins/rcpt_to.host_list_base.js +58 -93
  45. package/test/plugins/rcpt_to.in_host_list.js +126 -175
  46. package/test/plugins/record_envelope_addresses.js +8 -8
  47. package/test/plugins/status.js +10 -10
  48. package/test/plugins/tls.js +9 -19
  49. package/test/plugins/xclient.js +75 -110
  50. package/test/plugins.js +10 -13
  51. package/test/rfc1869.js +50 -70
  52. package/test/server.js +438 -421
  53. package/test/smtp_client.js +1192 -218
  54. package/test/tls_socket.js +242 -0
  55. package/tls_socket.js +18 -22
@@ -1,143 +1,3 @@
1
1
  # aliases
2
2
 
3
- This plugin allows one to configure aliases that may perform an action or
4
- change the RCPT address in a number of ways. All aliases are specified in
5
- a JSON formatted configuration file, and must have at very least an action.
6
- Any syntax error found in the JSON format config file will stop the server
7
- from running.
8
-
9
- IMPORTANT: this plugin must appear in `config/plugins` before other plugins
10
- that run on hook_rcpt
11
-
12
- WARNING: DO NOT USE THIS PLUGIN WITH queue/smtp_proxy.
13
-
14
- ## Configuration
15
-
16
- - aliases
17
-
18
- JSON formatted configuration file that must contain, at very least, a key
19
- to match against RCPT address, and a value that is an associative array
20
- with an "action" : "<action>" key, value pair. An example:
21
-
22
- { "test1" : { "action" : "drop" } }
23
-
24
- In the above example the "test1" alias will drop any message that matches
25
- test1, or test1-_ or test1+_ (wildcard '-' or '+', see below). Actions
26
- may in turn have 0 or more options listed with them like so:
27
-
28
- { "test3" : { "action" : "alias", "to" : "test3-works" } }
29
-
30
- In the above example the "test3" alias has an action of "alias", and
31
- a required "to" field. If this "to" field were missing the alias would
32
- fail to run, and an error would be printed in the logs.
33
-
34
- Now aliases of 'user', '@host' and 'user@host' possible:
35
-
36
- { "demo" : { "action" : "drop" } }
37
- or
38
- { "@example.com" : { "action" : "drop" } }
39
- or
40
- { "demo@example.com" : { "action" : "drop" } }
41
-
42
- Aliases may also be exploded to multiple recipients:
43
-
44
- { "sales@example.com": { "action: "alias", "to": ["alice@example.com", "bob@example.com"] } }
45
-
46
- - wildcard notation
47
-
48
- In an effort to match some of the functionality of other alias parsers
49
- we've allowed wildcard matching of the alias against the right most
50
- string of a RCPT address. The characters '-' and '+' are commonly used
51
- for subaddressing and this plugin has built-in support to alias the
52
- "user" part of the email address.
53
-
54
- That is, if our address were test2-testing@example.com (or
55
- test2+testing@example.com), the below alias would match:
56
-
57
- { "test2" : { "action" : "drop" } }
58
-
59
- The larger, and more specific alias, should always match first when
60
- using wildcard '-' notation. So if the above RCPT were put up against
61
- this alias config, it would not drop, but rather map to another
62
- address:
63
-
64
- {
65
- "test2" : { "action" : "drop" },
66
- "test2-testing" : { "action" : "alias", "to" : "test@foo.com" }
67
- }
68
-
69
- - chaining and circuits
70
-
71
- In short, we do not allow chaining of aliases at this time. As a
72
- side-effect, we enjoy protections against alias circuits.
73
-
74
- - optional one line formatting
75
-
76
- Any valid JSON will due, however, please consider keeping each alias
77
- on its own line so that others that wish to grep the aliases file
78
- have an easier time finding the full configuration for an alias.
79
-
80
- - nondeterministic duplicate matches
81
-
82
- This plugin was written with speed in mind. That means every lookup
83
- hashes into the alias file for its match. While the act of doing so
84
- is fast, it does mean that any duplicate alias entries will match
85
- nondeterministically. That is, we cannot predict what will happen
86
- here:
87
-
88
- {
89
- "coinflip" : { "action" : "alias", "to" : "heads@coin.com" },
90
- "coinflip" : { "action" : "alias", "to" : "tails@coin.com" }
91
- }
92
-
93
- Truth be told, one result will likely always be chosen over the other,
94
- so this is not exactly a coinflip. We simply cannot say what the
95
- language implementation will do here, it could change tomorrow.
96
-
97
- - action (required)
98
-
99
- The following is a list of supported actions, and the options they require.
100
- - drop
101
-
102
- This action simply drops a message, while pretending everything was
103
- okay to the sender. This acts much like an alias to /dev/null in
104
- other servers.
105
-
106
- - alias
107
-
108
- This action will map the alias key to the address specified in the
109
- "to" option. A note about matching in addition to the note
110
- about wildcard '-' above. When we match an alias, we store the
111
- hostname of the match for a shortcut substitution syntax later.
112
- - to (required)
113
-
114
- This option is the full address, or local part at matched hostname
115
- that the RCPT address will be re-written to. For an example of
116
- an alias to a full address consider the following:
117
-
118
- { "test5" : { "action" : "alias", "to" : "test5@foo.com" } }
119
-
120
- This will map RCPT matches for "test5" to "test5-works@foo.com".
121
- This would map "test5@somedomain.com" to "test5-works@foo.com"
122
- every time. Now compare this notation with its shortcut
123
- counterpart, best used when the "to" address is at the same
124
- domain as the match:
125
-
126
- { "test4" : { "action" : "alias", "to" : "test4" } }
127
-
128
- Clearly, this notation is more compact, but what does it do. Well,
129
- mail to "test4-foo@anydomain.com" will map to "test4@anydomain.com".
130
- One can see the clear benefit of using this notation with lots of
131
- aliases on a single domain that map to other local parts at the
132
- same domain.
133
-
134
- ### Example Configuration
135
-
136
- {
137
- "test1" : { "action" : "drop" },
138
- "test2" : { "action" : "drop" },
139
- "test3" : { "action" : "alias", "to" : "test3-works" },
140
- "test4" : { "action" : "alias", "to" : "test4" },
141
- "test5" : { "action" : "alias", "to" : "test5-works@success.com" },
142
- "test6" : { "action" : "alias", "to" : "test6-works@success.com" }
143
- }
3
+ Repackaged as [haraka-plugin-aliases](https://github.com/haraka/haraka-plugin-aliases).
@@ -1,41 +1,4 @@
1
1
  # auth/auth_ldap
2
2
 
3
- The `auth/auth_ldap` plugin uses an LDAP bind to authenticate a user. Currently
4
- only one server and multiple DNs can be configured. If any of the DN binds succeed,
5
- the user is authenticated.
6
-
7
- ## Configuration
8
-
9
- Configuration is stored in `config/auth_ldap.ini` and uses the INI
10
- style formatting.
11
-
12
- Only the `LOGIN` authentication method is supported assuming that passwords in the
13
- LDAP database are not stored in cleartext (which would allow for CRAM-MD5). Note
14
- that this means passwords will be sent in the clear to the LDAP server unless
15
- an `ldaps://` conection is used.
16
-
17
- Current configuration options in `[core]` are:
18
-
19
- server - the url of the LDAP server (ldap:// or ldaps://)
20
- timeout - time in miliseconds to wait for the server resonse before giving up
21
- rejectUnauthorized - boolean (true or false) as to whether to reject connections
22
- not verified against a CA. Meaning, a "false" allows non-verified.
23
-
24
- Example:
25
-
26
- [core]
27
- server=ldaps://ldap.opoet.com
28
- timeout=5000
29
- rejectUnauthorized=false
30
-
31
- The `[dns]` section (that is plural DN and not domain name system), is a list of DNs to use
32
- to bind. The `%u` in the strings is substituted with the user name used in the SMTP
33
- authentication. Note that the keys have no meaning and the DNs are tried in series until
34
- the first successful bind. The LDAP RFC does not allow for parallel binds on a connection,
35
- so it is suggested that the most commonly used DN be placed earlier in the list.
36
-
37
- Example:
38
-
39
- [dns]
40
- dn1=uid=%u,ou=Users,dc=opoet,dc=com
41
- dn2=uid=%u,ou=people,dc=opoet,dc=com
3
+ Repackaged as [haraka-plugin-auth-ldap](https://github.com/haraka/haraka-plugin-auth-ldap).
4
+ Loading `auth/auth_ldap` in `config/plugins` is auto-redirected to `auth-ldap`.
@@ -1,20 +1,6 @@
1
1
  # max_unrecognized_commands
2
2
 
3
- This plugin places a maximum limit on the number of unrecognized commands
4
- allowed before recognising that the connection is bad.
5
-
6
- If the limit is reached the connecting client is sent an error message and
7
- immediately (and rudely - technically an RFC violation) disconnected.
8
-
9
- **IMPORTANT**:
10
- This plugin should be listed near the bottom of `config/plugins` so that it
11
- runs after any plugins that use the unrecognized_command hook to implement
12
- other SMTP verbs and extensions (such as the auth/\* plugins), otherwise
13
- commands valid for these plugins will be counted as unknown by this plugin.
14
-
15
- ## Configuration
16
-
17
- - max_unrecognized_commands
18
-
19
- Specifies the number of unrecognized commands to allow before disconnecting.
20
- Default: 10.
3
+ The functionality of this plugin was folded into
4
+ [haraka-plugin-limit](https://github.com/haraka/haraka-plugin-limit).
5
+ Loading `max_unrecognized_commands` in `config/plugins` is auto-redirected
6
+ to `limit`.
@@ -37,6 +37,6 @@ across all workers, with the exception of outbound stats.
37
37
  All of the counts shown are since the process started, so if a
38
38
  worker has been re-started then the counts may not add up.
39
39
 
40
- Note: this plugin will only work on node >= 0.8 and should be
41
- added at the top of config/plugins to ensure that it functions
42
- correctly.
40
+ Note: this plugin should be added at the top of `config/plugins` so
41
+ that its `connect_init`, `rcpt`, `data`, and `disconnect` hooks run
42
+ before any plugin that might short-circuit those hooks.
@@ -1,18 +1,16 @@
1
1
  # reseed_rng
2
2
 
3
- The V8 that ships with node 0.4.x uses an unsophisticated method of
4
- seeding its random number generator- it simply uses the current time
5
- in ms. Worse, that version of V8 (at least) doesn't provide a way
6
- to explicitly reseed the RNG.
3
+ Reseeds `Math.random()` in each cluster worker at start-up using
4
+ `crypto.randomBytes(256)`. Without this, workers forked at nearly the
5
+ same time can end up with correlated PRNG state, which can produce
6
+ UUID collisions and other "this should be impossible" bugs.
7
7
 
8
- In situations where multiple processes can spawn in the same
9
- ms, processes can be seeded with the same value, leading to bad
10
- problems like UUID collisions. When using the 'cluster' module, it's
11
- quite easy to observe this behavior.
8
+ The plugin relies on [seedrandom](https://www.npmjs.com/package/seedrandom)
9
+ being loaded so that `Math.seedrandom()` is available.
12
10
 
13
- This plugin uses David Bao's reseed.js (see http://davidbau.com/archives/2010/01/30/random_seeds_coded_hints_and_quintillions.html)
14
- to provide a reseedable Math.random(), and hooks the init_child event
15
- to reseed the RNG with a sligtly better seed at spawned-process startup
16
- time.
11
+ Anyone running with `nodes=...` in `smtp.ini` (i.e. cluster mode) should
12
+ consider enabling this plugin.
17
13
 
18
- All users of the 'cluster' module should consider using this plugin.
14
+ ## Configuration
15
+
16
+ No configuration.
@@ -155,9 +155,9 @@ Specifies minimum allowable TLS protocol version to use. Example:
155
155
 
156
156
  minVersion=TLSv1.1
157
157
 
158
- If unset, the default is node's tls.DEFAULT_MIN_VERSION constant.
159
-
160
- (**Node.js 11.4+ required**, for older instances you can use _secureProtocol_ settings)
158
+ If unset, the default is Node's `tls.DEFAULT_MIN_VERSION` constant
159
+ (currently `'TLSv1.2'`). Valid values: `'TLSv1.3'`, `'TLSv1.2'`,
160
+ `'TLSv1.1'`, `'TLSv1'`.
161
161
 
162
162
  ### honorCipherOrder
163
163
 
@@ -195,10 +195,10 @@ requireAuthorized[]=465
195
195
 
196
196
  ### secureProtocol
197
197
 
198
- Specifies the OpenSSL API function used for handling the TLS session. Choose
199
- one of the methods described at the
200
- [OpenSSL API page](https://www.openssl.org/docs/manmaster/ssl/ssl.html).
201
- The default is `SSLv23_method`.
198
+ Legacy. Specifies the OpenSSL API function used to negotiate TLS see
199
+ the [OpenSSL API page](https://www.openssl.org/docs/manmaster/ssl/ssl.html).
200
+ Prefer `minVersion` for modern setups; `secureProtocol` is only useful
201
+ to lock to a specific historic protocol.
202
202
 
203
203
  ### requestOCSP
204
204
 
@@ -5,11 +5,17 @@ latency is too high.
5
5
 
6
6
  See https://github.com/STRML/node-toobusy for details.
7
7
 
8
- To use this plugin you have to install the 'toobusy-js' module by running
9
- 'npm install toobusy-js' in your Haraka configuration directory.
8
+ To use this plugin you must install the [`toobusy-js`](https://www.npmjs.com/package/toobusy-js)
9
+ module it is not bundled with Haraka. From your Haraka install
10
+ directory:
10
11
 
11
- This plugin should be listed at the top of your config/plugins file so that
12
- it runs before any other plugin that hooks lookup_rdns.
12
+ ```sh
13
+ npm install toobusy-js
14
+ ```
15
+
16
+ This plugin registers on the `connect` hook with priority `-100`, so it
17
+ runs ahead of other `connect`/`lookup_rdns` plugins. Listing it near the
18
+ top of `config/plugins` is still a good idea for clarity.
13
19
 
14
20
  ## Configuration
15
21
 
@@ -1,70 +1,62 @@
1
1
  # Configuring Haraka For Outbound Email
2
2
 
3
- It is trivially easy to configure Haraka as an outbound email server. But
4
- first there are external things you may want to sort out:
3
+ It is straightforward to run Haraka as an outbound (submission) mail server. Before turning on the server itself, get a few external things in order:
5
4
 
6
- - Get your DNS PTR record working - make sure it matches the A record of the
7
- host you are sending from.
8
- - Consider implementing an SPF record. I don't personally do this, but some
9
- people seem to think it helps.
5
+ - **DNS PTR record** make sure it matches the A/AAAA record of the host you are sending from. Receivers that disagree on `HELO`/PTR will treat your mail with suspicion.
6
+ - **SPF, DKIM, and DMARC** — publish records for any domain you send from. Most receivers downgrade or reject mail without them. Haraka signs outbound with [haraka-plugin-dkim](https://github.com/haraka/haraka-plugin-dkim).
7
+ - **Reverse DNS at the IP owner** if your hosting provider controls the PTR, set the value through their console.
10
8
 
11
- There's lots of information elsewhere on the internet about getting these
12
- things working, and they are specific to your network and your DNS hosting.
9
+ How to provision DNS varies by provider; the records are network-specific so no one-size-fits-all command applies.
13
10
 
14
- ## First Some Background
11
+ ## Background
15
12
 
16
- Sending outbound mail through Haraka is called "relaying", and that is the
17
- term the internals use. The process is simple - if a plugin in Haraka tells
18
- the internals that this mail is to be relayed, then it gets queued in the
19
- "queue" directory for delivery. Then it will go through several delivery
20
- attempts until it is either successful or fails hard for some reason. A
21
- hard failure will result in a bounce email being sent to the "MAIL FROM"
22
- address used when connecting to Haraka. If that address also bounces then
23
- it is considered a "double bounce" and Haraka will log an error and drop it
24
- on the floor.
13
+ Haraka treats outbound mail as "relaying". When any plugin sets `connection.relaying = true`, the message is queued for outbound delivery once `DATA` ends. The outbound engine then tries each MX in sequence; on permanent failure a DSN is generated and sent to the `MAIL FROM` address. If the DSN itself bounces, Haraka logs the "double bounce" and drops it.
25
14
 
26
- ## The Setup
15
+ ## Setup
27
16
 
28
- Outbound mail servers should run on port 587 and enforce authentication. This
29
- is slightly different from the "old" model where there would simply be a
30
- check based on the connecting IP address to see if it was valid to relay.
31
- Note however that Haraka doesn't stop you doing it this way - we just don't
32
- provide a plugin to do that by default - you will have to write one. The
33
- reason is purely based on security and personal preference.
17
+ Modern submission uses **implicit TLS on port 465** (RFC 8314); port 587 with `STARTTLS` is also still common. Plain port 25 is for server-to-server traffic and should not be used for submission.
34
18
 
35
- Let's create a new Haraka instance:
19
+ Create a new Haraka instance:
36
20
 
37
- haraka -i haraka-outbound
38
- cd haraka-outbound
21
+ ```sh
22
+ haraka -i haraka-outbound
23
+ cd haraka-outbound
24
+ ```
39
25
 
40
- Now edit config/smtp.ini - change the port to 587.
26
+ In `config/smtp.ini`, set the listener:
41
27
 
42
- Next we setup our plugins - all we need is the tls and auth plugin. AUTH capability is only advertised after TLS/SSL negotiation (except for connections from the local host):
28
+ ```ini
29
+ listen=[::0]:465,[::0]:587
30
+ smtps_port=465
31
+ ```
43
32
 
44
- echo "tls
45
- auth/flat_file" > config/plugins
33
+ Anything in `smtps_port` runs implicit TLS; the other ports advertise `STARTTLS`.
46
34
 
47
- Now edit the flat file password file, and put in an appropriate username
48
- and password:
35
+ Enable just the TLS and auth plugins. AUTH is only advertised after TLS is established (except for connections from localhost):
49
36
 
50
- vi config/auth_flat_file.ini
37
+ ```sh
38
+ cat > config/plugins <<'EOF'
39
+ tls
40
+ auth/flat_file
41
+ EOF
42
+ ```
51
43
 
52
- See the documentation in docs/plugins/auth/flat_file.md for information about
53
- what can go in that file.
44
+ Add a user to `config/auth_flat_file.ini`. See [`docs/plugins/auth/flat_file.md`](../plugins/auth/flat_file.md) for the format.
54
45
 
55
- Now you can start Haraka. That's all the configuration you need.
46
+ Start Haraka:
56
47
 
57
- haraka -c .
48
+ ```sh
49
+ haraka -c .
50
+ ```
58
51
 
59
- Now in another window you can run swaks to test this - be sure to substitute
60
- an email address you can monitor in place of youremail@yourdomain.com, and the
61
- username and password you added for the --auth-user and --auth-password params:
52
+ In another shell, test with [swaks](https://www.jetmore.org/john/code/swaks/) substitute your real test address and the credentials you configured:
62
53
 
63
- swaks --to youremail@yourdomain.com --from test@example.com --server localhost \
64
- --port 587 --auth-user testuser --auth-password testpassword
54
+ ```sh
55
+ swaks --to youremail@yourdomain.com --from test@example.com \
56
+ --server localhost --port 587 --tls \
57
+ --auth-user testuser --auth-password testpassword
58
+ ```
65
59
 
66
- Watch the output of swaks and ensure no errors have occurred. Then watch
67
- the recipient email address (easiest to make this your webmail account) and
68
- see that the email arrived.
60
+ For port 465 (implicit TLS), use `--tls-on-connect` instead of `--tls`.
69
61
 
70
- You are done!
62
+ Watch the swaks output for errors and confirm the message arrives. That's all the basic configuration you need; once you're satisfied, turn on DKIM signing for the domains you send from.
package/endpoint.js CHANGED
@@ -2,12 +2,42 @@
2
2
  // Socket address parser/formatter and server binding helper
3
3
 
4
4
  const fs = require('node:fs/promises')
5
- const sockaddr = require('sockaddr')
5
+ const net = require('node:net')
6
+
7
+ function parseSockaddr(addr, defaultPort = 0) {
8
+ let match
9
+ if (/^[0-9]+$/.test(addr)) return { host: '::', port: parseInt(addr, 10) }
10
+
11
+ const lastColon = addr.lastIndexOf(':')
12
+ if (lastColon !== -1) {
13
+ const host = addr.slice(0, lastColon)
14
+ const port = addr.slice(lastColon + 1)
15
+
16
+ if (host.includes(':') && /^\d+$/.test(port) && net.isIP(host) === 6) {
17
+ return { host: host.toLowerCase(), port: parseInt(port, 10) }
18
+ }
19
+ }
20
+ if (net.isIP(addr) === 6) return { host: addr.toLowerCase(), port: defaultPort }
21
+
22
+ if ((match = /^(\d{1,3}(?:\.\d{1,3}){3})(?::(\d+))?$/.exec(addr)))
23
+ return { host: match[1], port: match[2] !== undefined ? parseInt(match[2], 10) : defaultPort }
24
+ if ((match = /^\[([0-9a-fA-F:]+)\](?::(\d+))?$/.exec(addr)))
25
+ return { host: match[1].toLowerCase(), port: match[2] !== undefined ? parseInt(match[2], 10) : defaultPort }
26
+ if (
27
+ (match =
28
+ /^([a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)*)(?::(\d+))?$/.exec(
29
+ addr,
30
+ ))
31
+ )
32
+ return { host: match[1].toLowerCase(), port: match[2] !== undefined ? parseInt(match[2], 10) : defaultPort }
33
+ if (addr.includes('/')) return { path: addr }
34
+ throw new Error(`Invalid socket address ${addr}`)
35
+ }
6
36
 
7
37
  module.exports = function endpoint(addr, defaultPort) {
8
38
  try {
9
39
  if ('string' === typeof addr || 'number' === typeof addr) {
10
- addr = sockaddr(addr, { defaultPort })
40
+ addr = parseSockaddr(addr, defaultPort)
11
41
  const match = /^(.*):([0-7]{3})$/.exec(addr.path || '')
12
42
  if (match) {
13
43
  addr.path = match[1]
package/outbound/hmail.js CHANGED
@@ -87,9 +87,10 @@ class HMailItem extends events.EventEmitter {
87
87
  this.file_size = stats.size
88
88
  this.read_todo()
89
89
  } catch (err) {
90
- // we are fucked... guess I need somewhere for this to go
90
+ // The file is unreadable (deleted, permissions, I/O error) and this.todo
91
+ // is still null, so there is no sender to bounce to. Release the queue slot.
91
92
  this.logerror(`Error obtaining file size: ${err}`)
92
- this.temp_fail('Error obtaining file size')
93
+ this.next_cb()
93
94
  }
94
95
  }
95
96
 
package/outbound/index.js CHANGED
@@ -248,6 +248,9 @@ exports.send_trans_email = function (transaction, next) {
248
248
 
249
249
  let todo_index = 1
250
250
 
251
+ // See haraka/Haraka#3551
252
+ await new Promise((resolve) => setImmediate(resolve))
253
+
251
254
  try {
252
255
  for (const deliv of deliveries) {
253
256
  const todo = new TODOItem(deliv.domain, deliv.rcpts, transaction)
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "server",
10
10
  "email"
11
11
  ],
12
- "version": "3.1.4",
12
+ "version": "3.1.6",
13
13
  "homepage": "http://haraka.github.io",
14
14
  "repository": {
15
15
  "type": "git",
@@ -20,71 +20,58 @@
20
20
  "node": ">=20"
21
21
  },
22
22
  "dependencies": {
23
- "address-rfc2821": "^2.1.5",
23
+ "address-rfc2821": "^2.2.0",
24
24
  "address-rfc2822": "^2.2.3",
25
- "daemon": "~1.1.0",
26
25
  "haraka-config": "^1.5.0",
27
26
  "haraka-constants": "^1.0.7",
28
27
  "haraka-dsn": "^1.1.0",
29
- "haraka-email-message": "^1.3.1",
30
- "haraka-message-stream": "^2.0.0",
31
- "haraka-net-utils": "^1.7.2",
28
+ "haraka-email-message": "^1.3.3",
29
+ "haraka-net-utils": "^1.8.2",
32
30
  "haraka-notes": "^1.1.1",
33
31
  "haraka-plugin-redis": "^2.0.11",
34
32
  "haraka-results": "^2.3.0",
35
- "haraka-tld": "^1.3.1",
33
+ "haraka-tld": "^1.3.4",
36
34
  "haraka-utils": "^1.1.4",
37
- "ipaddr.js": "~2.3.0",
38
- "node-gyp": "^12.2.0",
35
+ "ipaddr.js": "~2.4.0",
36
+ "node-gyp": "^12.3.0",
39
37
  "nopt": "^9.0.0",
40
- "npid": "~0.4.0",
41
- "redis": "~5.11.0",
42
- "semver": "^7.7.4",
43
- "sockaddr": "^1.0.1",
44
- "sprintf-js": "~1.1.3"
38
+ "redis": "~5.12.1",
39
+ "semver": "^7.8.0"
45
40
  },
46
41
  "optionalDependencies": {
47
- "haraka-plugin-access": "^1.1.10",
42
+ "@haraka/ocsp": "^1.2.0",
43
+ "haraka-plugin-access": "^1.2.0",
48
44
  "haraka-plugin-aliases": "^1.0.3",
49
45
  "haraka-plugin-asn": "^2.0.6",
50
46
  "haraka-plugin-attachment": "^1.2.0",
51
- "haraka-plugin-avg": "^1.1.0",
52
47
  "haraka-plugin-bounce": "^2.1.2",
53
48
  "haraka-plugin-clamd": "^1.0.2",
54
49
  "haraka-plugin-dcc": "^1.0.3",
55
- "haraka-plugin-dkim": "^1.0.11",
50
+ "haraka-plugin-dkim": "^1.1.2",
56
51
  "haraka-plugin-dns-list": "^1.2.4",
57
52
  "haraka-plugin-early_talker": "^1.0.2",
58
- "haraka-plugin-elasticsearch": "^8.1.6",
59
- "haraka-plugin-esets": "^1.0.1",
60
53
  "haraka-plugin-fcrdns": "^1.1.2",
61
54
  "haraka-plugin-geoip": "^1.1.2",
62
- "haraka-plugin-greylist": "^1.1.0",
55
+ "haraka-plugin-greylist": "^1.1.1",
63
56
  "haraka-plugin-headers": "^1.1.0",
64
57
  "haraka-plugin-helo.checks": "^1.1.0",
65
- "haraka-plugin-karma": "^2.1.8",
58
+ "haraka-plugin-karma": "^2.4.1",
66
59
  "haraka-plugin-known-senders": "^1.1.3",
67
60
  "haraka-plugin-limit": "^1.2.7",
68
- "haraka-plugin-mail_from.is_resolvable": "^1.1.0",
61
+ "haraka-plugin-mail_from.is_resolvable": "^1.2.0",
69
62
  "haraka-plugin-messagesniffer": "^1.0.1",
70
- "haraka-plugin-p0f": "^1.0.11",
71
- "haraka-plugin-qmail-deliverable": "^1.3.2",
72
- "haraka-plugin-recipient-routes": "^1.3.1",
63
+ "haraka-plugin-qmail-deliverable": "^1.3.5",
73
64
  "haraka-plugin-relay": "^1.0.1",
74
- "haraka-plugin-rspamd": "^1.4.2",
75
- "haraka-plugin-spamassassin": "^1.0.3",
65
+ "haraka-plugin-rspamd": "^1.5.0",
66
+ "haraka-plugin-spamassassin": "^1.0.4",
76
67
  "haraka-plugin-spf": "^1.2.11",
77
68
  "haraka-plugin-syslog": "^1.0.7",
78
- "haraka-plugin-uribl": "^1.0.10",
79
- "haraka-plugin-watch": "^2.0.9",
80
- "@techteamer/ocsp": "^1.0.1"
69
+ "haraka-plugin-uribl": "^1.0.10"
81
70
  },
82
71
  "devDependencies": {
83
72
  "@haraka/eslint-config": "^2.0.4",
84
- "haraka-test-fixtures": "^1.4.1",
85
- "mocha": "^11.7.5",
86
- "mock-require": "^3.0.3",
87
- "nodemailer": "^8.0.4"
73
+ "haraka-test-fixtures": "^1.4.3",
74
+ "mock-require": "^3.0.3"
88
75
  },
89
76
  "bugs": {
90
77
  "mail": "haraka.mail@gmail.com",
@@ -247,7 +247,7 @@ exports.queue_forward = function (next, connection) {
247
247
  )
248
248
 
249
249
  function get_rs() {
250
- return connection?.transaction?.results ? connection.transaction.results : connection.results
250
+ return txn?.results ?? connection.results
251
251
  }
252
252
 
253
253
  function dead_sender() {
@@ -320,10 +320,10 @@ exports.get_mx_next_hop = (next_hop) => {
320
320
  exchange: dest.hostname,
321
321
  }
322
322
  if (dest.protocol === 'lmtp:') mx.using_lmtp = true
323
- if (dest.auth) {
323
+ if (dest.username) {
324
324
  mx.auth_type = 'plain'
325
- mx.auth_user = dest.auth.split(':')[0]
326
- mx.auth_pass = dest.auth.split(':')[1]
325
+ mx.auth_user = dest.username
326
+ mx.auth_pass = dest.password
327
327
  }
328
328
  return mx
329
329
  }
package/run_tests CHANGED
@@ -1,23 +1,11 @@
1
1
  #!/bin/sh
2
2
 
3
3
  # Files using node:test
4
- NODE_TEST_FILES="test/transaction.js test/connection.js test/outbound/*.js"
5
-
6
- # Mocha scans test/ but skips transaction.js, connection.js, and outbound/
7
- MOCHA_IGNORE="--ignore=test/transaction.js --ignore=test/connection.js"
8
- MOCHA_TESTS="test test/plugins/auth test/plugins/queue test/plugins"
4
+ NODE_TEST_FILES="test/*.js test/outbound/*.js test/plugins/*.js test/plugins/auth/*.js test/plugins/queue/*.js"
9
5
 
10
6
  if [ -n "$1" ]; then
11
- case "$1" in
12
- test/transaction.js | test/connection.js | test/outbound/*)
13
- node --test "$1"
14
- ;;
15
- *)
16
- npx mocha --exit --timeout=4000 "$1"
17
- ;;
18
- esac
7
+ node --test "$1"
19
8
  else
20
9
  # default, run 'em all
21
- node --test --test-concurrency=1 $NODE_TEST_FILES && \
22
- npx mocha --exit --timeout=4000 $MOCHA_IGNORE $MOCHA_TESTS
10
+ node --test --test-concurrency=1 $NODE_TEST_FILES
23
11
  fi