Haraka 3.2.1 → 3.3.1
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/.githooks/pre-commit +41 -0
- package/.prettierignore +1 -0
- package/.qlty/.gitignore +7 -0
- package/.qlty/configs/.shellcheckrc +1 -0
- package/.qlty/qlty.toml +15 -0
- package/CHANGELOG.md +29 -5
- package/CONTRIBUTORS.md +5 -5
- package/README.md +6 -3
- package/bin/haraka +12 -4
- package/config/connection.ini +6 -0
- package/connection.js +67 -68
- package/contrib/bsd-rc.d/haraka +2 -0
- package/docs/CoreConfig.md +2 -0
- package/docs/HAProxy.md +4 -1
- package/eslint.config.mjs +2 -30
- package/haraka.js +2 -2
- package/line_socket.js +6 -33
- package/outbound/hmail.js +18 -29
- package/outbound/index.js +3 -3
- package/outbound/queue.js +8 -5
- package/package.json +49 -46
- package/plugins/auth/auth_proxy.js +7 -4
- package/plugins/block_me.js +1 -1
- package/plugins/delay_deny.js +1 -1
- package/plugins/queue/qmail-queue.js +1 -1
- package/plugins/queue/quarantine.js +5 -5
- package/plugins/queue/smtp_bridge.js +1 -1
- package/plugins/queue/smtp_proxy.js +2 -2
- package/plugins/status.js +2 -2
- package/plugins/toobusy.js +1 -1
- package/plugins.js +4 -3
- package/server.js +172 -28
- package/smtp_client.js +2 -1
- package/test/connection.js +119 -2
- package/test/fixtures/haproxy_allowed/config/connection.ini +3 -0
- package/test/fixtures/haproxy_disabled/config/connection.ini +3 -0
- package/test/fixtures/haproxy_untrusted/config/connection.ini +3 -0
- package/test/fixtures/line_socket.js +1 -1
- package/test/fixtures/util_hmailitem.js +2 -3
- package/test/outbound/index.js +6 -7
- package/test/outbound/qfile.js +1 -1
- package/test/outbound/queue.js +2 -2
- package/test/plugins/auth/auth_base.js +17 -17
- package/test/plugins/auth/auth_bridge.js +3 -3
- package/test/plugins/auth/auth_vpopmaild.js +3 -3
- package/test/plugins/auth/flat_file.js +16 -21
- package/test/plugins/block_me.js +7 -23
- package/test/plugins/data.signatures.js +17 -20
- package/test/plugins/delay_deny.js +3 -4
- package/test/plugins/prevent_credential_leaks.js +17 -21
- package/test/plugins/process_title.js +12 -6
- package/test/plugins/queue/deliver.js +7 -8
- package/test/plugins/queue/discard.js +3 -4
- package/test/plugins/queue/lmtp.js +5 -6
- package/test/plugins/queue/qmail-queue.js +7 -8
- package/test/plugins/queue/quarantine.js +3 -4
- package/test/plugins/queue/smtp_bridge.js +5 -7
- package/test/plugins/queue/smtp_forward.js +49 -60
- package/test/plugins/queue/smtp_proxy.js +6 -7
- package/test/plugins/rcpt_to.host_list_base.js +6 -9
- package/test/plugins/rcpt_to.in_host_list.js +6 -11
- package/test/plugins/record_envelope_addresses.js +33 -60
- package/test/plugins/reseed_rng.js +3 -3
- package/test/plugins/status.js +4 -5
- package/test/plugins/tarpit.js +3 -4
- package/test/plugins/tls.js +3 -5
- package/test/plugins/toobusy.js +186 -9
- package/test/plugins/xclient.js +7 -4
- package/test/server.js +425 -1
- package/test/smtp_client.js +11 -18
- package/test/tls_socket.js +3 -6
- package/tls_socket.js +3 -3
- package/transaction.js +3 -3
- package/address.js +0 -53
- package/endpoint.js +0 -96
- package/host_pool.js +0 -169
- package/outbound/fsync_writestream.js +0 -44
- package/outbound/timer_queue.js +0 -86
- package/rfc1869.js +0 -93
- package/test/endpoint.js +0 -128
- package/test/host_pool.js +0 -188
- package/test/rfc1869.js +0 -89
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# pre-commit: if package.json has a `prettier` script AND prettier is
|
|
3
|
+
# resolvable without triggering npx auto-install, run it and block the
|
|
4
|
+
# commit on formatting violations.
|
|
5
|
+
|
|
6
|
+
[ -f package.json ] || exit 0
|
|
7
|
+
command -v npm >/dev/null 2>&1 || exit 0
|
|
8
|
+
|
|
9
|
+
has_script() {
|
|
10
|
+
# Detect a "scriptname": "..." entry. Prefers jq; falls back to grep so
|
|
11
|
+
# the hook works on minimal environments. The grep pattern requires a
|
|
12
|
+
# string value (opening quote after the colon), which avoids matching
|
|
13
|
+
# the top-level "prettier": { ... } config block.
|
|
14
|
+
if command -v jq >/dev/null 2>&1; then
|
|
15
|
+
jq -e ".scripts[\"$1\"]" package.json >/dev/null 2>&1
|
|
16
|
+
else
|
|
17
|
+
grep -qE "\"$1\"[[:space:]]*:[[:space:]]*\"" package.json
|
|
18
|
+
fi
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
has_script prettier || exit 0
|
|
22
|
+
|
|
23
|
+
# Bail if prettier isn't locally or globally installed — `npx prettier`
|
|
24
|
+
# would otherwise prompt to install it, which a hook can't answer.
|
|
25
|
+
if [ ! -x node_modules/.bin/prettier ] && ! command -v prettier >/dev/null 2>&1; then
|
|
26
|
+
exit 0
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
npm run --silent prettier && exit 0
|
|
30
|
+
|
|
31
|
+
echo
|
|
32
|
+
echo "Prettier reported formatting violations. To fix:"
|
|
33
|
+
if has_script format; then
|
|
34
|
+
echo " npm run format # prettier:fix + lint:fix"
|
|
35
|
+
elif has_script prettier:fix; then
|
|
36
|
+
echo " npm run prettier:fix"
|
|
37
|
+
else
|
|
38
|
+
echo " npx prettier . --write"
|
|
39
|
+
fi
|
|
40
|
+
echo "Or bypass with: git commit --no-verify"
|
|
41
|
+
exit 1
|
package/.prettierignore
CHANGED
package/.qlty/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
source-path=SCRIPTDIR
|
package/.qlty/qlty.toml
ADDED
package/CHANGELOG.md
CHANGED
|
@@ -4,14 +4,36 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
|
|
4
4
|
|
|
5
5
|
### Unreleased
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
### [3.3.1] - 2026-06-12
|
|
8
|
+
|
|
9
|
+
- fix(conn): flag soft queue denials in results
|
|
10
|
+
- feat: expose fetch to plugins
|
|
11
|
+
- change: remove @haraka/email-address wrapper #3598
|
|
12
|
+
- change: log when MFROM or RCPT fail to parse #3581
|
|
13
|
+
- fix: change package name from Haraka to haraka #3596
|
|
14
|
+
- fix(haraka): wrap util.createFile in a try #3595
|
|
15
|
+
- fix(conn): update local and remote results after proxy #3593
|
|
16
|
+
- feat(conn): add main.postel option #3592
|
|
17
|
+
- feat: proxy support for smtps (465) #3577
|
|
18
|
+
- refactor(auth_proxy): use net_utils.endpoint #3584
|
|
19
|
+
- refactor: move endpoint, HostPool, LineSocket to net-utils #3583
|
|
20
|
+
- refactor: move rfc1869, FsyncWriteStream, TimerQueue to haraka-utils #3585
|
|
21
|
+
- refactor: move cram_md5_response to haraka-utils #3585
|
|
22
|
+
- dep(many): bump to latest #3587
|
|
23
|
+
- dep(eslint): update @haraka/eslint-config to v3, #3586
|
|
24
|
+
- dep(haraka-utils): ~2.1.1 for position-aware MAIL/RCPT address errors
|
|
25
|
+
- build: add qlty config and README badge #3588
|
|
26
|
+
- test: update to test-fixtures 1.7.0 helpers #3589
|
|
27
|
+
|
|
28
|
+
### [3.2.1] - 2026-05-24
|
|
29
|
+
|
|
30
|
+
- fix(deps): update rspamd, dkim plugins to latest #3576
|
|
31
|
+
|
|
32
|
+
### [3.2.0] - 2026-05-24
|
|
11
33
|
|
|
12
34
|
- fix(status): merge worker status into summary #3574
|
|
13
35
|
- dep: replace address-rfc282{1,2} with @haraka/email-address #3566
|
|
14
|
-
- change(BREAKING for some plugins), see https://github.com/haraka/Haraka/issues/3564
|
|
36
|
+
- change(BREAKING for some plugins), see https://github.com/haraka/Haraka/issues/3564
|
|
15
37
|
|
|
16
38
|
### [3.1.7] - 2026-05-19
|
|
17
39
|
|
|
@@ -1872,3 +1894,5 @@ config files.
|
|
|
1872
1894
|
[3.1.6]: https://github.com/haraka/Haraka/releases/tag/v3.1.6
|
|
1873
1895
|
[3.1.7]: https://github.com/haraka/Haraka/releases/tag/v3.1.7
|
|
1874
1896
|
[3.2.0]: https://github.com/haraka/Haraka/releases/tag/v3.2.0
|
|
1897
|
+
[3.2.1]: https://github.com/haraka/Haraka/releases/tag/v3.2.1
|
|
1898
|
+
[3.3.1]: https://github.com/haraka/Haraka/releases/tag/v3.3.1
|
package/CONTRIBUTORS.md
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
This handcrafted artisanal software is brought to you by:
|
|
4
4
|
|
|
5
|
-
| <img height="80" src="https://avatars.githubusercontent.com/u/261635?v=4"><br><a href="https://github.com/msimerson">msimerson</a> (<a href="https://github.com/haraka/Haraka/commits?author=msimerson">
|
|
5
|
+
| <img height="80" src="https://avatars.githubusercontent.com/u/261635?v=4"><br><a href="https://github.com/msimerson">msimerson</a> (<a href="https://github.com/haraka/Haraka/commits?author=msimerson">1685</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/662371?v=4"><br><a href="https://github.com/baudehlo">baudehlo</a> (<a href="https://github.com/haraka/Haraka/commits?author=baudehlo">969</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/550490?v=4"><br><a href="https://github.com/smfreegard">smfreegard</a> (<a href="https://github.com/haraka/Haraka/commits?author=smfreegard">794</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/959600?v=4"><br><a href="https://github.com/godsflaw">godsflaw</a> (<a href="https://github.com/haraka/Haraka/commits?author=godsflaw">171</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/934254?v=4"><br><a href="https://github.com/analogic">analogic</a> (<a href="https://github.com/haraka/Haraka/commits?author=analogic">42</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/1674289?v=4"><br><a href="https://github.com/Dexus">Dexus</a> (<a href="https://github.com/haraka/Haraka/commits?author=Dexus">42</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/82041?v=4"><br><a href="https://github.com/gramakri">gramakri</a> (<a href="https://github.com/haraka/Haraka/commits?author=gramakri">40</a>) |
|
|
6
6
|
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
|
|
7
7
|
| <img height="80" src="https://avatars.githubusercontent.com/u/203240?v=4"><br><a href="https://github.com/lnedry">lnedry</a> (<a href="https://github.com/haraka/Haraka/commits?author=lnedry">27</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/748075?v=4"><br><a href="https://github.com/celesteking">celesteking</a> (<a href="https://github.com/haraka/Haraka/commits?author=celesteking">21</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/791972?v=4"><br><a href="https://github.com/lpatters">lpatters</a> (<a href="https://github.com/haraka/Haraka/commits?author=lpatters">20</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/366268?v=4"><br><a href="https://github.com/chazomaticus">chazomaticus</a> (<a href="https://github.com/haraka/Haraka/commits?author=chazomaticus">19</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/123708?v=4"><br><a href="https://github.com/arlolra">arlolra</a> (<a href="https://github.com/haraka/Haraka/commits?author=arlolra">16</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/271024?v=4"><br><a href="https://github.com/hayesgm">hayesgm</a> (<a href="https://github.com/haraka/Haraka/commits?author=hayesgm">16</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/1573133?v=4"><br><a href="https://github.com/gauravaror">gauravaror</a> (<a href="https://github.com/haraka/Haraka/commits?author=gauravaror">14</a>) |
|
|
8
8
|
| <img height="80" src="https://avatars.githubusercontent.com/u/260607?v=4"><br><a href="https://github.com/typingArtist">typingArtist</a> (<a href="https://github.com/haraka/Haraka/commits?author=typingArtist">14</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/11343494?v=4"><br><a href="https://github.com/superman20">superman20</a> (<a href="https://github.com/haraka/Haraka/commits?author=superman20">13</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/158380?v=4"><br><a href="https://github.com/darkpixel">darkpixel</a> (<a href="https://github.com/haraka/Haraka/commits?author=darkpixel">12</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/9887966?v=4"><br><a href="https://github.com/KingNoosh">KingNoosh</a> (<a href="https://github.com/haraka/Haraka/commits?author=KingNoosh">11</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/5229495?v=4"><br><a href="https://github.com/tstonis">tstonis</a> (<a href="https://github.com/haraka/Haraka/commits?author=tstonis">10</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/1746394?v=4"><br><a href="https://github.com/wltsmrz">wltsmrz</a> (<a href="https://github.com/haraka/Haraka/commits?author=wltsmrz">9</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/218741413?v=4"><br><a href="https://github.com/SamuelGrave">SamuelGrave</a> (<a href="https://github.com/haraka/Haraka/commits?author=SamuelGrave">9</a>) |
|
|
9
|
-
| <img height="80" src="https://avatars.githubusercontent.com/u/2176651?v=4"><br><a href="https://github.com/fatalbanana">fatalbanana</a> (<a href="https://github.com/haraka/Haraka/commits?author=fatalbanana">9</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/231081?v=4"><br><a href="https://github.com/EyePulp">EyePulp</a> (<a href="https://github.com/haraka/Haraka/commits?author=EyePulp">8</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/81561?v=4"><br><a href="https://github.com/Synchro">Synchro</a> (<a href="https://github.com/haraka/Haraka/commits?author=Synchro">8</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/3957811?v=4"><br><a href="https://github.com/gene-hightower">gene-hightower</a> (<a href="https://github.com/haraka/Haraka/commits?author=gene-hightower">7</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/8224508?v=4"><br><a href="https://github.com/DarkSorrow">DarkSorrow</a> (<a href="https://github.com/haraka/Haraka/commits?author=DarkSorrow">6</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/103802?v=4"><br><a href="https://github.com/joshuathayer">joshuathayer</a> (<a href="https://github.com/haraka/Haraka/commits?author=joshuathayer">6</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/
|
|
10
|
-
| <img height="80" src="https://avatars.githubusercontent.com/u/
|
|
11
|
-
| <img height="80" src="https://avatars.githubusercontent.com/u/791835?v=4"><br><a href="https://github.com/Juerd">Juerd</a> (<a href="https://github.com/haraka/Haraka/commits?author=Juerd">4</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/5351045?v=4"><br><a href="https://github.com/rhedshi">rhedshi</a> (<a href="https://github.com/haraka/Haraka/commits?author=rhedshi">4</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/7155684?v=4"><br><a href="https://github.com/rw251">rw251</a> (<a href="https://github.com/haraka/Haraka/commits?author=rw251">4</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/
|
|
12
|
-
| <img height="80" src="https://avatars.githubusercontent.com/u/
|
|
9
|
+
| <img height="80" src="https://avatars.githubusercontent.com/u/2176651?v=4"><br><a href="https://github.com/fatalbanana">fatalbanana</a> (<a href="https://github.com/haraka/Haraka/commits?author=fatalbanana">9</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/231081?v=4"><br><a href="https://github.com/EyePulp">EyePulp</a> (<a href="https://github.com/haraka/Haraka/commits?author=EyePulp">8</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/81561?v=4"><br><a href="https://github.com/Synchro">Synchro</a> (<a href="https://github.com/haraka/Haraka/commits?author=Synchro">8</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/3957811?v=4"><br><a href="https://github.com/gene-hightower">gene-hightower</a> (<a href="https://github.com/haraka/Haraka/commits?author=gene-hightower">7</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/8224508?v=4"><br><a href="https://github.com/DarkSorrow">DarkSorrow</a> (<a href="https://github.com/haraka/Haraka/commits?author=DarkSorrow">6</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/103802?v=4"><br><a href="https://github.com/joshuathayer">joshuathayer</a> (<a href="https://github.com/haraka/Haraka/commits?author=joshuathayer">6</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/918201?v=4"><br><a href="https://github.com/DoobleD">DoobleD</a> (<a href="https://github.com/haraka/Haraka/commits?author=DoobleD">5</a>) |
|
|
10
|
+
| <img height="80" src="https://avatars.githubusercontent.com/u/298453?v=4"><br><a href="https://github.com/zllovesuki">zllovesuki</a> (<a href="https://github.com/haraka/Haraka/commits?author=zllovesuki">5</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/132251?v=4"><br><a href="https://github.com/schamane">schamane</a> (<a href="https://github.com/haraka/Haraka/commits?author=schamane">5</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/2158203?v=4"><br><a href="https://github.com/luto">luto</a> (<a href="https://github.com/haraka/Haraka/commits?author=luto">5</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/21576638?v=4"><br><a href="https://github.com/steflw">steflw</a> (<a href="https://github.com/haraka/Haraka/commits?author=steflw">5</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/1263856?v=4"><br><a href="https://github.com/ricardopolo">ricardopolo</a> (<a href="https://github.com/haraka/Haraka/commits?author=ricardopolo">5</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/1521113?v=4"><br><a href="https://github.com/hontas">hontas</a> (<a href="https://github.com/haraka/Haraka/commits?author=hontas">5</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/11270?v=4"><br><a href="https://github.com/swerter">swerter</a> (<a href="https://github.com/haraka/Haraka/commits?author=swerter">4</a>) |
|
|
11
|
+
| <img height="80" src="https://avatars.githubusercontent.com/u/791835?v=4"><br><a href="https://github.com/Juerd">Juerd</a> (<a href="https://github.com/haraka/Haraka/commits?author=Juerd">4</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/5351045?v=4"><br><a href="https://github.com/rhedshi">rhedshi</a> (<a href="https://github.com/haraka/Haraka/commits?author=rhedshi">4</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/7155684?v=4"><br><a href="https://github.com/rw251">rw251</a> (<a href="https://github.com/haraka/Haraka/commits?author=rw251">4</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/13922268?v=4"><br><a href="https://github.com/jdai8">jdai8</a> (<a href="https://github.com/haraka/Haraka/commits?author=jdai8">4</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/34087?v=4"><br><a href="https://github.com/fredjean">fredjean</a> (<a href="https://github.com/haraka/Haraka/commits?author=fredjean">4</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/8285462?v=4"><br><a href="https://github.com/CalgaryMichael">CalgaryMichael</a> (<a href="https://github.com/haraka/Haraka/commits?author=CalgaryMichael">4</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/5624708?v=4"><br><a href="https://github.com/Bramzor">Bramzor</a> (<a href="https://github.com/haraka/Haraka/commits?author=Bramzor">4</a>) |
|
|
12
|
+
| <img height="80" src="https://avatars.githubusercontent.com/u/308887?v=4"><br><a href="https://github.com/benjisg">benjisg</a> (<a href="https://github.com/haraka/Haraka/commits?author=benjisg">4</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/1451395?v=4"><br><a href="https://github.com/bmonty">bmonty</a> (<a href="https://github.com/haraka/Haraka/commits?author=bmonty">4</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/147893?v=4"><br><a href="https://github.com/Andrew565">Andrew565</a> (<a href="https://github.com/haraka/Haraka/commits?author=Andrew565">4</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/255701?v=4"><br><a href="https://github.com/abhas">abhas</a> (<a href="https://github.com/haraka/Haraka/commits?author=abhas">4</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/6220422?v=4"><br><a href="https://github.com/dcharbonnier">dcharbonnier</a> (<a href="https://github.com/haraka/Haraka/commits?author=dcharbonnier">3</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/232225?v=4"><br><a href="https://github.com/soncodi">soncodi</a> (<a href="https://github.com/haraka/Haraka/commits?author=soncodi">3</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/2115696?v=4"><br><a href="https://github.com/AuspeXeu">AuspeXeu</a> (<a href="https://github.com/haraka/Haraka/commits?author=AuspeXeu">3</a>) |
|
|
13
13
|
| <img height="80" src="https://avatars.githubusercontent.com/u/6684599?v=4"><br><a href="https://github.com/acharkizakaria">acharkizakaria</a> (<a href="https://github.com/haraka/Haraka/commits?author=acharkizakaria">3</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/1810178?v=4"><br><a href="https://github.com/pvsousalima">pvsousalima</a> (<a href="https://github.com/haraka/Haraka/commits?author=pvsousalima">3</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/759912?v=4"><br><a href="https://github.com/davebeyer">davebeyer</a> (<a href="https://github.com/haraka/Haraka/commits?author=davebeyer">3</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/706078?v=4"><br><a href="https://github.com/brunolm">brunolm</a> (<a href="https://github.com/haraka/Haraka/commits?author=brunolm">3</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/7008027?v=4"><br><a href="https://github.com/mkp7">mkp7</a> (<a href="https://github.com/haraka/Haraka/commits?author=mkp7">2</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/1007551?v=4"><br><a href="https://github.com/unquietwiki">unquietwiki</a> (<a href="https://github.com/haraka/Haraka/commits?author=unquietwiki">2</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/10368105?v=4"><br><a href="https://github.com/mtrivera">mtrivera</a> (<a href="https://github.com/haraka/Haraka/commits?author=mtrivera">2</a>) |
|
|
14
14
|
| <img height="80" src="https://avatars.githubusercontent.com/u/1829326?v=4"><br><a href="https://github.com/slattery">slattery</a> (<a href="https://github.com/haraka/Haraka/commits?author=slattery">2</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/8569238?v=4"><br><a href="https://github.com/MuraraAllan">MuraraAllan</a> (<a href="https://github.com/haraka/Haraka/commits?author=MuraraAllan">2</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/994004?v=4"><br><a href="https://github.com/stefanocoding">stefanocoding</a> (<a href="https://github.com/haraka/Haraka/commits?author=stefanocoding">2</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/5483299?v=4"><br><a href="https://github.com/thihara">thihara</a> (<a href="https://github.com/haraka/Haraka/commits?author=thihara">2</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/43457281?v=4"><br><a href="https://github.com/benjamonnguyen">benjamonnguyen</a> (<a href="https://github.com/haraka/Haraka/commits?author=benjamonnguyen">2</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/15035337?v=4"><br><a href="https://github.com/gtech99">gtech99</a> (<a href="https://github.com/haraka/Haraka/commits?author=gtech99">2</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/3073292?v=4"><br><a href="https://github.com/joelchornik">joelchornik</a> (<a href="https://github.com/haraka/Haraka/commits?author=joelchornik">2</a>) |
|
|
15
15
|
| <img height="80" src="https://avatars.githubusercontent.com/u/934178?v=4"><br><a href="https://github.com/vrevo">vrevo</a> (<a href="https://github.com/haraka/Haraka/commits?author=vrevo">2</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/1249760?v=4"><br><a href="https://github.com/andreis">andreis</a> (<a href="https://github.com/haraka/Haraka/commits?author=andreis">2</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/15694566?v=4"><br><a href="https://github.com/apoorv-mishra">apoorv-mishra</a> (<a href="https://github.com/haraka/Haraka/commits?author=apoorv-mishra">2</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/563469?v=4"><br><a href="https://github.com/niieani">niieani</a> (<a href="https://github.com/haraka/Haraka/commits?author=niieani">2</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/196198?v=4"><br><a href="https://github.com/hathaway">hathaway</a> (<a href="https://github.com/haraka/Haraka/commits?author=hathaway">2</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/14928995?v=4"><br><a href="https://github.com/carolinaknoll">carolinaknoll</a> (<a href="https://github.com/haraka/Haraka/commits?author=carolinaknoll">2</a>) | <img height="80" src="https://avatars.githubusercontent.com/u/654682?v=4"><br><a href="https://github.com/martin1yness">martin1yness</a> (<a href="https://github.com/haraka/Haraka/commits?author=martin1yness">2</a>) |
|
package/README.md
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# Haraka — a Node.js Mail Server
|
|
2
2
|
|
|
3
|
-
![
|
|
4
|
-
[![Coverage Status][cov-img]][cov-url]
|
|
3
|
+
[![Build][ci-img]][ci-url] [![Cover][cov-img]][cov-url] [![Qlty][qlty-img]][qlty-url]
|
|
5
4
|
|
|
6
5
|
Haraka is a highly scalable [Node.js][1] SMTP server with a modular plugin architecture. It handles thousands of concurrent connections and delivers thousands of messages per second. Haraka and its plugins are written in asynchronous JavaScript, optimised for throughput and low latency.
|
|
7
6
|
|
|
@@ -115,5 +114,9 @@ Haraka is released under the MIT License. See [LICENSE][license] for details.
|
|
|
115
114
|
[msimerson]: https://github.com/msimerson
|
|
116
115
|
[spamassassin]: https://spamassassin.apache.org/
|
|
117
116
|
[qpsmtpd]: https://github.com/smtpd/qpsmtpd/
|
|
117
|
+
[ci-img]: https://github.com/haraka/Haraka/actions/workflows/ci.yml/badge.svg
|
|
118
|
+
[ci-url]: https://github.com/haraka/Haraka/actions/workflows/ci.yml
|
|
118
119
|
[cov-img]: https://codecov.io/github/haraka/Haraka/coverage.svg
|
|
119
|
-
[cov-url]: https://codecov.io/github/haraka/Haraka
|
|
120
|
+
[cov-url]: https://codecov.io/github/haraka/Haraka
|
|
121
|
+
[qlty-img]: https://qlty.sh/gh/haraka/projects/Haraka/maintainability.svg
|
|
122
|
+
[qlty-url]: https://qlty.sh/gh/haraka/projects/Haraka
|
package/bin/haraka
CHANGED
|
@@ -165,6 +165,14 @@ function setupRequire() {
|
|
|
165
165
|
|
|
166
166
|
function noop() {}
|
|
167
167
|
|
|
168
|
+
function tryCreateFile(filePath, data, info = {}, force = false) {
|
|
169
|
+
try {
|
|
170
|
+
utils.createFile(...arguments)
|
|
171
|
+
} catch (e) {
|
|
172
|
+
warning(`EEXIST, File exists '${filePath}'`)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
168
176
|
const readme = `Haraka
|
|
169
177
|
|
|
170
178
|
Congratulations on creating a new installation of Haraka.
|
|
@@ -406,7 +414,7 @@ if (parsed.version) {
|
|
|
406
414
|
plugins.load_plugins(parsed.test && parsed.test[0] !== 'all' ? parsed.test : null)
|
|
407
415
|
const Connection = require(path.join(base, 'connection'))
|
|
408
416
|
// var Transaction = require(path.join(base, "transaction"));
|
|
409
|
-
const Address = require('
|
|
417
|
+
const Address = require('@haraka/email-address').Address
|
|
410
418
|
const Notes = require('haraka-notes')
|
|
411
419
|
const constants = require('haraka-constants')
|
|
412
420
|
const client = {
|
|
@@ -549,10 +557,10 @@ if (parsed.version) {
|
|
|
549
557
|
utils.mkDir(path.join(pa, d))
|
|
550
558
|
}
|
|
551
559
|
utils.copyFile(path.join(base, 'docs', 'Plugins.md'), path.join(pa, 'docs', 'Plugins.md'))
|
|
552
|
-
|
|
553
|
-
|
|
560
|
+
tryCreateFile(path.join(pa, 'README'), readme, {}, parsed.force)
|
|
561
|
+
tryCreateFile(path.join(pa, 'package.json'), packageJson, {}, parsed.force)
|
|
554
562
|
const bytes = require('crypto').randomBytes(32)
|
|
555
|
-
|
|
563
|
+
tryCreateFile(path.join(pa, 'config', 'internalcmd_key'), bytes.toString('hex'), {}, parsed.force)
|
|
556
564
|
setupHostname(path.join(pa, 'config'))
|
|
557
565
|
setupBaseConfig(path.join(pa, 'config'))
|
|
558
566
|
} else {
|
package/config/connection.ini
CHANGED
|
@@ -14,11 +14,17 @@
|
|
|
14
14
|
; Require senders to conform to RFC 1869 and RFC 821 when sending the MAIL FROM and RCPT TO commands. In particular, the inclusion of spurious spaces or missing angle brackets will be rejected.
|
|
15
15
|
; strict_rfc1869 = false
|
|
16
16
|
|
|
17
|
+
; Liberal envelope parsing. See @haraka/email-address for what postel mode relaxes.
|
|
18
|
+
; postel = false
|
|
19
|
+
|
|
17
20
|
; Advertise support for SMTPUTF8 (RFC-6531)
|
|
18
21
|
; smtputf8=true
|
|
19
22
|
|
|
20
23
|
|
|
21
24
|
[haproxy]
|
|
25
|
+
; Bool: enable HAProxy PROXY protocol support and SMTPS pre-parsing.
|
|
26
|
+
; enabled=true
|
|
27
|
+
|
|
22
28
|
; Array: hosts or CIDRs that Haraka should enable the PROXY protocol from. See docs/HAProxy for format
|
|
23
29
|
hosts[] =
|
|
24
30
|
; hosts[] = 192.0.2.4
|
package/connection.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// a single connection
|
|
3
3
|
|
|
4
4
|
const dns = require('node:dns')
|
|
5
|
-
const net = require('node:net')
|
|
6
5
|
const os = require('node:os')
|
|
7
6
|
|
|
8
7
|
// npm libs
|
|
@@ -12,14 +11,14 @@ const constants = require('haraka-constants')
|
|
|
12
11
|
const net_utils = require('haraka-net-utils')
|
|
13
12
|
const Notes = require('haraka-notes')
|
|
14
13
|
const utils = require('haraka-utils')
|
|
15
|
-
const { Address } = require('
|
|
14
|
+
const { Address } = require('@haraka/email-address')
|
|
16
15
|
const ResultStore = require('haraka-results')
|
|
17
16
|
|
|
18
17
|
// Haraka libs
|
|
19
18
|
const logger = require('./logger')
|
|
20
19
|
const trans = require('./transaction')
|
|
21
20
|
const plugins = require('./plugins')
|
|
22
|
-
const rfc1869 =
|
|
21
|
+
const rfc1869 = utils.rfc1869
|
|
23
22
|
const outbound = require('./outbound')
|
|
24
23
|
|
|
25
24
|
const states = constants.connection.state
|
|
@@ -27,6 +26,7 @@ const states = constants.connection.state
|
|
|
27
26
|
const cfg = config.get('connection.ini', {
|
|
28
27
|
booleans: [
|
|
29
28
|
'-main.strict_rfc1869',
|
|
29
|
+
'-main.postel',
|
|
30
30
|
'+main.smtputf8',
|
|
31
31
|
'+headers.add_received',
|
|
32
32
|
'+headers.show_version',
|
|
@@ -34,20 +34,8 @@ const cfg = config.get('connection.ini', {
|
|
|
34
34
|
],
|
|
35
35
|
})
|
|
36
36
|
|
|
37
|
-
const haproxy_hosts_ipv4 = []
|
|
38
|
-
const haproxy_hosts_ipv6 = []
|
|
39
|
-
|
|
40
|
-
for (const ip of cfg.haproxy.hosts) {
|
|
41
|
-
if (!ip) continue
|
|
42
|
-
if (net.isIPv6(ip.split('/')[0])) {
|
|
43
|
-
haproxy_hosts_ipv6.push([ipaddr.IPv6.parse(ip.split('/')[0]), parseInt(ip.split('/')[1] || 64)])
|
|
44
|
-
} else {
|
|
45
|
-
haproxy_hosts_ipv4.push([ipaddr.IPv4.parse(ip.split('/')[0]), parseInt(ip.split('/')[1] || 32)])
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
37
|
class Connection {
|
|
50
|
-
constructor(client, server
|
|
38
|
+
constructor(client, server) {
|
|
51
39
|
this.client = client
|
|
52
40
|
this.server = server
|
|
53
41
|
|
|
@@ -160,7 +148,7 @@ class Connection {
|
|
|
160
148
|
self.fail()
|
|
161
149
|
})
|
|
162
150
|
|
|
163
|
-
self.client.on('close', (
|
|
151
|
+
self.client.on('close', () => {
|
|
164
152
|
if (self.state >= states.DISCONNECTING) return
|
|
165
153
|
self.remote.closed = true
|
|
166
154
|
self.loginfo('client dropped connection', log_data)
|
|
@@ -190,8 +178,20 @@ class Connection {
|
|
|
190
178
|
self.process_data(data)
|
|
191
179
|
})
|
|
192
180
|
|
|
193
|
-
|
|
194
|
-
|
|
181
|
+
// SMTPS pre-parser state: proxy means the PROXY line was already consumed;
|
|
182
|
+
// peer_allowed means a trusted PROXY peer sent direct TLS instead.
|
|
183
|
+
const smtps = self.client.haraka_smtps
|
|
184
|
+
if (smtps?.proxy) {
|
|
185
|
+
self.proxy.allowed = true
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (smtps?.peer_allowed) {
|
|
190
|
+
plugins.run_hooks('connect_init', self)
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (net_utils.is_haproxy_allowed(self.remote.ip)) {
|
|
195
195
|
self.proxy.allowed = true
|
|
196
196
|
// Wait for PROXY command
|
|
197
197
|
self.proxy.timer = setTimeout(() => {
|
|
@@ -678,8 +678,7 @@ class Connection {
|
|
|
678
678
|
}
|
|
679
679
|
/////////////////////////////////////////////////////////////////////////////
|
|
680
680
|
// SMTP Responses
|
|
681
|
-
connect_init_respond(
|
|
682
|
-
// retval and message are ignored
|
|
681
|
+
connect_init_respond() {
|
|
683
682
|
this.logdebug('running connect_init_respond')
|
|
684
683
|
plugins.run_hooks('lookup_rdns', this)
|
|
685
684
|
}
|
|
@@ -889,7 +888,7 @@ class Connection {
|
|
|
889
888
|
}
|
|
890
889
|
}
|
|
891
890
|
}
|
|
892
|
-
capabilities_respond(
|
|
891
|
+
capabilities_respond() {
|
|
893
892
|
this.respond(250, this.capabilities)
|
|
894
893
|
}
|
|
895
894
|
quit_respond(retval, msg) {
|
|
@@ -940,7 +939,7 @@ class Connection {
|
|
|
940
939
|
this.respond(250, 'OK')
|
|
941
940
|
}
|
|
942
941
|
}
|
|
943
|
-
rset_respond(
|
|
942
|
+
rset_respond() {
|
|
944
943
|
this.respond(250, 'OK', () => {
|
|
945
944
|
this.reset_transaction()
|
|
946
945
|
})
|
|
@@ -1137,39 +1136,14 @@ class Connection {
|
|
|
1137
1136
|
/////////////////////////////////////////////////////////////////////////////
|
|
1138
1137
|
// HAProxy support
|
|
1139
1138
|
|
|
1140
|
-
|
|
1141
|
-
if (
|
|
1142
|
-
|
|
1143
|
-
|
|
1139
|
+
apply_proxy(proxy) {
|
|
1140
|
+
if (this.proxy.timer) {
|
|
1141
|
+
clearTimeout(this.proxy.timer)
|
|
1142
|
+
this.proxy.timer = null
|
|
1144
1143
|
}
|
|
1145
1144
|
|
|
1146
|
-
const
|
|
1147
|
-
|
|
1148
|
-
this.respond(421, 'Invalid PROXY format')
|
|
1149
|
-
return this.disconnect()
|
|
1150
|
-
}
|
|
1151
|
-
const proto = match[1]
|
|
1152
|
-
const src_ip = match[2]
|
|
1153
|
-
const dst_ip = match[3]
|
|
1154
|
-
const src_port = match[4]
|
|
1155
|
-
const dst_port = match[5]
|
|
1156
|
-
|
|
1157
|
-
// Validate source/destination IP
|
|
1158
|
-
/*eslint no-fallthrough: 0 */
|
|
1159
|
-
switch (proto) {
|
|
1160
|
-
case 'TCP4':
|
|
1161
|
-
if (ipaddr.IPv4.isValid(src_ip) && ipaddr.IPv4.isValid(dst_ip)) {
|
|
1162
|
-
break
|
|
1163
|
-
}
|
|
1164
|
-
case 'TCP6':
|
|
1165
|
-
if (ipaddr.IPv6.isValid(src_ip) && ipaddr.IPv6.isValid(dst_ip)) {
|
|
1166
|
-
break
|
|
1167
|
-
}
|
|
1168
|
-
// case 'UNKNOWN':
|
|
1169
|
-
default:
|
|
1170
|
-
this.respond(421, 'Invalid PROXY format')
|
|
1171
|
-
return this.disconnect()
|
|
1172
|
-
}
|
|
1145
|
+
const { proto, src_ip, src_port, dst_ip, dst_port } = proxy
|
|
1146
|
+
const proxy_ip = proxy.proxy_ip || this.remote.ip
|
|
1173
1147
|
|
|
1174
1148
|
// Apply changes
|
|
1175
1149
|
this.loginfo('HAProxy', {
|
|
@@ -1185,11 +1159,11 @@ class Connection {
|
|
|
1185
1159
|
src_port,
|
|
1186
1160
|
dst_ip,
|
|
1187
1161
|
dst_port,
|
|
1188
|
-
proxy_ip
|
|
1162
|
+
proxy_ip,
|
|
1189
1163
|
}
|
|
1190
1164
|
|
|
1191
1165
|
this.reset_transaction(() => {
|
|
1192
|
-
this.set('proxy.ip',
|
|
1166
|
+
this.set('proxy.ip', proxy_ip)
|
|
1193
1167
|
this.set('proxy.type', 'haproxy')
|
|
1194
1168
|
this.relaying = false
|
|
1195
1169
|
this.set('local.ip', dst_ip)
|
|
@@ -1198,9 +1172,26 @@ class Connection {
|
|
|
1198
1172
|
this.set('remote.port', parseInt(src_port, 10))
|
|
1199
1173
|
this.set('remote.host', null)
|
|
1200
1174
|
this.set('hello.host', null)
|
|
1175
|
+
this.results.add({ name: 'local' }, this.local)
|
|
1176
|
+
this.results.add({ name: 'remote' }, this.remote)
|
|
1201
1177
|
plugins.run_hooks('connect_init', this)
|
|
1202
1178
|
})
|
|
1203
1179
|
}
|
|
1180
|
+
|
|
1181
|
+
cmd_proxy(line) {
|
|
1182
|
+
if (!this.proxy.allowed) {
|
|
1183
|
+
this.respond(421, `PROXY not allowed from ${this.remote.ip}`)
|
|
1184
|
+
return this.disconnect()
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
const proxy = net_utils.parse_proxy_line(line)
|
|
1188
|
+
if (!proxy) {
|
|
1189
|
+
this.respond(421, 'Invalid PROXY format')
|
|
1190
|
+
return this.disconnect()
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
this.apply_proxy(proxy)
|
|
1194
|
+
}
|
|
1204
1195
|
/////////////////////////////////////////////////////////////////////////////
|
|
1205
1196
|
// SMTP Commands
|
|
1206
1197
|
|
|
@@ -1283,7 +1274,7 @@ class Connection {
|
|
|
1283
1274
|
}
|
|
1284
1275
|
plugins.run_hooks('rset', this)
|
|
1285
1276
|
}
|
|
1286
|
-
cmd_vrfy(
|
|
1277
|
+
cmd_vrfy() {
|
|
1287
1278
|
// only supported via plugins
|
|
1288
1279
|
plugins.run_hooks('vrfy', this)
|
|
1289
1280
|
}
|
|
@@ -1323,10 +1314,13 @@ class Connection {
|
|
|
1323
1314
|
}
|
|
1324
1315
|
|
|
1325
1316
|
let from
|
|
1317
|
+
const from_raw = results.shift()
|
|
1326
1318
|
try {
|
|
1327
|
-
from = new Address(
|
|
1319
|
+
from = new Address(from_raw, { postel: cfg.main.postel })
|
|
1328
1320
|
} catch (err) {
|
|
1329
|
-
|
|
1321
|
+
const msg = `Invalid MAIL FROM address ${utils.sanitize(from_raw)}: ${err.message}`
|
|
1322
|
+
this.lognotice(msg)
|
|
1323
|
+
return this.respond(501, msg)
|
|
1330
1324
|
}
|
|
1331
1325
|
|
|
1332
1326
|
// Get rest of key=value pairs
|
|
@@ -1382,10 +1376,13 @@ class Connection {
|
|
|
1382
1376
|
}
|
|
1383
1377
|
|
|
1384
1378
|
let recip
|
|
1379
|
+
const recip_raw = results.shift()
|
|
1385
1380
|
try {
|
|
1386
|
-
recip = new Address(
|
|
1381
|
+
recip = new Address(recip_raw, { postel: cfg.main.postel })
|
|
1387
1382
|
} catch (err) {
|
|
1388
|
-
|
|
1383
|
+
const msg = `Invalid RCPT TO address ${utils.sanitize(recip_raw)}: ${err.message}`
|
|
1384
|
+
this.lognotice(msg)
|
|
1385
|
+
return this.respond(501, msg)
|
|
1389
1386
|
}
|
|
1390
1387
|
|
|
1391
1388
|
// Get rest of key=value pairs
|
|
@@ -1448,19 +1445,17 @@ class Connection {
|
|
|
1448
1445
|
// value (e.g. a failed AUTH username, see auth_base) must not be
|
|
1449
1446
|
// able to inject extra header lines into Authentication-Results.
|
|
1450
1447
|
// The legitimate folding (;\r\n\t) is added by the join below.
|
|
1451
|
-
const ar_clean = (s) => String(s).replace(/[\x00-\x1f\x7f]/g, '')
|
|
1452
|
-
|
|
1453
1448
|
// if message, store it in the appropriate note
|
|
1454
1449
|
if (message) {
|
|
1455
1450
|
if (has_tran === true) {
|
|
1456
|
-
this.transaction.notes.authentication_results.push(
|
|
1451
|
+
this.transaction.notes.authentication_results.push(utils.sanitize(message))
|
|
1457
1452
|
} else {
|
|
1458
|
-
this.notes.authentication_results.push(
|
|
1453
|
+
this.notes.authentication_results.push(utils.sanitize(message))
|
|
1459
1454
|
}
|
|
1460
1455
|
}
|
|
1461
1456
|
|
|
1462
1457
|
// assemble the new header
|
|
1463
|
-
let header = [
|
|
1458
|
+
let header = [utils.sanitize(this.local.host), ...this.notes.authentication_results]
|
|
1464
1459
|
if (has_tran === true) {
|
|
1465
1460
|
header = [...header, ...this.transaction.notes.authentication_results]
|
|
1466
1461
|
}
|
|
@@ -1668,7 +1663,7 @@ class Connection {
|
|
|
1668
1663
|
}
|
|
1669
1664
|
}
|
|
1670
1665
|
}
|
|
1671
|
-
max_data_exceeded_respond(retval
|
|
1666
|
+
max_data_exceeded_respond(retval) {
|
|
1672
1667
|
// TODO: Maybe figure out what to do with other return codes
|
|
1673
1668
|
this.respond(retval === constants.denysoft ? 450 : 550, 'Message too big!', () => {
|
|
1674
1669
|
this.reset_transaction()
|
|
@@ -1703,9 +1698,11 @@ class Connection {
|
|
|
1703
1698
|
break
|
|
1704
1699
|
case constants.deny:
|
|
1705
1700
|
case constants.denydisconnect:
|
|
1701
|
+
this.transaction.results.add(res_as, { fail: msg })
|
|
1702
|
+
break
|
|
1706
1703
|
case constants.denysoft:
|
|
1707
1704
|
case constants.denysoftdisconnect:
|
|
1708
|
-
this.transaction.results.add(res_as, { fail: msg })
|
|
1705
|
+
this.transaction.results.add(res_as, { fail: msg, soft: true })
|
|
1709
1706
|
break
|
|
1710
1707
|
case constants.cont:
|
|
1711
1708
|
break
|
|
@@ -1859,6 +1856,8 @@ class Connection {
|
|
|
1859
1856
|
|
|
1860
1857
|
exports.Connection = Connection
|
|
1861
1858
|
|
|
1859
|
+
exports.cfg = cfg
|
|
1860
|
+
|
|
1862
1861
|
exports.createConnection = (client, server, cfg) => {
|
|
1863
1862
|
return new Connection(client, server, cfg)
|
|
1864
1863
|
}
|
package/contrib/bsd-rc.d/haraka
CHANGED
package/docs/CoreConfig.md
CHANGED
|
@@ -48,6 +48,8 @@ Per-connection limits and behaviours. See inline comments in the shipped
|
|
|
48
48
|
| `main.spool_after` | `-1` | Size (bytes) at which to spool the message to disk. `-1` never spools; `0` always spools. |
|
|
49
49
|
| `main.strict_rfc1869` | `false` | Reject `MAIL FROM` / `RCPT TO` that violates RFC 1869/821 (spurious spaces, missing brackets). |
|
|
50
50
|
| `main.smtputf8` | `true` | Advertise `SMTPUTF8` (RFC 6531). |
|
|
51
|
+
| `main.postel` | `false` | Liberal envelope parsing. |
|
|
52
|
+
| `haproxy.enabled` | `true` | Enable HAProxy PROXY protocol handling. Set `false` to bypass PROXY support. |
|
|
51
53
|
| `haproxy.hosts` | empty | Array of IPs/CIDRs allowed to send the PROXY protocol. See [HAProxy.md](HAProxy.md). |
|
|
52
54
|
| `headers.add_received` | `true` | Add a `Received:` header to incoming mail. |
|
|
53
55
|
| `headers.clean_auth_results` | `true` | Strip inbound `Authentication-Results:` headers before plugins run. |
|
package/docs/HAProxy.md
CHANGED
|
@@ -6,15 +6,18 @@ The PROXY v2 binary header is not currently supported.
|
|
|
6
6
|
|
|
7
7
|
## Configuration
|
|
8
8
|
|
|
9
|
-
PROXY support is
|
|
9
|
+
PROXY support is enabled by default, but inactive until trusted proxy IPs or CIDRs are listed in `connection.ini`:
|
|
10
10
|
|
|
11
11
|
```ini
|
|
12
12
|
[haproxy]
|
|
13
|
+
enabled=true
|
|
13
14
|
hosts[] = 192.0.2.4
|
|
14
15
|
hosts[] = 192.0.2.5/30
|
|
15
16
|
hosts[] = 2001:db8::1
|
|
16
17
|
```
|
|
17
18
|
|
|
19
|
+
Set `enabled=false` to disable PROXY protocol handling entirely. On SMTPS listeners this bypasses pre-TLS PROXY parsing and uses the standard implicit TLS server.
|
|
20
|
+
|
|
18
21
|
Connections from any other IP get a `DENYSOFTDISCONNECT` if they send a `PROXY` command. `DENYSOFT` is deliberate — it avoids permanently rejecting valid mail when a misconfiguration causes a legitimate proxy to fall outside the allow-list.
|
|
19
22
|
|
|
20
23
|
When a listed proxy connects, Haraka **does not** send the SMTP banner; it waits for the `PROXY` command. If none arrives within 30 seconds the connection is closed with `421 PROXY timed out`.
|