haraka 0.0.32 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. package/.claude/settings.local.json +28 -0
  2. package/.githooks/pre-commit +41 -0
  3. package/.prettierignore +6 -0
  4. package/.qlty/.gitignore +7 -0
  5. package/.qlty/configs/.shellcheckrc +1 -0
  6. package/.qlty/qlty.toml +15 -0
  7. package/CHANGELOG.md +1872 -62
  8. package/CLAUDE.md +40 -0
  9. package/CONTRIBUTORS.md +34 -0
  10. package/Dockerfile +50 -0
  11. package/GEMINI.md +38 -0
  12. package/LICENSE +2 -1
  13. package/Plugins.md +227 -0
  14. package/README.md +100 -115
  15. package/SECURITY.md +178 -0
  16. package/TODO +22 -0
  17. package/address.js +53 -0
  18. package/bin/haraka +593 -0
  19. package/bin/haraka_grep +32 -0
  20. package/config/aliases +2 -0
  21. package/config/auth_flat_file.ini +7 -0
  22. package/config/auth_vpopmaild.ini +9 -0
  23. package/config/connection.ini +79 -0
  24. package/config/delay_deny.ini +7 -0
  25. package/config/dhparams.pem +8 -0
  26. package/config/host_list +3 -0
  27. package/config/host_list_regex +6 -0
  28. package/config/http.ini +11 -0
  29. package/config/lmtp.ini +7 -0
  30. package/config/log.ini +11 -0
  31. package/config/me +1 -0
  32. package/config/outbound.bounce_message +18 -0
  33. package/config/outbound.bounce_message_html +36 -0
  34. package/config/outbound.bounce_message_image +106 -0
  35. package/config/outbound.ini +24 -0
  36. package/config/plugins +67 -0
  37. package/config/smtp.ini +37 -0
  38. package/config/smtp_bridge.ini +4 -0
  39. package/config/smtp_forward.ini +31 -0
  40. package/config/smtp_proxy.ini +27 -0
  41. package/config/tarpit.timeout +1 -0
  42. package/config/tls.ini +83 -0
  43. package/config/tls_cert.pem +23 -0
  44. package/config/tls_key.pem +28 -0
  45. package/config/watch.ini +12 -0
  46. package/config/xclient.hosts +2 -0
  47. package/connection.js +1863 -0
  48. package/contrib/Haraka.cf +6 -0
  49. package/contrib/Haraka.pm +35 -0
  50. package/contrib/bad_smtp_server.pl +25 -0
  51. package/contrib/bsd-rc.d/haraka +61 -0
  52. package/contrib/debian-init.d/haraka +87 -0
  53. package/contrib/haraka.init +96 -0
  54. package/contrib/haraka.service +23 -0
  55. package/contrib/plugin2npm.sh +81 -0
  56. package/contrib/ubuntu-upstart/haraka.conf +27 -0
  57. package/coverage/coverage-final.json +2 -0
  58. package/coverage/coverage-summary.json +33 -0
  59. package/coverage/tmp/coverage-79131-1779241025146-0.json +1 -0
  60. package/coverage/tmp/coverage-79132-1779240999690-0.json +1 -0
  61. package/coverage/tmp/coverage-79172-1779241000095-0.json +1 -0
  62. package/coverage/tmp/coverage-79210-1779241000156-0.json +1 -0
  63. package/coverage/tmp/coverage-79211-1779241000209-0.json +1 -0
  64. package/coverage/tmp/coverage-79212-1779241000266-0.json +1 -0
  65. package/coverage/tmp/coverage-79213-1779241000441-0.json +1 -0
  66. package/coverage/tmp/coverage-79214-1779241000626-0.json +1 -0
  67. package/coverage/tmp/coverage-79215-1779241000795-0.json +1 -0
  68. package/coverage/tmp/coverage-79216-1779241000965-0.json +1 -0
  69. package/coverage/tmp/coverage-79218-1779241001013-0.json +1 -0
  70. package/coverage/tmp/coverage-79219-1779241001179-0.json +1 -0
  71. package/coverage/tmp/coverage-79220-1779241006249-0.json +1 -0
  72. package/coverage/tmp/coverage-79227-1779241011453-0.json +1 -0
  73. package/coverage/tmp/coverage-79229-1779241011537-0.json +1 -0
  74. package/coverage/tmp/coverage-79230-1779241011647-0.json +1 -0
  75. package/coverage/tmp/coverage-79231-1779241011765-0.json +1 -0
  76. package/coverage/tmp/coverage-79232-1779241011841-0.json +1 -0
  77. package/coverage/tmp/coverage-79233-1779241011909-0.json +1 -0
  78. package/coverage/tmp/coverage-79234-1779241011984-0.json +1 -0
  79. package/coverage/tmp/coverage-79235-1779241012055-0.json +1 -0
  80. package/coverage/tmp/coverage-79236-1779241012230-0.json +1 -0
  81. package/coverage/tmp/coverage-79237-1779241012300-0.json +1 -0
  82. package/coverage/tmp/coverage-79238-1779241012368-0.json +1 -0
  83. package/coverage/tmp/coverage-79239-1779241012438-0.json +1 -0
  84. package/coverage/tmp/coverage-79240-1779241012511-0.json +1 -0
  85. package/coverage/tmp/coverage-79241-1779241012582-0.json +1 -0
  86. package/coverage/tmp/coverage-79242-1779241012652-0.json +1 -0
  87. package/coverage/tmp/coverage-79243-1779241012814-0.json +1 -0
  88. package/coverage/tmp/coverage-79244-1779241012931-0.json +1 -0
  89. package/coverage/tmp/coverage-79245-1779241013007-0.json +1 -0
  90. package/coverage/tmp/coverage-79246-1779241013106-0.json +1 -0
  91. package/coverage/tmp/coverage-79247-1779241013178-0.json +1 -0
  92. package/coverage/tmp/coverage-79248-1779241013244-0.json +1 -0
  93. package/coverage/tmp/coverage-79249-1779241013409-0.json +1 -0
  94. package/coverage/tmp/coverage-79250-1779241013697-0.json +1 -0
  95. package/coverage/tmp/coverage-79251-1779241013847-0.json +1 -0
  96. package/coverage/tmp/coverage-79252-1779241014288-0.json +1 -0
  97. package/coverage/tmp/coverage-79253-1779241014378-0.json +1 -0
  98. package/coverage/tmp/coverage-79254-1779241014428-0.json +1 -0
  99. package/coverage/tmp/coverage-79255-1779241021774-0.json +1 -0
  100. package/coverage/tmp/coverage-80382-1779241021949-0.json +1 -0
  101. package/coverage/tmp/coverage-80383-1779241025019-0.json +1 -0
  102. package/coverage/tmp/coverage-80384-1779241025133-0.json +1 -0
  103. package/docs/Body.md +1 -0
  104. package/docs/Config.md +1 -0
  105. package/docs/Connection.md +153 -0
  106. package/docs/CoreConfig.md +96 -0
  107. package/docs/CustomReturnCodes.md +3 -0
  108. package/docs/HAProxy.md +62 -0
  109. package/docs/Header.md +1 -0
  110. package/docs/Logging.md +129 -0
  111. package/docs/Outbound.md +210 -0
  112. package/docs/Plugins.md +372 -0
  113. package/docs/Results.md +7 -0
  114. package/docs/Transaction.md +135 -0
  115. package/docs/Tutorial.md +183 -0
  116. package/docs/deprecated/access.md +3 -0
  117. package/docs/deprecated/backscatterer.md +9 -0
  118. package/docs/deprecated/connect.rdns_access.md +53 -0
  119. package/docs/deprecated/data.headers.md +3 -0
  120. package/docs/deprecated/data.nomsgid.md +7 -0
  121. package/docs/deprecated/data.noreceived.md +11 -0
  122. package/docs/deprecated/data.rfc5322_header_checks.md +11 -0
  123. package/docs/deprecated/dkim_sign.md +97 -0
  124. package/docs/deprecated/dkim_verify.md +28 -0
  125. package/docs/deprecated/dnsbl.md +80 -0
  126. package/docs/deprecated/dnswl.md +73 -0
  127. package/docs/deprecated/lookup_rdns.strict.md +67 -0
  128. package/docs/deprecated/mail_from.access.md +52 -0
  129. package/docs/deprecated/mail_from.blocklist.md +18 -0
  130. package/docs/deprecated/mail_from.nobounces.md +8 -0
  131. package/docs/deprecated/rcpt_to.access.md +53 -0
  132. package/docs/deprecated/rcpt_to.blocklist.md +18 -0
  133. package/docs/deprecated/rcpt_to.routes.md +3 -0
  134. package/docs/deprecated/rdns.regexp.md +30 -0
  135. package/docs/plugins/aliases.md +3 -0
  136. package/docs/plugins/auth/auth_bridge.md +34 -0
  137. package/docs/plugins/auth/auth_ldap.md +4 -0
  138. package/docs/plugins/auth/auth_proxy.md +36 -0
  139. package/docs/plugins/auth/auth_vpopmaild.md +33 -0
  140. package/docs/plugins/auth/flat_file.md +40 -0
  141. package/docs/plugins/block_me.md +18 -0
  142. package/docs/plugins/data.signatures.md +11 -0
  143. package/docs/plugins/delay_deny.md +23 -0
  144. package/docs/plugins/max_unrecognized_commands.md +6 -0
  145. package/docs/plugins/prevent_credential_leaks.md +22 -0
  146. package/docs/plugins/process_title.md +42 -0
  147. package/docs/plugins/queue/deliver.md +3 -0
  148. package/docs/plugins/queue/discard.md +32 -0
  149. package/docs/plugins/queue/lmtp.md +24 -0
  150. package/docs/plugins/queue/qmail-queue.md +16 -0
  151. package/docs/plugins/queue/quarantine.md +87 -0
  152. package/docs/plugins/queue/smtp_bridge.md +32 -0
  153. package/docs/plugins/queue/smtp_forward.md +127 -0
  154. package/docs/plugins/queue/smtp_proxy.md +68 -0
  155. package/docs/plugins/queue/test.md +7 -0
  156. package/docs/plugins/rcpt_to.in_host_list.md +34 -0
  157. package/docs/plugins/rcpt_to.max_count.md +3 -0
  158. package/docs/plugins/record_envelope_addresses.md +20 -0
  159. package/docs/plugins/relay.md +3 -0
  160. package/docs/plugins/reseed_rng.md +16 -0
  161. package/docs/plugins/status.md +41 -0
  162. package/docs/plugins/tarpit.md +50 -0
  163. package/docs/plugins/tls.md +235 -0
  164. package/docs/plugins/toobusy.md +27 -0
  165. package/docs/plugins/xclient.md +10 -0
  166. package/docs/tutorials/Migrating_from_v1_to_v2.md +96 -0
  167. package/docs/tutorials/SettingUpOutbound.md +62 -0
  168. package/eslint.config.mjs +2 -0
  169. package/haraka.js +74 -0
  170. package/haraka.sh +2 -0
  171. package/http/html/404.html +58 -0
  172. package/http/html/index.html +47 -0
  173. package/http/package.json +21 -0
  174. package/line_socket.js +24 -0
  175. package/logger.js +322 -0
  176. package/outbound/client_pool.js +59 -0
  177. package/outbound/config.js +134 -0
  178. package/outbound/hmail.js +1504 -0
  179. package/outbound/index.js +349 -0
  180. package/outbound/qfile.js +93 -0
  181. package/outbound/queue.js +399 -0
  182. package/outbound/tls.js +85 -0
  183. package/outbound/todo.js +17 -0
  184. package/package.json +91 -29
  185. package/plugins/.eslintrc.yaml +3 -0
  186. package/plugins/auth/auth_base.js +261 -0
  187. package/plugins/auth/auth_bridge.js +20 -0
  188. package/plugins/auth/auth_proxy.js +227 -0
  189. package/plugins/auth/auth_vpopmaild.js +162 -0
  190. package/plugins/auth/flat_file.js +44 -0
  191. package/plugins/block_me.js +88 -0
  192. package/plugins/data.signatures.js +30 -0
  193. package/plugins/delay_deny.js +153 -0
  194. package/plugins/prevent_credential_leaks.js +61 -0
  195. package/plugins/process_title.js +197 -0
  196. package/plugins/profile.js +11 -0
  197. package/plugins/queue/deliver.js +12 -0
  198. package/plugins/queue/discard.js +27 -0
  199. package/plugins/queue/lmtp.js +45 -0
  200. package/plugins/queue/qmail-queue.js +93 -0
  201. package/plugins/queue/quarantine.js +133 -0
  202. package/plugins/queue/smtp_bridge.js +45 -0
  203. package/plugins/queue/smtp_forward.js +371 -0
  204. package/plugins/queue/smtp_proxy.js +142 -0
  205. package/plugins/queue/test.js +15 -0
  206. package/plugins/rcpt_to.host_list_base.js +65 -0
  207. package/plugins/rcpt_to.in_host_list.js +56 -0
  208. package/plugins/record_envelope_addresses.js +17 -0
  209. package/plugins/reseed_rng.js +7 -0
  210. package/plugins/status.js +274 -0
  211. package/plugins/tarpit.js +45 -0
  212. package/plugins/tls.js +164 -0
  213. package/plugins/toobusy.js +47 -0
  214. package/plugins/xclient.js +124 -0
  215. package/plugins.js +604 -0
  216. package/queue/1772642154987_1775581346001_4_82235_TGwgfd_2_mattbook-m3.home.simerson.net +0 -0
  217. package/run_tests +11 -0
  218. package/server.js +827 -0
  219. package/smtp_client.js +504 -0
  220. package/test/.eslintrc.yaml +11 -0
  221. package/test/config/auth_flat_file.ini +5 -0
  222. package/test/config/block_me.recipient +1 -0
  223. package/test/config/block_me.senders +1 -0
  224. package/test/config/dhparams.pem +8 -0
  225. package/test/config/host_list +2 -0
  226. package/test/config/outbound_tls_cert.pem +1 -0
  227. package/test/config/outbound_tls_key.pem +1 -0
  228. package/test/config/plugins +7 -0
  229. package/test/config/smtp.ini +11 -0
  230. package/test/config/smtp_forward.ini +30 -0
  231. package/test/config/tls/example.com/_.example.com.key +28 -0
  232. package/test/config/tls/example.com/example.com.crt +25 -0
  233. package/test/config/tls/haraka.local.pem +51 -0
  234. package/test/config/tls.ini +45 -0
  235. package/test/config/tls_cert.pem +21 -0
  236. package/test/config/tls_key.pem +28 -0
  237. package/test/connection.js +817 -0
  238. package/test/fixtures/haproxy_allowed/config/connection.ini +3 -0
  239. package/test/fixtures/haproxy_disabled/config/connection.ini +3 -0
  240. package/test/fixtures/haproxy_untrusted/config/connection.ini +3 -0
  241. package/test/fixtures/line_socket.js +21 -0
  242. package/test/fixtures/todo_qfile.txt +0 -0
  243. package/test/fixtures/util_hmailitem.js +156 -0
  244. package/test/installation/config/test-plugin-flat +1 -0
  245. package/test/installation/config/test-plugin.ini +10 -0
  246. package/test/installation/config/tls.ini +1 -0
  247. package/test/installation/node_modules/load_first/index.js +5 -0
  248. package/test/installation/node_modules/load_first/package.json +11 -0
  249. package/test/installation/node_modules/test-plugin/config/test-plugin-flat +1 -0
  250. package/test/installation/node_modules/test-plugin/config/test-plugin.ini +9 -0
  251. package/test/installation/node_modules/test-plugin/package.json +5 -0
  252. package/test/installation/node_modules/test-plugin/test-plugin.js +5 -0
  253. package/test/installation/plugins/base_plugin.js +3 -0
  254. package/test/installation/plugins/folder_plugin/index.js +3 -0
  255. package/test/installation/plugins/folder_plugin/package.json +11 -0
  256. package/test/installation/plugins/inherits.js +7 -0
  257. package/test/installation/plugins/load_first.js +3 -0
  258. package/test/installation/plugins/plugin.js +1 -0
  259. package/test/installation/plugins/tls.js +3 -0
  260. package/test/logger.js +217 -0
  261. package/test/loud/config/dhparams.pem +0 -0
  262. package/test/loud/config/tls/goobered.pem +45 -0
  263. package/test/loud/config/tls.ini +43 -0
  264. package/test/mail_specimen/base64-root-part.txt +23 -0
  265. package/test/mail_specimen/varied-fold-lengths-preserve-data.txt +283 -0
  266. package/test/outbound/bounce_net_errors.js +133 -0
  267. package/test/outbound/bounce_rfc3464.js +226 -0
  268. package/test/outbound/hmail.js +210 -0
  269. package/test/outbound/index.js +385 -0
  270. package/test/outbound/qfile.js +124 -0
  271. package/test/outbound/queue.js +325 -0
  272. package/test/plugins/auth/auth_base.js +620 -0
  273. package/test/plugins/auth/auth_bridge.js +80 -0
  274. package/test/plugins/auth/auth_vpopmaild.js +81 -0
  275. package/test/plugins/auth/flat_file.js +123 -0
  276. package/test/plugins/block_me.js +141 -0
  277. package/test/plugins/data.signatures.js +111 -0
  278. package/test/plugins/delay_deny.js +262 -0
  279. package/test/plugins/prevent_credential_leaks.js +174 -0
  280. package/test/plugins/process_title.js +141 -0
  281. package/test/plugins/queue/deliver.js +98 -0
  282. package/test/plugins/queue/discard.js +78 -0
  283. package/test/plugins/queue/lmtp.js +137 -0
  284. package/test/plugins/queue/qmail-queue.js +98 -0
  285. package/test/plugins/queue/quarantine.js +80 -0
  286. package/test/plugins/queue/smtp_bridge.js +152 -0
  287. package/test/plugins/queue/smtp_forward.js +1023 -0
  288. package/test/plugins/queue/smtp_proxy.js +138 -0
  289. package/test/plugins/rcpt_to.host_list_base.js +102 -0
  290. package/test/plugins/rcpt_to.in_host_list.js +186 -0
  291. package/test/plugins/record_envelope_addresses.js +66 -0
  292. package/test/plugins/reseed_rng.js +34 -0
  293. package/test/plugins/status.js +207 -0
  294. package/test/plugins/tarpit.js +90 -0
  295. package/test/plugins/tls.js +86 -0
  296. package/test/plugins/toobusy.js +21 -0
  297. package/test/plugins/xclient.js +119 -0
  298. package/test/plugins.js +230 -0
  299. package/test/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_fixed +0 -0
  300. package/test/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka +0 -0
  301. package/test/queue/1508269674999_1508269674999_0_34002_socVUF_1_haraka +0 -0
  302. package/test/queue/1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka +0 -0
  303. package/test/queue/zero-length +0 -0
  304. package/test/server.js +1012 -0
  305. package/test/smtp_client.js +1303 -0
  306. package/test/tls_socket.js +321 -0
  307. package/test/transaction.js +554 -0
  308. package/tls_socket.js +771 -0
  309. package/transaction.js +267 -0
  310. package/lib/index.js +0 -371
