@push.rocks/smartmta 5.1.2 → 5.2.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/changelog.md +14 -0
- package/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/index.d.ts +3 -0
- package/dist_ts/index.js +4 -0
- package/dist_ts/logger.d.ts +17 -0
- package/dist_ts/logger.js +76 -0
- package/dist_ts/mail/core/classes.bouncemanager.d.ts +185 -0
- package/dist_ts/mail/core/classes.bouncemanager.js +569 -0
- package/dist_ts/mail/core/classes.email.d.ts +291 -0
- package/dist_ts/mail/core/classes.email.js +802 -0
- package/dist_ts/mail/core/classes.emailvalidator.d.ts +61 -0
- package/dist_ts/mail/core/classes.emailvalidator.js +184 -0
- package/dist_ts/mail/core/classes.templatemanager.d.ts +95 -0
- package/dist_ts/mail/core/classes.templatemanager.js +240 -0
- package/dist_ts/mail/core/index.d.ts +4 -0
- package/dist_ts/mail/core/index.js +6 -0
- package/dist_ts/mail/delivery/classes.delivery.queue.d.ts +163 -0
- package/dist_ts/mail/delivery/classes.delivery.queue.js +488 -0
- package/dist_ts/mail/delivery/classes.delivery.system.d.ts +160 -0
- package/dist_ts/mail/delivery/classes.delivery.system.js +630 -0
- package/dist_ts/mail/delivery/classes.unified.rate.limiter.d.ts +200 -0
- package/dist_ts/mail/delivery/classes.unified.rate.limiter.js +820 -0
- package/dist_ts/mail/delivery/index.d.ts +4 -0
- package/dist_ts/mail/delivery/index.js +6 -0
- package/dist_ts/mail/delivery/interfaces.d.ts +140 -0
- package/dist_ts/mail/delivery/interfaces.js +17 -0
- package/dist_ts/mail/index.d.ts +7 -0
- package/dist_ts/mail/index.js +12 -0
- package/dist_ts/mail/routing/classes.dkim.manager.d.ts +25 -0
- package/dist_ts/mail/routing/classes.dkim.manager.js +127 -0
- package/dist_ts/mail/routing/classes.dns.manager.d.ts +79 -0
- package/dist_ts/mail/routing/classes.dns.manager.js +415 -0
- package/dist_ts/mail/routing/classes.domain.registry.d.ts +54 -0
- package/dist_ts/mail/routing/classes.domain.registry.js +119 -0
- package/dist_ts/mail/routing/classes.email.action.executor.d.ts +33 -0
- package/dist_ts/mail/routing/classes.email.action.executor.js +137 -0
- package/dist_ts/mail/routing/classes.email.router.d.ts +171 -0
- package/dist_ts/mail/routing/classes.email.router.js +494 -0
- package/dist_ts/mail/routing/classes.unified.email.server.d.ts +241 -0
- package/dist_ts/mail/routing/classes.unified.email.server.js +935 -0
- package/dist_ts/mail/routing/index.d.ts +7 -0
- package/dist_ts/mail/routing/index.js +9 -0
- package/dist_ts/mail/routing/interfaces.d.ts +187 -0
- package/dist_ts/mail/routing/interfaces.js +2 -0
- package/dist_ts/mail/security/classes.dkimcreator.d.ts +72 -0
- package/dist_ts/mail/security/classes.dkimcreator.js +360 -0
- package/dist_ts/mail/security/classes.spfverifier.d.ts +62 -0
- package/dist_ts/mail/security/classes.spfverifier.js +87 -0
- package/dist_ts/mail/security/index.d.ts +2 -0
- package/dist_ts/mail/security/index.js +4 -0
- package/dist_ts/paths.d.ts +14 -0
- package/dist_ts/paths.js +39 -0
- package/dist_ts/plugins.d.ts +24 -0
- package/dist_ts/plugins.js +28 -0
- package/dist_ts/security/classes.contentscanner.d.ts +130 -0
- package/dist_ts/security/classes.contentscanner.js +338 -0
- package/dist_ts/security/classes.ipreputationchecker.d.ts +73 -0
- package/dist_ts/security/classes.ipreputationchecker.js +263 -0
- package/dist_ts/security/classes.rustsecuritybridge.d.ts +398 -0
- package/dist_ts/security/classes.rustsecuritybridge.js +484 -0
- package/dist_ts/security/classes.securitylogger.d.ts +140 -0
- package/dist_ts/security/classes.securitylogger.js +235 -0
- package/dist_ts/security/index.d.ts +4 -0
- package/dist_ts/security/index.js +5 -0
- package/package.json +6 -1
- package/readme.md +52 -9
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/index.ts +3 -0
- package/ts/logger.ts +91 -0
- package/ts/mail/core/classes.bouncemanager.ts +731 -0
- package/ts/mail/core/classes.email.ts +942 -0
- package/ts/mail/core/classes.emailvalidator.ts +239 -0
- package/ts/mail/core/classes.templatemanager.ts +320 -0
- package/ts/mail/core/index.ts +5 -0
- package/ts/mail/delivery/classes.delivery.queue.ts +645 -0
- package/ts/mail/delivery/classes.delivery.system.ts +816 -0
- package/ts/mail/delivery/classes.unified.rate.limiter.ts +1053 -0
- package/ts/mail/delivery/index.ts +5 -0
- package/ts/mail/delivery/interfaces.ts +167 -0
- package/ts/mail/index.ts +17 -0
- package/ts/mail/routing/classes.dkim.manager.ts +157 -0
- package/ts/mail/routing/classes.dns.manager.ts +573 -0
- package/ts/mail/routing/classes.domain.registry.ts +139 -0
- package/ts/mail/routing/classes.email.action.executor.ts +175 -0
- package/ts/mail/routing/classes.email.router.ts +575 -0
- package/ts/mail/routing/classes.unified.email.server.ts +1207 -0
- package/ts/mail/routing/index.ts +9 -0
- package/ts/mail/routing/interfaces.ts +202 -0
- package/ts/mail/security/classes.dkimcreator.ts +447 -0
- package/ts/mail/security/classes.spfverifier.ts +126 -0
- package/ts/mail/security/index.ts +3 -0
- package/ts/paths.ts +48 -0
- package/ts/plugins.ts +53 -0
- package/ts/security/classes.contentscanner.ts +400 -0
- package/ts/security/classes.ipreputationchecker.ts +315 -0
- package/ts/security/classes.rustsecuritybridge.ts +943 -0
- package/ts/security/classes.securitylogger.ts +299 -0
- package/ts/security/index.ts +40 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import { logger } from '../../logger.js';
|
|
3
|
+
/**
|
|
4
|
+
* Manages DNS configuration for email domains
|
|
5
|
+
* Handles both validation and creation of DNS records
|
|
6
|
+
*/
|
|
7
|
+
export class DnsManager {
|
|
8
|
+
dcRouter;
|
|
9
|
+
storageManager;
|
|
10
|
+
constructor(dcRouter) {
|
|
11
|
+
this.dcRouter = dcRouter;
|
|
12
|
+
this.storageManager = dcRouter.storageManager;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Validate all domain configurations
|
|
16
|
+
*/
|
|
17
|
+
async validateAllDomains(domainConfigs) {
|
|
18
|
+
const results = new Map();
|
|
19
|
+
for (const config of domainConfigs) {
|
|
20
|
+
const result = await this.validateDomain(config);
|
|
21
|
+
results.set(config.domain, result);
|
|
22
|
+
}
|
|
23
|
+
return results;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Validate a single domain configuration
|
|
27
|
+
*/
|
|
28
|
+
async validateDomain(config) {
|
|
29
|
+
switch (config.dnsMode) {
|
|
30
|
+
case 'forward':
|
|
31
|
+
return this.validateForwardMode(config);
|
|
32
|
+
case 'internal-dns':
|
|
33
|
+
return this.validateInternalDnsMode(config);
|
|
34
|
+
case 'external-dns':
|
|
35
|
+
return this.validateExternalDnsMode(config);
|
|
36
|
+
default:
|
|
37
|
+
return {
|
|
38
|
+
valid: false,
|
|
39
|
+
errors: [`Unknown DNS mode: ${config.dnsMode}`],
|
|
40
|
+
warnings: [],
|
|
41
|
+
requiredChanges: []
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Validate forward mode configuration
|
|
47
|
+
*/
|
|
48
|
+
async validateForwardMode(config) {
|
|
49
|
+
const result = {
|
|
50
|
+
valid: true,
|
|
51
|
+
errors: [],
|
|
52
|
+
warnings: [],
|
|
53
|
+
requiredChanges: []
|
|
54
|
+
};
|
|
55
|
+
// Forward mode doesn't require DNS validation by default
|
|
56
|
+
if (!config.dns?.forward?.skipDnsValidation) {
|
|
57
|
+
logger.log('info', `DNS validation skipped for forward mode domain: ${config.domain}`);
|
|
58
|
+
}
|
|
59
|
+
// DKIM keys are still generated for consistency
|
|
60
|
+
result.warnings.push(`Domain "${config.domain}" uses forward mode. DKIM keys will be generated but signing only happens if email is processed.`);
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Validate internal DNS mode configuration
|
|
65
|
+
*/
|
|
66
|
+
async validateInternalDnsMode(config) {
|
|
67
|
+
const result = {
|
|
68
|
+
valid: true,
|
|
69
|
+
errors: [],
|
|
70
|
+
warnings: [],
|
|
71
|
+
requiredChanges: []
|
|
72
|
+
};
|
|
73
|
+
// Check if DNS configuration is set up
|
|
74
|
+
const dnsNsDomains = this.dcRouter.options?.dnsNsDomains;
|
|
75
|
+
const dnsScopes = this.dcRouter.options?.dnsScopes;
|
|
76
|
+
if (!dnsNsDomains || dnsNsDomains.length === 0) {
|
|
77
|
+
result.valid = false;
|
|
78
|
+
result.errors.push(`Domain "${config.domain}" is configured to use internal DNS, but dnsNsDomains is not set in DcRouter configuration.`);
|
|
79
|
+
console.error(`❌ ERROR: Domain "${config.domain}" is configured to use internal DNS,\n` +
|
|
80
|
+
' but dnsNsDomains is not set in DcRouter configuration.\n' +
|
|
81
|
+
' Please configure dnsNsDomains to enable the DNS server.\n' +
|
|
82
|
+
' Example: dnsNsDomains: ["ns1.myservice.com", "ns2.myservice.com"]');
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
if (!dnsScopes || dnsScopes.length === 0) {
|
|
86
|
+
result.valid = false;
|
|
87
|
+
result.errors.push(`Domain "${config.domain}" is configured to use internal DNS, but dnsScopes is not set in DcRouter configuration.`);
|
|
88
|
+
console.error(`❌ ERROR: Domain "${config.domain}" is configured to use internal DNS,\n` +
|
|
89
|
+
' but dnsScopes is not set in DcRouter configuration.\n' +
|
|
90
|
+
' Please configure dnsScopes to define authoritative domains.\n' +
|
|
91
|
+
' Example: dnsScopes: ["myservice.com", "mail.myservice.com"]');
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
// Check if the email domain is in dnsScopes
|
|
95
|
+
if (!dnsScopes.includes(config.domain)) {
|
|
96
|
+
result.valid = false;
|
|
97
|
+
result.errors.push(`Domain "${config.domain}" is configured to use internal DNS, but is not included in dnsScopes.`);
|
|
98
|
+
console.error(`❌ ERROR: Domain "${config.domain}" is configured to use internal DNS,\n` +
|
|
99
|
+
` but is not included in dnsScopes: [${dnsScopes.join(', ')}].\n` +
|
|
100
|
+
' Please add this domain to dnsScopes to enable internal DNS.\n' +
|
|
101
|
+
` Example: dnsScopes: [..., "${config.domain}"]`);
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
const primaryNameserver = dnsNsDomains[0];
|
|
105
|
+
// Check NS delegation
|
|
106
|
+
try {
|
|
107
|
+
const nsRecords = await this.resolveNs(config.domain);
|
|
108
|
+
const delegatedNameservers = dnsNsDomains.filter(ns => nsRecords.includes(ns));
|
|
109
|
+
const isDelegated = delegatedNameservers.length > 0;
|
|
110
|
+
if (!isDelegated) {
|
|
111
|
+
result.warnings.push(`NS delegation not found for ${config.domain}. Please add NS records at your registrar.`);
|
|
112
|
+
dnsNsDomains.forEach(ns => {
|
|
113
|
+
result.requiredChanges.push(`Add NS record: ${config.domain}. NS ${ns}.`);
|
|
114
|
+
});
|
|
115
|
+
console.log(`📋 DNS Delegation Required for ${config.domain}:\n` +
|
|
116
|
+
'━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n' +
|
|
117
|
+
'Please add these NS records at your domain registrar:\n' +
|
|
118
|
+
dnsNsDomains.map(ns => ` ${config.domain}. NS ${ns}.`).join('\n') + '\n' +
|
|
119
|
+
'━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n' +
|
|
120
|
+
'This delegation is required for internal DNS mode to work.');
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
console.log(`✅ NS delegation verified: ${config.domain} -> [${delegatedNameservers.join(', ')}]`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
result.warnings.push(`Could not verify NS delegation for ${config.domain}: ${error.message}`);
|
|
128
|
+
}
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Validate external DNS mode configuration
|
|
133
|
+
*/
|
|
134
|
+
async validateExternalDnsMode(config) {
|
|
135
|
+
const result = {
|
|
136
|
+
valid: true,
|
|
137
|
+
errors: [],
|
|
138
|
+
warnings: [],
|
|
139
|
+
requiredChanges: []
|
|
140
|
+
};
|
|
141
|
+
try {
|
|
142
|
+
// Get current DNS records
|
|
143
|
+
const records = await this.checkDnsRecords(config);
|
|
144
|
+
const requiredRecords = config.dns?.external?.requiredRecords || ['MX', 'SPF', 'DKIM', 'DMARC'];
|
|
145
|
+
// Check MX record
|
|
146
|
+
if (requiredRecords.includes('MX') && !records.mx?.length) {
|
|
147
|
+
result.requiredChanges.push(`Add MX record: ${this.getBaseDomain(config.domain)} -> ${config.domain} (priority 10)`);
|
|
148
|
+
}
|
|
149
|
+
// Check SPF record
|
|
150
|
+
if (requiredRecords.includes('SPF') && !records.spf) {
|
|
151
|
+
result.requiredChanges.push(`Add TXT record: ${this.getBaseDomain(config.domain)} -> "v=spf1 a mx ~all"`);
|
|
152
|
+
}
|
|
153
|
+
// Check DKIM record
|
|
154
|
+
if (requiredRecords.includes('DKIM') && !records.dkim) {
|
|
155
|
+
const selector = config.dkim?.selector || 'default';
|
|
156
|
+
const dkimPublicKey = await this.storageManager.get(`/email/dkim/${config.domain}/public.key`);
|
|
157
|
+
if (dkimPublicKey) {
|
|
158
|
+
const publicKeyBase64 = dkimPublicKey
|
|
159
|
+
.replace(/-----BEGIN PUBLIC KEY-----/g, '')
|
|
160
|
+
.replace(/-----END PUBLIC KEY-----/g, '')
|
|
161
|
+
.replace(/\s/g, '');
|
|
162
|
+
result.requiredChanges.push(`Add TXT record: ${selector}._domainkey.${config.domain} -> "v=DKIM1; k=rsa; p=${publicKeyBase64}"`);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
result.warnings.push(`DKIM public key not found for ${config.domain}. It will be generated on first use.`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Check DMARC record
|
|
169
|
+
if (requiredRecords.includes('DMARC') && !records.dmarc) {
|
|
170
|
+
result.requiredChanges.push(`Add TXT record: _dmarc.${this.getBaseDomain(config.domain)} -> "v=DMARC1; p=none; rua=mailto:dmarc@${config.domain}"`);
|
|
171
|
+
}
|
|
172
|
+
// Show setup instructions if needed
|
|
173
|
+
if (result.requiredChanges.length > 0) {
|
|
174
|
+
console.log(`📋 DNS Configuration Required for ${config.domain}:\n` +
|
|
175
|
+
'━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n' +
|
|
176
|
+
result.requiredChanges.map((change, i) => `${i + 1}. ${change}`).join('\n') +
|
|
177
|
+
'\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
result.errors.push(`DNS validation failed: ${error.message}`);
|
|
182
|
+
result.valid = false;
|
|
183
|
+
}
|
|
184
|
+
return result;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Check DNS records for a domain
|
|
188
|
+
*/
|
|
189
|
+
async checkDnsRecords(config) {
|
|
190
|
+
const records = {};
|
|
191
|
+
const baseDomain = this.getBaseDomain(config.domain);
|
|
192
|
+
const selector = config.dkim?.selector || 'default';
|
|
193
|
+
// Use custom DNS servers if specified
|
|
194
|
+
const resolver = new plugins.dns.promises.Resolver();
|
|
195
|
+
if (config.dns?.external?.servers?.length) {
|
|
196
|
+
resolver.setServers(config.dns.external.servers);
|
|
197
|
+
}
|
|
198
|
+
// Check MX records
|
|
199
|
+
try {
|
|
200
|
+
const mxRecords = await resolver.resolveMx(baseDomain);
|
|
201
|
+
records.mx = mxRecords.map(mx => mx.exchange);
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
logger.log('debug', `No MX records found for ${baseDomain}`);
|
|
205
|
+
}
|
|
206
|
+
// Check SPF record
|
|
207
|
+
try {
|
|
208
|
+
const txtRecords = await resolver.resolveTxt(baseDomain);
|
|
209
|
+
const spfRecord = txtRecords.find(records => records.some(record => record.startsWith('v=spf1')));
|
|
210
|
+
if (spfRecord) {
|
|
211
|
+
records.spf = spfRecord.join('');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
logger.log('debug', `No SPF record found for ${baseDomain}`);
|
|
216
|
+
}
|
|
217
|
+
// Check DKIM record
|
|
218
|
+
try {
|
|
219
|
+
const dkimRecords = await resolver.resolveTxt(`${selector}._domainkey.${config.domain}`);
|
|
220
|
+
const dkimRecord = dkimRecords.find(records => records.some(record => record.includes('v=DKIM1')));
|
|
221
|
+
if (dkimRecord) {
|
|
222
|
+
records.dkim = dkimRecord.join('');
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
logger.log('debug', `No DKIM record found for ${selector}._domainkey.${config.domain}`);
|
|
227
|
+
}
|
|
228
|
+
// Check DMARC record
|
|
229
|
+
try {
|
|
230
|
+
const dmarcRecords = await resolver.resolveTxt(`_dmarc.${baseDomain}`);
|
|
231
|
+
const dmarcRecord = dmarcRecords.find(records => records.some(record => record.startsWith('v=DMARC1')));
|
|
232
|
+
if (dmarcRecord) {
|
|
233
|
+
records.dmarc = dmarcRecord.join('');
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
logger.log('debug', `No DMARC record found for _dmarc.${baseDomain}`);
|
|
238
|
+
}
|
|
239
|
+
return records;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Resolve NS records for a domain
|
|
243
|
+
*/
|
|
244
|
+
async resolveNs(domain) {
|
|
245
|
+
try {
|
|
246
|
+
const resolver = new plugins.dns.promises.Resolver();
|
|
247
|
+
const nsRecords = await resolver.resolveNs(domain);
|
|
248
|
+
return nsRecords;
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
logger.log('warn', `Failed to resolve NS records for ${domain}: ${error.message}`);
|
|
252
|
+
return [];
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Get base domain from email domain (e.g., mail.example.com -> example.com)
|
|
257
|
+
*/
|
|
258
|
+
getBaseDomain(domain) {
|
|
259
|
+
const parts = domain.split('.');
|
|
260
|
+
if (parts.length <= 2) {
|
|
261
|
+
return domain;
|
|
262
|
+
}
|
|
263
|
+
// For subdomains like mail.example.com, return example.com
|
|
264
|
+
// But preserve domain structure for longer TLDs like .co.uk
|
|
265
|
+
if (parts[parts.length - 2].length <= 3 && parts[parts.length - 1].length === 2) {
|
|
266
|
+
// Likely a country code TLD like .co.uk
|
|
267
|
+
return parts.slice(-3).join('.');
|
|
268
|
+
}
|
|
269
|
+
return parts.slice(-2).join('.');
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Ensure all DNS records are created for configured domains
|
|
273
|
+
* This is the main entry point for DNS record management
|
|
274
|
+
*/
|
|
275
|
+
async ensureDnsRecords(domainConfigs, dkimCreator) {
|
|
276
|
+
logger.log('info', `Ensuring DNS records for ${domainConfigs.length} domains`);
|
|
277
|
+
// First, validate all domains
|
|
278
|
+
const validationResults = await this.validateAllDomains(domainConfigs);
|
|
279
|
+
// Then create records for internal-dns domains
|
|
280
|
+
const internalDnsDomains = domainConfigs.filter(config => config.dnsMode === 'internal-dns');
|
|
281
|
+
if (internalDnsDomains.length > 0) {
|
|
282
|
+
await this.createInternalDnsRecords(internalDnsDomains);
|
|
283
|
+
// Create DKIM records if DKIMCreator is provided
|
|
284
|
+
if (dkimCreator) {
|
|
285
|
+
await this.createDkimRecords(domainConfigs, dkimCreator);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// Log validation results for external-dns domains
|
|
289
|
+
for (const [domain, result] of validationResults) {
|
|
290
|
+
const config = domainConfigs.find(c => c.domain === domain);
|
|
291
|
+
if (config?.dnsMode === 'external-dns' && result.requiredChanges.length > 0) {
|
|
292
|
+
logger.log('warn', `External DNS configuration required for ${domain}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Create DNS records for internal-dns mode domains
|
|
298
|
+
*/
|
|
299
|
+
async createInternalDnsRecords(domainConfigs) {
|
|
300
|
+
// Check if DNS server is available
|
|
301
|
+
if (!this.dcRouter.dnsServer) {
|
|
302
|
+
logger.log('warn', 'DNS server not available, skipping internal DNS record creation');
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
logger.log('info', `Creating DNS records for ${domainConfigs.length} internal-dns domains`);
|
|
306
|
+
for (const domainConfig of domainConfigs) {
|
|
307
|
+
const domain = domainConfig.domain;
|
|
308
|
+
const ttl = domainConfig.dns?.internal?.ttl || 3600;
|
|
309
|
+
const mxPriority = domainConfig.dns?.internal?.mxPriority || 10;
|
|
310
|
+
try {
|
|
311
|
+
// 1. Register MX record - points to the email domain itself
|
|
312
|
+
this.dcRouter.dnsServer.registerHandler(domain, ['MX'], () => ({
|
|
313
|
+
name: domain,
|
|
314
|
+
type: 'MX',
|
|
315
|
+
class: 'IN',
|
|
316
|
+
ttl: ttl,
|
|
317
|
+
data: {
|
|
318
|
+
priority: mxPriority,
|
|
319
|
+
exchange: domain
|
|
320
|
+
}
|
|
321
|
+
}));
|
|
322
|
+
logger.log('info', `MX record registered for ${domain} -> ${domain} (priority ${mxPriority})`);
|
|
323
|
+
// Store MX record in StorageManager
|
|
324
|
+
await this.storageManager.set(`/email/dns/${domain}/mx`, JSON.stringify({
|
|
325
|
+
type: 'MX',
|
|
326
|
+
priority: mxPriority,
|
|
327
|
+
exchange: domain,
|
|
328
|
+
ttl: ttl
|
|
329
|
+
}));
|
|
330
|
+
// 2. Register SPF record - allows the domain to send emails
|
|
331
|
+
const spfRecord = `v=spf1 a mx ~all`;
|
|
332
|
+
this.dcRouter.dnsServer.registerHandler(domain, ['TXT'], () => ({
|
|
333
|
+
name: domain,
|
|
334
|
+
type: 'TXT',
|
|
335
|
+
class: 'IN',
|
|
336
|
+
ttl: ttl,
|
|
337
|
+
data: spfRecord
|
|
338
|
+
}));
|
|
339
|
+
logger.log('info', `SPF record registered for ${domain}: "${spfRecord}"`);
|
|
340
|
+
// Store SPF record in StorageManager
|
|
341
|
+
await this.storageManager.set(`/email/dns/${domain}/spf`, JSON.stringify({
|
|
342
|
+
type: 'TXT',
|
|
343
|
+
data: spfRecord,
|
|
344
|
+
ttl: ttl
|
|
345
|
+
}));
|
|
346
|
+
// 3. Register DMARC record - policy for handling email authentication
|
|
347
|
+
const dmarcRecord = `v=DMARC1; p=none; rua=mailto:dmarc@${domain}`;
|
|
348
|
+
this.dcRouter.dnsServer.registerHandler(`_dmarc.${domain}`, ['TXT'], () => ({
|
|
349
|
+
name: `_dmarc.${domain}`,
|
|
350
|
+
type: 'TXT',
|
|
351
|
+
class: 'IN',
|
|
352
|
+
ttl: ttl,
|
|
353
|
+
data: dmarcRecord
|
|
354
|
+
}));
|
|
355
|
+
logger.log('info', `DMARC record registered for _dmarc.${domain}: "${dmarcRecord}"`);
|
|
356
|
+
// Store DMARC record in StorageManager
|
|
357
|
+
await this.storageManager.set(`/email/dns/${domain}/dmarc`, JSON.stringify({
|
|
358
|
+
type: 'TXT',
|
|
359
|
+
name: `_dmarc.${domain}`,
|
|
360
|
+
data: dmarcRecord,
|
|
361
|
+
ttl: ttl
|
|
362
|
+
}));
|
|
363
|
+
// Log summary of DNS records created
|
|
364
|
+
logger.log('info', `✅ DNS records created for ${domain}:
|
|
365
|
+
- MX: ${domain} (priority ${mxPriority})
|
|
366
|
+
- SPF: ${spfRecord}
|
|
367
|
+
- DMARC: ${dmarcRecord}
|
|
368
|
+
- DKIM: Will be created when keys are generated`);
|
|
369
|
+
}
|
|
370
|
+
catch (error) {
|
|
371
|
+
logger.log('error', `Failed to create DNS records for ${domain}: ${error.message}`);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Create DKIM DNS records for all domains
|
|
377
|
+
*/
|
|
378
|
+
async createDkimRecords(domainConfigs, dkimCreator) {
|
|
379
|
+
for (const domainConfig of domainConfigs) {
|
|
380
|
+
const domain = domainConfig.domain;
|
|
381
|
+
const selector = domainConfig.dkim?.selector || 'default';
|
|
382
|
+
try {
|
|
383
|
+
// Get DKIM DNS record from DKIMCreator
|
|
384
|
+
const dnsRecord = await dkimCreator.getDNSRecordForDomain(domain);
|
|
385
|
+
// For internal-dns domains, register the DNS handler
|
|
386
|
+
if (domainConfig.dnsMode === 'internal-dns' && this.dcRouter.dnsServer) {
|
|
387
|
+
const ttl = domainConfig.dns?.internal?.ttl || 3600;
|
|
388
|
+
this.dcRouter.dnsServer.registerHandler(`${selector}._domainkey.${domain}`, ['TXT'], () => ({
|
|
389
|
+
name: `${selector}._domainkey.${domain}`,
|
|
390
|
+
type: 'TXT',
|
|
391
|
+
class: 'IN',
|
|
392
|
+
ttl: ttl,
|
|
393
|
+
data: dnsRecord.value
|
|
394
|
+
}));
|
|
395
|
+
logger.log('info', `DKIM DNS record registered for ${selector}._domainkey.${domain}`);
|
|
396
|
+
// Store DKIM record in StorageManager
|
|
397
|
+
await this.storageManager.set(`/email/dns/${domain}/dkim`, JSON.stringify({
|
|
398
|
+
type: 'TXT',
|
|
399
|
+
name: `${selector}._domainkey.${domain}`,
|
|
400
|
+
data: dnsRecord.value,
|
|
401
|
+
ttl: ttl
|
|
402
|
+
}));
|
|
403
|
+
}
|
|
404
|
+
// For external-dns domains, just log what should be configured
|
|
405
|
+
if (domainConfig.dnsMode === 'external-dns') {
|
|
406
|
+
logger.log('info', `DKIM record for external DNS: ${dnsRecord.name} -> "${dnsRecord.value}"`);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
catch (error) {
|
|
410
|
+
logger.log('warn', `Could not create DKIM DNS record for ${domain}: ${error.message}`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5kbnMubWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL21haWwvcm91dGluZy9jbGFzc2VzLmRucy5tYW5hZ2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFFNUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBbUN6Qzs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sVUFBVTtJQUNiLFFBQVEsQ0FBZ0I7SUFDeEIsY0FBYyxDQUFzQjtJQUU1QyxZQUFZLFFBQXVCO1FBQ2pDLElBQUksQ0FBQyxRQUFRLEdBQUcsUUFBUSxDQUFDO1FBQ3pCLElBQUksQ0FBQyxjQUFjLEdBQUcsUUFBUSxDQUFDLGNBQWMsQ0FBQztJQUNoRCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxLQUFLLENBQUMsa0JBQWtCLENBQUMsYUFBbUM7UUFDMUQsTUFBTSxPQUFPLEdBQUcsSUFBSSxHQUFHLEVBQWdDLENBQUM7UUFFeEQsS0FBSyxNQUFNLE1BQU0sSUFBSSxhQUFhLEVBQUUsQ0FBQztZQUNuQyxNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDakQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ3JDLENBQUM7UUFFRCxPQUFPLE9BQU8sQ0FBQztJQUNqQixDQUFDO0lBRUQ7O09BRUc7SUFDSCxLQUFLLENBQUMsY0FBYyxDQUFDLE1BQTBCO1FBQzdDLFFBQVEsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ3ZCLEtBQUssU0FBUztnQkFDWixPQUFPLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUMxQyxLQUFLLGNBQWM7Z0JBQ2pCLE9BQU8sSUFBSSxDQUFDLHVCQUF1QixDQUFDLE1BQU0sQ0FBQyxDQUFD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
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { IEmailDomainConfig } from './interfaces.js';
|
|
2
|
+
/**
|
|
3
|
+
* Registry for email domain configurations
|
|
4
|
+
* Provides fast lookups and validation for domains
|
|
5
|
+
*/
|
|
6
|
+
export declare class DomainRegistry {
|
|
7
|
+
private domains;
|
|
8
|
+
private defaults;
|
|
9
|
+
constructor(domainConfigs: IEmailDomainConfig[], defaults?: {
|
|
10
|
+
dnsMode?: 'forward' | 'internal-dns' | 'external-dns';
|
|
11
|
+
dkim?: IEmailDomainConfig['dkim'];
|
|
12
|
+
rateLimits?: IEmailDomainConfig['rateLimits'];
|
|
13
|
+
});
|
|
14
|
+
/**
|
|
15
|
+
* Get default DKIM configuration
|
|
16
|
+
*/
|
|
17
|
+
private getDefaultDkimConfig;
|
|
18
|
+
/**
|
|
19
|
+
* Apply defaults to a domain configuration
|
|
20
|
+
*/
|
|
21
|
+
private applyDefaults;
|
|
22
|
+
/**
|
|
23
|
+
* Check if a domain is registered
|
|
24
|
+
*/
|
|
25
|
+
isDomainRegistered(domain: string): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Check if an email address belongs to a registered domain
|
|
28
|
+
*/
|
|
29
|
+
isEmailRegistered(email: string): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Get domain configuration
|
|
32
|
+
*/
|
|
33
|
+
getDomainConfig(domain: string): IEmailDomainConfig | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Get domain configuration for an email address
|
|
36
|
+
*/
|
|
37
|
+
getEmailDomainConfig(email: string): IEmailDomainConfig | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Extract domain from email address
|
|
40
|
+
*/
|
|
41
|
+
private extractDomain;
|
|
42
|
+
/**
|
|
43
|
+
* Get all registered domains
|
|
44
|
+
*/
|
|
45
|
+
getAllDomains(): string[];
|
|
46
|
+
/**
|
|
47
|
+
* Get all domain configurations
|
|
48
|
+
*/
|
|
49
|
+
getAllConfigs(): IEmailDomainConfig[];
|
|
50
|
+
/**
|
|
51
|
+
* Get domains by DNS mode
|
|
52
|
+
*/
|
|
53
|
+
getDomainsByMode(mode: 'forward' | 'internal-dns' | 'external-dns'): IEmailDomainConfig[];
|
|
54
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { logger } from '../../logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* Registry for email domain configurations
|
|
4
|
+
* Provides fast lookups and validation for domains
|
|
5
|
+
*/
|
|
6
|
+
export class DomainRegistry {
|
|
7
|
+
domains = new Map();
|
|
8
|
+
defaults;
|
|
9
|
+
constructor(domainConfigs, defaults) {
|
|
10
|
+
// Set defaults
|
|
11
|
+
this.defaults = {
|
|
12
|
+
dnsMode: defaults?.dnsMode || 'external-dns',
|
|
13
|
+
...this.getDefaultDkimConfig(),
|
|
14
|
+
...defaults?.dkim,
|
|
15
|
+
rateLimits: defaults?.rateLimits
|
|
16
|
+
};
|
|
17
|
+
// Process and store domain configurations
|
|
18
|
+
for (const config of domainConfigs) {
|
|
19
|
+
const processedConfig = this.applyDefaults(config);
|
|
20
|
+
this.domains.set(config.domain.toLowerCase(), processedConfig);
|
|
21
|
+
logger.log('info', `Registered domain: ${config.domain} with DNS mode: ${processedConfig.dnsMode}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Get default DKIM configuration
|
|
26
|
+
*/
|
|
27
|
+
getDefaultDkimConfig() {
|
|
28
|
+
return {
|
|
29
|
+
selector: 'default',
|
|
30
|
+
keySize: 2048,
|
|
31
|
+
rotateKeys: false,
|
|
32
|
+
rotationInterval: 90
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Apply defaults to a domain configuration
|
|
37
|
+
*/
|
|
38
|
+
applyDefaults(config) {
|
|
39
|
+
return {
|
|
40
|
+
...config,
|
|
41
|
+
dnsMode: config.dnsMode || this.defaults.dnsMode,
|
|
42
|
+
dkim: {
|
|
43
|
+
...this.getDefaultDkimConfig(),
|
|
44
|
+
...this.defaults,
|
|
45
|
+
...config.dkim
|
|
46
|
+
},
|
|
47
|
+
rateLimits: {
|
|
48
|
+
...this.defaults.rateLimits,
|
|
49
|
+
...config.rateLimits,
|
|
50
|
+
outbound: {
|
|
51
|
+
...this.defaults.rateLimits?.outbound,
|
|
52
|
+
...config.rateLimits?.outbound
|
|
53
|
+
},
|
|
54
|
+
inbound: {
|
|
55
|
+
...this.defaults.rateLimits?.inbound,
|
|
56
|
+
...config.rateLimits?.inbound
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Check if a domain is registered
|
|
63
|
+
*/
|
|
64
|
+
isDomainRegistered(domain) {
|
|
65
|
+
return this.domains.has(domain.toLowerCase());
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Check if an email address belongs to a registered domain
|
|
69
|
+
*/
|
|
70
|
+
isEmailRegistered(email) {
|
|
71
|
+
const domain = this.extractDomain(email);
|
|
72
|
+
if (!domain)
|
|
73
|
+
return false;
|
|
74
|
+
return this.isDomainRegistered(domain);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get domain configuration
|
|
78
|
+
*/
|
|
79
|
+
getDomainConfig(domain) {
|
|
80
|
+
return this.domains.get(domain.toLowerCase());
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get domain configuration for an email address
|
|
84
|
+
*/
|
|
85
|
+
getEmailDomainConfig(email) {
|
|
86
|
+
const domain = this.extractDomain(email);
|
|
87
|
+
if (!domain)
|
|
88
|
+
return undefined;
|
|
89
|
+
return this.getDomainConfig(domain);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Extract domain from email address
|
|
93
|
+
*/
|
|
94
|
+
extractDomain(email) {
|
|
95
|
+
const parts = email.toLowerCase().split('@');
|
|
96
|
+
if (parts.length !== 2)
|
|
97
|
+
return null;
|
|
98
|
+
return parts[1];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get all registered domains
|
|
102
|
+
*/
|
|
103
|
+
getAllDomains() {
|
|
104
|
+
return Array.from(this.domains.keys());
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get all domain configurations
|
|
108
|
+
*/
|
|
109
|
+
getAllConfigs() {
|
|
110
|
+
return Array.from(this.domains.values());
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Get domains by DNS mode
|
|
114
|
+
*/
|
|
115
|
+
getDomainsByMode(mode) {
|
|
116
|
+
return Array.from(this.domains.values()).filter(config => config.dnsMode === mode);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5kb21haW4ucmVnaXN0cnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL3JvdXRpbmcvY2xhc3Nlcy5kb21haW4ucmVnaXN0cnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBRXpDOzs7R0FHRztBQUNILE1BQU0sT0FBTyxjQUFjO0lBQ2pCLE9BQU8sR0FBb0MsSUFBSSxHQUFHLEVBQUUsQ0FBQztJQUNyRCxRQUFRLENBR2Q7SUFFRixZQUNFLGFBQW1DLEVBQ25DLFFBSUM7UUFFRCxlQUFlO1FBQ2YsSUFBSSxDQUFDLFFBQVEsR0FBRztZQUNkLE9BQU8sRUFBRSxRQUFRLEVBQUUsT0FBTyxJQUFJLGNBQWM7WUFDNUMsR0FBRyxJQUFJLENBQUMsb0JBQW9CLEVBQUU7WUFDOUIsR0FBRyxRQUFRLEVBQUUsSUFBSTtZQUNqQixVQUFVLEVBQUUsUUFBUSxFQUFFLFVBQVU7U0FDakMsQ0FBQztRQUVGLDBDQUEwQztRQUMxQyxLQUFLLE1BQU0sTUFBTSxJQUFJLGFBQWEsRUFBRSxDQUFDO1lBQ25DLE1BQU0sZUFBZSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDbkQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxXQUFXLEVBQUUsRUFBRSxlQUFlLENBQUMsQ0FBQztZQUMvRCxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxzQkFBc0IsTUFBTSxDQUFDLE1BQU0sbUJBQW1CLGVBQWUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1FBQ3RHLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxvQkFBb0I7UUFDMUIsT0FBTztZQUNMLFFBQVEsRUFBRSxTQUFTO1lBQ25CLE9BQU8sRUFBRSxJQUFJO1lBQ2IsVUFBVSxFQUFFLEtBQUs7WUFDakIsZ0JB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
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { IEmailAction, IEmailContext } from './interfaces.js';
|
|
2
|
+
import { Email } from '../core/classes.email.js';
|
|
3
|
+
import { BounceManager } from '../core/classes.bouncemanager.js';
|
|
4
|
+
import { UnifiedDeliveryQueue } from '../delivery/classes.delivery.queue.js';
|
|
5
|
+
import type { ISmtpSendResult } from '../../security/classes.rustsecuritybridge.js';
|
|
6
|
+
/**
|
|
7
|
+
* Dependencies injected from UnifiedEmailServer to avoid circular imports
|
|
8
|
+
*/
|
|
9
|
+
export interface IActionExecutorDeps {
|
|
10
|
+
sendOutboundEmail: (host: string, port: number, email: Email, options?: {
|
|
11
|
+
auth?: {
|
|
12
|
+
user: string;
|
|
13
|
+
pass: string;
|
|
14
|
+
};
|
|
15
|
+
dkimDomain?: string;
|
|
16
|
+
dkimSelector?: string;
|
|
17
|
+
tlsOpportunistic?: boolean;
|
|
18
|
+
}) => Promise<ISmtpSendResult>;
|
|
19
|
+
bounceManager: BounceManager;
|
|
20
|
+
deliveryQueue: UnifiedDeliveryQueue;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Executes email routing actions (forward, process, deliver, reject)
|
|
24
|
+
*/
|
|
25
|
+
export declare class EmailActionExecutor {
|
|
26
|
+
private deps;
|
|
27
|
+
constructor(deps: IActionExecutorDeps);
|
|
28
|
+
executeAction(action: IEmailAction, email: Email, context: IEmailContext): Promise<void>;
|
|
29
|
+
private handleForwardAction;
|
|
30
|
+
private handleProcessAction;
|
|
31
|
+
private handleDeliverAction;
|
|
32
|
+
private handleRejectAction;
|
|
33
|
+
}
|