Haraka 2.8.28 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.yaml +2 -10
- package/Changes.md +84 -2
- package/Dockerfile +1 -1
- package/Plugins.md +9 -4
- package/README.md +2 -6
- package/bin/haraka +5 -4
- package/config/outbound.ini +0 -7
- package/config/plugins +1 -1
- package/config/smtp.ini +1 -1
- package/config/smtp_forward.ini +2 -8
- package/config/smtp_proxy.ini +0 -6
- package/connection.js +178 -204
- package/coverage/lcov.info +13863 -0
- package/coverage/tmp/coverage-42958-1658373250585-0.json +1 -0
- package/coverage/tmp/coverage-42961-1658373250529-0.json +1 -0
- package/dkim.js +66 -73
- package/docs/Body.md +1 -22
- package/docs/CoreConfig.md +2 -2
- package/docs/Header.md +1 -47
- package/docs/Outbound.md +8 -36
- package/endpoint.js +1 -1
- package/haraka.js +1 -1
- package/host_pool.js +8 -12
- package/logger.js +25 -32
- package/outbound/client_pool.js +11 -153
- package/outbound/config.js +5 -11
- package/outbound/hmail.js +109 -143
- package/outbound/index.js +13 -25
- package/outbound/mx_lookup.js +10 -7
- package/outbound/queue.js +8 -12
- package/outbound/timer_queue.js +2 -4
- package/outbound/tls.js +17 -18
- package/outbound/todo.js +1 -0
- package/package.json +57 -55
- package/plugins/auth/auth_base.js +39 -63
- package/plugins/auth/auth_bridge.js +3 -4
- package/plugins/auth/auth_proxy.js +16 -16
- package/plugins/auth/auth_vpopmaild.js +30 -37
- package/plugins/auth/flat_file.js +9 -13
- package/plugins/avg.js +9 -11
- package/plugins/backscatterer.js +1 -1
- package/plugins/block_me.js +2 -6
- package/plugins/bounce.js +106 -124
- package/plugins/clamd.js +59 -63
- package/plugins/data.signatures.js +6 -6
- package/plugins/data.uribl.js +1 -415
- package/plugins/delay_deny.js +19 -20
- package/plugins/dkim_sign.js +56 -62
- package/plugins/dkim_verify.js +9 -8
- package/plugins/dns_list_base.js +43 -42
- package/plugins/dnsbl.js +41 -46
- package/plugins/dnswl.js +23 -26
- package/plugins/early_talker.js +24 -28
- package/plugins/esets.js +8 -11
- package/plugins/greylist.js +161 -190
- package/plugins/helo.checks.js +175 -197
- package/plugins/mail_from.is_resolvable.js +38 -38
- package/plugins/messagesniffer.js +33 -40
- package/plugins/prevent_credential_leaks.js +7 -5
- package/plugins/process_title.js +16 -17
- package/plugins/queue/deliver.js +2 -2
- package/plugins/queue/lmtp.js +5 -6
- package/plugins/queue/qmail-queue.js +11 -13
- package/plugins/queue/quarantine.js +25 -34
- package/plugins/queue/rabbitmq.js +3 -2
- package/plugins/queue/rabbitmq_amqplib.js +9 -9
- package/plugins/queue/smtp_bridge.js +5 -4
- package/plugins/queue/smtp_forward.js +81 -89
- package/plugins/queue/smtp_proxy.js +21 -22
- package/plugins/queue/test.js +2 -1
- package/plugins/rcpt_to.host_list_base.js +20 -30
- package/plugins/rcpt_to.in_host_list.js +12 -14
- package/plugins/rcpt_to.max_count.js +7 -5
- package/plugins/record_envelope_addresses.js +4 -6
- package/plugins/relay.js +64 -74
- package/plugins/reseed_rng.js +1 -2
- package/plugins/spamassassin.js +56 -68
- package/plugins/status.js +2 -3
- package/plugins/tarpit.js +8 -11
- package/plugins/tls.js +14 -17
- package/plugins/toobusy.js +6 -8
- package/plugins/xclient.js +14 -25
- package/plugins.js +24 -29
- package/rfc1869.js +2 -2
- package/server.js +3 -13
- package/smtp_client.js +138 -215
- package/tests/config/smtp_forward.ini +0 -6
- package/tests/fixtures/line_socket.js +1 -1
- package/tests/fixtures/util_hmailitem.js +5 -7
- package/tests/fixtures/vm_harness.js +2 -2
- package/tests/host_pool.js +13 -14
- package/tests/installation/plugins/inherits.js +1 -2
- package/tests/logger.js +2 -2
- package/tests/plugins/bounce.js +6 -8
- package/tests/plugins/dkim_signer.js +7 -7
- package/tests/plugins/dns_list_base.js +7 -7
- package/tests/plugins/helo.checks.js +1 -1
- package/tests/plugins/mail_from.is_resolvable.js +10 -54
- package/tests/plugins/queue/smtp_forward.js +11 -11
- package/tests/plugins/rcpt_to.host_list_base.js +1 -1
- package/tests/plugins/rcpt_to.in_host_list.js +1 -1
- package/tests/plugins/spamassassin.js +1 -1
- package/tests/queue/multibyte +0 -0
- package/tests/queue/plain +0 -0
- package/tests/rfc1869.js +4 -1
- package/tests/server.js +15 -9
- package/tests/smtp_client/auth.js +4 -14
- package/tests/smtp_client/basic.js +5 -15
- package/tests/smtp_client.js +7 -3
- package/tests/transaction.js +72 -19
- package/tls_socket.js +75 -85
- package/transaction.js +7 -9
- package/attachment_stream.js +0 -118
- package/bin/spf +0 -48
- package/chunkemitter.js +0 -75
- package/config/data.uribl.excludes +0 -202
- package/config/data.uribl.ini +0 -37
- package/config/spf.ini +0 -1
- package/docs/plugins/attachment.md +0 -92
- package/docs/plugins/data.uribl.md +0 -120
- package/docs/plugins/spf.md +0 -142
- package/mailbody.js +0 -502
- package/mailheader.js +0 -304
- package/messagestream.js +0 -441
- package/plugins/aliases.js +0 -120
- package/plugins/attachment.js +0 -503
- package/plugins/connect.p0f.js +0 -5
- package/plugins/spf.js +0 -327
- package/spf.js +0 -689
- package/tests/mailbody.js +0 -348
- package/tests/mailheader.js +0 -138
- package/tests/messagestream.js +0 -34
- package/tests/plugins/aliases.js +0 -376
- package/tests/plugins/spf.js +0 -251
- package/tests/spf.js +0 -96
package/tests/transaction.js
CHANGED
|
@@ -17,13 +17,12 @@ exports.transaction = {
|
|
|
17
17
|
tearDown : _tear_down,
|
|
18
18
|
|
|
19
19
|
'add_body_filter' (test) {
|
|
20
|
-
const self = this;
|
|
21
20
|
|
|
22
21
|
test.expect(3);
|
|
23
22
|
|
|
24
23
|
this.transaction.add_body_filter('text/plain', (ct, enc, buf) => {
|
|
25
|
-
// The
|
|
26
|
-
//
|
|
24
|
+
// The functionality of these filter functions is tested in
|
|
25
|
+
// haraka-email-message. This just assures the plumbing is in place.
|
|
27
26
|
|
|
28
27
|
test.ok(ct.indexOf('text/plain') === 0, "correct body part");
|
|
29
28
|
test.ok(/utf-?8/i.test(enc), "correct encoding");
|
|
@@ -43,7 +42,7 @@ exports.transaction = {
|
|
|
43
42
|
"<p>HTML part</p>\n",
|
|
44
43
|
"--abcd--\n",
|
|
45
44
|
].forEach(line => {
|
|
46
|
-
|
|
45
|
+
this.transaction.add_data(line);
|
|
47
46
|
});
|
|
48
47
|
this.transaction.end_data(() => {
|
|
49
48
|
test.done();
|
|
@@ -51,7 +50,6 @@ exports.transaction = {
|
|
|
51
50
|
},
|
|
52
51
|
|
|
53
52
|
'regression: attachment_hooks before set_banner/add_body_filter' (test) {
|
|
54
|
-
const self = this;
|
|
55
53
|
|
|
56
54
|
test.expect(2);
|
|
57
55
|
|
|
@@ -65,10 +63,10 @@ exports.transaction = {
|
|
|
65
63
|
"\n",
|
|
66
64
|
"Some text\n",
|
|
67
65
|
].forEach(line => {
|
|
68
|
-
|
|
66
|
+
this.transaction.add_data(line);
|
|
69
67
|
});
|
|
70
68
|
this.transaction.end_data(() => {
|
|
71
|
-
|
|
69
|
+
this.transaction.message_stream.get_data(body => {
|
|
72
70
|
test.ok(/banner$/.test(body.toString().trim()), "banner applied");
|
|
73
71
|
test.done();
|
|
74
72
|
});
|
|
@@ -103,7 +101,6 @@ exports.transaction = {
|
|
|
103
101
|
},
|
|
104
102
|
|
|
105
103
|
'no munging of bytes if not parsing body' (test) {
|
|
106
|
-
const self = this;
|
|
107
104
|
|
|
108
105
|
// Czech panagram "Příliš žluťoučký kůň úpěl ďábelské ódy.\n" in ISO-8859-2 encoding
|
|
109
106
|
const message = Buffer.from([0x50, 0xF8, 0xED, 0x6C, 0x69, 0xB9, 0x20, 0xBE, 0x6C, 0x75, 0xBB, 0x6F, 0x76, 0xE8, 0x6B, 0xFD, 0x20, 0x6B, 0xF9, 0xF2, 0xFA, 0xEC, 0x6C, 0x20, 0xEF, 0xE2, 0x62, 0x65, 0x6C, 0x73, 0x6b, 0xE9, 0x20, 0xF3, 0x64, 0x79, 0x2E, 0x0A]);
|
|
@@ -116,10 +113,10 @@ exports.transaction = {
|
|
|
116
113
|
test.expect(1);
|
|
117
114
|
|
|
118
115
|
payload.forEach(line => {
|
|
119
|
-
|
|
116
|
+
this.transaction.add_data(line);
|
|
120
117
|
});
|
|
121
118
|
this.transaction.end_data(() => {
|
|
122
|
-
|
|
119
|
+
this.transaction.message_stream.get_data(body => {
|
|
123
120
|
test.ok(body.includes(message), "message not damaged");
|
|
124
121
|
test.done();
|
|
125
122
|
});
|
|
@@ -186,12 +183,11 @@ exports.base64_handling = {
|
|
|
186
183
|
tearDown: _tear_down,
|
|
187
184
|
|
|
188
185
|
'varied-base64-fold-lengths-preserve-data' (test) {
|
|
189
|
-
const self = this;
|
|
190
186
|
|
|
191
187
|
const parsed_attachments = {};
|
|
192
|
-
|
|
188
|
+
this.transaction.parse_body = true;
|
|
193
189
|
//accumulate attachment buffers.
|
|
194
|
-
|
|
190
|
+
this.transaction.attachment_hooks((ct, filename, body, stream) => {
|
|
195
191
|
let attachment = Buffer.alloc(0);
|
|
196
192
|
stream.on('data', data => {
|
|
197
193
|
attachment = Buffer.concat([attachment, data]);
|
|
@@ -202,9 +198,9 @@ exports.base64_handling = {
|
|
|
202
198
|
});
|
|
203
199
|
|
|
204
200
|
const specimen_path = path.join(__dirname, 'mail_specimen', 'varied-fold-lengths-preserve-data.txt');
|
|
205
|
-
write_file_data_to_transaction(
|
|
201
|
+
write_file_data_to_transaction(this.transaction, specimen_path);
|
|
206
202
|
|
|
207
|
-
test.equal(
|
|
203
|
+
test.equal(this.transaction.body.children.length, 6);
|
|
208
204
|
|
|
209
205
|
let first_attachment = null;
|
|
210
206
|
for (const i in parsed_attachments) {
|
|
@@ -219,13 +215,70 @@ exports.base64_handling = {
|
|
|
219
215
|
},
|
|
220
216
|
|
|
221
217
|
'base64-root-html-decodes-correct-number-of-bytes' (test) {
|
|
222
|
-
const self = this;
|
|
223
218
|
|
|
224
|
-
|
|
219
|
+
this.transaction.parse_body = true;
|
|
225
220
|
const specimen_path = path.join(__dirname, 'mail_specimen', 'base64-root-part.txt');
|
|
226
|
-
write_file_data_to_transaction(
|
|
221
|
+
write_file_data_to_transaction(this.transaction, specimen_path);
|
|
227
222
|
|
|
228
|
-
test.equal(
|
|
223
|
+
test.equal(this.transaction.body.bodytext.length, 425);
|
|
229
224
|
test.done();
|
|
230
225
|
},
|
|
231
226
|
}
|
|
227
|
+
|
|
228
|
+
// Test is to ensure boundary marker just after the headers, is in-tact
|
|
229
|
+
// Issue: "User1990" <--abcd
|
|
230
|
+
// Expected: --abcd
|
|
231
|
+
exports.boundarymarkercorrupt_test = {
|
|
232
|
+
setUp : _set_up,
|
|
233
|
+
tearDown: _tear_down,
|
|
234
|
+
|
|
235
|
+
// populate the same email data in transaction (self.transaction.add_data()) and
|
|
236
|
+
// in raw buffer, then compare
|
|
237
|
+
'fix mime boundary corruption issue' (test) {
|
|
238
|
+
const self = this;
|
|
239
|
+
let buffer = '';
|
|
240
|
+
self.transaction.add_data("Content-Type: multipart/alternative; boundary=abcd\r\n");
|
|
241
|
+
buffer += "Content-Type: multipart/alternative; boundary=abcd\r\n";
|
|
242
|
+
self.transaction.add_data('To: "User1_firstname_middlename_lastname" <user1_firstname_middlename_lastname@test.com>,\r\n');
|
|
243
|
+
buffer += 'To: "User1_firstname_middlename_lastname" <user1_firstname_middlename_lastname@test.com>,\r\n';
|
|
244
|
+
// make sure we add headers so that it exceeds 64k bytes to expose this issue
|
|
245
|
+
for (let i=0;i<725;i++){
|
|
246
|
+
self.transaction.add_data(` "User${i}_firstname_middlename_lastname" <user${i}_firstname_middlename_lastname@test.com>,\r\n`);
|
|
247
|
+
buffer += ` "User${i}_firstname_middlename_lastname" <user${i}_firstname_middlename_lastname@test.com>,\r\n`
|
|
248
|
+
}
|
|
249
|
+
self.transaction.add_data(' "Final User_firstname_middlename_lastname" <final_user_firstname_middlename_lastname@test.com>\r\n');
|
|
250
|
+
buffer += ' "Final User_firstname_middlename_lastname" <final_user_firstname_middlename_lastname@test.com>\r\n';
|
|
251
|
+
self.transaction.add_data('Message-ID: <Boundary_Marker_Test>\r\n');
|
|
252
|
+
buffer += 'Message-ID: <Boundary_Marker_Test>\r\n';
|
|
253
|
+
self.transaction.add_data('MIME-Version: 1.0\r\n');
|
|
254
|
+
buffer += 'MIME-Version: 1.0\r\n';
|
|
255
|
+
self.transaction.add_data('Date: Wed, 1 Jun 2022 16:44:39 +0530 (IST)\r\n');
|
|
256
|
+
buffer += 'Date: Wed, 1 Jun 2022 16:44:39 +0530 (IST)\r\n';
|
|
257
|
+
self.transaction.add_data('\r\n');
|
|
258
|
+
buffer += '\r\n';
|
|
259
|
+
self.transaction.add_data("--abcd\r\n");
|
|
260
|
+
buffer += "--abcd\r\n";
|
|
261
|
+
|
|
262
|
+
[
|
|
263
|
+
"Content-Type: text/plain\r\n",
|
|
264
|
+
"\r\n",
|
|
265
|
+
"Text part\r\n",
|
|
266
|
+
"--abcd\r\n",
|
|
267
|
+
"Content-Type: text/html\r\n",
|
|
268
|
+
"\r\n",
|
|
269
|
+
"<p>HTML part</p>\r\n",
|
|
270
|
+
"--abcd--\r\n",
|
|
271
|
+
].forEach(line => {
|
|
272
|
+
self.transaction.add_data(line);
|
|
273
|
+
buffer += line;
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
this.transaction.end_data(function () {
|
|
277
|
+
self.transaction.message_stream.get_data(function (body) {
|
|
278
|
+
test.ok(body.includes(buffer), "message is damaged");
|
|
279
|
+
test.done();
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
},
|
|
283
|
+
}
|
|
284
|
+
|
package/tls_socket.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
const cluster = require('cluster');
|
|
8
8
|
const net = require('net');
|
|
9
9
|
const path = require('path');
|
|
10
|
-
const spawn
|
|
10
|
+
const { spawn } = require('child_process');
|
|
11
11
|
const stream = require('stream');
|
|
12
12
|
const tls = require('tls');
|
|
13
13
|
const util = require('util');
|
|
@@ -53,61 +53,60 @@ class pluggableStream extends stream.Stream {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
attach (socket) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
self.emit('data', data);
|
|
56
|
+
this.targetsocket = socket;
|
|
57
|
+
this.targetsocket.on('data', data => {
|
|
58
|
+
this.emit('data', data);
|
|
60
59
|
});
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
this.targetsocket.on('connect', (a, b) => {
|
|
61
|
+
this.emit('connect', a, b);
|
|
63
62
|
});
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
this.targetsocket.on('secureConnect', (a, b) => {
|
|
64
|
+
this.emit('secureConnect', a, b);
|
|
65
|
+
this.emit('secure', a, b);
|
|
67
66
|
});
|
|
68
|
-
|
|
67
|
+
this.targetsocket.on('secureConnection', (a, b) => {
|
|
69
68
|
// investigate this for removal, see #2743
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
this.emit('secureConnection', a, b);
|
|
70
|
+
this.emit('secure', a, b);
|
|
72
71
|
});
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
this.targetsocket.on('secure', (a, b) => {
|
|
73
|
+
this.emit('secureConnection', a, b);
|
|
74
|
+
this.emit('secure', a, b);
|
|
76
75
|
});
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
this.targetsocket.on('end', () => {
|
|
77
|
+
this.writable = this.targetsocket.writable;
|
|
78
|
+
this.emit('end');
|
|
80
79
|
});
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
this.targetsocket.on('close', had_error => {
|
|
81
|
+
this.writable = this.targetsocket.writable;
|
|
82
|
+
this.emit('close', had_error);
|
|
84
83
|
});
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
this.targetsocket.on('drain', () => {
|
|
85
|
+
this.emit('drain');
|
|
87
86
|
});
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
this.targetsocket.once('error', exception => {
|
|
88
|
+
this.writable = this.targetsocket.writable;
|
|
90
89
|
exception.source = 'tls';
|
|
91
|
-
|
|
90
|
+
this.emit('error', exception);
|
|
92
91
|
});
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
this.targetsocket.on('timeout', () => {
|
|
93
|
+
this.emit('timeout');
|
|
95
94
|
});
|
|
96
|
-
if (
|
|
97
|
-
|
|
95
|
+
if (this.targetsocket.remotePort) {
|
|
96
|
+
this.remotePort = this.targetsocket.remotePort;
|
|
98
97
|
}
|
|
99
|
-
if (
|
|
100
|
-
|
|
98
|
+
if (this.targetsocket.remoteAddress) {
|
|
99
|
+
this.remoteAddress = this.targetsocket.remoteAddress;
|
|
101
100
|
}
|
|
102
|
-
if (
|
|
103
|
-
|
|
101
|
+
if (this.targetsocket.localPort) {
|
|
102
|
+
this.localPort = this.targetsocket.localPort;
|
|
104
103
|
}
|
|
105
|
-
if (
|
|
106
|
-
|
|
104
|
+
if (this.targetsocket.localAddress) {
|
|
105
|
+
this.localAddress = this.targetsocket.localAddress;
|
|
107
106
|
}
|
|
108
107
|
}
|
|
109
108
|
clean (data) {
|
|
110
|
-
if (this.targetsocket
|
|
109
|
+
if (this.targetsocket?.removeAllListeners) {
|
|
111
110
|
[ 'data', 'secure', 'secureConnect', 'secureConnection',
|
|
112
111
|
'end', 'close', 'error', 'drain'
|
|
113
112
|
].forEach((name) => {
|
|
@@ -224,7 +223,6 @@ exports.parse_x509 = string => {
|
|
|
224
223
|
}
|
|
225
224
|
|
|
226
225
|
exports.load_tls_ini = (opts) => {
|
|
227
|
-
const tlss = this;
|
|
228
226
|
|
|
229
227
|
log.loginfo(`loading tls.ini`); // from ${this.config.root_path}`);
|
|
230
228
|
|
|
@@ -247,7 +245,7 @@ exports.load_tls_ini = (opts) => {
|
|
|
247
245
|
'-main.mutual_tls',
|
|
248
246
|
]
|
|
249
247
|
}, () => {
|
|
250
|
-
|
|
248
|
+
this.load_tls_ini();
|
|
251
249
|
});
|
|
252
250
|
|
|
253
251
|
if (cfg.no_tls_hosts === undefined) cfg.no_tls_hosts = {};
|
|
@@ -279,11 +277,11 @@ exports.load_tls_ini = (opts) => {
|
|
|
279
277
|
|
|
280
278
|
if (!Array.isArray(cfg.main.no_starttls_ports)) cfg.main.no_starttls_ports = [];
|
|
281
279
|
|
|
282
|
-
|
|
280
|
+
this.cfg = cfg;
|
|
283
281
|
|
|
284
282
|
if (!opts || opts.role === 'server') {
|
|
285
|
-
|
|
286
|
-
|
|
283
|
+
this.applySocketOpts('*');
|
|
284
|
+
this.load_default_opts();
|
|
287
285
|
}
|
|
288
286
|
|
|
289
287
|
return cfg;
|
|
@@ -295,7 +293,6 @@ exports.saveOpt = (name, opt, val) => {
|
|
|
295
293
|
}
|
|
296
294
|
|
|
297
295
|
exports.applySocketOpts = name => {
|
|
298
|
-
const tlss = this;
|
|
299
296
|
|
|
300
297
|
if (!certsByHost[name]) certsByHost[name] = {};
|
|
301
298
|
|
|
@@ -318,56 +315,55 @@ exports.applySocketOpts = name => {
|
|
|
318
315
|
|
|
319
316
|
for (const opt of allOpts) {
|
|
320
317
|
|
|
321
|
-
if (
|
|
318
|
+
if (this.cfg[name] && this.cfg[name][opt] !== undefined) {
|
|
322
319
|
// if the setting exists in tls.ini [name]
|
|
323
|
-
|
|
320
|
+
this.saveOpt(name, opt, this.cfg[name][opt]);
|
|
324
321
|
continue;
|
|
325
322
|
}
|
|
326
323
|
|
|
327
|
-
if (
|
|
324
|
+
if (this.cfg.main[opt] !== undefined) {
|
|
328
325
|
// if the setting exists in tls.ini [main]
|
|
329
326
|
// then save it to the certsByHost options
|
|
330
|
-
|
|
327
|
+
this.saveOpt(name, opt, this.cfg.main[opt]);
|
|
331
328
|
continue;
|
|
332
329
|
}
|
|
333
330
|
|
|
334
331
|
// defaults
|
|
335
332
|
switch (opt) {
|
|
336
333
|
case 'sessionIdContext':
|
|
337
|
-
|
|
334
|
+
this.saveOpt(name, opt, 'haraka');
|
|
338
335
|
break;
|
|
339
336
|
case 'isServer':
|
|
340
|
-
|
|
337
|
+
this.saveOpt(name, opt, true);
|
|
341
338
|
break;
|
|
342
339
|
case 'key':
|
|
343
|
-
|
|
340
|
+
this.saveOpt(name, opt, 'tls_key.pem');
|
|
344
341
|
break;
|
|
345
342
|
case 'cert':
|
|
346
|
-
|
|
343
|
+
this.saveOpt(name, opt, 'tls_cert.pem');
|
|
347
344
|
break;
|
|
348
345
|
case 'dhparam':
|
|
349
|
-
|
|
346
|
+
this.saveOpt(name, opt, 'dhparams.pem');
|
|
350
347
|
break;
|
|
351
348
|
case 'SNICallback':
|
|
352
|
-
|
|
349
|
+
this.saveOpt(name, opt, SNICallback);
|
|
353
350
|
break;
|
|
354
351
|
}
|
|
355
352
|
}
|
|
356
353
|
}
|
|
357
354
|
|
|
358
355
|
exports.load_default_opts = () => {
|
|
359
|
-
const tlss = this;
|
|
360
356
|
|
|
361
357
|
const cfg = certsByHost['*'];
|
|
362
358
|
|
|
363
359
|
if (cfg.dhparam && typeof cfg.dhparam === 'string') {
|
|
364
360
|
log.logdebug(`loading dhparams from ${cfg.dhparam}`);
|
|
365
|
-
|
|
361
|
+
this.saveOpt('*', 'dhparam', this.config.get(cfg.dhparam, 'binary'));
|
|
366
362
|
}
|
|
367
363
|
|
|
368
364
|
if (cfg.ca && typeof cfg.ca === 'string') {
|
|
369
365
|
log.loginfo(`loading CA certs from ${cfg.ca}`);
|
|
370
|
-
|
|
366
|
+
this.saveOpt('*', 'ca', this.config.get(cfg.ca, 'binary'));
|
|
371
367
|
}
|
|
372
368
|
|
|
373
369
|
// make non-array key/cert option into Arrays with one entry
|
|
@@ -383,32 +379,32 @@ exports.load_default_opts = () => {
|
|
|
383
379
|
// turn key/cert file names into actual key/cert binary data
|
|
384
380
|
const asArray = cfg.key.map(keyFileName => {
|
|
385
381
|
if (!keyFileName) return;
|
|
386
|
-
const key =
|
|
382
|
+
const key = this.config.get(keyFileName, 'binary');
|
|
387
383
|
if (!key) {
|
|
388
|
-
log.logerror(`tls key ${path.join(
|
|
384
|
+
log.logerror(`tls key ${path.join(this.config.root_path, keyFileName)} could not be loaded.`);
|
|
389
385
|
}
|
|
390
386
|
return key;
|
|
391
387
|
})
|
|
392
|
-
|
|
388
|
+
this.saveOpt('*', 'key', asArray);
|
|
393
389
|
}
|
|
394
390
|
|
|
395
391
|
if (typeof cfg.cert[0] === 'string') {
|
|
396
392
|
const asArray = cfg.cert.map(certFileName => {
|
|
397
393
|
if (!certFileName) return;
|
|
398
|
-
const cert =
|
|
394
|
+
const cert = this.config.get(certFileName, 'binary');
|
|
399
395
|
if (!cert) {
|
|
400
|
-
log.logerror(`tls cert ${path.join(
|
|
396
|
+
log.logerror(`tls cert ${path.join(this.config.root_path, certFileName)} could not be loaded.`);
|
|
401
397
|
}
|
|
402
398
|
return cert;
|
|
403
399
|
})
|
|
404
|
-
|
|
400
|
+
this.saveOpt('*', 'cert', asArray);
|
|
405
401
|
}
|
|
406
402
|
|
|
407
403
|
if (cfg.cert[0] && cfg.key[0]) {
|
|
408
|
-
|
|
404
|
+
this.tls_valid = true;
|
|
409
405
|
|
|
410
406
|
// now that all opts are applied, generate TLS context
|
|
411
|
-
|
|
407
|
+
this.ensureDhparams(() => {
|
|
412
408
|
ctxByHost['*'] = tls.createSecureContext(cfg);
|
|
413
409
|
})
|
|
414
410
|
}
|
|
@@ -423,9 +419,8 @@ function SNICallback (servername, sniDone) {
|
|
|
423
419
|
}
|
|
424
420
|
|
|
425
421
|
exports.get_certs_dir = (tlsDir, done) => {
|
|
426
|
-
const tlss = this;
|
|
427
422
|
|
|
428
|
-
|
|
423
|
+
this.config.getDir(tlsDir, {}, (iterErr, files) => {
|
|
429
424
|
if (iterErr) return done(iterErr);
|
|
430
425
|
|
|
431
426
|
async.map(files, (file, iter_done) => {
|
|
@@ -452,7 +447,7 @@ exports.get_certs_dir = (tlsDir, done) => {
|
|
|
452
447
|
log.logerror(e);
|
|
453
448
|
}
|
|
454
449
|
|
|
455
|
-
const expire =
|
|
450
|
+
const expire = this.parse_x509_expire(file, as_str);
|
|
456
451
|
if (expire && expire < new Date()) {
|
|
457
452
|
log.logerror(`${file.path} expired on ${expire}`);
|
|
458
453
|
}
|
|
@@ -462,7 +457,7 @@ exports.get_certs_dir = (tlsDir, done) => {
|
|
|
462
457
|
file: path.basename(file.path),
|
|
463
458
|
key: parsed.key,
|
|
464
459
|
cert: parsed.cert,
|
|
465
|
-
names:
|
|
460
|
+
names: this.parse_x509_names(as_str),
|
|
466
461
|
expires: expire,
|
|
467
462
|
})
|
|
468
463
|
})
|
|
@@ -486,13 +481,13 @@ exports.get_certs_dir = (tlsDir, done) => {
|
|
|
486
481
|
|
|
487
482
|
// log.logdebug(cert); // DANGER: Also logs private key!
|
|
488
483
|
cert.names.forEach(name => {
|
|
489
|
-
|
|
484
|
+
this.applySocketOpts(name);
|
|
490
485
|
|
|
491
|
-
|
|
492
|
-
|
|
486
|
+
this.saveOpt(name, 'cert', cert.cert);
|
|
487
|
+
this.saveOpt(name, 'key', cert.key);
|
|
493
488
|
if (certsByHost['*'] !== undefined && certsByHost['*'].dhparam) {
|
|
494
489
|
// copy in dhparam from default '*' TLS config
|
|
495
|
-
|
|
490
|
+
this.saveOpt(name, 'dhparam', certsByHost['*'].dhparam);
|
|
496
491
|
}
|
|
497
492
|
|
|
498
493
|
// now that all opts are applied, generate TLS context
|
|
@@ -507,12 +502,11 @@ exports.get_certs_dir = (tlsDir, done) => {
|
|
|
507
502
|
}
|
|
508
503
|
|
|
509
504
|
exports.getSocketOpts = (name, done) => {
|
|
510
|
-
const tlss = this;
|
|
511
505
|
|
|
512
506
|
// startup time, load the config/tls dir
|
|
513
|
-
if (!certsByHost['*'])
|
|
507
|
+
if (!certsByHost['*']) this.load_tls_ini();
|
|
514
508
|
|
|
515
|
-
|
|
509
|
+
this.get_certs_dir('tls', () => {
|
|
516
510
|
if (certsByHost[name]) {
|
|
517
511
|
// log.logdebug(certsByHost[name]);
|
|
518
512
|
return done(certsByHost[name]);
|
|
@@ -538,7 +532,6 @@ function pipe (cleartext, socket) {
|
|
|
538
532
|
}
|
|
539
533
|
|
|
540
534
|
exports.ensureDhparams = done => {
|
|
541
|
-
const tlss = this;
|
|
542
535
|
|
|
543
536
|
// empty/missing dhparams file
|
|
544
537
|
if (certsByHost['*'].dhparam) {
|
|
@@ -547,7 +540,7 @@ exports.ensureDhparams = done => {
|
|
|
547
540
|
|
|
548
541
|
if (cluster.isWorker) return; // only once, on the master process
|
|
549
542
|
|
|
550
|
-
const filePath =
|
|
543
|
+
const filePath = this.cfg.main.dhparam || 'dhparams.pem';
|
|
551
544
|
const fpResolved = path.resolve(exports.config.root_path, filePath);
|
|
552
545
|
|
|
553
546
|
log.loginfo(`Generating a 2048 bit dhparams file at ${fpResolved}`);
|
|
@@ -568,9 +561,9 @@ exports.ensureDhparams = done => {
|
|
|
568
561
|
}
|
|
569
562
|
|
|
570
563
|
log.loginfo(`Saved to ${fpResolved}`);
|
|
571
|
-
const content =
|
|
564
|
+
const content = this.config.get(filePath, 'binary');
|
|
572
565
|
|
|
573
|
-
|
|
566
|
+
this.saveOpt('*', 'dhparam', content);
|
|
574
567
|
done(null, certsByHost['*'].dhparam);
|
|
575
568
|
});
|
|
576
569
|
}
|
|
@@ -636,9 +629,7 @@ exports.get_rejectUnauthorized = (rejectUnauthorized, port, port_list) => {
|
|
|
636
629
|
|
|
637
630
|
if (rejectUnauthorized) return true;
|
|
638
631
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
return false;
|
|
632
|
+
return !!(port_list.includes(port));
|
|
642
633
|
}
|
|
643
634
|
|
|
644
635
|
function createServer (cb) {
|
|
@@ -704,11 +695,10 @@ function getCertFor (host) {
|
|
|
704
695
|
return certsByHost['*']; // the default TLS cert
|
|
705
696
|
}
|
|
706
697
|
|
|
707
|
-
function connect (port, host
|
|
698
|
+
function connect (port, host) {
|
|
708
699
|
let conn_options = {};
|
|
709
700
|
if (typeof port === 'object') {
|
|
710
701
|
conn_options = port;
|
|
711
|
-
cb = host;
|
|
712
702
|
}
|
|
713
703
|
else {
|
|
714
704
|
conn_options.port = port;
|
package/transaction.js
CHANGED
|
@@ -9,9 +9,7 @@ const Notes = require('haraka-notes');
|
|
|
9
9
|
const utils = require('haraka-utils');
|
|
10
10
|
|
|
11
11
|
// Haraka modules
|
|
12
|
-
const
|
|
13
|
-
const body = require('./mailbody');
|
|
14
|
-
const MessageStream = require('./messagestream');
|
|
12
|
+
const message = require('haraka-email-message')
|
|
15
13
|
|
|
16
14
|
class Transaction {
|
|
17
15
|
constructor (uuid, cfg) {
|
|
@@ -31,8 +29,8 @@ class Transaction {
|
|
|
31
29
|
this.parse_body = false;
|
|
32
30
|
this.notes = new Notes();
|
|
33
31
|
this.notes.skip_plugins = [];
|
|
34
|
-
this.header = new Header();
|
|
35
|
-
this.message_stream = new
|
|
32
|
+
this.header = new message.Header();
|
|
33
|
+
this.message_stream = new message.stream(this.cfg, this.uuid, this.header.header_list);
|
|
36
34
|
this.discard_data = false;
|
|
37
35
|
this.resetting = false;
|
|
38
36
|
this.rcpt_count = {
|
|
@@ -50,7 +48,7 @@ class Transaction {
|
|
|
50
48
|
ensure_body () {
|
|
51
49
|
if (this.body) return;
|
|
52
50
|
|
|
53
|
-
this.body = new
|
|
51
|
+
this.body = new message.Body(this.header);
|
|
54
52
|
this.body.on('mime_boundary', m => this.incr_mime_count());
|
|
55
53
|
this.attachment_start_hooks.forEach(h => {
|
|
56
54
|
this.body.on('attachment_start', h);
|
|
@@ -62,7 +60,7 @@ class Transaction {
|
|
|
62
60
|
|
|
63
61
|
for (const o of this.body_filters) {
|
|
64
62
|
this.body.add_filter((ct, enc, buf) => {
|
|
65
|
-
const re_match = (util.isRegExp(o.ct_match) && o.ct_match.test(ct.toLowerCase()));
|
|
63
|
+
const re_match = (util.types.isRegExp(o.ct_match) && o.ct_match.test(ct.toLowerCase()));
|
|
66
64
|
const ct_begins = ct.toLowerCase().indexOf(String(o.ct_match).toLowerCase()) === 0;
|
|
67
65
|
if (re_match || ct_begins) return o.filter(ct, enc, buf);
|
|
68
66
|
})
|
|
@@ -186,8 +184,8 @@ class Transaction {
|
|
|
186
184
|
this.header_pos = header_pos;
|
|
187
185
|
if (this.parse_body) {
|
|
188
186
|
this.ensure_body();
|
|
189
|
-
for (
|
|
190
|
-
this.body.parse_more(
|
|
187
|
+
for (const bodyLine of body_lines) {
|
|
188
|
+
this.body.parse_more(bodyLine);
|
|
191
189
|
}
|
|
192
190
|
}
|
|
193
191
|
}
|