Haraka 3.1.0 → 3.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierignore +4 -0
- package/CONTRIBUTORS.md +5 -5
- package/Changes.md +69 -50
- package/Plugins.md +3 -1
- package/README.md +1 -1
- package/bin/haraka +475 -478
- package/config/outbound.ini +3 -0
- package/connection.js +1072 -1108
- package/docs/Connection.md +29 -30
- package/docs/CoreConfig.md +38 -39
- package/docs/CustomReturnCodes.md +0 -1
- package/docs/HAProxy.md +2 -2
- package/docs/Header.md +1 -1
- package/docs/Logging.md +29 -5
- package/docs/Outbound.md +93 -78
- package/docs/Plugins.md +103 -108
- package/docs/Transaction.md +49 -51
- package/docs/Tutorial.md +127 -143
- package/docs/deprecated/access.md +0 -1
- package/docs/deprecated/backscatterer.md +2 -3
- package/docs/deprecated/connect.rdns_access.md +18 -27
- package/docs/deprecated/data.headers.md +0 -1
- package/docs/deprecated/data.nomsgid.md +1 -2
- package/docs/deprecated/data.noreceived.md +1 -2
- package/docs/deprecated/data.rfc5322_header_checks.md +1 -2
- package/docs/deprecated/dkim_sign.md +13 -17
- package/docs/deprecated/dkim_verify.md +9 -17
- package/docs/deprecated/dnsbl.md +36 -38
- package/docs/deprecated/dnswl.md +41 -43
- package/docs/deprecated/lookup_rdns.strict.md +21 -34
- package/docs/deprecated/mail_from.access.md +17 -25
- package/docs/deprecated/mail_from.blocklist.md +9 -12
- package/docs/deprecated/mail_from.nobounces.md +1 -2
- package/docs/deprecated/rcpt_to.access.md +20 -27
- package/docs/deprecated/rcpt_to.blocklist.md +10 -13
- package/docs/deprecated/rcpt_to.routes.md +0 -1
- package/docs/deprecated/rdns.regexp.md +13 -15
- package/docs/plugins/aliases.md +89 -89
- package/docs/plugins/auth/auth_bridge.md +5 -7
- package/docs/plugins/auth/auth_ldap.md +11 -14
- package/docs/plugins/auth/auth_proxy.md +10 -12
- package/docs/plugins/auth/auth_vpopmaild.md +5 -6
- package/docs/plugins/auth/flat_file.md +4 -4
- package/docs/plugins/block_me.md +3 -3
- package/docs/plugins/data.signatures.md +1 -2
- package/docs/plugins/delay_deny.md +3 -4
- package/docs/plugins/max_unrecognized_commands.md +4 -4
- package/docs/plugins/prevent_credential_leaks.md +6 -6
- package/docs/plugins/process_title.md +18 -18
- package/docs/plugins/queue/deliver.md +2 -3
- package/docs/plugins/queue/discard.md +4 -4
- package/docs/plugins/queue/lmtp.md +1 -3
- package/docs/plugins/queue/qmail-queue.md +7 -9
- package/docs/plugins/queue/quarantine.md +16 -21
- package/docs/plugins/queue/rabbitmq.md +8 -11
- package/docs/plugins/queue/rabbitmq_amqplib.md +43 -39
- package/docs/plugins/queue/smtp_bridge.md +7 -10
- package/docs/plugins/queue/smtp_forward.md +42 -34
- package/docs/plugins/queue/smtp_proxy.md +30 -29
- package/docs/plugins/queue/test.md +1 -3
- package/docs/plugins/rcpt_to.in_host_list.md +6 -6
- package/docs/plugins/rcpt_to.max_count.md +1 -1
- package/docs/plugins/record_envelope_addresses.md +3 -3
- package/docs/plugins/reseed_rng.md +6 -6
- package/docs/plugins/status.md +9 -8
- package/docs/plugins/tarpit.md +7 -11
- package/docs/plugins/tls.md +12 -17
- package/docs/plugins/toobusy.md +4 -4
- package/docs/plugins/xclient.md +3 -3
- package/docs/tutorials/Migrating_from_v1_to_v2.md +19 -41
- package/docs/tutorials/SettingUpOutbound.md +6 -9
- package/endpoint.js +35 -38
- package/eslint.config.mjs +22 -19
- package/haraka.js +42 -47
- package/host_pool.js +75 -79
- package/http/html/404.html +45 -49
- package/http/html/index.html +39 -28
- package/http/package.json +2 -4
- package/line_socket.js +27 -28
- package/logger.js +182 -201
- package/outbound/client_pool.js +34 -27
- package/outbound/config.js +64 -59
- package/outbound/fsync_writestream.js +24 -25
- package/outbound/hmail.js +888 -835
- package/outbound/index.js +194 -187
- package/outbound/qfile.js +49 -52
- package/outbound/queue.js +197 -190
- package/outbound/timer_queue.js +41 -43
- package/outbound/tls.js +68 -61
- package/outbound/todo.js +11 -11
- package/package.json +38 -33
- package/plugins/.eslintrc.yaml +0 -1
- package/plugins/auth/auth_base.js +123 -127
- package/plugins/auth/auth_bridge.js +7 -7
- package/plugins/auth/auth_proxy.js +121 -126
- package/plugins/auth/auth_vpopmaild.js +84 -85
- package/plugins/auth/flat_file.js +18 -17
- package/plugins/block_me.js +31 -31
- package/plugins/data.signatures.js +13 -13
- package/plugins/delay_deny.js +65 -61
- package/plugins/prevent_credential_leaks.js +23 -23
- package/plugins/process_title.js +125 -128
- package/plugins/profile.js +5 -5
- package/plugins/queue/deliver.js +3 -3
- package/plugins/queue/discard.js +13 -14
- package/plugins/queue/lmtp.js +16 -17
- package/plugins/queue/qmail-queue.js +54 -55
- package/plugins/queue/quarantine.js +68 -70
- package/plugins/queue/rabbitmq.js +80 -87
- package/plugins/queue/rabbitmq_amqplib.js +75 -54
- package/plugins/queue/smtp_bridge.js +16 -16
- package/plugins/queue/smtp_forward.js +175 -179
- package/plugins/queue/smtp_proxy.js +69 -71
- package/plugins/queue/test.js +9 -9
- package/plugins/rcpt_to.host_list_base.js +30 -34
- package/plugins/rcpt_to.in_host_list.js +19 -19
- package/plugins/record_envelope_addresses.js +4 -4
- package/plugins/reseed_rng.js +4 -4
- package/plugins/status.js +90 -97
- package/plugins/tarpit.js +25 -14
- package/plugins/tls.js +68 -68
- package/plugins/toobusy.js +21 -23
- package/plugins/xclient.js +51 -53
- package/plugins.js +276 -293
- package/rfc1869.js +30 -35
- package/server.js +308 -299
- package/smtp_client.js +244 -228
- package/test/.eslintrc.yaml +0 -1
- package/test/connection.js +127 -134
- package/test/endpoint.js +53 -47
- package/test/fixtures/line_socket.js +12 -12
- package/test/fixtures/util_hmailitem.js +89 -85
- package/test/host_pool.js +90 -92
- package/test/installation/plugins/base_plugin.js +2 -2
- package/test/installation/plugins/folder_plugin/index.js +2 -3
- package/test/installation/plugins/inherits.js +3 -3
- package/test/installation/plugins/load_first.js +2 -3
- package/test/installation/plugins/plugin.js +1 -3
- package/test/installation/plugins/tls.js +2 -4
- package/test/logger.js +135 -116
- package/test/outbound/hmail.js +49 -35
- package/test/outbound/index.js +118 -101
- package/test/outbound/qfile.js +51 -53
- package/test/outbound_bounce_net_errors.js +84 -69
- package/test/outbound_bounce_rfc3464.js +235 -165
- package/test/plugins/auth/auth_base.js +420 -279
- package/test/plugins/auth/auth_vpopmaild.js +38 -39
- package/test/plugins/queue/smtp_forward.js +126 -104
- package/test/plugins/rcpt_to.host_list_base.js +85 -67
- package/test/plugins/rcpt_to.in_host_list.js +159 -112
- package/test/plugins/status.js +71 -64
- package/test/plugins/tls.js +37 -34
- package/test/plugins.js +97 -92
- package/test/rfc1869.js +19 -26
- package/test/server.js +293 -272
- package/test/smtp_client.js +180 -176
- package/test/tls_socket.js +62 -66
- package/test/transaction.js +159 -160
- package/tls_socket.js +331 -333
- package/transaction.js +129 -137
package/plugins/toobusy.js
CHANGED
|
@@ -1,49 +1,47 @@
|
|
|
1
1
|
// Stop accepting new connections when we are too busy
|
|
2
2
|
|
|
3
|
-
let toobusy
|
|
4
|
-
let was_busy = false
|
|
3
|
+
let toobusy
|
|
4
|
+
let was_busy = false
|
|
5
5
|
|
|
6
6
|
exports.register = function () {
|
|
7
|
-
|
|
8
7
|
try {
|
|
9
|
-
toobusy = require('toobusy-js')
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
this.logerror(
|
|
13
|
-
|
|
14
|
-
return;
|
|
8
|
+
toobusy = require('toobusy-js')
|
|
9
|
+
} catch (e) {
|
|
10
|
+
this.logerror(e)
|
|
11
|
+
this.logerror("try: 'npm install -g toobusy-js'")
|
|
12
|
+
return
|
|
15
13
|
}
|
|
16
14
|
|
|
17
|
-
this.loadConfig()
|
|
15
|
+
this.loadConfig()
|
|
18
16
|
|
|
19
|
-
this.register_hook('connect', 'check_busy', -100)
|
|
17
|
+
this.register_hook('connect', 'check_busy', -100)
|
|
20
18
|
}
|
|
21
19
|
|
|
22
20
|
exports.loadConfig = function () {
|
|
23
|
-
let maxLag = this.config.get('toobusy.maxlag','value', () => {
|
|
24
|
-
this.loadConfig()
|
|
25
|
-
})
|
|
21
|
+
let maxLag = this.config.get('toobusy.maxlag', 'value', () => {
|
|
22
|
+
this.loadConfig()
|
|
23
|
+
})
|
|
26
24
|
|
|
27
|
-
maxLag = parseInt(maxLag)
|
|
25
|
+
maxLag = parseInt(maxLag)
|
|
28
26
|
if (maxLag) {
|
|
29
27
|
// This will throw an exception on error
|
|
30
|
-
toobusy.maxLag(maxLag)
|
|
28
|
+
toobusy.maxLag(maxLag)
|
|
31
29
|
}
|
|
32
30
|
}
|
|
33
31
|
|
|
34
32
|
exports.check_busy = function (next, connection) {
|
|
35
33
|
if (!toobusy()) {
|
|
36
|
-
was_busy = false
|
|
37
|
-
return next()
|
|
34
|
+
was_busy = false
|
|
35
|
+
return next()
|
|
38
36
|
}
|
|
39
37
|
|
|
40
38
|
if (!was_busy) {
|
|
41
|
-
was_busy = true
|
|
39
|
+
was_busy = true
|
|
42
40
|
// Log a CRIT error at the first occurrence
|
|
43
|
-
const currentLag = toobusy.lag()
|
|
44
|
-
const maxLag = toobusy.maxLag()
|
|
45
|
-
this.logcrit(`deferring connections: lag=${currentLag} max=${maxLag}`)
|
|
41
|
+
const currentLag = toobusy.lag()
|
|
42
|
+
const maxLag = toobusy.maxLag()
|
|
43
|
+
this.logcrit(`deferring connections: lag=${currentLag} max=${maxLag}`)
|
|
46
44
|
}
|
|
47
45
|
|
|
48
|
-
return next(DENYSOFTDISCONNECT, 'Too busy; please try again later')
|
|
46
|
+
return next(DENYSOFTDISCONNECT, 'Too busy; please try again later')
|
|
49
47
|
}
|
package/plugins/xclient.js
CHANGED
|
@@ -1,124 +1,122 @@
|
|
|
1
1
|
// Implementation of XCLIENT protocol
|
|
2
2
|
// See http://www.postfix.org/XCLIENT_README.html
|
|
3
3
|
|
|
4
|
-
const net = require('node:net')
|
|
4
|
+
const net = require('node:net')
|
|
5
5
|
|
|
6
|
-
const utils = require('haraka-utils')
|
|
7
|
-
const DSN = require('haraka-dsn')
|
|
8
|
-
let allowed_hosts = {}
|
|
6
|
+
const utils = require('haraka-utils')
|
|
7
|
+
const DSN = require('haraka-dsn')
|
|
8
|
+
let allowed_hosts = {}
|
|
9
9
|
|
|
10
10
|
exports.register = function () {
|
|
11
|
-
this.load_xclient_hosts()
|
|
11
|
+
this.load_xclient_hosts()
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
exports.load_xclient_hosts = function () {
|
|
15
15
|
const cfg = this.config.get('xclient.hosts', 'list', () => {
|
|
16
|
-
this.load_xclient_hosts()
|
|
17
|
-
})
|
|
18
|
-
const ah = {}
|
|
16
|
+
this.load_xclient_hosts()
|
|
17
|
+
})
|
|
18
|
+
const ah = {}
|
|
19
19
|
for (const i in cfg) {
|
|
20
|
-
ah[cfg[i]] = true
|
|
20
|
+
ah[cfg[i]] = true
|
|
21
21
|
}
|
|
22
|
-
allowed_hosts = ah
|
|
22
|
+
allowed_hosts = ah
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
function xclient_allowed
|
|
26
|
-
return !!(ip === '127.0.0.1' || ip === '::1' || allowed_hosts[ip])
|
|
25
|
+
function xclient_allowed(ip) {
|
|
26
|
+
return !!(ip === '127.0.0.1' || ip === '::1' || allowed_hosts[ip])
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
exports.hook_capabilities = (next, connection) => {
|
|
30
30
|
if (xclient_allowed(connection.remote.ip)) {
|
|
31
|
-
connection.capabilities.push('XCLIENT NAME ADDR PROTO HELO LOGIN')
|
|
31
|
+
connection.capabilities.push('XCLIENT NAME ADDR PROTO HELO LOGIN')
|
|
32
32
|
}
|
|
33
|
-
next()
|
|
33
|
+
next()
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
exports.hook_unrecognized_command = function (next, connection, params) {
|
|
37
|
-
if (params[0] !== 'XCLIENT') return next()
|
|
37
|
+
if (params[0] !== 'XCLIENT') return next()
|
|
38
38
|
|
|
39
39
|
// XCLIENT is not allowed after transaction start
|
|
40
40
|
if (connection?.transaction) {
|
|
41
|
-
return next(DENY, DSN.proto_unspecified('Mail transaction in progress', 503))
|
|
41
|
+
return next(DENY, DSN.proto_unspecified('Mail transaction in progress', 503))
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
if (!
|
|
45
|
-
return next(DENY, DSN.proto_unspecified('Not authorized', 550))
|
|
44
|
+
if (!xclient_allowed(connection?.remote?.ip)) {
|
|
45
|
+
return next(DENY, DSN.proto_unspecified('Not authorized', 550))
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
// If we get here - the client is allowed to use XCLIENT
|
|
49
49
|
// Process arguments
|
|
50
|
-
const args =
|
|
51
|
-
const xclient = {}
|
|
50
|
+
const args = new String(params[1]).toLowerCase().split(/ /)
|
|
51
|
+
const xclient = {}
|
|
52
52
|
for (const arg of args) {
|
|
53
|
-
const match = /^([^=]+)=([^ ]+)/.exec(arg)
|
|
53
|
+
const match = /^([^=]+)=([^ ]+)/.exec(arg)
|
|
54
54
|
if (match) {
|
|
55
|
-
connection.logdebug(this, `found key=${match[1]} value=${match[2]}`)
|
|
55
|
+
connection.logdebug(this, `found key=${match[1]} value=${match[2]}`)
|
|
56
56
|
switch (match[1]) {
|
|
57
57
|
case 'destaddr':
|
|
58
58
|
case 'addr': {
|
|
59
59
|
// IPv6 is prefixed in the XCLIENT protocol
|
|
60
|
-
let ipv6
|
|
60
|
+
let ipv6
|
|
61
61
|
if ((ipv6 = /^IPV6:(.+)$/i.exec(match[2]))) {
|
|
62
62
|
// Validate
|
|
63
63
|
if (net.isIPv6(ipv6[1])) {
|
|
64
|
-
xclient[match[1]] = ipv6[1]
|
|
64
|
+
xclient[match[1]] = ipv6[1]
|
|
65
65
|
}
|
|
66
|
-
}
|
|
67
|
-
else if (!/\[UNAVAILABLE\]/i.test(match[2])) {
|
|
66
|
+
} else if (!/\[UNAVAILABLE\]/i.test(match[2])) {
|
|
68
67
|
// IPv4
|
|
69
68
|
if (net.isIPv4(match[2])) {
|
|
70
|
-
xclient[match[1]] = match[2]
|
|
69
|
+
xclient[match[1]] = match[2]
|
|
71
70
|
}
|
|
72
71
|
}
|
|
73
|
-
break
|
|
72
|
+
break
|
|
74
73
|
}
|
|
75
74
|
case 'proto':
|
|
76
75
|
// SMTP or ESMTP
|
|
77
76
|
if (/^e?smtp/i.test(match[2])) {
|
|
78
|
-
xclient[match[1]] = match[2]
|
|
77
|
+
xclient[match[1]] = match[2]
|
|
79
78
|
}
|
|
80
|
-
break
|
|
79
|
+
break
|
|
81
80
|
case 'name':
|
|
82
81
|
case 'port':
|
|
83
82
|
case 'helo':
|
|
84
83
|
case 'login':
|
|
85
84
|
case 'destport':
|
|
86
85
|
if (!/\[(UNAVAILABLE|TEMPUNAVAIL)\]/i.test(match[2])) {
|
|
87
|
-
xclient[match[1]] = match[2]
|
|
86
|
+
xclient[match[1]] = match[2]
|
|
88
87
|
}
|
|
89
|
-
break
|
|
88
|
+
break
|
|
90
89
|
default:
|
|
91
|
-
connection.logwarn(this, `unknown argument: ${arg}`)
|
|
90
|
+
connection.logwarn(this, `unknown argument: ${arg}`)
|
|
92
91
|
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
connection.logwarn(this, `unknown argument: ${arg}`);
|
|
92
|
+
} else {
|
|
93
|
+
connection.logwarn(this, `unknown argument: ${arg}`)
|
|
96
94
|
}
|
|
97
95
|
}
|
|
98
96
|
|
|
99
97
|
// Abort if we don't have a valid IP address
|
|
100
98
|
if (!xclient.addr) {
|
|
101
|
-
return next(DENY, DSN.proto_invalid_cmd_args('No valid IP address found', 501))
|
|
99
|
+
return next(DENY, DSN.proto_invalid_cmd_args('No valid IP address found', 501))
|
|
102
100
|
}
|
|
103
101
|
|
|
104
102
|
// Apply changes
|
|
105
|
-
const new_uuid = utils.uuid()
|
|
106
|
-
connection.loginfo(this, `new uuid=${new_uuid}`)
|
|
107
|
-
connection.uuid = new_uuid
|
|
108
|
-
connection.reset_transaction()
|
|
109
|
-
connection.relaying = false
|
|
110
|
-
connection.set('remote.ip', xclient.addr)
|
|
111
|
-
connection.set('remote.host',
|
|
112
|
-
connection.set('remote.login',
|
|
113
|
-
connection.set('hello.host',
|
|
114
|
-
connection.set('local.ip',
|
|
115
|
-
connection.set('local.port',
|
|
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
|
+
connection.set('local.port', xclient.destport ? xclient.destport : undefined)
|
|
116
114
|
if (xclient.proto) {
|
|
117
|
-
connection.set('hello', 'verb',
|
|
115
|
+
connection.set('hello', 'verb', xclient.proto === 'esmtp' ? 'EHLO' : 'HELO')
|
|
118
116
|
}
|
|
119
|
-
connection.esmtp =
|
|
120
|
-
connection.xclient = true
|
|
121
|
-
if (!xclient.name) return next(NEXT_HOOK, 'lookup_rdns')
|
|
117
|
+
connection.esmtp = xclient.proto === 'esmtp'
|
|
118
|
+
connection.xclient = true
|
|
119
|
+
if (!xclient.name) return next(NEXT_HOOK, 'lookup_rdns')
|
|
122
120
|
|
|
123
|
-
next(NEXT_HOOK, 'connect')
|
|
121
|
+
next(NEXT_HOOK, 'connect')
|
|
124
122
|
}
|