haraka 0.0.33 → 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.
- package/.claude/settings.local.json +28 -0
- package/.githooks/pre-commit +41 -0
- package/.prettierignore +6 -0
- package/.qlty/.gitignore +7 -0
- package/.qlty/configs/.shellcheckrc +1 -0
- package/.qlty/qlty.toml +15 -0
- package/CHANGELOG.md +1894 -0
- package/CLAUDE.md +40 -0
- package/CONTRIBUTORS.md +34 -0
- package/Dockerfile +50 -0
- package/GEMINI.md +38 -0
- package/LICENSE +22 -0
- package/Plugins.md +227 -0
- package/README.md +119 -4
- package/SECURITY.md +178 -0
- package/TODO +22 -0
- package/address.js +53 -0
- package/bin/haraka +593 -0
- package/bin/haraka_grep +32 -0
- package/config/aliases +2 -0
- package/config/auth_flat_file.ini +7 -0
- package/config/auth_vpopmaild.ini +9 -0
- package/config/connection.ini +79 -0
- package/config/delay_deny.ini +7 -0
- package/config/dhparams.pem +8 -0
- package/config/host_list +3 -0
- package/config/host_list_regex +6 -0
- package/config/http.ini +11 -0
- package/config/lmtp.ini +7 -0
- package/config/log.ini +11 -0
- package/config/me +1 -0
- package/config/outbound.bounce_message +18 -0
- package/config/outbound.bounce_message_html +36 -0
- package/config/outbound.bounce_message_image +106 -0
- package/config/outbound.ini +24 -0
- package/config/plugins +67 -0
- package/config/smtp.ini +37 -0
- package/config/smtp_bridge.ini +4 -0
- package/config/smtp_forward.ini +31 -0
- package/config/smtp_proxy.ini +27 -0
- package/config/tarpit.timeout +1 -0
- package/config/tls.ini +83 -0
- package/config/tls_cert.pem +23 -0
- package/config/tls_key.pem +28 -0
- package/config/watch.ini +12 -0
- package/config/xclient.hosts +2 -0
- package/connection.js +1863 -0
- package/contrib/Haraka.cf +6 -0
- package/contrib/Haraka.pm +35 -0
- package/contrib/bad_smtp_server.pl +25 -0
- package/contrib/bsd-rc.d/haraka +61 -0
- package/contrib/debian-init.d/haraka +87 -0
- package/contrib/haraka.init +96 -0
- package/contrib/haraka.service +23 -0
- package/contrib/plugin2npm.sh +81 -0
- package/contrib/ubuntu-upstart/haraka.conf +27 -0
- package/coverage/coverage-final.json +2 -0
- package/coverage/coverage-summary.json +33 -0
- package/coverage/tmp/coverage-79131-1779241025146-0.json +1 -0
- package/coverage/tmp/coverage-79132-1779240999690-0.json +1 -0
- package/coverage/tmp/coverage-79172-1779241000095-0.json +1 -0
- package/coverage/tmp/coverage-79210-1779241000156-0.json +1 -0
- package/coverage/tmp/coverage-79211-1779241000209-0.json +1 -0
- package/coverage/tmp/coverage-79212-1779241000266-0.json +1 -0
- package/coverage/tmp/coverage-79213-1779241000441-0.json +1 -0
- package/coverage/tmp/coverage-79214-1779241000626-0.json +1 -0
- package/coverage/tmp/coverage-79215-1779241000795-0.json +1 -0
- package/coverage/tmp/coverage-79216-1779241000965-0.json +1 -0
- package/coverage/tmp/coverage-79218-1779241001013-0.json +1 -0
- package/coverage/tmp/coverage-79219-1779241001179-0.json +1 -0
- package/coverage/tmp/coverage-79220-1779241006249-0.json +1 -0
- package/coverage/tmp/coverage-79227-1779241011453-0.json +1 -0
- package/coverage/tmp/coverage-79229-1779241011537-0.json +1 -0
- package/coverage/tmp/coverage-79230-1779241011647-0.json +1 -0
- package/coverage/tmp/coverage-79231-1779241011765-0.json +1 -0
- package/coverage/tmp/coverage-79232-1779241011841-0.json +1 -0
- package/coverage/tmp/coverage-79233-1779241011909-0.json +1 -0
- package/coverage/tmp/coverage-79234-1779241011984-0.json +1 -0
- package/coverage/tmp/coverage-79235-1779241012055-0.json +1 -0
- package/coverage/tmp/coverage-79236-1779241012230-0.json +1 -0
- package/coverage/tmp/coverage-79237-1779241012300-0.json +1 -0
- package/coverage/tmp/coverage-79238-1779241012368-0.json +1 -0
- package/coverage/tmp/coverage-79239-1779241012438-0.json +1 -0
- package/coverage/tmp/coverage-79240-1779241012511-0.json +1 -0
- package/coverage/tmp/coverage-79241-1779241012582-0.json +1 -0
- package/coverage/tmp/coverage-79242-1779241012652-0.json +1 -0
- package/coverage/tmp/coverage-79243-1779241012814-0.json +1 -0
- package/coverage/tmp/coverage-79244-1779241012931-0.json +1 -0
- package/coverage/tmp/coverage-79245-1779241013007-0.json +1 -0
- package/coverage/tmp/coverage-79246-1779241013106-0.json +1 -0
- package/coverage/tmp/coverage-79247-1779241013178-0.json +1 -0
- package/coverage/tmp/coverage-79248-1779241013244-0.json +1 -0
- package/coverage/tmp/coverage-79249-1779241013409-0.json +1 -0
- package/coverage/tmp/coverage-79250-1779241013697-0.json +1 -0
- package/coverage/tmp/coverage-79251-1779241013847-0.json +1 -0
- package/coverage/tmp/coverage-79252-1779241014288-0.json +1 -0
- package/coverage/tmp/coverage-79253-1779241014378-0.json +1 -0
- package/coverage/tmp/coverage-79254-1779241014428-0.json +1 -0
- package/coverage/tmp/coverage-79255-1779241021774-0.json +1 -0
- package/coverage/tmp/coverage-80382-1779241021949-0.json +1 -0
- package/coverage/tmp/coverage-80383-1779241025019-0.json +1 -0
- package/coverage/tmp/coverage-80384-1779241025133-0.json +1 -0
- package/docs/Body.md +1 -0
- package/docs/Config.md +1 -0
- package/docs/Connection.md +153 -0
- package/docs/CoreConfig.md +96 -0
- package/docs/CustomReturnCodes.md +3 -0
- package/docs/HAProxy.md +62 -0
- package/docs/Header.md +1 -0
- package/docs/Logging.md +129 -0
- package/docs/Outbound.md +210 -0
- package/docs/Plugins.md +372 -0
- package/docs/Results.md +7 -0
- package/docs/Transaction.md +135 -0
- package/docs/Tutorial.md +183 -0
- package/docs/deprecated/access.md +3 -0
- package/docs/deprecated/backscatterer.md +9 -0
- package/docs/deprecated/connect.rdns_access.md +53 -0
- package/docs/deprecated/data.headers.md +3 -0
- package/docs/deprecated/data.nomsgid.md +7 -0
- package/docs/deprecated/data.noreceived.md +11 -0
- package/docs/deprecated/data.rfc5322_header_checks.md +11 -0
- package/docs/deprecated/dkim_sign.md +97 -0
- package/docs/deprecated/dkim_verify.md +28 -0
- package/docs/deprecated/dnsbl.md +80 -0
- package/docs/deprecated/dnswl.md +73 -0
- package/docs/deprecated/lookup_rdns.strict.md +67 -0
- package/docs/deprecated/mail_from.access.md +52 -0
- package/docs/deprecated/mail_from.blocklist.md +18 -0
- package/docs/deprecated/mail_from.nobounces.md +8 -0
- package/docs/deprecated/rcpt_to.access.md +53 -0
- package/docs/deprecated/rcpt_to.blocklist.md +18 -0
- package/docs/deprecated/rcpt_to.routes.md +3 -0
- package/docs/deprecated/rdns.regexp.md +30 -0
- package/docs/plugins/aliases.md +3 -0
- package/docs/plugins/auth/auth_bridge.md +34 -0
- package/docs/plugins/auth/auth_ldap.md +4 -0
- package/docs/plugins/auth/auth_proxy.md +36 -0
- package/docs/plugins/auth/auth_vpopmaild.md +33 -0
- package/docs/plugins/auth/flat_file.md +40 -0
- package/docs/plugins/block_me.md +18 -0
- package/docs/plugins/data.signatures.md +11 -0
- package/docs/plugins/delay_deny.md +23 -0
- package/docs/plugins/max_unrecognized_commands.md +6 -0
- package/docs/plugins/prevent_credential_leaks.md +22 -0
- package/docs/plugins/process_title.md +42 -0
- package/docs/plugins/queue/deliver.md +3 -0
- package/docs/plugins/queue/discard.md +32 -0
- package/docs/plugins/queue/lmtp.md +24 -0
- package/docs/plugins/queue/qmail-queue.md +16 -0
- package/docs/plugins/queue/quarantine.md +87 -0
- package/docs/plugins/queue/smtp_bridge.md +32 -0
- package/docs/plugins/queue/smtp_forward.md +127 -0
- package/docs/plugins/queue/smtp_proxy.md +68 -0
- package/docs/plugins/queue/test.md +7 -0
- package/docs/plugins/rcpt_to.in_host_list.md +34 -0
- package/docs/plugins/rcpt_to.max_count.md +3 -0
- package/docs/plugins/record_envelope_addresses.md +20 -0
- package/docs/plugins/relay.md +3 -0
- package/docs/plugins/reseed_rng.md +16 -0
- package/docs/plugins/status.md +41 -0
- package/docs/plugins/tarpit.md +50 -0
- package/docs/plugins/tls.md +235 -0
- package/docs/plugins/toobusy.md +27 -0
- package/docs/plugins/xclient.md +10 -0
- package/docs/tutorials/Migrating_from_v1_to_v2.md +96 -0
- package/docs/tutorials/SettingUpOutbound.md +62 -0
- package/eslint.config.mjs +2 -0
- package/haraka.js +74 -0
- package/haraka.sh +2 -0
- package/http/html/404.html +58 -0
- package/http/html/index.html +47 -0
- package/http/package.json +21 -0
- package/line_socket.js +24 -0
- package/logger.js +322 -0
- package/outbound/client_pool.js +59 -0
- package/outbound/config.js +134 -0
- package/outbound/hmail.js +1504 -0
- package/outbound/index.js +349 -0
- package/outbound/qfile.js +93 -0
- package/outbound/queue.js +399 -0
- package/outbound/tls.js +85 -0
- package/outbound/todo.js +17 -0
- package/package.json +99 -4
- package/plugins/.eslintrc.yaml +3 -0
- package/plugins/auth/auth_base.js +261 -0
- package/plugins/auth/auth_bridge.js +20 -0
- package/plugins/auth/auth_proxy.js +227 -0
- package/plugins/auth/auth_vpopmaild.js +162 -0
- package/plugins/auth/flat_file.js +44 -0
- package/plugins/block_me.js +88 -0
- package/plugins/data.signatures.js +30 -0
- package/plugins/delay_deny.js +153 -0
- package/plugins/prevent_credential_leaks.js +61 -0
- package/plugins/process_title.js +197 -0
- package/plugins/profile.js +11 -0
- package/plugins/queue/deliver.js +12 -0
- package/plugins/queue/discard.js +27 -0
- package/plugins/queue/lmtp.js +45 -0
- package/plugins/queue/qmail-queue.js +93 -0
- package/plugins/queue/quarantine.js +133 -0
- package/plugins/queue/smtp_bridge.js +45 -0
- package/plugins/queue/smtp_forward.js +371 -0
- package/plugins/queue/smtp_proxy.js +142 -0
- package/plugins/queue/test.js +15 -0
- package/plugins/rcpt_to.host_list_base.js +65 -0
- package/plugins/rcpt_to.in_host_list.js +56 -0
- package/plugins/record_envelope_addresses.js +17 -0
- package/plugins/reseed_rng.js +7 -0
- package/plugins/status.js +274 -0
- package/plugins/tarpit.js +45 -0
- package/plugins/tls.js +164 -0
- package/plugins/toobusy.js +47 -0
- package/plugins/xclient.js +124 -0
- package/plugins.js +604 -0
- package/queue/1772642154987_1775581346001_4_82235_TGwgfd_2_mattbook-m3.home.simerson.net +0 -0
- package/run_tests +11 -0
- package/server.js +827 -0
- package/smtp_client.js +504 -0
- package/test/.eslintrc.yaml +11 -0
- package/test/config/auth_flat_file.ini +5 -0
- package/test/config/block_me.recipient +1 -0
- package/test/config/block_me.senders +1 -0
- package/test/config/dhparams.pem +8 -0
- package/test/config/host_list +2 -0
- package/test/config/outbound_tls_cert.pem +1 -0
- package/test/config/outbound_tls_key.pem +1 -0
- package/test/config/plugins +7 -0
- package/test/config/smtp.ini +11 -0
- package/test/config/smtp_forward.ini +30 -0
- package/test/config/tls/example.com/_.example.com.key +28 -0
- package/test/config/tls/example.com/example.com.crt +25 -0
- package/test/config/tls/haraka.local.pem +51 -0
- package/test/config/tls.ini +45 -0
- package/test/config/tls_cert.pem +21 -0
- package/test/config/tls_key.pem +28 -0
- package/test/connection.js +817 -0
- 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 +21 -0
- package/test/fixtures/todo_qfile.txt +0 -0
- package/test/fixtures/util_hmailitem.js +156 -0
- package/test/installation/config/test-plugin-flat +1 -0
- package/test/installation/config/test-plugin.ini +10 -0
- package/test/installation/config/tls.ini +1 -0
- package/test/installation/node_modules/load_first/index.js +5 -0
- package/test/installation/node_modules/load_first/package.json +11 -0
- package/test/installation/node_modules/test-plugin/config/test-plugin-flat +1 -0
- package/test/installation/node_modules/test-plugin/config/test-plugin.ini +9 -0
- package/test/installation/node_modules/test-plugin/package.json +5 -0
- package/test/installation/node_modules/test-plugin/test-plugin.js +5 -0
- package/test/installation/plugins/base_plugin.js +3 -0
- package/test/installation/plugins/folder_plugin/index.js +3 -0
- package/test/installation/plugins/folder_plugin/package.json +11 -0
- package/test/installation/plugins/inherits.js +7 -0
- package/test/installation/plugins/load_first.js +3 -0
- package/test/installation/plugins/plugin.js +1 -0
- package/test/installation/plugins/tls.js +3 -0
- package/test/logger.js +217 -0
- package/test/loud/config/dhparams.pem +0 -0
- package/test/loud/config/tls/goobered.pem +45 -0
- package/test/loud/config/tls.ini +43 -0
- package/test/mail_specimen/base64-root-part.txt +23 -0
- package/test/mail_specimen/varied-fold-lengths-preserve-data.txt +283 -0
- package/test/outbound/bounce_net_errors.js +133 -0
- package/test/outbound/bounce_rfc3464.js +226 -0
- package/test/outbound/hmail.js +210 -0
- package/test/outbound/index.js +385 -0
- package/test/outbound/qfile.js +124 -0
- package/test/outbound/queue.js +325 -0
- package/test/plugins/auth/auth_base.js +620 -0
- package/test/plugins/auth/auth_bridge.js +80 -0
- package/test/plugins/auth/auth_vpopmaild.js +81 -0
- package/test/plugins/auth/flat_file.js +123 -0
- package/test/plugins/block_me.js +141 -0
- package/test/plugins/data.signatures.js +111 -0
- package/test/plugins/delay_deny.js +262 -0
- package/test/plugins/prevent_credential_leaks.js +174 -0
- package/test/plugins/process_title.js +141 -0
- package/test/plugins/queue/deliver.js +98 -0
- package/test/plugins/queue/discard.js +78 -0
- package/test/plugins/queue/lmtp.js +137 -0
- package/test/plugins/queue/qmail-queue.js +98 -0
- package/test/plugins/queue/quarantine.js +80 -0
- package/test/plugins/queue/smtp_bridge.js +152 -0
- package/test/plugins/queue/smtp_forward.js +1023 -0
- package/test/plugins/queue/smtp_proxy.js +138 -0
- package/test/plugins/rcpt_to.host_list_base.js +102 -0
- package/test/plugins/rcpt_to.in_host_list.js +186 -0
- package/test/plugins/record_envelope_addresses.js +66 -0
- package/test/plugins/reseed_rng.js +34 -0
- package/test/plugins/status.js +207 -0
- package/test/plugins/tarpit.js +90 -0
- package/test/plugins/tls.js +86 -0
- package/test/plugins/toobusy.js +21 -0
- package/test/plugins/xclient.js +119 -0
- package/test/plugins.js +230 -0
- package/test/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_fixed +0 -0
- package/test/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka +0 -0
- package/test/queue/1508269674999_1508269674999_0_34002_socVUF_1_haraka +0 -0
- package/test/queue/1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka +0 -0
- package/test/queue/zero-length +0 -0
- package/test/server.js +1012 -0
- package/test/smtp_client.js +1303 -0
- package/test/tls_socket.js +321 -0
- package/test/transaction.js +554 -0
- package/tls_socket.js +771 -0
- package/transaction.js +267 -0
package/logger.js
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
// Log class
|
|
3
|
+
|
|
4
|
+
const util = require('node:util')
|
|
5
|
+
const tty = require('node:tty')
|
|
6
|
+
|
|
7
|
+
const config = require('haraka-config')
|
|
8
|
+
const constants = require('haraka-constants')
|
|
9
|
+
|
|
10
|
+
let plugins
|
|
11
|
+
|
|
12
|
+
const regex = /(^$|[ ="\\])/
|
|
13
|
+
const escape_replace_regex = /["\\]/g
|
|
14
|
+
|
|
15
|
+
function stringify(obj) {
|
|
16
|
+
let str = ''
|
|
17
|
+
let key
|
|
18
|
+
for (key in obj) {
|
|
19
|
+
let v = obj[key]
|
|
20
|
+
if (v == null) {
|
|
21
|
+
str += `${key}="" `
|
|
22
|
+
continue
|
|
23
|
+
}
|
|
24
|
+
v = v.toString()
|
|
25
|
+
if (regex.test(v)) {
|
|
26
|
+
str += `${key}="${v.replace(escape_replace_regex, '\\$&')}" `
|
|
27
|
+
} else {
|
|
28
|
+
str += `${key}=${v} `
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return str.trim()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const logger = exports
|
|
35
|
+
|
|
36
|
+
logger.levels = {
|
|
37
|
+
DATA: 9,
|
|
38
|
+
PROTOCOL: 8,
|
|
39
|
+
DEBUG: 7,
|
|
40
|
+
INFO: 6,
|
|
41
|
+
NOTICE: 5,
|
|
42
|
+
WARN: 4,
|
|
43
|
+
ERROR: 3,
|
|
44
|
+
CRIT: 2,
|
|
45
|
+
ALERT: 1,
|
|
46
|
+
EMERG: 0,
|
|
47
|
+
}
|
|
48
|
+
const level_names = Object.keys(logger.levels)
|
|
49
|
+
|
|
50
|
+
for (const le in logger.levels) {
|
|
51
|
+
logger.levels[`LOG${le}`] = logger.levels[le]
|
|
52
|
+
logger[`LOG${le}`] = logger.levels[le]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
logger.formats = {
|
|
56
|
+
DEFAULT: 'DEFAULT',
|
|
57
|
+
LOGFMT: 'LOGFMT',
|
|
58
|
+
JSON: 'JSON',
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
logger.loglevel = logger.levels.WARN
|
|
62
|
+
logger.format = logger.formats.DEFAULT
|
|
63
|
+
logger.timestamps = false
|
|
64
|
+
logger.deferred_logs = []
|
|
65
|
+
logger.name = 'logger'
|
|
66
|
+
|
|
67
|
+
logger.colors = {
|
|
68
|
+
DATA: 'green',
|
|
69
|
+
PROTOCOL: 'green',
|
|
70
|
+
DEBUG: 'grey',
|
|
71
|
+
INFO: 'cyan',
|
|
72
|
+
NOTICE: 'blue',
|
|
73
|
+
WARN: 'red',
|
|
74
|
+
ERROR: 'red',
|
|
75
|
+
CRIT: 'red',
|
|
76
|
+
ALERT: 'red',
|
|
77
|
+
EMERG: 'red',
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const stdout_is_tty = tty.isatty(process.stdout.fd)
|
|
81
|
+
|
|
82
|
+
logger._init = function () {
|
|
83
|
+
this.load_log_ini()
|
|
84
|
+
this._init_loglevel()
|
|
85
|
+
this._init_timestamps()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
logger.load_log_ini = function () {
|
|
89
|
+
this.cfg = config.get(
|
|
90
|
+
'log.ini',
|
|
91
|
+
{
|
|
92
|
+
booleans: ['+main.timestamps'],
|
|
93
|
+
},
|
|
94
|
+
() => {
|
|
95
|
+
this.load_log_ini()
|
|
96
|
+
},
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
this.set_loglevel(this.cfg.main.level)
|
|
100
|
+
this.set_timestamps(this.cfg.main.timestamps)
|
|
101
|
+
this.set_format(this.cfg.main.format)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
logger.colorize = (color, str) => {
|
|
105
|
+
if (!util.inspect.colors[color]) {
|
|
106
|
+
return str
|
|
107
|
+
} // unknown color
|
|
108
|
+
return `\u001b[${util.inspect.colors[color][0]}m${str}\u001b[${util.inspect.colors[color][1]}m`
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
logger.dump_logs = (cb) => {
|
|
112
|
+
while (logger.deferred_logs.length > 0) {
|
|
113
|
+
const log_item = logger.deferred_logs.shift()
|
|
114
|
+
plugins.run_hooks('log', logger, log_item)
|
|
115
|
+
}
|
|
116
|
+
// Run callback after flush
|
|
117
|
+
if (cb) process.stdout.write('', cb)
|
|
118
|
+
return true
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
logger.dump_and_exit = function (code) {
|
|
122
|
+
this.dump_logs(() => {
|
|
123
|
+
if (typeof code === 'function') return code()
|
|
124
|
+
process.exit(code)
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
logger.log = (level, data, logobj) => {
|
|
129
|
+
if (level === 'PROTOCOL') {
|
|
130
|
+
data = data.replace(/\n/g, '\\n')
|
|
131
|
+
}
|
|
132
|
+
data = data.replace(/\r/g, '\\r').replace(/\n$/, '')
|
|
133
|
+
|
|
134
|
+
const item = { level, data, obj: logobj }
|
|
135
|
+
|
|
136
|
+
// buffer until plugins are loaded
|
|
137
|
+
const emptyPluginList = !plugins || (Array.isArray(plugins.plugin_list) && !plugins.plugin_list.length)
|
|
138
|
+
if (emptyPluginList) {
|
|
139
|
+
logger.deferred_logs.push(item)
|
|
140
|
+
return true
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// process buffered logs
|
|
144
|
+
while (logger.deferred_logs.length > 0) {
|
|
145
|
+
const log_item = logger.deferred_logs.shift()
|
|
146
|
+
plugins.run_hooks('log', logger, log_item)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
plugins.run_hooks('log', logger, item)
|
|
150
|
+
return true
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
logger.log_respond = (retval, msg, data) => {
|
|
154
|
+
// any other return code is irrelevant
|
|
155
|
+
if (retval !== constants.cont) return false
|
|
156
|
+
|
|
157
|
+
let timestamp_string = ''
|
|
158
|
+
if (logger.timestamps) timestamp_string = `${new Date().toISOString()} `
|
|
159
|
+
|
|
160
|
+
const color = logger.colors[data.level]
|
|
161
|
+
if (color && stdout_is_tty) {
|
|
162
|
+
process.stdout.write(`${timestamp_string}${logger.colorize(color, data.data)}\n`)
|
|
163
|
+
} else {
|
|
164
|
+
process.stdout.write(`${timestamp_string}${data.data}\n`)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return true
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
logger.set_loglevel = function (level) {
|
|
171
|
+
if (level === undefined || level === null) return
|
|
172
|
+
|
|
173
|
+
const loglevel_num = parseInt(level)
|
|
174
|
+
if (typeof level === 'string') {
|
|
175
|
+
this.log('INFO', `loglevel: ${level.toUpperCase()}`)
|
|
176
|
+
logger.loglevel = logger.levels[level.toUpperCase()]
|
|
177
|
+
} else {
|
|
178
|
+
logger.loglevel = loglevel_num
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (!Number.isInteger(logger.loglevel)) {
|
|
182
|
+
this.log('WARN', `invalid loglevel: ${level} defaulting to LOGWARN`)
|
|
183
|
+
logger.loglevel = logger.levels.WARN
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
logger.set_format = function (format) {
|
|
188
|
+
if (format) {
|
|
189
|
+
logger.format = logger.formats[format.toUpperCase()]
|
|
190
|
+
this.log('INFO', `log format: ${format.toUpperCase()}`)
|
|
191
|
+
} else {
|
|
192
|
+
logger.format = null
|
|
193
|
+
}
|
|
194
|
+
if (!logger.format) {
|
|
195
|
+
this.log('WARN', `invalid log format: ${format} defaulting to DEFAULT`)
|
|
196
|
+
logger.format = logger.formats.DEFAULT
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
logger._init_loglevel = function () {
|
|
201
|
+
const _loglevel = config.get('loglevel', 'value', () => {
|
|
202
|
+
this._init_loglevel()
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
this.set_loglevel(_loglevel)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
logger.would_log = (level) => {
|
|
209
|
+
if (logger.loglevel < level) return false
|
|
210
|
+
return true
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
logger.set_timestamps = (value) => {
|
|
214
|
+
logger.timestamps = !!value
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
logger._init_timestamps = function () {
|
|
218
|
+
const _timestamps = config.get('log_timestamps', 'value', () => {
|
|
219
|
+
this._init_timestamps()
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
// If we've already been toggled to true by the cfg, we should respect this.
|
|
223
|
+
this.set_timestamps(logger.timestamps || _timestamps)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
logger._init()
|
|
227
|
+
|
|
228
|
+
logger.log_if_level = (level, key, origin) =>
|
|
229
|
+
function () {
|
|
230
|
+
if (logger.loglevel < logger[key]) return
|
|
231
|
+
|
|
232
|
+
let logobj = {
|
|
233
|
+
level,
|
|
234
|
+
uuid: '-',
|
|
235
|
+
origin: origin || 'core',
|
|
236
|
+
message: '',
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
for (const data of arguments) {
|
|
240
|
+
if (typeof data !== 'object') {
|
|
241
|
+
logobj.message += data
|
|
242
|
+
continue
|
|
243
|
+
}
|
|
244
|
+
if (!data) continue
|
|
245
|
+
|
|
246
|
+
// if the object is a connection, add the connection id
|
|
247
|
+
if (data.constructor?.name === 'Connection') {
|
|
248
|
+
logobj.uuid = data.uuid
|
|
249
|
+
if (data.tran_count > 0) logobj.uuid += `.${data.tran_count}`
|
|
250
|
+
} else if (data instanceof plugins.Plugin) {
|
|
251
|
+
logobj.origin = data.name
|
|
252
|
+
} else if (Object.hasOwn(data, 'name')) {
|
|
253
|
+
// outbound
|
|
254
|
+
logobj.origin = data.name
|
|
255
|
+
if (Object.hasOwn(data, 'uuid')) logobj.uuid = data.uuid
|
|
256
|
+
if (data.todo?.uuid) logobj.uuid = data.todo.uuid // outbound/hmail
|
|
257
|
+
} else if (logger.format === logger.formats.LOGFMT && data.constructor === Object) {
|
|
258
|
+
logobj = { ...logobj, ...data }
|
|
259
|
+
} else if (logger.format === logger.formats.JSON && data.constructor === Object) {
|
|
260
|
+
logobj = { ...logobj, ...data }
|
|
261
|
+
} else if (Object.hasOwn(data, 'uuid')) {
|
|
262
|
+
// outbound/client_pool
|
|
263
|
+
logobj.uuid = data.uuid
|
|
264
|
+
} else if (data.constructor === Object) {
|
|
265
|
+
if (!logobj.message.endsWith(' ')) logobj.message += ' '
|
|
266
|
+
logobj.message += stringify(data)
|
|
267
|
+
} else {
|
|
268
|
+
logobj.message += util.inspect(data)
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
switch (logger.format) {
|
|
273
|
+
case logger.formats.LOGFMT:
|
|
274
|
+
logger.log(level, stringify(logobj))
|
|
275
|
+
break
|
|
276
|
+
case logger.formats.JSON:
|
|
277
|
+
logger.log(level, JSON.stringify(logobj))
|
|
278
|
+
break
|
|
279
|
+
case logger.formats.DEFAULT:
|
|
280
|
+
default:
|
|
281
|
+
logger.log(level, `[${logobj.level}] [${logobj.uuid}] [${logobj.origin}] ${logobj.message}`)
|
|
282
|
+
}
|
|
283
|
+
return true
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
logger.add_log_methods = (object, logName) => {
|
|
287
|
+
if (!object) return
|
|
288
|
+
|
|
289
|
+
if (typeof object === 'function') {
|
|
290
|
+
// add logging methods to class prototypes (Connection, Plugin, etc.)
|
|
291
|
+
|
|
292
|
+
for (const level of level_names.map((l) => l.toLowerCase())) {
|
|
293
|
+
object.prototype[`log${level}`] = (function (level) {
|
|
294
|
+
return function () {
|
|
295
|
+
logger[level].apply(logger, [this, ...arguments])
|
|
296
|
+
}
|
|
297
|
+
})(`log${level}`)
|
|
298
|
+
}
|
|
299
|
+
} else if (typeof object === 'object') {
|
|
300
|
+
// add logging methods to objects
|
|
301
|
+
|
|
302
|
+
for (const level of level_names) {
|
|
303
|
+
// objects gets log function names: loginfo, logwarn, logdebug, ...
|
|
304
|
+
const fnNames = [`log${level.toLowerCase()}`]
|
|
305
|
+
|
|
306
|
+
// logger also gets short names
|
|
307
|
+
if (Object.hasOwn(object, 'name') && object.name === 'logger') {
|
|
308
|
+
fnNames.push(level.toLowerCase())
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
for (const fnName of fnNames) {
|
|
312
|
+
if (Object.hasOwn(object, fnName)) continue // already added
|
|
313
|
+
object[fnName] = logger.log_if_level(level, `LOG${level}`, logName)
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
logger.add_log_methods(logger)
|
|
320
|
+
|
|
321
|
+
// load these down here so it sees all the logger methods compiled above
|
|
322
|
+
plugins = require('./plugins')
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const util = require('node:util')
|
|
4
|
+
|
|
5
|
+
const utils = require('haraka-utils')
|
|
6
|
+
const net_utils = require('haraka-net-utils')
|
|
7
|
+
|
|
8
|
+
const tls_socket = require('../tls_socket')
|
|
9
|
+
const logger = require('../logger')
|
|
10
|
+
const obc = require('./config')
|
|
11
|
+
|
|
12
|
+
exports.name = 'outbound'
|
|
13
|
+
|
|
14
|
+
// Get a socket for the given attributes.
|
|
15
|
+
exports.get_client = function (mx, callback) {
|
|
16
|
+
const socketArgs = mx.path ? { path: mx.path } : { port: mx.port, host: mx.exchange, localAddress: mx.bind }
|
|
17
|
+
|
|
18
|
+
const socket = tls_socket.connect(socketArgs)
|
|
19
|
+
net_utils.add_line_processor(socket)
|
|
20
|
+
|
|
21
|
+
socket.name = `outbound::${JSON.stringify(socketArgs)}`
|
|
22
|
+
socket.__uuid = utils.uuid()
|
|
23
|
+
socket.setTimeout(obc.cfg.connect_timeout * 1000)
|
|
24
|
+
|
|
25
|
+
logger.debug(exports, `created ${socket.name}`, { uuid: socket.__uuid })
|
|
26
|
+
|
|
27
|
+
socket.once('connect', () => {
|
|
28
|
+
socket.removeAllListeners('error') // these get added after callback
|
|
29
|
+
socket.removeAllListeners('timeout')
|
|
30
|
+
callback(null, socket)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
socket.once('error', (err) => {
|
|
34
|
+
socket.end()
|
|
35
|
+
socket.removeAllListeners()
|
|
36
|
+
socket.destroy()
|
|
37
|
+
const errMsg = err.message
|
|
38
|
+
? err.message
|
|
39
|
+
: err instanceof AggregateError
|
|
40
|
+
? err.errors.map((e) => e.message).join(', ')
|
|
41
|
+
: util.inspect(err, { depth: 3 })
|
|
42
|
+
callback(errMsg, null)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
socket.once('timeout', () => {
|
|
46
|
+
socket.end()
|
|
47
|
+
socket.removeAllListeners()
|
|
48
|
+
socket.destroy()
|
|
49
|
+
callback(`connection timed out to ${socket.name}`, null)
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
exports.release_client = (socket, mx) => {
|
|
54
|
+
let logMsg = `release_client: ${socket.name}`
|
|
55
|
+
if (mx.bind) logMsg += ` from ${mx.bind}`
|
|
56
|
+
logger.debug(exports, logMsg)
|
|
57
|
+
socket.removeAllListeners()
|
|
58
|
+
socket.destroy()
|
|
59
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const config = require('haraka-config')
|
|
4
|
+
const logger = require('../logger')
|
|
5
|
+
|
|
6
|
+
exports.name = 'outbound/config'
|
|
7
|
+
|
|
8
|
+
function load_config() {
|
|
9
|
+
const cfg = (exports.cfg = config.get(
|
|
10
|
+
'outbound.ini',
|
|
11
|
+
{
|
|
12
|
+
booleans: ['-disabled', '-always_split', '+enable_tls', '-local_mx_ok'],
|
|
13
|
+
},
|
|
14
|
+
() => {
|
|
15
|
+
load_config()
|
|
16
|
+
},
|
|
17
|
+
).main)
|
|
18
|
+
|
|
19
|
+
if (!cfg.inet_prefer) cfg.inet_prefer = 'default'
|
|
20
|
+
if (!cfg.inet_prefer.match(/^(v4|v6|default)$/)) {
|
|
21
|
+
logger.warn(exports, `inet_prefer is set to an invalid value: ${cfg.inet_prefer}`)
|
|
22
|
+
cfg.inet_prefer = 'default'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// legacy config file support. Remove in Haraka 4.0
|
|
26
|
+
if (!cfg.disabled && config.get('outbound.disabled')) {
|
|
27
|
+
cfg.disabled = true
|
|
28
|
+
}
|
|
29
|
+
if (!cfg.enable_tls && config.get('outbound.enable_tls')) {
|
|
30
|
+
cfg.enable_tls = true
|
|
31
|
+
}
|
|
32
|
+
if (!cfg.temp_fail_intervals) {
|
|
33
|
+
cfg.temp_fail_intervals = config.get('outbound.temp_fail_intervals')
|
|
34
|
+
}
|
|
35
|
+
if (!cfg.maxTempFailures) {
|
|
36
|
+
cfg.maxTempFailures = config.get('outbound.maxTempFailures') || 13
|
|
37
|
+
}
|
|
38
|
+
if (!cfg.concurrency_max) {
|
|
39
|
+
cfg.concurrency_max = config.get('outbound.concurrency_max') || 10000
|
|
40
|
+
}
|
|
41
|
+
if (!cfg.connect_timeout) {
|
|
42
|
+
cfg.connect_timeout = 30
|
|
43
|
+
}
|
|
44
|
+
if (!cfg.received_header) {
|
|
45
|
+
cfg.received_header = config.get('outbound.received_header') || 'Haraka outbound'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
exports.set_temp_fail_intervals()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
exports.set_temp_fail_intervals = function () {
|
|
52
|
+
// Set the outbound temp fail intervals (retry times) using the following rules:
|
|
53
|
+
// 1) temp_fail_intervals takes precedence over maxTempFailures if both are specified
|
|
54
|
+
// 2) if temp_fail_intervals is not specified or is illegally specified, then initialize
|
|
55
|
+
// it with the equivalent times of maxTempFailures using the original 2^N formula
|
|
56
|
+
// 3) the word "none" can be specified if you do not want to retry a temp failure,
|
|
57
|
+
// equivalent behavior of specifying maxTempFailures=1
|
|
58
|
+
const { cfg } = this
|
|
59
|
+
|
|
60
|
+
// Fallback function to create an array of the original retry times
|
|
61
|
+
function set_old_defaults() {
|
|
62
|
+
cfg.temp_fail_intervals = []
|
|
63
|
+
for (let i = 1; i < cfg.maxTempFailures; i++) {
|
|
64
|
+
cfg.temp_fail_intervals.push(2 ** (i + 5))
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Helpful error function in case of parsing failure
|
|
69
|
+
function error(i, msg) {
|
|
70
|
+
logger.error(exports, `temp_fail_intervals syntax error parsing element ${i}: ${msg}`)
|
|
71
|
+
logger.warn(exports, 'Setting outbound temp_fail_intervals to old defaults')
|
|
72
|
+
set_old_defaults()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// If the new value isn't specified, then create the old defaults
|
|
76
|
+
if (!cfg.temp_fail_intervals) {
|
|
77
|
+
return set_old_defaults()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// If here then turn the text input into an expanded array of intervals (in seconds)
|
|
81
|
+
// i.e, turn "1m,5m*2,1h*3" into [60,300,300,3600,3600,3600]
|
|
82
|
+
// Parse manually to do better syntax checking and provide better failure messages
|
|
83
|
+
const times = []
|
|
84
|
+
let input = cfg.temp_fail_intervals.replace(/\s+/g, '').toLowerCase()
|
|
85
|
+
if (input.length === 0) return error(0, 'nothing specified')
|
|
86
|
+
if (input === 'none') {
|
|
87
|
+
cfg.temp_fail_intervals = []
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
input = input.split(',')
|
|
91
|
+
|
|
92
|
+
for (let i = 0; i < input.length; i++) {
|
|
93
|
+
const delay = input[i].split('*')
|
|
94
|
+
if (delay.length === 1) delay.push(1)
|
|
95
|
+
else if (delay.length === 2) delay[1] = Number(delay[1])
|
|
96
|
+
else return error(i, 'too many *')
|
|
97
|
+
if (!Number.isInteger(delay[1])) return error(i, 'multiplier is not an integer')
|
|
98
|
+
|
|
99
|
+
if (delay[0].length < 2) error(i, 'invalid time span')
|
|
100
|
+
const symbol = delay[0].charAt(delay[0].length - 1)
|
|
101
|
+
let num = Number(delay[0].slice(0, -1))
|
|
102
|
+
if (isNaN(num)) return error(i, 'invalid number or symbol')
|
|
103
|
+
|
|
104
|
+
switch (symbol) {
|
|
105
|
+
case 's':
|
|
106
|
+
// do nothing, this is the base unit
|
|
107
|
+
break
|
|
108
|
+
case 'm':
|
|
109
|
+
num *= 60
|
|
110
|
+
break
|
|
111
|
+
case 'h':
|
|
112
|
+
num *= 3600
|
|
113
|
+
break
|
|
114
|
+
case 'd':
|
|
115
|
+
num *= 86400
|
|
116
|
+
break
|
|
117
|
+
default:
|
|
118
|
+
return error(i, 'invalid time span symbol')
|
|
119
|
+
}
|
|
120
|
+
// Sanity check (what should this number be?)
|
|
121
|
+
if (num < 5) return error(i, 'delay time too small, should be >=5 seconds')
|
|
122
|
+
for (let j = 0; j < delay[1]; j++) {
|
|
123
|
+
times.push(num)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// One last check, just in case...should never be true
|
|
128
|
+
if (times.length === 0) return error(0, 'unexpected parsing result')
|
|
129
|
+
|
|
130
|
+
// If here, success, so actually store the calculated array in the config
|
|
131
|
+
cfg.temp_fail_intervals = times
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
load_config()
|