@push.rocks/smartmta 9.0.0 → 9.1.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/changelog.md +26 -0
- package/dist_rust/mailer-bin_linux_amd64 +0 -0
- package/dist_rust/mailer-bin_linux_arm64 +0 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +2 -1
- package/dist_ts/mail/delivery/classes.delivery.system.d.ts +7 -0
- package/dist_ts/mail/delivery/classes.delivery.system.js +16 -3
- package/dist_ts/mail/routing/classes.email.router.d.ts +14 -0
- package/dist_ts/mail/routing/classes.email.router.js +35 -7
- package/dist_ts/mail/routing/classes.unified.email.server.d.ts +95 -4
- package/dist_ts/mail/routing/classes.unified.email.server.js +412 -25
- package/dist_ts/mail/routing/functions.routing.validation.d.ts +14 -0
- package/dist_ts/mail/routing/functions.routing.validation.js +97 -1
- package/dist_ts/mail/routing/index.d.ts +1 -0
- package/dist_ts/mail/routing/index.js +2 -1
- package/dist_ts/mail/routing/interfaces.d.ts +59 -0
- package/package.json +1 -1
- package/readme.md +72 -3
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +1 -0
- package/ts/mail/delivery/classes.delivery.system.ts +16 -2
- package/ts/mail/routing/classes.email.router.ts +43 -9
- package/ts/mail/routing/classes.unified.email.server.ts +491 -32
- package/ts/mail/routing/functions.routing.validation.ts +120 -0
- package/ts/mail/routing/index.ts +1 -0
- package/ts/mail/routing/interfaces.ts +58 -0
|
@@ -2,7 +2,7 @@ import { EventEmitter } from 'events';
|
|
|
2
2
|
import { DKIMCreator } from '../security/classes.dkimcreator.js';
|
|
3
3
|
import type { IStorageManager } from '../interfaces.storage.js';
|
|
4
4
|
import type { IInboundEmailSecurityResult } from '../../security/classes.rustsecuritybridge.js';
|
|
5
|
-
import type { IEmailRoute, IEmailAction, IEmailDomainConfig } from './interfaces.js';
|
|
5
|
+
import type { IEmailRoute, IEmailAction, IEmailDomainConfig, ISmtpAuthAccount } from './interfaces.js';
|
|
6
6
|
import { Email } from '../core/classes.email.js';
|
|
7
7
|
import { DomainRegistry } from './classes.domain.registry.js';
|
|
8
8
|
import { BounceType, BounceCategory } from '../core/classes.bouncemanager.js';
|
|
@@ -18,7 +18,7 @@ interface DcRouter {
|
|
|
18
18
|
options?: any;
|
|
19
19
|
}
|
|
20
20
|
export type TRecipientRouteSource = 'explicit-route' | 'domain-policy' | 'no-route';
|
|
21
|
-
export type TRecipientRejectionReason = 'explicit-route' | 'domain-policy' | 'no-route' | 'domain-not-hosted' | 'relay-denied' | 'recipient-hook' | 'policy-error';
|
|
21
|
+
export type TRecipientRejectionReason = 'explicit-route' | 'domain-policy' | 'no-route' | 'domain-not-hosted' | 'relay-denied' | 'recipient-hook' | 'policy-error' | 'sender-scope' | 'recipient-scope' | 'account-disabled';
|
|
22
22
|
export interface IResolvedRecipientRoute {
|
|
23
23
|
recipient: string;
|
|
24
24
|
localDomain: string | null;
|
|
@@ -207,6 +207,11 @@ export interface IUnifiedEmailServerOptions {
|
|
|
207
207
|
username: string;
|
|
208
208
|
password: string;
|
|
209
209
|
}>;
|
|
210
|
+
/**
|
|
211
|
+
* Authenticated SMTP accounts with hashed credentials and send scopes.
|
|
212
|
+
* Coexists with legacy `users`; accounts win on username collision.
|
|
213
|
+
*/
|
|
214
|
+
accounts?: ISmtpAuthAccount[];
|
|
210
215
|
};
|
|
211
216
|
tls?: {
|
|
212
217
|
certPem?: string;
|
|
@@ -346,6 +351,15 @@ export declare class UnifiedEmailServer extends EventEmitter {
|
|
|
346
351
|
private acceptedEnvelopeDispatchCleanupTimer?;
|
|
347
352
|
private acceptedEnvelopeDispatchCleanupPromise?;
|
|
348
353
|
private acceptedEnvelopeDispatchCleanupCursor?;
|
|
354
|
+
private smtpListenerActive;
|
|
355
|
+
private smtpListenerShutdown;
|
|
356
|
+
private smtpListenerRestartPromise?;
|
|
357
|
+
private static readonly VERIFIER_DERIVATION_CONCURRENCY;
|
|
358
|
+
private static readonly VERIFIER_DERIVATION_MAX_QUEUE;
|
|
359
|
+
private verifierDerivationActive;
|
|
360
|
+
private verifierDerivationWaiters;
|
|
361
|
+
/** Compiled scope patterns; keys are config-supplied, bounded by config size. */
|
|
362
|
+
private scopePatternCache;
|
|
349
363
|
constructor(dcRouter: DcRouter, options: IUnifiedEmailServerOptions);
|
|
350
364
|
onRecipientRejected(listener: (event: IRecipientRejectedEvent) => Promise<void> | void): this;
|
|
351
365
|
offRecipientRejected(listener: (event: IRecipientRejectedEvent) => Promise<void> | void): this;
|
|
@@ -391,6 +405,23 @@ export declare class UnifiedEmailServer extends EventEmitter {
|
|
|
391
405
|
private unregisterBridgeStateHandler;
|
|
392
406
|
private unregisterBridgeEventHandlers;
|
|
393
407
|
private startSmtpServer;
|
|
408
|
+
/** Whether the current options require SMTP AUTH support on the listener. */
|
|
409
|
+
private isAuthConfigured;
|
|
410
|
+
/**
|
|
411
|
+
* Whether the Rust SMTP listener is currently accepting connections.
|
|
412
|
+
* Embedders should surface this next to getBridgeState() in health
|
|
413
|
+
* endpoints: a failed listener reconfiguration leaves it false.
|
|
414
|
+
*/
|
|
415
|
+
isSmtpListenerActive(): boolean;
|
|
416
|
+
/**
|
|
417
|
+
* Restart only the Rust SMTP listener so listener-level configuration such
|
|
418
|
+
* as AUTH availability takes effect without a full process restart.
|
|
419
|
+
* Restarts are serialized and become no-ops once shutdown has begun; a
|
|
420
|
+
* failed restart leaves the listener inactive, logs loudly, and emits
|
|
421
|
+
* 'smtpListenerRestartFailed' so embedders can react (e.g. by calling
|
|
422
|
+
* stop()/start()).
|
|
423
|
+
*/
|
|
424
|
+
private scheduleSmtpListenerRestart;
|
|
394
425
|
/**
|
|
395
426
|
* Stop the unified email server
|
|
396
427
|
*/
|
|
@@ -402,6 +433,39 @@ export declare class UnifiedEmailServer extends EventEmitter {
|
|
|
402
433
|
private notifyRecipientRejected;
|
|
403
434
|
private buildPolicySession;
|
|
404
435
|
private getAddressDomain;
|
|
436
|
+
/** Resolve the configured account for a username. Accounts win over legacy users. */
|
|
437
|
+
private findAuthAccount;
|
|
438
|
+
private normalizeScopeAddress;
|
|
439
|
+
/**
|
|
440
|
+
* Glob match ('a@b.com' | '*@b.com') against a normalized address, using
|
|
441
|
+
* the same pattern semantics as route matching. Compiled patterns are
|
|
442
|
+
* cached; keys are configuration-supplied (never remote-controlled), so
|
|
443
|
+
* the cache is bounded by account configuration size.
|
|
444
|
+
*/
|
|
445
|
+
private matchesScopePatterns;
|
|
446
|
+
/**
|
|
447
|
+
* Envelope-level scope decision for an authenticated session. Returns null
|
|
448
|
+
* when the envelope is permitted (unauthenticated sessions, usernames with
|
|
449
|
+
* no configured account, and accounts without a scope are unrestricted).
|
|
450
|
+
*/
|
|
451
|
+
private resolveAuthAccountScopeDecision;
|
|
452
|
+
/**
|
|
453
|
+
* Extract every mailbox address from the parsed From header. The Email
|
|
454
|
+
* abstraction keeps only the first From mailbox, which is not sufficient
|
|
455
|
+
* for scope enforcement: `From: allowed@a.com, victim@b.com` must not
|
|
456
|
+
* pass. Parsing stops at the first CRLFCRLF header boundary when present
|
|
457
|
+
* (the Rust receiver normalizes line endings to CRLF); a bodyless message
|
|
458
|
+
* without one is parsed whole.
|
|
459
|
+
*/
|
|
460
|
+
private parseHeaderFromMailboxes;
|
|
461
|
+
/**
|
|
462
|
+
* Header-From alignment for scoped authenticated accounts at DATA. The
|
|
463
|
+
* envelope MAIL FROM is enforced per recipient via resolveRecipientPolicy
|
|
464
|
+
* from session.envelope truth; every parsed header From mailbox must
|
|
465
|
+
* independently stay in scope because route matching and downstream
|
|
466
|
+
* consumers see the header identity.
|
|
467
|
+
*/
|
|
468
|
+
private checkAuthenticatedHeaderFromScope;
|
|
405
469
|
private getRecipientValidationTimeoutMs;
|
|
406
470
|
private isRecipientValidationEnabled;
|
|
407
471
|
private getMessageAcceptanceTimeoutMs;
|
|
@@ -440,12 +504,39 @@ export declare class UnifiedEmailServer extends EventEmitter {
|
|
|
440
504
|
private isStandardParsedHeader;
|
|
441
505
|
private stringifyParsedHeaderValue;
|
|
442
506
|
/**
|
|
443
|
-
*
|
|
507
|
+
* Compute SCRAM-SHA-256 credential fields from a plaintext password with a
|
|
508
|
+
* fresh random salt (used for legacy plaintext credentials only; verifier
|
|
509
|
+
* accounts return their stored fields directly).
|
|
510
|
+
*/
|
|
511
|
+
private computeScramCredentials;
|
|
512
|
+
/**
|
|
513
|
+
* Bound concurrent PBKDF2 derivations so credential-stuffing pressure at
|
|
514
|
+
* high configured iteration counts cannot exhaust the shared libuv
|
|
515
|
+
* threadpool. Returns false when the wait queue is saturated (fail fast).
|
|
516
|
+
*/
|
|
517
|
+
private acquireVerifierDerivationSlot;
|
|
518
|
+
private releaseVerifierDerivationSlot;
|
|
519
|
+
/**
|
|
520
|
+
* Verify a presented plaintext password against a stored SCRAM verifier by
|
|
521
|
+
* deriving StoredKey from the presented password and comparing in constant
|
|
522
|
+
* time. PBKDF2 runs on the async crypto threadpool so caller-configured
|
|
523
|
+
* iteration counts never block the event loop inside the Rust auth
|
|
524
|
+
* callback window; iteration bounds are enforced at configuration time and
|
|
525
|
+
* in-flight derivations are bounded by a small semaphore.
|
|
526
|
+
*/
|
|
527
|
+
private verifyPasswordAgainstScramVerifier;
|
|
528
|
+
/** Constant-time string comparison for legacy plaintext credentials. */
|
|
529
|
+
private timingSafeStringEqual;
|
|
530
|
+
/**
|
|
531
|
+
* Handle an authRequest event from the Rust SMTP server (PLAIN/LOGIN).
|
|
532
|
+
* Accounts win over legacy users on username collision; disabled accounts
|
|
533
|
+
* always fail.
|
|
444
534
|
*/
|
|
445
535
|
private handleRustAuthRequest;
|
|
446
536
|
/**
|
|
447
537
|
* Handle a SCRAM credential request from the Rust SMTP server.
|
|
448
|
-
*
|
|
538
|
+
* Verifier accounts return their stored fields directly; plaintext
|
|
539
|
+
* credentials (accounts or legacy users) derive fields on the fly.
|
|
449
540
|
*/
|
|
450
541
|
private handleScramCredentialRequest;
|
|
451
542
|
/**
|