package/plugins/tls.js ADDED
@@ -0,0 +1,164 @@
1
+ 'use strict'
2
+ // TLS is built into Haraka. This plugin conditionally advertises STARTTLS.
3
+ // see 'haraka -h tls' for help
4
+
5
+ /* global server */
6
+
7
+ const tls_socket = require('../tls_socket')
8
+
9
+ // exported so tests can override config dir
10
+ exports.net_utils = require('haraka-net-utils')
11
+
12
+ exports.register = function () {
13
+ tls_socket.load_tls_ini()
14
+
15
+ if (tls_socket.cfg.redis.disable_for_failed_hosts) this.logdebug('Will disable STARTTLS for failing TLS hosts')
16
+
17
+ this.register_hook('capabilities', 'advertise_starttls')
18
+ this.register_hook('unrecognized_command', 'upgrade_connection')
19
+ }
20
+
21
+ exports.shutdown = () => {
22
+ if (tls_socket.shutdown) tls_socket.shutdown()
23
+ }
24
+
25
+ exports.advertise_starttls = function (next, connection) {
26
+ // if no TLS setup incomplete/invalid, don't advertise
27
+ if (!tls_socket.tls_valid) {
28
+ this.logerror('no valid TLS config')
29
+ return next()
30
+ }
31
+
32
+ /* Caution: do not advertise STARTTLS if already TLS upgraded */
33
+ if (connection.tls.enabled) return next()
34
+
35
+ if (this.net_utils.ip_in_list(tls_socket.cfg.no_tls_hosts, connection.remote.ip)) {
36
+ return next()
37
+ }
38
+
39
+ function enable_tls() {
40
+ connection.capabilities.push('STARTTLS')
41
+ connection.tls.advertised = true
42
+ next()
43
+ }
44
+
45
+ // check if local port is excluded from starttls advertisement
46
+ if (tls_socket.cfg.main.no_starttls_ports.includes(connection.local.port)) return next()
47
+
48
+ // exclude port 587 from NO-GO
49
+ if (connection.local.port === 587) return enable_tls()
50
+
51
+ if (!tls_socket.cfg.redis || !server.notes.redis) {
52
+ return enable_tls()
53
+ }
54
+
55
+ const { redis } = server.notes
56
+ const dbkey = `no_tls|${connection.remote.ip}`
57
+
58
+ redis
59
+ .get(dbkey)
60
+ .then((dbr) => {
61
+ if (!dbr) return enable_tls()
62
+ connection.results.add(this, { msg: 'no_tls' })
63
+ next(CONT, 'STARTTLS disabled because previous attempt failed')
64
+ })
65
+ .catch((err) => {
66
+ connection.results.add(this, { err })
67
+ enable_tls()
68
+ })
69
+ }
70
+
71
+ exports.set_notls = function (connection) {
72
+ if (!tls_socket.cfg.redis.disable_for_failed_hosts) return
73
+ if (!server.notes.redis) return
74
+
75
+ const expiry = tls_socket.cfg.redis.disable_inbound_expiry || 3600
76
+
77
+ this.lognotice(connection, `STARTTLS failed. Marking ${connection.remote.ip} as non-TLS host for ${expiry} seconds`)
78
+
79
+ server.notes.redis.setEx(`no_tls|${connection.remote.ip}`, expiry, new Date().toISOString())
80
+ }
81
+
82
+ exports.upgrade_connection = function (next, connection, params) {
83
+ if (!connection.tls.advertised) return next()
84
+
85
+ /* Watch for STARTTLS directive from client. */
86
+ if (params[0].toUpperCase() !== 'STARTTLS') return next()
87
+
88
+ // RFC 3207 §4: discard any plaintext the client pipelined after
89
+ // STARTTLS. Otherwise connection.respond() restores state to CMD and
90
+ // re-enters _process_data(), parsing and executing those buffered
91
+ // cleartext commands before the TLS handshake completes (STARTTLS
92
+ // command injection). Mirrors the buffer-nuke already used for
93
+ // SSL-over-plaintext detection in connection.process_line().
94
+ connection.current_data = null
95
+
96
+ /* Respond to STARTTLS command. */
97
+ connection.respond(220, 'Go ahead.')
98
+
99
+ const plugin = this
100
+ let called_next = false
101
+ // adjust plugin.timeout like so: echo '45' > config/tls.timeout
102
+ const timeout = plugin.timeout - 1
103
+
104
+ function nextOnce(disconnected) {
105
+ if (called_next) return
106
+ called_next = true
107
+ clearTimeout(connection.notes.tls_timer)
108
+ if (!disconnected) connection.lognotice(plugin, 'timeout setting up TLS')
109
+ plugin.set_notls(connection)
110
+ return next(DENYSOFTDISCONNECT)
111
+ }
112
+
113
+ if (timeout && timeout > 0) {
114
+ connection.notes.tls_timer = setTimeout(nextOnce, timeout * 1000)
115
+ }
116
+
117
+ connection.notes.cleanUpDisconnect = nextOnce
118
+
119
+ /* Upgrade the connection to TLS. */
120
+ connection.client.upgrade((verified, verifyError, cert, cipher) => {
121
+ if (called_next) return
122
+ clearTimeout(connection.notes.tls_timer)
123
+ called_next = true
124
+ connection.reset_transaction(() => {
125
+ connection.setTLS({
126
+ cipher,
127
+ verified,
128
+ verifyError,
129
+ peerCertificate: cert,
130
+ })
131
+
132
+ connection.results.add(plugin, connection.tls)
133
+ plugin.emit_upgrade_msg(connection, verified, verifyError, cert, cipher)
134
+ next(OK)
135
+ })
136
+ })
137
+ }
138
+
139
+ exports.hook_disconnect = (next, connection) => {
140
+ if (connection.notes.cleanUpDisconnect) {
141
+ connection.notes.cleanUpDisconnect(true)
142
+ }
143
+ next()
144
+ }
145
+
146
+ exports.emit_upgrade_msg = function (conn, verified, verifyError, cert, cipher) {
147
+ let msg = 'secured:'
148
+ if (cipher) {
149
+ msg += ` cipher=${cipher.name} version=${cipher.version}`
150
+ }
151
+ msg += ` verified=${verified}`
152
+ if (verifyError) msg += ` error="${verifyError}"`
153
+ if (cert) {
154
+ if (cert.subject) {
155
+ msg += ` cn="${cert.subject.CN}" organization="${cert.subject.O}"`
156
+ }
157
+ if (cert.issuer) msg += ` issuer="${cert.issuer.O}"`
158
+ if (cert.valid_to) msg += ` expires="${cert.valid_to}"`
159
+ if (cert.fingerprint) msg += ` fingerprint=${cert.fingerprint}`
160
+ }
161
+
162
+ conn.loginfo(this, msg)
163
+ return msg
164
+ }
@@ -0,0 +1,47 @@
1
+ // Stop accepting new connections when we are too busy
2
+
3
+ let toobusy
4
+ let was_busy = false
5
+
6
+ exports.register = function () {
7
+ try {
8
+ toobusy = require('toobusy-js')
9
+ } catch (e) {
10
+ this.logerror(e)
11
+ this.logerror("try: 'npm install -g toobusy-js'")
12
+ return
13
+ }
14
+
15
+ this.loadConfig()
16
+
17
+ this.register_hook('connect', 'check_busy', -100)
18
+ }
19
+
20
+ exports.loadConfig = function () {
21
+ let maxLag = this.config.get('toobusy.maxlag', 'value', () => {
22
+ this.loadConfig()
23
+ })
24
+
25
+ maxLag = parseInt(maxLag)
26
+ if (maxLag) {
27
+ // This will throw an exception on error
28
+ toobusy.maxLag(maxLag)
29
+ }
30
+ }
31
+
32
+ exports.check_busy = function (next) {
33
+ if (!toobusy()) {
34
+ was_busy = false
35
+ return next()
36
+ }
37
+
38
+ if (!was_busy) {
39
+ was_busy = true
40
+ // Log a CRIT error at the first occurrence
41
+ const currentLag = toobusy.lag()
42
+ const maxLag = toobusy.maxLag()
43
+ this.logcrit(`deferring connections: lag=${currentLag} max=${maxLag}`)
44
+ }
45
+
46
+ return next(DENYSOFTDISCONNECT, 'Too busy; please try again later')
47
+ }
@@ -0,0 +1,124 @@
1
+ // Implementation of XCLIENT protocol
2
+ // See http://www.postfix.org/XCLIENT_README.html
3
+
4
+ const net = require('node:net')
5
+
6
+ const utils = require('haraka-utils')
7
+ const DSN = require('haraka-dsn')
8
+ let allowed_hosts = {}
9
+
10
+ exports.register = function () {
11
+ this.load_xclient_hosts()
12
+ }
13
+
14
+ exports.load_xclient_hosts = function () {
15
+ const cfg = this.config.get('xclient.hosts', 'list', () => {
16
+ this.load_xclient_hosts()
17
+ })
18
+ const ah = {}
19
+ for (const i in cfg) {
20
+ ah[cfg[i]] = true
21
+ }
22
+ allowed_hosts = ah
23
+ }
24
+
25
+ function xclient_allowed(ip) {
26
+ return !!(ip === '127.0.0.1' || ip === '::1' || allowed_hosts[ip])
27
+ }
28
+
29
+ exports.hook_capabilities = (next, connection) => {
30
+ if (xclient_allowed(connection.remote.ip)) {
31
+ connection.capabilities.push('XCLIENT NAME ADDR PROTO HELO LOGIN')
32
+ }
33
+ next()
34
+ }
35
+
36
+ exports.hook_unrecognized_command = function (next, connection, params) {
37
+ if (params[0] !== 'XCLIENT') return next()
38
+
39
+ // XCLIENT is not allowed after transaction start
40
+ if (connection?.transaction) {
41
+ return next(DENY, DSN.proto_unspecified('Mail transaction in progress', 503))
42
+ }
43
+
44
+ if (!xclient_allowed(connection?.remote?.ip)) {
45
+ return next(DENY, DSN.proto_unspecified('Not authorized', 550))
46
+ }
47
+
48
+ // If we get here - the client is allowed to use XCLIENT
49
+ // Process arguments
50
+ const args = new String(params[1]).toLowerCase().split(/ /)
51
+ const xclient = {}
52
+ for (const arg of args) {
53
+ const match = /^([^=]+)=([^ ]+)/.exec(arg)
54
+ if (match) {
55
+ connection.logdebug(this, `found key=${match[1]} value=${match[2]}`)
56
+ switch (match[1]) {
57
+ case 'destaddr':
58
+ case 'addr': {
59
+ // IPv6 is prefixed in the XCLIENT protocol
60
+ let ipv6
61
+ if ((ipv6 = /^IPV6:(.+)$/i.exec(match[2]))) {
62
+ // Validate
63
+ if (net.isIPv6(ipv6[1])) {
64
+ xclient[match[1]] = ipv6[1]
65
+ }
66
+ } else if (!/\[UNAVAILABLE\]/i.test(match[2])) {
67
+ // IPv4
68
+ if (net.isIPv4(match[2])) {
69
+ xclient[match[1]] = match[2]
70
+ }
71
+ }
72
+ break
73
+ }
74
+ case 'proto':
75
+ // SMTP or ESMTP
76
+ if (/^e?smtp/i.test(match[2])) {
77
+ xclient[match[1]] = match[2]
78
+ }
79
+ break
80
+ case 'name':
81
+ case 'port':
82
+ case 'helo':
83
+ case 'login':
84
+ case 'destport':
85
+ if (!/\[(UNAVAILABLE|TEMPUNAVAIL)\]/i.test(match[2])) {
86
+ xclient[match[1]] = match[2]
87
+ }
88
+ break
89
+ default:
90
+ connection.logwarn(this, `unknown argument: ${arg}`)
91
+ }
92
+ } else {
93
+ connection.logwarn(this, `unknown argument: ${arg}`)
94
+ }
95
+ }
96
+
97
+ // Abort if we don't have a valid IP address
98
+ if (!xclient.addr) {
99
+ return next(DENY, DSN.proto_invalid_cmd_args('No valid IP address found', 501))
100
+ }
101
+
102
+ // Apply changes
103
+ const new_uuid = utils.uuid()
104
+ connection.loginfo(this, `new uuid=${new_uuid}`)
105
+ connection.uuid = new_uuid
106
+ connection.reset_transaction()
107
+ connection.relaying = false
108
+ connection.set('remote.ip', xclient.addr)
109
+ connection.set('remote.host', xclient.name ? xclient.name : undefined)
110
+ connection.set('remote.login', xclient.login ? xclient.login : undefined)
111
+ connection.set('hello.host', xclient.helo ? xclient.helo : undefined)
112
+ connection.set('local.ip', xclient.destaddr ? xclient.destaddr : undefined)
113
+ // parseInt so downstream numeric checks (e.g. the 587/465 auth-required
114
+ // gate in connection.cmd_mail) work; matches the PROXY path.
115
+ connection.set('local.port', xclient.destport ? parseInt(xclient.destport, 10) : undefined)
116
+ if (xclient.proto) {
117
+ connection.set('hello', 'verb', xclient.proto === 'esmtp' ? 'EHLO' : 'HELO')
118
+ }
119
+ connection.esmtp = xclient.proto === 'esmtp'
120
+ connection.xclient = true
121
+ if (!xclient.name) return next(NEXT_HOOK, 'lookup_rdns')
122
+
123
+ next(NEXT_HOOK, 'connect')
124
+ }