Haraka 3.1.5 → 3.1.7
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} +54 -3
- package/CONTRIBUTORS.md +26 -26
- package/Plugins.md +99 -99
- package/README.md +68 -93
- package/SECURITY.md +178 -0
- package/bin/haraka +7 -14
- package/config/plugins +0 -3
- package/config/smtp_forward.ini +10 -0
- package/config/smtp_proxy.ini +10 -0
- package/connection.js +25 -8
- 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/queue/smtp_forward.md +19 -3
- package/docs/plugins/queue/smtp_proxy.md +10 -2
- 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/haraka.js +1 -1
- package/outbound/hmail.js +42 -41
- package/outbound/index.js +7 -4
- package/outbound/tls.js +2 -43
- package/package.json +51 -61
- package/plugins/auth/auth_base.js +9 -3
- package/plugins/auth/auth_proxy.js +14 -11
- package/plugins/block_me.js +4 -2
- package/plugins/prevent_credential_leaks.js +3 -1
- package/plugins/process_title.js +6 -6
- package/plugins/queue/qmail-queue.js +15 -19
- package/plugins/queue/smtp_forward.js +12 -4
- package/plugins/queue/smtp_proxy.js +14 -3
- package/plugins/tls.js +13 -5
- package/plugins/xclient.js +3 -1
- package/server.js +22 -10
- package/smtp_client.js +20 -11
- package/test/config/block_me.recipient +1 -0
- package/test/config/block_me.senders +1 -0
- package/test/connection.js +258 -0
- package/test/endpoint.js +27 -0
- package/test/outbound/bounce_net_errors.js +3 -2
- 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_bridge.js +80 -0
- package/test/plugins/auth/flat_file.js +128 -0
- package/test/plugins/block_me.js +157 -0
- package/test/plugins/data.signatures.js +114 -0
- package/test/plugins/delay_deny.js +263 -0
- package/test/plugins/prevent_credential_leaks.js +178 -0
- package/test/plugins/process_title.js +135 -0
- package/test/plugins/queue/deliver.js +99 -0
- package/test/plugins/queue/discard.js +79 -0
- package/test/plugins/queue/lmtp.js +138 -0
- package/test/plugins/queue/qmail-queue.js +99 -0
- package/test/plugins/queue/quarantine.js +81 -0
- package/test/plugins/queue/smtp_bridge.js +154 -0
- package/test/plugins/queue/smtp_forward.js +42 -6
- package/test/plugins/queue/smtp_proxy.js +139 -0
- package/test/plugins/reseed_rng.js +34 -0
- package/test/plugins/tarpit.js +91 -0
- package/test/plugins/tls.js +25 -0
- package/test/plugins/toobusy.js +21 -0
- package/test/plugins/xclient.js +14 -0
- package/test/server.js +231 -0
- package/test/smtp_client.js +45 -12
- package/test/tls_socket.js +220 -0
- package/tls_socket.js +52 -2
package/docs/Logging.md
CHANGED
|
@@ -1,83 +1,109 @@
|
|
|
1
1
|
# Haraka Logging
|
|
2
2
|
|
|
3
|
-
Haraka has built-in
|
|
3
|
+
Haraka has a built-in logger (described below) and a plugin hook (`log`) that lets log plugins ship messages elsewhere — for example to syslog via [haraka-plugin-syslog](https://github.com/haraka/haraka-plugin-syslog).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Configuration
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
### log.ini
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
```ini
|
|
10
|
+
[main]
|
|
11
|
+
; data, protocol, debug, info, notice, warn, error, crit, alert, emerg
|
|
12
|
+
level=info
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
; prepend ISO-8601 timestamps to log entries (built-in logger only)
|
|
15
|
+
timestamps=false
|
|
12
16
|
|
|
13
|
-
|
|
17
|
+
; default, logfmt, json
|
|
18
|
+
format=default
|
|
19
|
+
```
|
|
14
20
|
|
|
15
|
-
|
|
21
|
+
### loglevel
|
|
16
22
|
|
|
17
|
-
|
|
23
|
+
A single-line file for quick CLI tweaks:
|
|
18
24
|
|
|
19
|
-
|
|
25
|
+
```sh
|
|
26
|
+
echo DEBUG > config/loglevel
|
|
27
|
+
```
|
|
20
28
|
|
|
21
|
-
|
|
29
|
+
When both `log.ini` and the `loglevel` file are present, whichever was edited most recently wins at runtime — `loglevel` is convenient for an interactive bump without touching `log.ini`.
|
|
22
30
|
|
|
23
|
-
|
|
31
|
+
### log_timestamps
|
|
24
32
|
|
|
25
|
-
|
|
26
|
-
to the log method.
|
|
33
|
+
A single-value file that toggles timestamp prepending. Equivalent to `main.timestamps` in `log.ini`. If either source enables timestamps, they are enabled.
|
|
27
34
|
|
|
28
|
-
|
|
35
|
+
## Log Levels
|
|
29
36
|
|
|
30
|
-
|
|
37
|
+
In ascending severity (and decreasing verbosity):
|
|
31
38
|
|
|
32
|
-
|
|
39
|
+
| Level | Numeric | Use |
|
|
40
|
+
| -------- | ------- | --- |
|
|
41
|
+
| DATA | 9 | message body bytes — extremely verbose |
|
|
42
|
+
| PROTOCOL | 8 | SMTP wire protocol |
|
|
43
|
+
| DEBUG | 7 | developer diagnostics |
|
|
44
|
+
| INFO | 6 | general informational |
|
|
45
|
+
| NOTICE | 5 | normal but significant events (connect/disconnect, summary lines) |
|
|
46
|
+
| WARN | 4 | recoverable problems |
|
|
47
|
+
| ERROR | 3 | non-fatal errors |
|
|
48
|
+
| CRIT | 2 | critical errors |
|
|
49
|
+
| ALERT | 1 | needs immediate attention |
|
|
50
|
+
| EMERG | 0 | unusable |
|
|
33
51
|
|
|
34
|
-
|
|
52
|
+
A message is emitted when its level ≤ the configured level.
|
|
35
53
|
|
|
36
|
-
|
|
54
|
+
## Logging API
|
|
37
55
|
|
|
38
|
-
|
|
56
|
+
Every log call ultimately produces:
|
|
39
57
|
|
|
40
|
-
|
|
58
|
+
[level] [uuid] [origin] message
|
|
41
59
|
|
|
42
|
-
|
|
60
|
+
`origin` is `core` or the plugin name; `uuid` is the connection UUID (with `.N` appended for the Nth transaction).
|
|
43
61
|
|
|
44
|
-
|
|
62
|
+
The simplest call is on the connection or plugin object — origin and uuid are filled in automatically:
|
|
45
63
|
|
|
46
|
-
|
|
64
|
+
```js
|
|
65
|
+
connection.logdebug('turtles all the way down')
|
|
66
|
+
plugin.loginfo('checking sender', connection)
|
|
67
|
+
```
|
|
47
68
|
|
|
48
|
-
|
|
69
|
+
Each of the level names has a matching method:
|
|
70
|
+
`logdata`, `logprotocol`, `logdebug`, `loginfo`, `lognotice`, `logwarn`,
|
|
71
|
+
`logerror`, `logcrit`, `logalert`, `logemerg`.
|
|
49
72
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
73
|
+
Calling the logger directly works too — pass the plugin and/or connection anywhere in the arguments and the logger sniffs them:
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
logger.logdebug('i like turtles', plugin, connection)
|
|
77
|
+
// → [DEBUG] [7F1C820F-…] [dnsbl] i like turtles
|
|
54
78
|
```
|
|
55
79
|
|
|
56
|
-
|
|
80
|
+
Plain objects mixed into the arguments are merged into the log record (in `logfmt` / `json` formats) or stringified (`key=value` pairs in the default format).
|
|
81
|
+
|
|
82
|
+
## Log Formats
|
|
83
|
+
|
|
84
|
+
Set `main.format` in `log.ini` to one of `default`, `logfmt`, or `json`.
|
|
85
|
+
|
|
86
|
+
`logfmt`:
|
|
57
87
|
|
|
58
|
-
level=PROTOCOL uuid=9FF7F70E
|
|
88
|
+
level=PROTOCOL uuid=9FF7F70E-…1 source=core message="S: 354 go ahead, make my day"
|
|
59
89
|
|
|
60
|
-
|
|
90
|
+
`json`:
|
|
61
91
|
|
|
62
92
|
```json
|
|
63
93
|
{
|
|
64
94
|
"level": "PROTOCOL",
|
|
65
|
-
"uuid": "9FF7F70E
|
|
95
|
+
"uuid": "9FF7F70E-…1",
|
|
66
96
|
"source": "core",
|
|
67
97
|
"message": "S: 354 go ahead, make my day"
|
|
68
98
|
}
|
|
69
99
|
```
|
|
70
100
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
level=NOTICE uuid=9FF7F70E-5D57-435A-AAD9-EA069B6159D9.1 source=core message=disconnect ip=127.0.0.1 rdns=Unknown helo=3h2dnz8a0if relay=N early=N esmtp=N tls=N pipe=N errors=0 txns=1 rcpts=1/0/0 msgs=1/0/0 bytes=222 lr="" time=0.052
|
|
74
|
-
|
|
75
|
-
And using JSON:
|
|
101
|
+
A typical structured disconnect line looks like:
|
|
76
102
|
|
|
77
103
|
```json
|
|
78
104
|
{
|
|
79
105
|
"level": "NOTICE",
|
|
80
|
-
"uuid": "9FF7F70E
|
|
106
|
+
"uuid": "9FF7F70E-…1",
|
|
81
107
|
"source": "core",
|
|
82
108
|
"message": "disconnect",
|
|
83
109
|
"ip": "127.0.0.1",
|
|
@@ -97,3 +123,7 @@ And using JSON:
|
|
|
97
123
|
"time": 0.052
|
|
98
124
|
}
|
|
99
125
|
```
|
|
126
|
+
|
|
127
|
+
## The `log` hook
|
|
128
|
+
|
|
129
|
+
Each log message becomes a `log` hook invocation. The built-in handler writes to stdout (with ANSI colour when stdout is a TTY); log plugins can return `OK` or `STOP` to suppress the built-in output and ship the message elsewhere. Messages emitted before plugins finish loading are buffered and replayed once the plugin chain is ready.
|
package/docs/Outbound.md
CHANGED
|
@@ -1,265 +1,210 @@
|
|
|
1
1
|
# Outbound Mail with Haraka
|
|
2
2
|
|
|
3
|
-
A default
|
|
3
|
+
A default Haraka installation queues outbound mail to disk and delivers it to the appropriate MX for each recipient domain. Temporary failures are retried automatically using the configured backoff schedule.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
A mail is treated as outbound when a plugin sets `connection.relaying` to `true`. The simplest way is SMTP AUTH using `auth/flat_file` or one of the [auth plugins](plugins/auth/); the `relay` plugin offers allow-list-based variants, and a custom plugin can apply any policy.
|
|
6
6
|
|
|
7
|
-
For
|
|
7
|
+
For live stats on the outbound queue see the [`process_title`](plugins/process_title.md) plugin.
|
|
8
8
|
|
|
9
|
-
To flush the
|
|
9
|
+
To flush the temp-fail queue (e.g. after fixing network or DNS), send `SIGHUP` to the Haraka master process.
|
|
10
10
|
|
|
11
|
-
## Outbound Configuration
|
|
11
|
+
## Outbound Configuration
|
|
12
12
|
|
|
13
13
|
### outbound.ini
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
Within `tls.ini` you can specify global options for the values `ciphers`, `minVersion`, `requestCert` and `rejectUnauthorized`, alternatively you can provide separate values by putting them under a key: `[outbound]`, such as:
|
|
15
|
+
| Key | Default | Description |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| `disabled` | `false` | Pause outbound delivery while still queuing inbound mail. Reloadable at runtime. |
|
|
18
|
+
| `concurrency_max` | `10000` | Maximum concurrent outbound deliveries **per worker**. Effective total is `concurrency_max × nodes`. |
|
|
19
|
+
| `enable_tls` | `true` | Use opportunistic STARTTLS on outbound. |
|
|
20
|
+
| `maxTempFailures` | `13` | Maximum temp-fail retries before the message bounces. Ignored if `temp_fail_intervals` is set. |
|
|
21
|
+
| `temp_fail_intervals` | derived | Comma-separated `<n><unit>[*<count>]` pattern. `1m, 5m*2, 1h*3` → `[60,300,300,3600,3600,3600]` seconds. `none` bounces on first temp-fail. |
|
|
22
|
+
| `always_split` | `false` | Create one queue file per recipient (instead of one per destination domain). Hurts throughput but simplifies bounce handling. |
|
|
23
|
+
| `received_header` | `Haraka outbound` | Text used in the outbound `Received:` header. Set to the literal `disabled` to omit it. |
|
|
24
|
+
| `connect_timeout` | `30` | Seconds to wait for TCP connect to the remote MX. |
|
|
25
|
+
| `local_mx_ok` | `false` | Allow outbound delivery to local/private IPs (otherwise blocked to prevent loops). |
|
|
26
|
+
| `inet_prefer` | `default` | `default` (prefer IPv6 at equal MX priority), `v4`, or `v6`. Delivery still follows MX priority. |
|
|
27
|
+
|
|
28
|
+
TLS configuration is shared with the `tls` plugin (`tls_key.pem`, `tls_cert.pem`, and `tls.ini`). Outbound-specific overrides go under `[outbound]` in `tls.ini`:
|
|
30
29
|
|
|
31
30
|
```ini
|
|
32
31
|
[outbound]
|
|
33
32
|
ciphers=!DES
|
|
33
|
+
minVersion=TLSv1.2
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
- `always_split`
|
|
37
|
-
|
|
38
|
-
Default: false. By default, Haraka groups message recipients by domain so that messages with multiple recipients at the same domain get sent in a single SMTP session. When `always_split` is enabled, each recipient gets a queue entry and delivery in its own SMTP session. This carries a performance penalty but enables more flexibility in mail delivery and bounce handling.
|
|
39
|
-
|
|
40
|
-
- `received_header`
|
|
41
|
-
|
|
42
|
-
Default: "Haraka outbound". If this text is any string except _disabled_, the string is attached as a `Received` header to all outbound mail just before it is queued.
|
|
43
|
-
|
|
44
|
-
- `connect_timeout`
|
|
45
|
-
|
|
46
|
-
Timeout for connecting to remote servers. Default: 30s
|
|
47
|
-
|
|
48
|
-
- `local_mx_ok`
|
|
49
|
-
|
|
50
|
-
Default: false. By default, outbound to a local IP is disabled, to avoid creating mail loops. Set this to true if you want to allow outbound to local IPs. This could be useful if you want to deliver mail to private IPs or localhost on another port.
|
|
51
|
-
|
|
52
|
-
- `temp_fail_intervals`
|
|
53
|
-
|
|
54
|
-
Set this to specify the delay intervals to use between trying to re-send an email that has a temporary failure condition. The setting is a comma separated list of time spans and multipliers. The time span is a number followed by `s`, `m`, `h`, or `d` to represent seconds, minutes, hours, and days, respectively. The multiplier is an asterisk followed by an integer representing the number of times to repeat the interval. For example, the entry `1m, 5m*2, 1h*3` results in an array of delay times of `[60,300,300,3600,3600,3600]` in seconds. The email will be bounced when the array runs out of intervals (the 7th failure in this case). Set this to `none` to bounce the email on the first temporary failure.
|
|
55
|
-
|
|
56
|
-
* `inet_prefer`
|
|
57
|
-
|
|
58
|
-
Default: default. Selects the preferred address family (IP version) to deliver messages.
|
|
59
|
-
|
|
60
|
-
| Value | Description |
|
|
61
|
-
|------------------------|-------------|
|
|
62
|
-
| `default` | Prefer IPv6 when IPv4 and IPv6 IPs exist at the same MX priority |
|
|
63
|
-
| `v4` | Try IPv4 addresses first, then IPv6 |
|
|
64
|
-
| `v6` | Try IPv6 addresses first, then IPv4 |
|
|
65
|
-
|
|
66
|
-
Note: Delivery attempts follow MX priority order. Socket-based deliveries ignore this setting.
|
|
67
|
-
|
|
68
36
|
### outbound.bounce_message
|
|
69
37
|
|
|
70
|
-
See "Bounce Messages" below
|
|
38
|
+
Template for the bounce message body. See "Bounce Messages" below.
|
|
71
39
|
|
|
72
40
|
## The HMail Object
|
|
73
41
|
|
|
74
|
-
|
|
42
|
+
Most outbound hooks pass an `hmail` (HMailItem). You rarely need its methods, but these properties are useful:
|
|
75
43
|
|
|
76
|
-
|
|
44
|
+
| Property | Description |
|
|
45
|
+
| --- | --- |
|
|
46
|
+
| `path` | Full filesystem path to the queue file. |
|
|
47
|
+
| `filename` | Queue file's base name. |
|
|
48
|
+
| `num_failures` | Number of temp-fail attempts so far. |
|
|
49
|
+
| `notes` | Plain object for plugin state, scoped to this queue item. |
|
|
50
|
+
| `todo` | The `TODOItem` describing what to deliver (see below). |
|
|
77
51
|
|
|
78
|
-
The
|
|
52
|
+
## The TODO Object
|
|
79
53
|
|
|
80
|
-
|
|
81
|
-
- filename - the filename within the queue dir
|
|
82
|
-
- num_failures - the number of times this mail has been temp failed
|
|
83
|
-
- notes - notes you can store on a hmail object (similar to `transaction.notes`) to allow you to pass information between outbound hooks
|
|
84
|
-
- todo - see below
|
|
54
|
+
`hmail.todo` describes the delivery:
|
|
85
55
|
|
|
86
|
-
|
|
56
|
+
| Property | Description |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| `mail_from` | `Address`<sup>[1](#fn1)</sup> — the envelope sender. |
|
|
59
|
+
| `rcpt_to` | `Address`<sup>[1](#fn1)</sup> array — envelope recipients. |
|
|
60
|
+
| `domain` | Recipient domain (a single domain unless `always_split` is set). |
|
|
61
|
+
| `notes` | The original `transaction.notes`. Keys you may set: |
|
|
62
|
+
| `notes.outbound_ip` | IP to bind the outbound socket to. **Set via the `get_mx` hook**, not directly. |
|
|
63
|
+
| `notes.outbound_helo` | EHLO domain. **Set via the `get_mx` hook**, not directly. |
|
|
64
|
+
| `queue_time` | When the mail was queued (epoch ms). |
|
|
65
|
+
| `uuid` | Inherited from the source `transaction.uuid`. |
|
|
66
|
+
| `force_tls` | If `true`, defer instead of delivering in plaintext. |
|
|
87
67
|
|
|
88
|
-
|
|
68
|
+
## Outbound Hooks
|
|
89
69
|
|
|
90
|
-
|
|
91
|
-
- mail_from - an Address<sup>[1](#fn1)</sup> object - the rfc.2821 sender of this mail
|
|
92
|
-
- domain - the domain this mail is going to (see `always_split` above)
|
|
93
|
-
- notes - the original transaction.notes for this mail, also contains the following useful keys:
|
|
94
|
-
- outbound_ip - the IP address to bind to (do not set manually, use the `get_mx` hook)
|
|
95
|
-
- outbound_helo - the EHLO domain to use (again, do not set manually)
|
|
96
|
-
- queue_time - the epoch milliseconds time when this mail was queued
|
|
97
|
-
- uuid - the original transaction.uuid
|
|
98
|
-
- force_tls - if true, this mail will be sent over TLS or defer
|
|
70
|
+
### queue_outbound
|
|
99
71
|
|
|
100
|
-
|
|
72
|
+
Runs before queuing. Returning `CONT` (or having no hook) queues the mail. `OK` indicates the plugin queued it itself; the `DENY*` codes reject the message.
|
|
101
73
|
|
|
102
|
-
###
|
|
74
|
+
### pre_send_trans_email
|
|
103
75
|
|
|
104
|
-
|
|
76
|
+
Parameters: `next, connection`
|
|
105
77
|
|
|
106
|
-
|
|
78
|
+
Fired by `outbound.send_trans_email()` before the transaction is serialized to disk. Useful for plugins that synthesize mail programmatically — they can attach final headers or notes here.
|
|
107
79
|
|
|
108
|
-
|
|
80
|
+
### send_email
|
|
109
81
|
|
|
110
|
-
|
|
82
|
+
Parameters: `next, hmail`
|
|
111
83
|
|
|
112
|
-
|
|
84
|
+
Called just before delivery starts. `next(DELAY, seconds)` defers the attempt.
|
|
113
85
|
|
|
114
|
-
###
|
|
86
|
+
### get_mx
|
|
115
87
|
|
|
116
88
|
Parameters: `next, hmail, domain`
|
|
117
89
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
The MX is sent via next(OK, mx). `mx` is a [HarakaMx][url-harakamx] object, an array of HarakaMx objects, or any suitable HarakaMx input.
|
|
121
|
-
|
|
122
|
-
### The deferred hook
|
|
90
|
+
Called when delivery begins, with the destination domain. Plugins can override MX lookup; most installs leave Haraka to do DNS. Respond with `next(OK, mx)` where `mx` is a [HarakaMx][url-harakamx] object, an array of them, or any HarakaMx-compatible input. Set `mx.auth_user` / `mx.auth_pass` to AUTH against the remote, or `mx.bind` / `mx.bind_helo`
|
|
91
|
+
to control source address and EHLO.
|
|
123
92
|
|
|
124
|
-
|
|
93
|
+
### deferred
|
|
125
94
|
|
|
126
|
-
|
|
95
|
+
Parameters: `next, hmail, { delay, err }`
|
|
127
96
|
|
|
128
|
-
|
|
97
|
+
Fired on temporary failure. Return `OK` to drop the mail silently; return `DENYSOFT, seconds` to override the retry delay (useful for custom backoff indexed on `hmail.num_failures`).
|
|
129
98
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
### The bounce hook
|
|
99
|
+
### bounce
|
|
133
100
|
|
|
134
101
|
Parameters: `next, hmail, error`
|
|
135
102
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
- mx - the MX object that caused the bounce
|
|
139
|
-
- deferred_rcpt - the deferred recipients that eventually bounced
|
|
140
|
-
- bounced_rcpt - the bounced recipients
|
|
141
|
-
|
|
142
|
-
If you do not wish to have a bounce message sent to the originating sender of the email then you can return `OK` from this hook to stop it from sending a bounce message.
|
|
143
|
-
|
|
144
|
-
### The delivered hook
|
|
145
|
-
|
|
146
|
-
Parameters: `next, hmail, params`
|
|
103
|
+
Fired on permanent failure (5xx). Not called for temp-fails. `error` may carry:
|
|
147
104
|
|
|
148
|
-
|
|
105
|
+
- `mx` — the MX that caused the bounce
|
|
106
|
+
- `deferred_rcpt` — recipients that eventually bounced after deferrals
|
|
107
|
+
- `bounced_rcpt` — recipients that bounced outright
|
|
149
108
|
|
|
150
|
-
|
|
109
|
+
Return `OK` to suppress the DSN to the original sender.
|
|
151
110
|
|
|
152
|
-
|
|
153
|
-
- `ip` - IP address of the host that the message was delivered to,
|
|
154
|
-
- `response` - Variable contains the SMTP response text returned by the host that received the message and will typically contain the remote queue ID and
|
|
155
|
-
- `delay` - Time taken between the queue file being created and the message being delivered.
|
|
156
|
-
- `port` - Port number that the message was delivered to.
|
|
157
|
-
- `mode` - Shows whether SMTP or LMTP was used to deliver the mail.
|
|
158
|
-
- `ok_recips` - an `Address`<sup>[1](#fn1)</sup> array containing all of the recipients that were successfully delivered to.
|
|
159
|
-
- `secured` - A boolean denoting if the connection used TLS or not.
|
|
111
|
+
### delivered
|
|
160
112
|
|
|
161
|
-
|
|
113
|
+
Parameters: `next, hmail, [host, ip, response, delay, port, mode, ok_recips, secured, authenticated]`
|
|
162
114
|
|
|
163
|
-
|
|
115
|
+
Fired after a successful delivery. Return codes are ignored; the hook is for logging / accounting.
|
|
164
116
|
|
|
165
|
-
|
|
117
|
+
| Element | Description |
|
|
118
|
+
| --- | --- |
|
|
119
|
+
| `host` | Hostname of the receiving MX. |
|
|
120
|
+
| `ip` | IP we delivered to. |
|
|
121
|
+
| `response` | Remote SMTP response text (typically includes the remote queue ID). |
|
|
122
|
+
| `delay` | Seconds between queue write and delivery. |
|
|
123
|
+
| `port` | Destination port. |
|
|
124
|
+
| `mode` | `'smtp'` or `'lmtp'`. |
|
|
125
|
+
| `ok_recips` | `Address`<sup>[1](#fn1)</sup> array of successfully delivered recipients. |
|
|
126
|
+
| `secured` | `true` if STARTTLS succeeded. |
|
|
127
|
+
| `authenticated` | `true` if outbound AUTH succeeded. |
|
|
166
128
|
|
|
167
|
-
|
|
129
|
+
## Outbound IP Address
|
|
168
130
|
|
|
169
|
-
|
|
131
|
+
By default the OS routing table chooses the source IP. To pin outbound to a specific IP (per-sender, per-domain, etc.), bind that address to a local interface or alias, then set `mx.bind` (source IP) and `mx.bind_helo` (EHLO domain) in your `get_mx` hook.
|
|
170
132
|
|
|
171
|
-
|
|
133
|
+
## Outbound AUTH
|
|
172
134
|
|
|
173
|
-
|
|
135
|
+
Force AUTH for a domain or smart host by returning an MX with `auth_user` and `auth_pass` set from the `get_mx` hook. If the remote end does not advertise AUTH (or no compatible mechanism is found), delivery proceeds without AUTH and a warning is logged.
|
|
174
136
|
|
|
175
137
|
## Bounce Messages
|
|
176
138
|
|
|
177
|
-
The
|
|
139
|
+
The bounce body comes from `config/outbound.bounce_message`. Curly-brace template variables are filled in at bounce time:
|
|
178
140
|
|
|
179
|
-
|
|
141
|
+
- `pid` — current process id
|
|
142
|
+
- `date` — bounce timestamp
|
|
143
|
+
- `me` — contents of `config/me`
|
|
144
|
+
- `from` — original sender
|
|
145
|
+
- `msgid` — original message UUID
|
|
146
|
+
- `to` — original recipient (or first, for multi-recipient mail)
|
|
147
|
+
- `reason` — remote server's rejection text
|
|
180
148
|
|
|
181
|
-
|
|
182
|
-
- date - the current date when the bounce occurred
|
|
183
|
-
- me - the contents of `config/me`
|
|
184
|
-
- from - the originating sender of the message
|
|
185
|
-
- msgid - a uuid for the mail
|
|
186
|
-
- to - the end recipient of the message, or the first recipient if it was to
|
|
187
|
-
multiple people
|
|
188
|
-
- reason - the text from the remote server indicating why it bounced
|
|
149
|
+
The original message is appended to the bounce.
|
|
189
150
|
|
|
190
|
-
|
|
151
|
+
For HTML bounces, add `config/outbound.bounce_message_html` (and optionally an inline image in `config/outbound.bounce_message_image`).
|
|
191
152
|
|
|
192
|
-
##
|
|
153
|
+
## Generating Mail from a Plugin
|
|
193
154
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
To do that, you can use the `outbound` module directly:
|
|
155
|
+
To create and queue a new message from inside a plugin, use the `outbound` module:
|
|
197
156
|
|
|
198
157
|
```js
|
|
199
158
|
const outbound = require('./outbound')
|
|
200
159
|
|
|
201
|
-
const to = 'user@example.com'
|
|
202
160
|
const from = 'sender@example.com'
|
|
161
|
+
const to = 'user@example.com'
|
|
203
162
|
|
|
204
163
|
const contents = [
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
164
|
+
`From: ${from}`,
|
|
165
|
+
`To: ${to}`,
|
|
166
|
+
'MIME-Version: 1.0',
|
|
167
|
+
'Content-Type: text/plain; charset=us-ascii',
|
|
168
|
+
'Subject: Hello',
|
|
169
|
+
'',
|
|
170
|
+
'Body here.',
|
|
171
|
+
'',
|
|
213
172
|
].join('\n')
|
|
214
173
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
this.logerror('Unrecognized return code from sending email: ' + msg)
|
|
226
|
-
next()
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
outbound.send_email(from, to, contents, outnext)
|
|
174
|
+
outbound.send_email(from, to, contents, (code, msg) => {
|
|
175
|
+
switch (code) {
|
|
176
|
+
case OK:
|
|
177
|
+
plugin.loginfo('queued')
|
|
178
|
+
break
|
|
179
|
+
case DENY:
|
|
180
|
+
plugin.logerror(`queue failed: ${msg}`)
|
|
181
|
+
break
|
|
182
|
+
}
|
|
183
|
+
})
|
|
231
184
|
```
|
|
232
185
|
|
|
233
|
-
The callback
|
|
186
|
+
The callback fires when the mail is **queued**, not delivered — hook `delivered` and `bounce` to observe delivery outcomes.
|
|
234
187
|
|
|
235
|
-
The callback
|
|
188
|
+
The callback may be omitted if you don't need to handle queue failure:
|
|
236
189
|
|
|
237
190
|
```js
|
|
238
191
|
outbound.send_email(from, to, contents)
|
|
239
192
|
```
|
|
240
193
|
|
|
241
|
-
|
|
194
|
+
Options accepted by `send_email(from, to, contents, next, options)`:
|
|
242
195
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
196
|
+
| Option | Description |
|
|
197
|
+
| --- | --- |
|
|
198
|
+
| `dot_stuffed: true` | Content is already SMTP dot-stuffed. |
|
|
199
|
+
| `notes: { … }` | Seed the new transaction's `notes`. |
|
|
200
|
+
| `remove_msgid: true` | Drop any existing `Message-Id:` so Haraka generates one. Useful when releasing from quarantine. |
|
|
201
|
+
| `remove_date: true` | Drop any existing `Date:` so Haraka generates one. |
|
|
202
|
+
| `origin: <object>` | Object passed to the logger to identify the source plugin / connection / HMailItem. |
|
|
246
203
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
| Key/Value | Description |
|
|
250
|
-
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
251
|
-
| `dot_stuffed: true` | Use this if you are passing your content dot-stuffed (a dot at the start of a line is doubled, like it is in SMTP conversation, see [RFC 2821][url-rfc2821]. |
|
|
252
|
-
| `notes: { key: value}` | In case you need notes in the new transaction that `send_email()` creates. |
|
|
253
|
-
| `remove_msgid: true` | Remove any Message-Id header found in the message. If you are reading a message in from the filesystem and you want to ensure that a generated Message-Id header is used in preference over the original. This is useful if you are releasing mail from a quarantine. |
|
|
254
|
-
| `remove_date: true` | Remove any Date header found in the message. If you are reading a message in from the filesystem and you want to ensure that a generated Date header is used in preference over the original. This is useful if you are releasing mail from a quarantine. |
|
|
255
|
-
| `origin: Object` | Adds object as argument to logger.log calls inside outbound.send_email. Useful for tracking which Plugin/Connection/HMailItem object generated email. |
|
|
256
|
-
|
|
257
|
-
```js
|
|
258
|
-
outbound.send_email(from, to, contents, outnext, { notes: transaction.notes })
|
|
259
|
-
```
|
|
204
|
+
To send an already-built `Transaction` directly, use `outbound.send_trans_email(transaction, next)`. This is what `send_email()` calls internally and fires the `pre_send_trans_email` hook.
|
|
260
205
|
|
|
261
206
|
<a name="fn1">1</a>: `Address` objects are [address-rfc2821](https://github.com/haraka/node-address-rfc2821) objects.
|
|
262
207
|
|
|
263
|
-
[url-tls]:
|
|
208
|
+
[url-tls]: plugins/tls.md
|
|
264
209
|
[url-harakamx]: https://github.com/haraka/haraka-net-utils?tab=readme-ov-file#harakamx
|
|
265
210
|
[url-rfc2821]: https://tools.ietf.org/html/rfc2821#section-4.5.2
|