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.
- package/.prettierignore +1 -1
- package/{Changes.md → CHANGELOG.md} +34 -0
- package/CONTRIBUTORS.md +26 -26
- package/README.md +68 -93
- package/SECURITY.md +178 -0
- package/bin/haraka +7 -14
- package/config/plugins +0 -3
- package/docs/Connection.md +126 -39
- package/docs/CoreConfig.md +92 -74
- package/docs/HAProxy.md +41 -25
- package/docs/Logging.md +68 -38
- package/docs/Outbound.md +124 -179
- package/docs/Plugins.md +38 -59
- package/docs/Transaction.md +78 -83
- package/docs/Tutorial.md +122 -209
- package/docs/plugins/aliases.md +1 -141
- package/docs/plugins/auth/auth_ldap.md +2 -39
- package/docs/plugins/max_unrecognized_commands.md +4 -18
- package/docs/plugins/process_title.md +3 -3
- package/docs/plugins/reseed_rng.md +11 -13
- package/docs/plugins/tls.md +7 -7
- package/docs/plugins/toobusy.md +10 -4
- package/docs/tutorials/SettingUpOutbound.md +40 -48
- package/endpoint.js +32 -2
- package/outbound/hmail.js +3 -2
- package/outbound/index.js +3 -0
- package/package.json +21 -34
- package/plugins/queue/smtp_forward.js +4 -4
- package/run_tests +3 -15
- package/server.js +17 -7
- package/smtp_client.js +8 -6
- package/test/connection.js +234 -0
- package/test/endpoint.js +32 -4
- package/test/host_pool.js +57 -31
- package/test/logger.js +75 -135
- package/test/outbound/bounce_net_errors.js +87 -131
- package/test/outbound/bounce_rfc3464.js +177 -254
- package/test/outbound/hmail.js +19 -0
- package/test/outbound/index.js +189 -0
- package/test/outbound/queue.js +92 -0
- package/test/plugins/auth/auth_base.js +39 -44
- package/test/plugins/auth/auth_vpopmaild.js +8 -9
- package/test/plugins/queue/smtp_forward.js +953 -183
- package/test/plugins/rcpt_to.host_list_base.js +58 -93
- package/test/plugins/rcpt_to.in_host_list.js +126 -175
- package/test/plugins/record_envelope_addresses.js +8 -8
- package/test/plugins/status.js +10 -10
- package/test/plugins/tls.js +9 -19
- package/test/plugins/xclient.js +75 -110
- package/test/plugins.js +10 -13
- package/test/rfc1869.js +50 -70
- package/test/server.js +438 -421
- package/test/smtp_client.js +1192 -218
- package/test/tls_socket.js +242 -0
- package/tls_socket.js +18 -22
package/docs/plugins/aliases.md
CHANGED
|
@@ -1,143 +1,3 @@
|
|
|
1
1
|
# aliases
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
4
|
-
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
9
|
-
|
|
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
|
-
|
|
14
|
-
|
|
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
|
-
|
|
14
|
+
## Configuration
|
|
15
|
+
|
|
16
|
+
No configuration.
|
package/docs/plugins/tls.md
CHANGED
|
@@ -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
|
|
159
|
-
|
|
160
|
-
|
|
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
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
|
package/docs/plugins/toobusy.md
CHANGED
|
@@ -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
|
|
9
|
-
|
|
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
|
-
|
|
12
|
-
|
|
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
|
|
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
|
-
-
|
|
7
|
-
|
|
8
|
-
-
|
|
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
|
-
|
|
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
|
-
##
|
|
11
|
+
## Background
|
|
15
12
|
|
|
16
|
-
|
|
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
|
-
##
|
|
15
|
+
## Setup
|
|
27
16
|
|
|
28
|
-
|
|
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
|
-
|
|
19
|
+
Create a new Haraka instance:
|
|
36
20
|
|
|
37
|
-
|
|
38
|
-
|
|
21
|
+
```sh
|
|
22
|
+
haraka -i haraka-outbound
|
|
23
|
+
cd haraka-outbound
|
|
24
|
+
```
|
|
39
25
|
|
|
40
|
-
|
|
26
|
+
In `config/smtp.ini`, set the listener:
|
|
41
27
|
|
|
42
|
-
|
|
28
|
+
```ini
|
|
29
|
+
listen=[::0]:465,[::0]:587
|
|
30
|
+
smtps_port=465
|
|
31
|
+
```
|
|
43
32
|
|
|
44
|
-
|
|
45
|
-
auth/flat_file" > config/plugins
|
|
33
|
+
Anything in `smtps_port` runs implicit TLS; the other ports advertise `STARTTLS`.
|
|
46
34
|
|
|
47
|
-
|
|
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
|
-
|
|
37
|
+
```sh
|
|
38
|
+
cat > config/plugins <<'EOF'
|
|
39
|
+
tls
|
|
40
|
+
auth/flat_file
|
|
41
|
+
EOF
|
|
42
|
+
```
|
|
51
43
|
|
|
52
|
-
|
|
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
|
-
|
|
46
|
+
Start Haraka:
|
|
56
47
|
|
|
57
|
-
|
|
48
|
+
```sh
|
|
49
|
+
haraka -c .
|
|
50
|
+
```
|
|
58
51
|
|
|
59
|
-
|
|
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
|
-
|
|
64
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
-
//
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
30
|
-
"haraka-
|
|
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.
|
|
33
|
+
"haraka-tld": "^1.3.4",
|
|
36
34
|
"haraka-utils": "^1.1.4",
|
|
37
|
-
"ipaddr.js": "~2.
|
|
38
|
-
"node-gyp": "^12.
|
|
35
|
+
"ipaddr.js": "~2.4.0",
|
|
36
|
+
"node-gyp": "^12.3.0",
|
|
39
37
|
"nopt": "^9.0.0",
|
|
40
|
-
"
|
|
41
|
-
"
|
|
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
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
61
|
+
"haraka-plugin-mail_from.is_resolvable": "^1.2.0",
|
|
69
62
|
"haraka-plugin-messagesniffer": "^1.0.1",
|
|
70
|
-
"haraka-plugin-
|
|
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.
|
|
75
|
-
"haraka-plugin-spamassassin": "^1.0.
|
|
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.
|
|
85
|
-
"
|
|
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
|
|
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.
|
|
323
|
+
if (dest.username) {
|
|
324
324
|
mx.auth_type = 'plain'
|
|
325
|
-
mx.auth_user = dest.
|
|
326
|
-
mx.auth_pass = dest.
|
|
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/
|
|
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
|
-
|
|
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
|