Haraka 3.1.1 → 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 +62 -50
- package/Plugins.md +3 -1
- package/README.md +1 -1
- package/bin/haraka +475 -479
- 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 +33 -33
- 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 +32 -32
- 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
|
@@ -1,355 +1,351 @@
|
|
|
1
|
-
'use strict'
|
|
1
|
+
'use strict'
|
|
2
2
|
// Forward to an SMTP server
|
|
3
3
|
// Opens the connection to the ongoing SMTP server at queue time
|
|
4
4
|
// and passes back any errors seen on the ongoing server to the
|
|
5
5
|
// originating server.
|
|
6
6
|
|
|
7
|
-
const url = require('node:url')
|
|
7
|
+
const url = require('node:url')
|
|
8
8
|
|
|
9
|
-
const smtp_client_mod = require('./smtp_client')
|
|
9
|
+
const smtp_client_mod = require('./smtp_client')
|
|
10
10
|
|
|
11
11
|
exports.register = function () {
|
|
12
|
-
this.load_errs = []
|
|
12
|
+
this.load_errs = []
|
|
13
13
|
|
|
14
|
-
this.load_smtp_forward_ini()
|
|
14
|
+
this.load_smtp_forward_ini()
|
|
15
15
|
|
|
16
|
-
if (this.load_errs.length > 0) return
|
|
16
|
+
if (this.load_errs.length > 0) return
|
|
17
17
|
|
|
18
18
|
if (this.cfg.main.check_sender) {
|
|
19
|
-
this.register_hook('mail', 'check_sender')
|
|
19
|
+
this.register_hook('mail', 'check_sender')
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
if (this.cfg.main.check_recipient) {
|
|
23
|
-
this.register_hook('rcpt', 'check_recipient')
|
|
23
|
+
this.register_hook('rcpt', 'check_recipient')
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
this.register_hook('queue', 'queue_forward')
|
|
26
|
+
this.register_hook('queue', 'queue_forward')
|
|
27
27
|
|
|
28
28
|
if (this.cfg.main.enable_outbound) {
|
|
29
29
|
// deliver local message via smtp forward when relaying=true
|
|
30
|
-
this.register_hook('queue_outbound', 'queue_forward')
|
|
30
|
+
this.register_hook('queue_outbound', 'queue_forward')
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
// may specify more specific [per-domain] outbound routes
|
|
34
|
-
this.register_hook('get_mx', 'get_mx')
|
|
34
|
+
this.register_hook('get_mx', 'get_mx')
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
exports.load_smtp_forward_ini = function () {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
38
|
+
this.cfg = this.config.get(
|
|
39
|
+
'smtp_forward.ini',
|
|
40
|
+
{
|
|
41
|
+
booleans: [
|
|
42
|
+
'-main.enable_tls',
|
|
43
|
+
'-main.enable_outbound',
|
|
44
|
+
'main.one_message_per_rcpt',
|
|
45
|
+
'-main.check_sender',
|
|
46
|
+
'-main.check_recipient',
|
|
47
|
+
'*.enable_tls',
|
|
48
|
+
'*.enable_outbound',
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
() => {
|
|
52
|
+
this.load_smtp_forward_ini()
|
|
53
|
+
},
|
|
54
|
+
)
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
exports.get_config = function (conn) {
|
|
58
|
+
if (!conn.transaction) return this.cfg.main
|
|
56
59
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
let dom;
|
|
60
|
+
let dom, address
|
|
60
61
|
if (this.cfg.main.domain_selector === 'mail_from') {
|
|
61
|
-
if (!conn.transaction.mail_from) return this.cfg.main
|
|
62
|
-
dom = conn.transaction.mail_from.host
|
|
63
|
-
|
|
64
|
-
else {
|
|
65
|
-
if (!conn.transaction.rcpt_to[0]) return this.cfg.main
|
|
66
|
-
dom = conn.transaction.rcpt_to[0].host
|
|
62
|
+
if (!conn.transaction.mail_from) return this.cfg.main
|
|
63
|
+
dom = conn.transaction.mail_from.host
|
|
64
|
+
address = conn.transaction.mail_from.address()
|
|
65
|
+
} else {
|
|
66
|
+
if (!conn.transaction.rcpt_to[0]) return this.cfg.main
|
|
67
|
+
dom = conn.transaction.rcpt_to[0].host
|
|
67
68
|
}
|
|
68
69
|
|
|
69
|
-
if (
|
|
70
|
-
if (!
|
|
70
|
+
if (address && this.cfg[address]) return this.cfg[address]
|
|
71
|
+
if (!dom) return this.cfg.main
|
|
72
|
+
if (!this.cfg[dom]) return this.cfg.main // no specific route
|
|
71
73
|
|
|
72
|
-
return this.cfg[dom]
|
|
74
|
+
return this.cfg[dom]
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
exports.is_outbound_enabled = function (dom_cfg) {
|
|
78
|
+
if ('enable_outbound' in dom_cfg) return dom_cfg.enable_outbound // per-domain flag
|
|
76
79
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return this.cfg.main.enable_outbound; // follow the global configuration
|
|
80
|
+
return this.cfg.main.enable_outbound // follow the global configuration
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
exports.check_sender = function (next, connection, params) {
|
|
83
|
-
const txn = connection?.transaction
|
|
84
|
-
if (!txn) return
|
|
84
|
+
const txn = connection?.transaction
|
|
85
|
+
if (!txn) return
|
|
85
86
|
|
|
86
|
-
const email = params[0].address()
|
|
87
|
+
const email = params[0].address()
|
|
87
88
|
if (!email) {
|
|
88
|
-
txn.results.add(this, {skip: 'mail_from.null', emit: true})
|
|
89
|
-
return next()
|
|
89
|
+
txn.results.add(this, { skip: 'mail_from.null', emit: true })
|
|
90
|
+
return next()
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
const domain = params[0].host.toLowerCase()
|
|
93
|
-
if (!this.cfg[domain]) return next()
|
|
93
|
+
const domain = params[0].host.toLowerCase()
|
|
94
|
+
if (!this.cfg[domain]) return next()
|
|
94
95
|
|
|
95
96
|
// domain is defined in smtp_forward.ini
|
|
96
|
-
txn.notes.local_sender = true
|
|
97
|
+
txn.notes.local_sender = true
|
|
97
98
|
|
|
98
99
|
if (!connection.relaying) {
|
|
99
|
-
txn.results.add(this, {fail: 'mail_from!spoof'})
|
|
100
|
-
return next(DENY,
|
|
100
|
+
txn.results.add(this, { fail: 'mail_from!spoof' })
|
|
101
|
+
return next(DENY, 'Spoofed MAIL FROM')
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
txn.results.add(this, {pass: 'mail_from'})
|
|
104
|
-
next()
|
|
104
|
+
txn.results.add(this, { pass: 'mail_from' })
|
|
105
|
+
next()
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
exports.set_queue = function (connection, queue_wanted, domain) {
|
|
109
|
+
let dom_cfg = this.cfg[domain]
|
|
110
|
+
if (dom_cfg === undefined) dom_cfg = {}
|
|
108
111
|
|
|
109
|
-
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
if (!queue_wanted) queue_wanted = dom_cfg.queue || this.cfg.main.queue;
|
|
113
|
-
if (!queue_wanted) return true;
|
|
112
|
+
if (!queue_wanted) queue_wanted = dom_cfg.queue || this.cfg.main.queue
|
|
113
|
+
if (!queue_wanted) return true
|
|
114
114
|
|
|
115
|
-
let dst_host = dom_cfg.host || this.cfg.main.host
|
|
116
|
-
if (dst_host) dst_host = `smtp://${dst_host}
|
|
115
|
+
let dst_host = dom_cfg.host || this.cfg.main.host
|
|
116
|
+
if (dst_host) dst_host = `smtp://${dst_host}`
|
|
117
117
|
|
|
118
|
-
const notes = connection?.transaction?.notes
|
|
119
|
-
if (!notes) return false
|
|
118
|
+
const notes = connection?.transaction?.notes
|
|
119
|
+
if (!notes) return false
|
|
120
120
|
if (!notes.get('queue.wants')) {
|
|
121
|
-
notes.set('queue.wants', queue_wanted)
|
|
122
|
-
if (dst_host) notes.set('queue.next_hop', dst_host)
|
|
123
|
-
return true
|
|
121
|
+
notes.set('queue.wants', queue_wanted)
|
|
122
|
+
if (dst_host) notes.set('queue.next_hop', dst_host)
|
|
123
|
+
return true
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
// multiple recipients with same destination
|
|
127
127
|
if (notes.get('queue.wants') === queue_wanted) {
|
|
128
|
-
if (!dst_host) return true
|
|
128
|
+
if (!dst_host) return true
|
|
129
129
|
|
|
130
|
-
const next_hop = notes.get('queue.next_hop')
|
|
131
|
-
if (!next_hop) return true
|
|
132
|
-
if (next_hop === dst_host) return true
|
|
130
|
+
const next_hop = notes.get('queue.next_hop')
|
|
131
|
+
if (!next_hop) return true
|
|
132
|
+
if (next_hop === dst_host) return true
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
// multiple recipients with different forward host, soft deny
|
|
136
|
-
return false
|
|
136
|
+
return false
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
exports.check_recipient = function (next, connection, params) {
|
|
140
|
-
const txn = connection?.transaction
|
|
141
|
-
if (!txn) return
|
|
140
|
+
const txn = connection?.transaction
|
|
141
|
+
if (!txn) return
|
|
142
142
|
|
|
143
|
-
const rcpt = params[0]
|
|
143
|
+
const rcpt = params[0]
|
|
144
144
|
if (!rcpt.host) {
|
|
145
|
-
txn.results.add(this, {skip: 'rcpt!domain'})
|
|
146
|
-
return next()
|
|
145
|
+
txn.results.add(this, { skip: 'rcpt!domain' })
|
|
146
|
+
return next()
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
if (connection.relaying && txn.notes.local_sender) {
|
|
150
|
-
this.set_queue(connection, 'outbound')
|
|
151
|
-
txn.results.add(this, {pass: 'relaying local_sender'})
|
|
152
|
-
return next(OK)
|
|
150
|
+
this.set_queue(connection, 'outbound')
|
|
151
|
+
txn.results.add(this, { pass: 'relaying local_sender' })
|
|
152
|
+
return next(OK)
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
const domain = rcpt.host.toLowerCase()
|
|
155
|
+
const domain = rcpt.host.toLowerCase()
|
|
156
156
|
if (this.cfg[domain] !== undefined) {
|
|
157
157
|
if (this.set_queue(connection, 'smtp_forward', domain)) {
|
|
158
|
-
txn.results.add(this, {pass: 'rcpt_to'})
|
|
159
|
-
return next(OK)
|
|
158
|
+
txn.results.add(this, { pass: 'rcpt_to' })
|
|
159
|
+
return next(OK)
|
|
160
160
|
}
|
|
161
|
-
txn.results.add(this, {pass: 'rcpt_to.split'})
|
|
162
|
-
return next(DENYSOFT,
|
|
161
|
+
txn.results.add(this, { pass: 'rcpt_to.split' })
|
|
162
|
+
return next(DENYSOFT, 'Split transaction, retry soon')
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
// the MAIL FROM domain is not local and neither is the RCPT TO
|
|
166
166
|
// Another RCPT plugin may vouch for this recipient.
|
|
167
|
-
txn.results.add(this, {msg: 'rcpt!local'})
|
|
168
|
-
next()
|
|
167
|
+
txn.results.add(this, { msg: 'rcpt!local' })
|
|
168
|
+
next()
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
exports.auth = function (cfg, connection, smtp_client) {
|
|
172
|
-
|
|
173
|
-
connection.loginfo(this, `Configuring authentication for SMTP server ${cfg.host}:${cfg.port}`);
|
|
172
|
+
connection.loginfo(this, `Configuring authentication for SMTP server ${cfg.host}:${cfg.port}`)
|
|
174
173
|
smtp_client.on('capabilities', () => {
|
|
175
|
-
connection.loginfo(this, 'capabilities received')
|
|
174
|
+
connection.loginfo(this, 'capabilities received')
|
|
176
175
|
|
|
177
176
|
if ('secured' in smtp_client) {
|
|
178
|
-
connection.loginfo(this, 'secured is pending')
|
|
177
|
+
connection.loginfo(this, 'secured is pending')
|
|
179
178
|
if (smtp_client.secured === false) {
|
|
180
|
-
connection.loginfo(this,
|
|
181
|
-
return
|
|
179
|
+
connection.loginfo(this, 'Waiting for STARTTLS to complete. AUTH postponed')
|
|
180
|
+
return
|
|
182
181
|
}
|
|
183
182
|
}
|
|
184
183
|
|
|
185
|
-
function base64
|
|
186
|
-
const buffer = Buffer.from(str, 'UTF-8')
|
|
187
|
-
return buffer.toString('base64')
|
|
184
|
+
function base64(str) {
|
|
185
|
+
const buffer = Buffer.from(str, 'UTF-8')
|
|
186
|
+
return buffer.toString('base64')
|
|
188
187
|
}
|
|
189
188
|
|
|
190
189
|
if (cfg.auth_type === 'plain') {
|
|
191
|
-
connection.loginfo(this, `Authenticating with AUTH PLAIN ${cfg.auth_user}`)
|
|
192
|
-
smtp_client.send_command('AUTH', `PLAIN ${base64(`\0${cfg.auth_user}\0${cfg.auth_pass}`)}`)
|
|
190
|
+
connection.loginfo(this, `Authenticating with AUTH PLAIN ${cfg.auth_user}`)
|
|
191
|
+
smtp_client.send_command('AUTH', `PLAIN ${base64(`\0${cfg.auth_user}\0${cfg.auth_pass}`)}`)
|
|
193
192
|
return
|
|
194
193
|
}
|
|
195
194
|
|
|
196
195
|
if (cfg.auth_type === 'login') {
|
|
197
|
-
smtp_client.authenticating = true
|
|
198
|
-
smtp_client.authenticated = false
|
|
196
|
+
smtp_client.authenticating = true
|
|
197
|
+
smtp_client.authenticated = false
|
|
199
198
|
|
|
200
|
-
connection.loginfo(this, `Authenticating with AUTH LOGIN ${cfg.auth_user}`)
|
|
201
|
-
smtp_client.send_command('AUTH', 'LOGIN')
|
|
199
|
+
connection.loginfo(this, `Authenticating with AUTH LOGIN ${cfg.auth_user}`)
|
|
200
|
+
smtp_client.send_command('AUTH', 'LOGIN')
|
|
202
201
|
smtp_client.on('auth', () => {
|
|
203
202
|
// do nothing
|
|
204
|
-
})
|
|
203
|
+
})
|
|
205
204
|
smtp_client.on('auth_username', () => {
|
|
206
|
-
smtp_client.send_command(base64(cfg.auth_user))
|
|
207
|
-
})
|
|
205
|
+
smtp_client.send_command(base64(cfg.auth_user))
|
|
206
|
+
})
|
|
208
207
|
smtp_client.on('auth_password', () => {
|
|
209
|
-
smtp_client.send_command(base64(cfg.auth_pass))
|
|
210
|
-
})
|
|
208
|
+
smtp_client.send_command(base64(cfg.auth_pass))
|
|
209
|
+
})
|
|
211
210
|
}
|
|
212
|
-
})
|
|
211
|
+
})
|
|
213
212
|
}
|
|
214
213
|
|
|
215
214
|
exports.forward_enabled = function (conn, dom_cfg) {
|
|
216
|
-
|
|
217
|
-
const q_wants = conn.transaction.notes.get('queue.wants');
|
|
215
|
+
const q_wants = conn.transaction.notes.get('queue.wants')
|
|
218
216
|
if (q_wants && q_wants !== 'smtp_forward') {
|
|
219
|
-
conn.logdebug(this, `skipping, unwanted (${q_wants})`)
|
|
220
|
-
return false
|
|
217
|
+
conn.logdebug(this, `skipping, unwanted (${q_wants})`)
|
|
218
|
+
return false
|
|
221
219
|
}
|
|
222
220
|
|
|
223
221
|
if (conn.relaying && !this.is_outbound_enabled(dom_cfg)) {
|
|
224
|
-
conn.logdebug(this, 'skipping, outbound disabled')
|
|
225
|
-
return false
|
|
222
|
+
conn.logdebug(this, 'skipping, outbound disabled')
|
|
223
|
+
return false
|
|
226
224
|
}
|
|
227
225
|
|
|
228
|
-
return true
|
|
226
|
+
return true
|
|
229
227
|
}
|
|
230
228
|
|
|
231
229
|
exports.queue_forward = function (next, connection) {
|
|
232
|
-
const plugin = this
|
|
230
|
+
const plugin = this
|
|
233
231
|
if (connection.remote.closed) return
|
|
234
|
-
const txn = connection?.transaction
|
|
232
|
+
const txn = connection?.transaction
|
|
235
233
|
|
|
236
|
-
const cfg = plugin.get_config(connection)
|
|
237
|
-
if (!plugin.forward_enabled(connection, cfg)) return next()
|
|
234
|
+
const cfg = plugin.get_config(connection)
|
|
235
|
+
if (!plugin.forward_enabled(connection, cfg)) return next()
|
|
238
236
|
|
|
239
237
|
smtp_client_mod.get_client_plugin(plugin, connection, cfg, (err, smtp_client) => {
|
|
240
|
-
smtp_client.next = next
|
|
238
|
+
smtp_client.next = next
|
|
241
239
|
|
|
242
|
-
let rcpt = 0
|
|
240
|
+
let rcpt = 0
|
|
243
241
|
|
|
244
|
-
if (cfg.auth_user) plugin.auth(cfg, connection, smtp_client)
|
|
242
|
+
if (cfg.auth_user) plugin.auth(cfg, connection, smtp_client)
|
|
245
243
|
|
|
246
|
-
connection.loginfo(
|
|
247
|
-
|
|
248
|
-
|
|
244
|
+
connection.loginfo(
|
|
245
|
+
plugin,
|
|
246
|
+
`forwarding to ${cfg.forwarding_host_pool ? 'host_pool' : `${cfg.host}:${cfg.port}`}`,
|
|
247
|
+
)
|
|
249
248
|
|
|
250
|
-
function get_rs
|
|
249
|
+
function get_rs() {
|
|
251
250
|
return connection?.transaction?.results ? connection.transaction.results : connection.results
|
|
252
251
|
}
|
|
253
252
|
|
|
254
|
-
function dead_sender
|
|
253
|
+
function dead_sender() {
|
|
255
254
|
if (smtp_client.is_dead_sender(plugin, connection)) {
|
|
256
|
-
get_rs().add(plugin, { err: 'dead sender' })
|
|
257
|
-
return true
|
|
255
|
+
get_rs().add(plugin, { err: 'dead sender' })
|
|
256
|
+
return true
|
|
258
257
|
}
|
|
259
|
-
return false
|
|
258
|
+
return false
|
|
260
259
|
}
|
|
261
260
|
|
|
262
|
-
function send_rcpt
|
|
263
|
-
if (dead_sender() || !txn) return
|
|
261
|
+
function send_rcpt() {
|
|
262
|
+
if (dead_sender() || !txn) return
|
|
264
263
|
if (rcpt === txn.rcpt_to.length) {
|
|
265
|
-
smtp_client.send_command('DATA')
|
|
266
|
-
return
|
|
264
|
+
smtp_client.send_command('DATA')
|
|
265
|
+
return
|
|
267
266
|
}
|
|
268
|
-
smtp_client.send_command('RCPT', `TO:${txn.rcpt_to[rcpt].format(!smtp_client.smtp_utf8)}`)
|
|
269
|
-
rcpt
|
|
267
|
+
smtp_client.send_command('RCPT', `TO:${txn.rcpt_to[rcpt].format(!smtp_client.smtp_utf8)}`)
|
|
268
|
+
rcpt++
|
|
270
269
|
}
|
|
271
270
|
|
|
272
|
-
smtp_client.on('mail', send_rcpt)
|
|
271
|
+
smtp_client.on('mail', send_rcpt)
|
|
273
272
|
|
|
274
273
|
if (cfg.one_message_per_rcpt) {
|
|
275
274
|
smtp_client.on('rcpt', () => {
|
|
276
|
-
smtp_client.send_command('DATA')
|
|
277
|
-
})
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
smtp_client.on('rcpt', send_rcpt);
|
|
275
|
+
smtp_client.send_command('DATA')
|
|
276
|
+
})
|
|
277
|
+
} else {
|
|
278
|
+
smtp_client.on('rcpt', send_rcpt)
|
|
281
279
|
}
|
|
282
280
|
|
|
283
281
|
smtp_client.on('data', () => {
|
|
284
|
-
if (dead_sender()) return
|
|
285
|
-
smtp_client.start_data(txn.message_stream)
|
|
286
|
-
})
|
|
282
|
+
if (dead_sender()) return
|
|
283
|
+
smtp_client.start_data(txn.message_stream)
|
|
284
|
+
})
|
|
287
285
|
|
|
288
286
|
smtp_client.on('dot', () => {
|
|
289
|
-
if (dead_sender() || !txn) return
|
|
287
|
+
if (dead_sender() || !txn) return
|
|
290
288
|
|
|
291
|
-
get_rs().add(plugin, { pass: smtp_client.response })
|
|
289
|
+
get_rs().add(plugin, { pass: smtp_client.response })
|
|
292
290
|
if (rcpt < txn.rcpt_to.length) {
|
|
293
|
-
smtp_client.send_command('RSET')
|
|
294
|
-
return
|
|
291
|
+
smtp_client.send_command('RSET')
|
|
292
|
+
return
|
|
295
293
|
}
|
|
296
|
-
smtp_client.call_next(OK, smtp_client.response)
|
|
297
|
-
smtp_client.release()
|
|
298
|
-
})
|
|
294
|
+
smtp_client.call_next(OK, smtp_client.response)
|
|
295
|
+
smtp_client.release()
|
|
296
|
+
})
|
|
299
297
|
|
|
300
298
|
smtp_client.on('rset', () => {
|
|
301
|
-
if (dead_sender() || !txn) return
|
|
302
|
-
smtp_client.send_command('MAIL', `FROM:${txn.mail_from}`)
|
|
303
|
-
})
|
|
299
|
+
if (dead_sender() || !txn) return
|
|
300
|
+
smtp_client.send_command('MAIL', `FROM:${txn.mail_from}`)
|
|
301
|
+
})
|
|
304
302
|
|
|
305
303
|
smtp_client.on('bad_code', (code, msg) => {
|
|
306
|
-
if (dead_sender() || !txn) return
|
|
307
|
-
smtp_client.call_next(
|
|
308
|
-
smtp_client.release()
|
|
309
|
-
})
|
|
310
|
-
})
|
|
304
|
+
if (dead_sender() || !txn) return
|
|
305
|
+
smtp_client.call_next(code && code[0] === '5' ? DENY : DENYSOFT, msg)
|
|
306
|
+
smtp_client.release()
|
|
307
|
+
})
|
|
308
|
+
})
|
|
311
309
|
}
|
|
312
310
|
|
|
313
|
-
exports.get_mx_next_hop = next_hop => {
|
|
311
|
+
exports.get_mx_next_hop = (next_hop) => {
|
|
314
312
|
// queue.wants && queue.next_hop are mechanisms for fine-grained MX routing.
|
|
315
313
|
// Plugins can specify a queue to perform the delivery as well as a route. A
|
|
316
314
|
// plugin that uses this is qmail-deliverable, which can direct email delivery
|
|
317
315
|
// via smtp_forward, outbound (SMTP), and outbound (LMTP).
|
|
318
|
-
const dest = new url.URL(next_hop)
|
|
316
|
+
const dest = new url.URL(next_hop)
|
|
319
317
|
const mx = {
|
|
320
318
|
priority: 0,
|
|
321
319
|
port: dest.port || (dest.protocol === 'lmtp:' ? 24 : 25),
|
|
322
320
|
exchange: dest.hostname,
|
|
323
321
|
}
|
|
324
|
-
if (dest.protocol === 'lmtp:') mx.using_lmtp = true
|
|
322
|
+
if (dest.protocol === 'lmtp:') mx.using_lmtp = true
|
|
325
323
|
if (dest.auth) {
|
|
326
|
-
mx.auth_type = 'plain'
|
|
327
|
-
mx.auth_user = dest.auth.split(':')[0]
|
|
328
|
-
mx.auth_pass = dest.auth.split(':')[1]
|
|
324
|
+
mx.auth_type = 'plain'
|
|
325
|
+
mx.auth_user = dest.auth.split(':')[0]
|
|
326
|
+
mx.auth_pass = dest.auth.split(':')[1]
|
|
329
327
|
}
|
|
330
|
-
return mx
|
|
328
|
+
return mx
|
|
331
329
|
}
|
|
332
330
|
|
|
333
331
|
exports.get_mx = function (next, hmail, domain) {
|
|
334
|
-
|
|
335
332
|
const qw = hmail.todo.notes.get('queue.wants')
|
|
336
|
-
if (qw &&
|
|
333
|
+
if (qw && !['smtp_forward', 'outbound'].includes(qw)) return next()
|
|
337
334
|
|
|
338
335
|
if (qw === 'smtp_forward' && hmail.todo.notes.get('queue.next_hop')) {
|
|
339
|
-
return next(OK, this.get_mx_next_hop(hmail.todo.notes.get('queue.next_hop')))
|
|
336
|
+
return next(OK, this.get_mx_next_hop(hmail.todo.notes.get('queue.next_hop')))
|
|
340
337
|
}
|
|
341
338
|
|
|
342
|
-
const dom =
|
|
343
|
-
|
|
339
|
+
const dom =
|
|
340
|
+
this.cfg.main.domain_selector === 'mail_from' ? hmail.todo.mail_from.host.toLowerCase() : domain.toLowerCase()
|
|
341
|
+
const cfg = this.cfg[dom]
|
|
344
342
|
|
|
345
343
|
if (cfg === undefined) {
|
|
346
|
-
this.logdebug(`using DNS MX for: ${domain}`)
|
|
347
|
-
return next()
|
|
344
|
+
this.logdebug(`using DNS MX for: ${domain}`)
|
|
345
|
+
return next()
|
|
348
346
|
}
|
|
349
347
|
|
|
350
|
-
const mx_opts = [
|
|
351
|
-
'auth_type', 'auth_user', 'auth_pass', 'bind', 'bind_helo', 'using_lmtp'
|
|
352
|
-
]
|
|
348
|
+
const mx_opts = ['auth_type', 'auth_user', 'auth_pass', 'bind', 'bind_helo', 'using_lmtp']
|
|
353
349
|
|
|
354
350
|
const mx = {
|
|
355
351
|
priority: 0,
|
|
@@ -358,10 +354,10 @@ exports.get_mx = function (next, hmail, domain) {
|
|
|
358
354
|
}
|
|
359
355
|
|
|
360
356
|
// apply auth/mx options
|
|
361
|
-
mx_opts.forEach(o => {
|
|
362
|
-
if (cfg[o] === undefined) return
|
|
363
|
-
mx[o] = this.cfg[dom][o]
|
|
357
|
+
mx_opts.forEach((o) => {
|
|
358
|
+
if (cfg[o] === undefined) return
|
|
359
|
+
mx[o] = this.cfg[dom][o]
|
|
364
360
|
})
|
|
365
361
|
|
|
366
|
-
next(OK, mx)
|
|
362
|
+
next(OK, mx)
|
|
367
363
|
}
|