@serve.zone/dcrouter 2.12.4
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/.dockerignore +1 -0
- package/.gitea/workflows/docker_nottags.yaml +71 -0
- package/.gitea/workflows/docker_tags.yaml +106 -0
- package/.vscode/launch.json +11 -0
- package/.vscode/settings.json +26 -0
- package/Dockerfile +46 -0
- package/changelog.md +247 -0
- package/cli.child.js +4 -0
- package/cli.child.ts +4 -0
- package/cli.js +4 -0
- package/cli.ts.js +5 -0
- package/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/classes.dcrouter.d.ts +238 -0
- package/dist_ts/classes.dcrouter.js +1008 -0
- package/dist_ts/config/index.d.ts +1 -0
- package/dist_ts/config/index.js +3 -0
- package/dist_ts/config/validator.d.ts +104 -0
- package/dist_ts/config/validator.js +152 -0
- package/dist_ts/deliverability/classes.ipwarmupmanager.d.ts +253 -0
- package/dist_ts/deliverability/classes.ipwarmupmanager.js +639 -0
- package/dist_ts/deliverability/classes.senderreputationmonitor.d.ts +300 -0
- package/dist_ts/deliverability/classes.senderreputationmonitor.js +961 -0
- package/dist_ts/deliverability/index.d.ts +2 -0
- package/dist_ts/deliverability/index.js +3 -0
- package/dist_ts/errors/base.errors.d.ts +224 -0
- package/dist_ts/errors/base.errors.js +310 -0
- package/dist_ts/errors/email.errors.d.ts +175 -0
- package/dist_ts/errors/email.errors.js +265 -0
- package/dist_ts/errors/error-handler.d.ts +98 -0
- package/dist_ts/errors/error-handler.js +282 -0
- package/dist_ts/errors/error.codes.d.ts +115 -0
- package/dist_ts/errors/error.codes.js +136 -0
- package/dist_ts/errors/index.d.ts +56 -0
- package/dist_ts/errors/index.js +138 -0
- package/dist_ts/errors/mta.errors.d.ts +259 -0
- package/dist_ts/errors/mta.errors.js +472 -0
- package/dist_ts/errors/reputation.errors.d.ts +183 -0
- package/dist_ts/errors/reputation.errors.js +292 -0
- package/dist_ts/index.d.ts +4 -0
- package/dist_ts/index.js +6 -0
- package/dist_ts/logger.d.ts +17 -0
- package/dist_ts/logger.js +77 -0
- package/dist_ts/mail/core/classes.bouncemanager.d.ts +200 -0
- package/dist_ts/mail/core/classes.bouncemanager.js +778 -0
- package/dist_ts/mail/core/classes.email.d.ts +291 -0
- package/dist_ts/mail/core/classes.email.js +780 -0
- package/dist_ts/mail/core/classes.emailvalidator.d.ts +61 -0
- package/dist_ts/mail/core/classes.emailvalidator.js +182 -0
- package/dist_ts/mail/core/classes.templatemanager.d.ts +95 -0
- package/dist_ts/mail/core/classes.templatemanager.js +239 -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 +485 -0
- package/dist_ts/mail/delivery/classes.delivery.system.d.ts +186 -0
- package/dist_ts/mail/delivery/classes.delivery.system.js +846 -0
- package/dist_ts/mail/delivery/classes.emailsendjob.d.ts +84 -0
- package/dist_ts/mail/delivery/classes.emailsendjob.js +362 -0
- package/dist_ts/mail/delivery/classes.emailsignjob.d.ts +18 -0
- package/dist_ts/mail/delivery/classes.emailsignjob.js +44 -0
- package/dist_ts/mail/delivery/classes.mta.config.d.ts +22 -0
- package/dist_ts/mail/delivery/classes.mta.config.js +51 -0
- package/dist_ts/mail/delivery/classes.ratelimiter.d.ts +98 -0
- package/dist_ts/mail/delivery/classes.ratelimiter.js +205 -0
- package/dist_ts/mail/delivery/classes.smtp.client.legacy.d.ts +275 -0
- package/dist_ts/mail/delivery/classes.smtp.client.legacy.js +973 -0
- package/dist_ts/mail/delivery/classes.unified.rate.limiter.d.ts +200 -0
- package/dist_ts/mail/delivery/classes.unified.rate.limiter.js +817 -0
- package/dist_ts/mail/delivery/index.d.ts +12 -0
- package/dist_ts/mail/delivery/index.js +18 -0
- package/dist_ts/mail/delivery/interfaces.d.ts +243 -0
- package/dist_ts/mail/delivery/interfaces.js +17 -0
- package/dist_ts/mail/delivery/smtpclient/auth-handler.d.ts +43 -0
- package/dist_ts/mail/delivery/smtpclient/auth-handler.js +188 -0
- package/dist_ts/mail/delivery/smtpclient/command-handler.d.ts +67 -0
- package/dist_ts/mail/delivery/smtpclient/command-handler.js +276 -0
- package/dist_ts/mail/delivery/smtpclient/connection-manager.d.ts +48 -0
- package/dist_ts/mail/delivery/smtpclient/connection-manager.js +238 -0
- package/dist_ts/mail/delivery/smtpclient/constants.d.ts +129 -0
- package/dist_ts/mail/delivery/smtpclient/constants.js +135 -0
- package/dist_ts/mail/delivery/smtpclient/create-client.d.ts +22 -0
- package/dist_ts/mail/delivery/smtpclient/create-client.js +86 -0
- package/dist_ts/mail/delivery/smtpclient/error-handler.d.ts +28 -0
- package/dist_ts/mail/delivery/smtpclient/error-handler.js +110 -0
- package/dist_ts/mail/delivery/smtpclient/index.d.ts +16 -0
- package/dist_ts/mail/delivery/smtpclient/index.js +21 -0
- package/dist_ts/mail/delivery/smtpclient/interfaces.d.ts +183 -0
- package/dist_ts/mail/delivery/smtpclient/interfaces.js +19 -0
- package/dist_ts/mail/delivery/smtpclient/smtp-client.d.ts +58 -0
- package/dist_ts/mail/delivery/smtpclient/smtp-client.js +279 -0
- package/dist_ts/mail/delivery/smtpclient/tls-handler.d.ts +33 -0
- package/dist_ts/mail/delivery/smtpclient/tls-handler.js +202 -0
- package/dist_ts/mail/delivery/smtpclient/utils/helpers.d.ts +77 -0
- package/dist_ts/mail/delivery/smtpclient/utils/helpers.js +196 -0
- package/dist_ts/mail/delivery/smtpclient/utils/logging.d.ts +46 -0
- package/dist_ts/mail/delivery/smtpclient/utils/logging.js +153 -0
- package/dist_ts/mail/delivery/smtpclient/utils/validation.d.ts +38 -0
- package/dist_ts/mail/delivery/smtpclient/utils/validation.js +139 -0
- package/dist_ts/mail/delivery/smtpserver/certificate-utils.d.ts +45 -0
- package/dist_ts/mail/delivery/smtpserver/certificate-utils.js +345 -0
- package/dist_ts/mail/delivery/smtpserver/command-handler.d.ts +156 -0
- package/dist_ts/mail/delivery/smtpserver/command-handler.js +1159 -0
- package/dist_ts/mail/delivery/smtpserver/connection-manager.d.ts +159 -0
- package/dist_ts/mail/delivery/smtpserver/connection-manager.js +894 -0
- package/dist_ts/mail/delivery/smtpserver/constants.d.ts +130 -0
- package/dist_ts/mail/delivery/smtpserver/constants.js +162 -0
- package/dist_ts/mail/delivery/smtpserver/create-server.d.ts +14 -0
- package/dist_ts/mail/delivery/smtpserver/create-server.js +28 -0
- package/dist_ts/mail/delivery/smtpserver/data-handler.d.ts +123 -0
- package/dist_ts/mail/delivery/smtpserver/data-handler.js +1148 -0
- package/dist_ts/mail/delivery/smtpserver/index.d.ts +20 -0
- package/dist_ts/mail/delivery/smtpserver/index.js +27 -0
- package/dist_ts/mail/delivery/smtpserver/interfaces.d.ts +530 -0
- package/dist_ts/mail/delivery/smtpserver/interfaces.js +10 -0
- package/dist_ts/mail/delivery/smtpserver/secure-server.d.ts +15 -0
- package/dist_ts/mail/delivery/smtpserver/secure-server.js +79 -0
- package/dist_ts/mail/delivery/smtpserver/security-handler.d.ts +86 -0
- package/dist_ts/mail/delivery/smtpserver/security-handler.js +234 -0
- package/dist_ts/mail/delivery/smtpserver/session-manager.d.ts +140 -0
- package/dist_ts/mail/delivery/smtpserver/session-manager.js +469 -0
- package/dist_ts/mail/delivery/smtpserver/smtp-server.d.ts +137 -0
- package/dist_ts/mail/delivery/smtpserver/smtp-server.js +666 -0
- package/dist_ts/mail/delivery/smtpserver/starttls-handler.d.ts +21 -0
- package/dist_ts/mail/delivery/smtpserver/starttls-handler.js +207 -0
- package/dist_ts/mail/delivery/smtpserver/tls-handler.d.ts +66 -0
- package/dist_ts/mail/delivery/smtpserver/tls-handler.js +261 -0
- package/dist_ts/mail/delivery/smtpserver/utils/adaptive-logging.d.ts +117 -0
- package/dist_ts/mail/delivery/smtpserver/utils/adaptive-logging.js +411 -0
- package/dist_ts/mail/delivery/smtpserver/utils/helpers.d.ts +78 -0
- package/dist_ts/mail/delivery/smtpserver/utils/helpers.js +208 -0
- package/dist_ts/mail/delivery/smtpserver/utils/logging.d.ts +106 -0
- package/dist_ts/mail/delivery/smtpserver/utils/logging.js +181 -0
- package/dist_ts/mail/delivery/smtpserver/utils/validation.d.ts +69 -0
- package/dist_ts/mail/delivery/smtpserver/utils/validation.js +360 -0
- package/dist_ts/mail/index.d.ts +8 -0
- package/dist_ts/mail/index.js +13 -0
- package/dist_ts/mail/routing/classes.dns.manager.d.ts +65 -0
- package/dist_ts/mail/routing/classes.dns.manager.js +413 -0
- package/dist_ts/mail/routing/classes.dnsmanager.d.ts +165 -0
- package/dist_ts/mail/routing/classes.dnsmanager.js +430 -0
- package/dist_ts/mail/routing/classes.domain.registry.d.ts +54 -0
- package/dist_ts/mail/routing/classes.domain.registry.js +118 -0
- package/dist_ts/mail/routing/classes.email.config.d.ts +64 -0
- package/dist_ts/mail/routing/classes.email.config.js +2 -0
- package/dist_ts/mail/routing/classes.email.router.d.ts +171 -0
- package/dist_ts/mail/routing/classes.email.router.js +491 -0
- package/dist_ts/mail/routing/classes.unified.email.server.d.ts +426 -0
- package/dist_ts/mail/routing/classes.unified.email.server.js +1454 -0
- package/dist_ts/mail/routing/index.d.ts +5 -0
- package/dist_ts/mail/routing/index.js +7 -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 +68 -0
- package/dist_ts/mail/security/classes.dkimcreator.js +346 -0
- package/dist_ts/mail/security/classes.dkimverifier.d.ts +46 -0
- package/dist_ts/mail/security/classes.dkimverifier.js +317 -0
- package/dist_ts/mail/security/classes.dmarcverifier.d.ts +123 -0
- package/dist_ts/mail/security/classes.dmarcverifier.js +365 -0
- package/dist_ts/mail/security/classes.spfverifier.d.ts +103 -0
- package/dist_ts/mail/security/classes.spfverifier.js +492 -0
- package/dist_ts/mail/security/index.d.ts +4 -0
- package/dist_ts/mail/security/index.js +6 -0
- package/dist_ts/opsserver/classes.opsserver.d.ts +14 -0
- package/dist_ts/opsserver/classes.opsserver.js +37 -0
- package/dist_ts/opsserver/index.d.ts +1 -0
- package/dist_ts/opsserver/index.js +2 -0
- package/dist_ts/paths.d.ts +14 -0
- package/dist_ts/paths.js +39 -0
- package/dist_ts/plugins.d.ts +43 -0
- package/dist_ts/plugins.js +50 -0
- package/dist_ts/security/classes.contentscanner.d.ts +160 -0
- package/dist_ts/security/classes.contentscanner.js +634 -0
- package/dist_ts/security/classes.ipreputationchecker.d.ts +150 -0
- package/dist_ts/security/classes.ipreputationchecker.js +508 -0
- package/dist_ts/security/classes.securitylogger.d.ts +140 -0
- package/dist_ts/security/classes.securitylogger.js +232 -0
- package/dist_ts/security/index.d.ts +3 -0
- package/dist_ts/security/index.js +4 -0
- package/dist_ts/sms/classes.smsservice.d.ts +15 -0
- package/dist_ts/sms/classes.smsservice.js +72 -0
- package/dist_ts/sms/config/sms.config.d.ts +93 -0
- package/dist_ts/sms/config/sms.config.js +2 -0
- package/dist_ts/sms/config/sms.schema.d.ts +5 -0
- package/dist_ts/sms/config/sms.schema.js +121 -0
- package/dist_ts/sms/index.d.ts +1 -0
- package/dist_ts/sms/index.js +2 -0
- package/dist_ts/storage/classes.storagemanager.d.ts +82 -0
- package/dist_ts/storage/classes.storagemanager.js +341 -0
- package/dist_ts/storage/index.d.ts +1 -0
- package/dist_ts/storage/index.js +3 -0
- package/dist_ts/ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/ts/00_commitinfo_data.js +9 -0
- package/dist_ts/ts/classes.dcrouter.d.ts +238 -0
- package/dist_ts/ts/classes.dcrouter.js +1008 -0
- package/dist_ts/ts/config/index.d.ts +1 -0
- package/dist_ts/ts/config/index.js +3 -0
- package/dist_ts/ts/config/validator.d.ts +104 -0
- package/dist_ts/ts/config/validator.js +152 -0
- package/dist_ts/ts/deliverability/classes.ipwarmupmanager.d.ts +253 -0
- package/dist_ts/ts/deliverability/classes.ipwarmupmanager.js +639 -0
- package/dist_ts/ts/deliverability/classes.senderreputationmonitor.d.ts +300 -0
- package/dist_ts/ts/deliverability/classes.senderreputationmonitor.js +961 -0
- package/dist_ts/ts/deliverability/index.d.ts +2 -0
- package/dist_ts/ts/deliverability/index.js +3 -0
- package/dist_ts/ts/errors/base.errors.d.ts +224 -0
- package/dist_ts/ts/errors/base.errors.js +310 -0
- package/dist_ts/ts/errors/email.errors.d.ts +175 -0
- package/dist_ts/ts/errors/email.errors.js +265 -0
- package/dist_ts/ts/errors/error-handler.d.ts +98 -0
- package/dist_ts/ts/errors/error-handler.js +282 -0
- package/dist_ts/ts/errors/error.codes.d.ts +115 -0
- package/dist_ts/ts/errors/error.codes.js +136 -0
- package/dist_ts/ts/errors/index.d.ts +56 -0
- package/dist_ts/ts/errors/index.js +138 -0
- package/dist_ts/ts/errors/mta.errors.d.ts +259 -0
- package/dist_ts/ts/errors/mta.errors.js +472 -0
- package/dist_ts/ts/errors/reputation.errors.d.ts +183 -0
- package/dist_ts/ts/errors/reputation.errors.js +292 -0
- package/dist_ts/ts/index.d.ts +4 -0
- package/dist_ts/ts/index.js +6 -0
- package/dist_ts/ts/logger.d.ts +17 -0
- package/dist_ts/ts/logger.js +77 -0
- package/dist_ts/ts/mail/core/classes.bouncemanager.d.ts +200 -0
- package/dist_ts/ts/mail/core/classes.bouncemanager.js +778 -0
- package/dist_ts/ts/mail/core/classes.email.d.ts +291 -0
- package/dist_ts/ts/mail/core/classes.email.js +780 -0
- package/dist_ts/ts/mail/core/classes.emailvalidator.d.ts +61 -0
- package/dist_ts/ts/mail/core/classes.emailvalidator.js +182 -0
- package/dist_ts/ts/mail/core/classes.templatemanager.d.ts +95 -0
- package/dist_ts/ts/mail/core/classes.templatemanager.js +239 -0
- package/dist_ts/ts/mail/core/index.d.ts +4 -0
- package/dist_ts/ts/mail/core/index.js +6 -0
- package/dist_ts/ts/mail/delivery/classes.delivery.queue.d.ts +163 -0
- package/dist_ts/ts/mail/delivery/classes.delivery.queue.js +485 -0
- package/dist_ts/ts/mail/delivery/classes.delivery.system.d.ts +186 -0
- package/dist_ts/ts/mail/delivery/classes.delivery.system.js +846 -0
- package/dist_ts/ts/mail/delivery/classes.emailsendjob.d.ts +84 -0
- package/dist_ts/ts/mail/delivery/classes.emailsendjob.js +362 -0
- package/dist_ts/ts/mail/delivery/classes.emailsignjob.d.ts +18 -0
- package/dist_ts/ts/mail/delivery/classes.emailsignjob.js +44 -0
- package/dist_ts/ts/mail/delivery/classes.mta.config.d.ts +22 -0
- package/dist_ts/ts/mail/delivery/classes.mta.config.js +51 -0
- package/dist_ts/ts/mail/delivery/classes.ratelimiter.d.ts +98 -0
- package/dist_ts/ts/mail/delivery/classes.ratelimiter.js +205 -0
- package/dist_ts/ts/mail/delivery/classes.smtp.client.legacy.d.ts +275 -0
- package/dist_ts/ts/mail/delivery/classes.smtp.client.legacy.js +973 -0
- package/dist_ts/ts/mail/delivery/classes.unified.rate.limiter.d.ts +200 -0
- package/dist_ts/ts/mail/delivery/classes.unified.rate.limiter.js +817 -0
- package/dist_ts/ts/mail/delivery/index.d.ts +12 -0
- package/dist_ts/ts/mail/delivery/index.js +18 -0
- package/dist_ts/ts/mail/delivery/interfaces.d.ts +243 -0
- package/dist_ts/ts/mail/delivery/interfaces.js +17 -0
- package/dist_ts/ts/mail/delivery/smtpclient/auth-handler.d.ts +43 -0
- package/dist_ts/ts/mail/delivery/smtpclient/auth-handler.js +188 -0
- package/dist_ts/ts/mail/delivery/smtpclient/command-handler.d.ts +67 -0
- package/dist_ts/ts/mail/delivery/smtpclient/command-handler.js +276 -0
- package/dist_ts/ts/mail/delivery/smtpclient/connection-manager.d.ts +48 -0
- package/dist_ts/ts/mail/delivery/smtpclient/connection-manager.js +238 -0
- package/dist_ts/ts/mail/delivery/smtpclient/constants.d.ts +129 -0
- package/dist_ts/ts/mail/delivery/smtpclient/constants.js +135 -0
- package/dist_ts/ts/mail/delivery/smtpclient/create-client.d.ts +22 -0
- package/dist_ts/ts/mail/delivery/smtpclient/create-client.js +86 -0
- package/dist_ts/ts/mail/delivery/smtpclient/error-handler.d.ts +28 -0
- package/dist_ts/ts/mail/delivery/smtpclient/error-handler.js +110 -0
- package/dist_ts/ts/mail/delivery/smtpclient/index.d.ts +16 -0
- package/dist_ts/ts/mail/delivery/smtpclient/index.js +21 -0
- package/dist_ts/ts/mail/delivery/smtpclient/interfaces.d.ts +183 -0
- package/dist_ts/ts/mail/delivery/smtpclient/interfaces.js +19 -0
- package/dist_ts/ts/mail/delivery/smtpclient/smtp-client.d.ts +58 -0
- package/dist_ts/ts/mail/delivery/smtpclient/smtp-client.js +279 -0
- package/dist_ts/ts/mail/delivery/smtpclient/tls-handler.d.ts +33 -0
- package/dist_ts/ts/mail/delivery/smtpclient/tls-handler.js +202 -0
- package/dist_ts/ts/mail/delivery/smtpclient/utils/helpers.d.ts +77 -0
- package/dist_ts/ts/mail/delivery/smtpclient/utils/helpers.js +196 -0
- package/dist_ts/ts/mail/delivery/smtpclient/utils/logging.d.ts +46 -0
- package/dist_ts/ts/mail/delivery/smtpclient/utils/logging.js +153 -0
- package/dist_ts/ts/mail/delivery/smtpclient/utils/validation.d.ts +38 -0
- package/dist_ts/ts/mail/delivery/smtpclient/utils/validation.js +139 -0
- package/dist_ts/ts/mail/delivery/smtpserver/certificate-utils.d.ts +45 -0
- package/dist_ts/ts/mail/delivery/smtpserver/certificate-utils.js +345 -0
- package/dist_ts/ts/mail/delivery/smtpserver/command-handler.d.ts +156 -0
- package/dist_ts/ts/mail/delivery/smtpserver/command-handler.js +1159 -0
- package/dist_ts/ts/mail/delivery/smtpserver/connection-manager.d.ts +159 -0
- package/dist_ts/ts/mail/delivery/smtpserver/connection-manager.js +894 -0
- package/dist_ts/ts/mail/delivery/smtpserver/constants.d.ts +130 -0
- package/dist_ts/ts/mail/delivery/smtpserver/constants.js +162 -0
- package/dist_ts/ts/mail/delivery/smtpserver/create-server.d.ts +14 -0
- package/dist_ts/ts/mail/delivery/smtpserver/create-server.js +28 -0
- package/dist_ts/ts/mail/delivery/smtpserver/data-handler.d.ts +123 -0
- package/dist_ts/ts/mail/delivery/smtpserver/data-handler.js +1148 -0
- package/dist_ts/ts/mail/delivery/smtpserver/index.d.ts +20 -0
- package/dist_ts/ts/mail/delivery/smtpserver/index.js +27 -0
- package/dist_ts/ts/mail/delivery/smtpserver/interfaces.d.ts +530 -0
- package/dist_ts/ts/mail/delivery/smtpserver/interfaces.js +10 -0
- package/dist_ts/ts/mail/delivery/smtpserver/secure-server.d.ts +15 -0
- package/dist_ts/ts/mail/delivery/smtpserver/secure-server.js +79 -0
- package/dist_ts/ts/mail/delivery/smtpserver/security-handler.d.ts +86 -0
- package/dist_ts/ts/mail/delivery/smtpserver/security-handler.js +234 -0
- package/dist_ts/ts/mail/delivery/smtpserver/session-manager.d.ts +140 -0
- package/dist_ts/ts/mail/delivery/smtpserver/session-manager.js +469 -0
- package/dist_ts/ts/mail/delivery/smtpserver/smtp-server.d.ts +137 -0
- package/dist_ts/ts/mail/delivery/smtpserver/smtp-server.js +666 -0
- package/dist_ts/ts/mail/delivery/smtpserver/starttls-handler.d.ts +21 -0
- package/dist_ts/ts/mail/delivery/smtpserver/starttls-handler.js +207 -0
- package/dist_ts/ts/mail/delivery/smtpserver/tls-handler.d.ts +66 -0
- package/dist_ts/ts/mail/delivery/smtpserver/tls-handler.js +261 -0
- package/dist_ts/ts/mail/delivery/smtpserver/utils/adaptive-logging.d.ts +117 -0
- package/dist_ts/ts/mail/delivery/smtpserver/utils/adaptive-logging.js +411 -0
- package/dist_ts/ts/mail/delivery/smtpserver/utils/helpers.d.ts +78 -0
- package/dist_ts/ts/mail/delivery/smtpserver/utils/helpers.js +208 -0
- package/dist_ts/ts/mail/delivery/smtpserver/utils/logging.d.ts +106 -0
- package/dist_ts/ts/mail/delivery/smtpserver/utils/logging.js +181 -0
- package/dist_ts/ts/mail/delivery/smtpserver/utils/validation.d.ts +69 -0
- package/dist_ts/ts/mail/delivery/smtpserver/utils/validation.js +360 -0
- package/dist_ts/ts/mail/index.d.ts +8 -0
- package/dist_ts/ts/mail/index.js +13 -0
- package/dist_ts/ts/mail/routing/classes.dns.manager.d.ts +65 -0
- package/dist_ts/ts/mail/routing/classes.dns.manager.js +413 -0
- package/dist_ts/ts/mail/routing/classes.dnsmanager.d.ts +165 -0
- package/dist_ts/ts/mail/routing/classes.dnsmanager.js +430 -0
- package/dist_ts/ts/mail/routing/classes.domain.registry.d.ts +54 -0
- package/dist_ts/ts/mail/routing/classes.domain.registry.js +118 -0
- package/dist_ts/ts/mail/routing/classes.email.config.d.ts +64 -0
- package/dist_ts/ts/mail/routing/classes.email.config.js +2 -0
- package/dist_ts/ts/mail/routing/classes.email.router.d.ts +171 -0
- package/dist_ts/ts/mail/routing/classes.email.router.js +491 -0
- package/dist_ts/ts/mail/routing/classes.unified.email.server.d.ts +426 -0
- package/dist_ts/ts/mail/routing/classes.unified.email.server.js +1454 -0
- package/dist_ts/ts/mail/routing/index.d.ts +5 -0
- package/dist_ts/ts/mail/routing/index.js +7 -0
- package/dist_ts/ts/mail/routing/interfaces.d.ts +187 -0
- package/dist_ts/ts/mail/routing/interfaces.js +2 -0
- package/dist_ts/ts/mail/security/classes.dkimcreator.d.ts +68 -0
- package/dist_ts/ts/mail/security/classes.dkimcreator.js +346 -0
- package/dist_ts/ts/mail/security/classes.dkimverifier.d.ts +46 -0
- package/dist_ts/ts/mail/security/classes.dkimverifier.js +317 -0
- package/dist_ts/ts/mail/security/classes.dmarcverifier.d.ts +123 -0
- package/dist_ts/ts/mail/security/classes.dmarcverifier.js +365 -0
- package/dist_ts/ts/mail/security/classes.spfverifier.d.ts +103 -0
- package/dist_ts/ts/mail/security/classes.spfverifier.js +492 -0
- package/dist_ts/ts/mail/security/index.d.ts +4 -0
- package/dist_ts/ts/mail/security/index.js +6 -0
- package/dist_ts/ts/opsserver/classes.opsserver.d.ts +20 -0
- package/dist_ts/ts/opsserver/classes.opsserver.js +44 -0
- package/dist_ts/ts/opsserver/handlers/admin.handler.d.ts +31 -0
- package/dist_ts/ts/opsserver/handlers/admin.handler.js +177 -0
- package/dist_ts/ts/opsserver/handlers/config.handler.d.ts +10 -0
- package/dist_ts/ts/opsserver/handlers/config.handler.js +100 -0
- package/dist_ts/ts/opsserver/handlers/index.d.ts +5 -0
- package/dist_ts/ts/opsserver/handlers/index.js +6 -0
- package/dist_ts/ts/opsserver/handlers/logs.handler.d.ts +10 -0
- package/dist_ts/ts/opsserver/handlers/logs.handler.js +121 -0
- package/dist_ts/ts/opsserver/handlers/security.handler.d.ts +11 -0
- package/dist_ts/ts/opsserver/handlers/security.handler.js +118 -0
- package/dist_ts/ts/opsserver/handlers/stats.handler.d.ts +13 -0
- package/dist_ts/ts/opsserver/handlers/stats.handler.js +233 -0
- package/dist_ts/ts/opsserver/helpers/guards.d.ts +25 -0
- package/dist_ts/ts/opsserver/helpers/guards.js +41 -0
- package/dist_ts/ts/opsserver/index.d.ts +1 -0
- package/dist_ts/ts/opsserver/index.js +2 -0
- package/dist_ts/ts/paths.d.ts +14 -0
- package/dist_ts/ts/paths.js +39 -0
- package/dist_ts/ts/plugins.d.ts +46 -0
- package/dist_ts/ts/plugins.js +53 -0
- package/dist_ts/ts/security/classes.contentscanner.d.ts +160 -0
- package/dist_ts/ts/security/classes.contentscanner.js +634 -0
- package/dist_ts/ts/security/classes.ipreputationchecker.d.ts +150 -0
- package/dist_ts/ts/security/classes.ipreputationchecker.js +508 -0
- package/dist_ts/ts/security/classes.securitylogger.d.ts +140 -0
- package/dist_ts/ts/security/classes.securitylogger.js +232 -0
- package/dist_ts/ts/security/index.d.ts +3 -0
- package/dist_ts/ts/security/index.js +4 -0
- package/dist_ts/ts/sms/classes.smsservice.d.ts +15 -0
- package/dist_ts/ts/sms/classes.smsservice.js +72 -0
- package/dist_ts/ts/sms/config/sms.config.d.ts +93 -0
- package/dist_ts/ts/sms/config/sms.config.js +2 -0
- package/dist_ts/ts/sms/config/sms.schema.d.ts +5 -0
- package/dist_ts/ts/sms/config/sms.schema.js +121 -0
- package/dist_ts/ts/sms/index.d.ts +1 -0
- package/dist_ts/ts/sms/index.js +2 -0
- package/dist_ts/ts/storage/classes.storagemanager.d.ts +82 -0
- package/dist_ts/ts/storage/classes.storagemanager.js +341 -0
- package/dist_ts/ts/storage/index.d.ts +1 -0
- package/dist_ts/ts/storage/index.js +3 -0
- package/dist_ts/ts_interfaces/data/auth.d.ts +8 -0
- package/dist_ts/ts_interfaces/data/auth.js +2 -0
- package/dist_ts/ts_interfaces/data/index.d.ts +2 -0
- package/dist_ts/ts_interfaces/data/index.js +3 -0
- package/dist_ts/ts_interfaces/data/stats.d.ts +93 -0
- package/dist_ts/ts_interfaces/data/stats.js +2 -0
- package/dist_ts/ts_interfaces/index.d.ts +5 -0
- package/dist_ts/ts_interfaces/index.js +8 -0
- package/dist_ts/ts_interfaces/plugins.d.ts +2 -0
- package/dist_ts/ts_interfaces/plugins.js +4 -0
- package/dist_ts/ts_interfaces/requests/admin.d.ts +31 -0
- package/dist_ts/ts_interfaces/requests/admin.js +3 -0
- package/dist_ts/ts_interfaces/requests/config.d.ts +25 -0
- package/dist_ts/ts_interfaces/requests/config.js +3 -0
- package/dist_ts/ts_interfaces/requests/index.d.ts +4 -0
- package/dist_ts/ts_interfaces/requests/index.js +5 -0
- package/dist_ts/ts_interfaces/requests/logs.d.ts +34 -0
- package/dist_ts/ts_interfaces/requests/logs.js +4 -0
- package/dist_ts/ts_interfaces/requests/stats.d.ts +131 -0
- package/dist_ts/ts_interfaces/requests/stats.js +4 -0
- package/html/index.html +121 -0
- package/npmextra.json +45 -0
- package/package.json +83 -0
- package/readme.hints.md +906 -0
- package/readme.md +1253 -0
- package/readme.opsserver.md +351 -0
- package/test/helpers/server.loader.ts +347 -0
- package/test/helpers/smtp.client.ts +209 -0
- package/test/helpers/utils.ts +311 -0
- package/test/readme.md +443 -0
- package/test/suite/smtpclient_commands/test.ccmd-01.ehlo-helo-sending.ts +168 -0
- package/test/suite/smtpclient_commands/test.ccmd-02.mail-from-parameters.ts +277 -0
- package/test/suite/smtpclient_commands/test.ccmd-03.rcpt-to-multiple.ts +283 -0
- package/test/suite/smtpclient_commands/test.ccmd-04.data-transmission.ts +274 -0
- package/test/suite/smtpclient_commands/test.ccmd-05.auth-mechanisms.ts +306 -0
- package/test/suite/smtpclient_commands/test.ccmd-06.command-pipelining.ts +233 -0
- package/test/suite/smtpclient_commands/test.ccmd-07.response-parsing.ts +243 -0
- package/test/suite/smtpclient_commands/test.ccmd-08.rset-command.ts +333 -0
- package/test/suite/smtpclient_commands/test.ccmd-09.noop-command.ts +339 -0
- package/test/suite/smtpclient_commands/test.ccmd-10.vrfy-expn.ts +457 -0
- package/test/suite/smtpclient_commands/test.ccmd-11.help-command.ts +409 -0
- package/test/suite/smtpclient_connection/test.ccm-01.basic-tcp-connection.ts +150 -0
- package/test/suite/smtpclient_connection/test.ccm-02.tls-connection.ts +140 -0
- package/test/suite/smtpclient_connection/test.ccm-03.starttls-upgrade.ts +208 -0
- package/test/suite/smtpclient_connection/test.ccm-04.connection-pooling.ts +250 -0
- package/test/suite/smtpclient_connection/test.ccm-05.connection-reuse.ts +288 -0
- package/test/suite/smtpclient_connection/test.ccm-06.connection-timeout.ts +267 -0
- package/test/suite/smtpclient_connection/test.ccm-07.automatic-reconnection.ts +324 -0
- package/test/suite/smtpclient_connection/test.ccm-08.dns-resolution.ts +139 -0
- package/test/suite/smtpclient_connection/test.ccm-09.ipv6-dual-stack.ts +167 -0
- package/test/suite/smtpclient_connection/test.ccm-10.proxy-support.ts +305 -0
- package/test/suite/smtpclient_connection/test.ccm-11.keepalive.ts +299 -0
- package/test/suite/smtpclient_edge-cases/test.cedge-01.unusual-server-responses.ts +529 -0
- package/test/suite/smtpclient_edge-cases/test.cedge-02.malformed-commands.ts +438 -0
- package/test/suite/smtpclient_edge-cases/test.cedge-03.protocol-violations.ts +446 -0
- package/test/suite/smtpclient_edge-cases/test.cedge-04.resource-constraints.ts +530 -0
- package/test/suite/smtpclient_edge-cases/test.cedge-05.encoding-issues.ts +145 -0
- package/test/suite/smtpclient_edge-cases/test.cedge-06.large-headers.ts +180 -0
- package/test/suite/smtpclient_edge-cases/test.cedge-07.concurrent-operations.ts +204 -0
- package/test/suite/smtpclient_email-composition/test.cep-01.basic-headers.ts +245 -0
- package/test/suite/smtpclient_email-composition/test.cep-02.mime-multipart.ts +321 -0
- package/test/suite/smtpclient_email-composition/test.cep-03.attachment-encoding.ts +334 -0
- package/test/suite/smtpclient_email-composition/test.cep-04.bcc-handling.ts +187 -0
- package/test/suite/smtpclient_email-composition/test.cep-05.reply-to-return-path.ts +277 -0
- package/test/suite/smtpclient_email-composition/test.cep-06.utf8-international.ts +235 -0
- package/test/suite/smtpclient_email-composition/test.cep-07.html-inline-images.ts +489 -0
- package/test/suite/smtpclient_email-composition/test.cep-08.custom-headers.ts +293 -0
- package/test/suite/smtpclient_email-composition/test.cep-09.priority-importance.ts +314 -0
- package/test/suite/smtpclient_email-composition/test.cep-10.receipts-dsn.ts +411 -0
- package/test/suite/smtpclient_error-handling/test.cerr-01.4xx-errors.ts +232 -0
- package/test/suite/smtpclient_error-handling/test.cerr-02.5xx-errors.ts +309 -0
- package/test/suite/smtpclient_error-handling/test.cerr-03.network-failures.ts +299 -0
- package/test/suite/smtpclient_error-handling/test.cerr-04.greylisting-handling.ts +255 -0
- package/test/suite/smtpclient_error-handling/test.cerr-05.quota-exceeded.ts +273 -0
- package/test/suite/smtpclient_error-handling/test.cerr-06.invalid-recipients.ts +320 -0
- package/test/suite/smtpclient_error-handling/test.cerr-07.message-size-limits.ts +320 -0
- package/test/suite/smtpclient_error-handling/test.cerr-08.rate-limiting.ts +261 -0
- package/test/suite/smtpclient_error-handling/test.cerr-09.connection-pool-errors.ts +299 -0
- package/test/suite/smtpclient_error-handling/test.cerr-10.partial-failure.ts +373 -0
- package/test/suite/smtpclient_performance/test.cperf-01.bulk-sending.ts +332 -0
- package/test/suite/smtpclient_performance/test.cperf-02.message-throughput.ts +304 -0
- package/test/suite/smtpclient_performance/test.cperf-03.memory-usage.ts +332 -0
- package/test/suite/smtpclient_performance/test.cperf-04.cpu-utilization.ts +373 -0
- package/test/suite/smtpclient_performance/test.cperf-05.network-efficiency.ts +181 -0
- package/test/suite/smtpclient_performance/test.cperf-06.caching-strategies.ts +190 -0
- package/test/suite/smtpclient_performance/test.cperf-07.queue-management.ts +171 -0
- package/test/suite/smtpclient_performance/test.cperf-08.dns-caching.ts +50 -0
- package/test/suite/smtpclient_reliability/test.crel-01.reconnection-logic.ts +305 -0
- package/test/suite/smtpclient_reliability/test.crel-02.network-interruption.ts +207 -0
- package/test/suite/smtpclient_reliability/test.crel-03.queue-persistence.ts +469 -0
- package/test/suite/smtpclient_reliability/test.crel-04.crash-recovery.ts +520 -0
- package/test/suite/smtpclient_reliability/test.crel-05.memory-leaks.ts +503 -0
- package/test/suite/smtpclient_reliability/test.crel-06.concurrency-safety.ts +558 -0
- package/test/suite/smtpclient_reliability/test.crel-07.resource-cleanup.ts +52 -0
- package/test/suite/smtpclient_rfc-compliance/test.crfc-01.rfc5321-client.ts +283 -0
- package/test/suite/smtpclient_rfc-compliance/test.crfc-02.esmtp-compliance.ts +77 -0
- package/test/suite/smtpclient_rfc-compliance/test.crfc-03.command-syntax.ts +67 -0
- package/test/suite/smtpclient_rfc-compliance/test.crfc-04.response-codes.ts +54 -0
- package/test/suite/smtpclient_rfc-compliance/test.crfc-05.state-machine.ts +703 -0
- package/test/suite/smtpclient_rfc-compliance/test.crfc-06.protocol-negotiation.ts +688 -0
- package/test/suite/smtpclient_rfc-compliance/test.crfc-07.interoperability.ts +728 -0
- package/test/suite/smtpclient_rfc-compliance/test.crfc-08.smtp-extensions.ts +656 -0
- package/test/suite/smtpclient_security/test.csec-01.tls-verification.ts +88 -0
- package/test/suite/smtpclient_security/test.csec-02.oauth2-authentication.ts +132 -0
- package/test/suite/smtpclient_security/test.csec-03.dkim-signing.ts +138 -0
- package/test/suite/smtpclient_security/test.csec-04.spf-compliance.ts +163 -0
- package/test/suite/smtpclient_security/test.csec-05.dmarc-policy.ts +200 -0
- package/test/suite/smtpclient_security/test.csec-06.certificate-validation.ts +145 -0
- package/test/suite/smtpclient_security/test.csec-07.cipher-suites.ts +153 -0
- package/test/suite/smtpclient_security/test.csec-08.authentication-fallback.ts +154 -0
- package/test/suite/smtpclient_security/test.csec-09.relay-restrictions.ts +166 -0
- package/test/suite/smtpclient_security/test.csec-10.anti-spam-measures.ts +196 -0
- package/test/suite/smtpserver_commands/test.cmd-01.ehlo-command.ts +193 -0
- package/test/suite/smtpserver_commands/test.cmd-02.mail-from.ts +330 -0
- package/test/suite/smtpserver_commands/test.cmd-03.rcpt-to.ts +296 -0
- package/test/suite/smtpserver_commands/test.cmd-04.data-command.ts +395 -0
- package/test/suite/smtpserver_commands/test.cmd-05.noop-command.ts +320 -0
- package/test/suite/smtpserver_commands/test.cmd-06.rset-command.ts +399 -0
- package/test/suite/smtpserver_commands/test.cmd-07.vrfy-command.ts +391 -0
- package/test/suite/smtpserver_commands/test.cmd-08.expn-command.ts +450 -0
- package/test/suite/smtpserver_commands/test.cmd-09.size-extension.ts +465 -0
- package/test/suite/smtpserver_commands/test.cmd-10.help-command.ts +454 -0
- package/test/suite/smtpserver_commands/test.cmd-11.command-pipelining.ts +334 -0
- package/test/suite/smtpserver_commands/test.cmd-12.helo-command.ts +420 -0
- package/test/suite/smtpserver_commands/test.cmd-13.quit-command.ts +384 -0
- package/test/suite/smtpserver_connection/test.cm-01.tls-connection.ts +61 -0
- package/test/suite/smtpserver_connection/test.cm-02.multiple-connections.ts +112 -0
- package/test/suite/smtpserver_connection/test.cm-03.connection-timeout.ts +134 -0
- package/test/suite/smtpserver_connection/test.cm-04.connection-limits.ts +374 -0
- package/test/suite/smtpserver_connection/test.cm-05.connection-rejection.ts +296 -0
- package/test/suite/smtpserver_connection/test.cm-06.starttls-upgrade.ts +468 -0
- package/test/suite/smtpserver_connection/test.cm-07.abrupt-disconnection.ts +321 -0
- package/test/suite/smtpserver_connection/test.cm-08.tls-versions.ts +361 -0
- package/test/suite/smtpserver_connection/test.cm-09.tls-ciphers.ts +556 -0
- package/test/suite/smtpserver_connection/test.cm-10.plain-connection.ts +293 -0
- package/test/suite/smtpserver_connection/test.cm-11.keepalive.ts +382 -0
- package/test/suite/smtpserver_edge-cases/test.edge-01.very-large-email.ts +239 -0
- package/test/suite/smtpserver_edge-cases/test.edge-02.very-small-email.ts +389 -0
- package/test/suite/smtpserver_edge-cases/test.edge-03.invalid-character-handling.ts +479 -0
- package/test/suite/smtpserver_edge-cases/test.edge-04.empty-commands.ts +430 -0
- package/test/suite/smtpserver_edge-cases/test.edge-05.extremely-long-lines.ts +425 -0
- package/test/suite/smtpserver_edge-cases/test.edge-06.extremely-long-headers.ts +404 -0
- package/test/suite/smtpserver_edge-cases/test.edge-07.unusual-mime-types.ts +333 -0
- package/test/suite/smtpserver_edge-cases/test.edge-08.nested-mime-structures.ts +379 -0
- package/test/suite/smtpserver_email-processing/test.ep-01.basic-email-sending.ts +338 -0
- package/test/suite/smtpserver_email-processing/test.ep-02.invalid-email-addresses.ts +315 -0
- package/test/suite/smtpserver_email-processing/test.ep-03.multiple-recipients.ts +493 -0
- package/test/suite/smtpserver_email-processing/test.ep-04.large-email.ts +528 -0
- package/test/suite/smtpserver_email-processing/test.ep-05.mime-handling.ts +515 -0
- package/test/suite/smtpserver_email-processing/test.ep-06.attachment-handling.ts +629 -0
- package/test/suite/smtpserver_email-processing/test.ep-07.special-character-handling.ts +462 -0
- package/test/suite/smtpserver_email-processing/test.ep-08.email-routing.ts +527 -0
- package/test/suite/smtpserver_email-processing/test.ep-09.delivery-status-notifications.ts +486 -0
- package/test/suite/smtpserver_error-handling/test.err-01.syntax-errors.ts +475 -0
- package/test/suite/smtpserver_error-handling/test.err-02.invalid-sequence.ts +450 -0
- package/test/suite/smtpserver_error-handling/test.err-03.temporary-failures.ts +453 -0
- package/test/suite/smtpserver_error-handling/test.err-04.permanent-failures.ts +325 -0
- package/test/suite/smtpserver_error-handling/test.err-05.resource-exhaustion.ts +302 -0
- package/test/suite/smtpserver_error-handling/test.err-06.malformed-mime.ts +374 -0
- package/test/suite/smtpserver_error-handling/test.err-07.exception-handling.ts +333 -0
- package/test/suite/smtpserver_error-handling/test.err-08.error-logging.ts +324 -0
- package/test/suite/smtpserver_performance/test.perf-01.throughput.ts +183 -0
- package/test/suite/smtpserver_performance/test.perf-02.concurrency.ts +388 -0
- package/test/suite/smtpserver_performance/test.perf-03.cpu-utilization.ts +245 -0
- package/test/suite/smtpserver_performance/test.perf-04.memory-usage.ts +238 -0
- package/test/suite/smtpserver_performance/test.perf-05.connection-processing-time.ts +363 -0
- package/test/suite/smtpserver_performance/test.perf-06.message-processing-time.ts +252 -0
- package/test/suite/smtpserver_performance/test.perf-07.resource-cleanup.ts +317 -0
- package/test/suite/smtpserver_reliability/test.rel-01.long-running-operation.ts +344 -0
- package/test/suite/smtpserver_reliability/test.rel-02.restart-recovery.ts +328 -0
- package/test/suite/smtpserver_reliability/test.rel-03.resource-leak-detection.ts +394 -0
- package/test/suite/smtpserver_reliability/test.rel-04.error-recovery.ts +401 -0
- package/test/suite/smtpserver_reliability/test.rel-05.dns-resolution-failure.ts +335 -0
- package/test/suite/smtpserver_reliability/test.rel-06.network-interruption.ts +410 -0
- package/test/suite/smtpserver_rfc-compliance/test.rfc-01.rfc5321-compliance.ts +382 -0
- package/test/suite/smtpserver_rfc-compliance/test.rfc-02.rfc5322-compliance.ts +428 -0
- package/test/suite/smtpserver_rfc-compliance/test.rfc-03.rfc7208-spf-compliance.ts +330 -0
- package/test/suite/smtpserver_rfc-compliance/test.rfc-04.rfc6376-dkim-compliance.ts +450 -0
- package/test/suite/smtpserver_rfc-compliance/test.rfc-05.rfc7489-dmarc-compliance.ts +408 -0
- package/test/suite/smtpserver_rfc-compliance/test.rfc-06.rfc8314-tls-compliance.ts +366 -0
- package/test/suite/smtpserver_rfc-compliance/test.rfc-07.rfc3461-dsn-compliance.ts +399 -0
- package/test/suite/smtpserver_security/test.sec-01.authentication.ts +218 -0
- package/test/suite/smtpserver_security/test.sec-02.authorization.ts +286 -0
- package/test/suite/smtpserver_security/test.sec-03.dkim-processing.ts +414 -0
- package/test/suite/smtpserver_security/test.sec-04.spf-checking.ts +280 -0
- package/test/suite/smtpserver_security/test.sec-05.dmarc-policy.ts +374 -0
- package/test/suite/smtpserver_security/test.sec-06.ip-reputation.ts +303 -0
- package/test/suite/smtpserver_security/test.sec-07.content-scanning.ts +409 -0
- package/test/suite/smtpserver_security/test.sec-08.rate-limiting.ts +324 -0
- package/test/suite/smtpserver_security/test.sec-09.tls-certificate-validation.ts +312 -0
- package/test/suite/smtpserver_security/test.sec-10.header-injection-prevention.ts +332 -0
- package/test/suite/smtpserver_security/test.sec-11.bounce-management.ts +363 -0
- package/test/test.base.ts +65 -0
- package/test/test.bouncemanager.ts +196 -0
- package/test/test.config.md +175 -0
- package/test/test.contentscanner.ts +265 -0
- package/test/test.dcrouter.email.ts +201 -0
- package/test/test.deliverability.ts +55 -0
- package/test/test.dns-manager-creation.ts +141 -0
- package/test/test.dns-mode-switching.ts +257 -0
- package/test/test.dns-server-config.ts +140 -0
- package/test/test.dns-socket-handler.ts +169 -0
- package/test/test.dns-validation.ts +283 -0
- package/test/test.email-socket-handler.ts +228 -0
- package/test/test.email.integration.ts +377 -0
- package/test/test.email.router.ts +283 -0
- package/test/test.emailauth.ts +195 -0
- package/test/test.errors.ts +408 -0
- package/test/test.integration.storage.ts +313 -0
- package/test/test.integration.ts +75 -0
- package/test/test.ipreputationchecker.ts +179 -0
- package/test/test.ipwarmupmanager.ts +323 -0
- package/test/test.jwt-auth.ts +130 -0
- package/test/test.minimal.ts +66 -0
- package/test/test.opsserver-api.ts +83 -0
- package/test/test.protected-endpoint.ts +115 -0
- package/test/test.rate-limiting-integration.ts +236 -0
- package/test/test.ratelimiter.ts +141 -0
- package/test/test.reputationmonitor.ts +262 -0
- package/test/test.smartmail.ts +248 -0
- package/test/test.smtp.client.compatibility.ts +154 -0
- package/test/test.smtp.client.ts +191 -0
- package/test/test.smtp.server.ts +180 -0
- package/test/test.socket-handler-integration.ts +240 -0
- package/test/test.socket-handler-unit.ts +198 -0
- package/test/test.storagemanager.ts +289 -0
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/classes.dcrouter.ts +1310 -0
- package/ts/config/index.ts +2 -0
- package/ts/config/validator.ts +266 -0
- package/ts/deliverability/classes.ipwarmupmanager.ts +896 -0
- package/ts/deliverability/classes.senderreputationmonitor.ts +1244 -0
- package/ts/deliverability/index.ts +13 -0
- package/ts/errors/base.errors.ts +525 -0
- package/ts/errors/email.errors.ts +383 -0
- package/ts/errors/error-handler.ts +412 -0
- package/ts/errors/error.codes.ts +165 -0
- package/ts/errors/index.ts +195 -0
- package/ts/errors/mta.errors.ts +681 -0
- package/ts/errors/reputation.errors.ts +422 -0
- package/ts/index.ts +7 -0
- package/ts/logger.ts +91 -0
- package/ts/mail/core/classes.bouncemanager.ts +965 -0
- package/ts/mail/core/classes.email.ts +941 -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 +1089 -0
- package/ts/mail/delivery/classes.emailsendjob.ts +447 -0
- package/ts/mail/delivery/classes.emailsendjob.ts.backup +691 -0
- package/ts/mail/delivery/classes.emailsignjob.ts +67 -0
- package/ts/mail/delivery/classes.mta.config.ts +73 -0
- package/ts/mail/delivery/classes.ratelimiter.ts +281 -0
- package/ts/mail/delivery/classes.smtp.client.legacy.ts +1422 -0
- package/ts/mail/delivery/classes.unified.rate.limiter.ts +1053 -0
- package/ts/mail/delivery/index.ts +24 -0
- package/ts/mail/delivery/interfaces.ts +291 -0
- package/ts/mail/delivery/smtpclient/auth-handler.ts +232 -0
- package/ts/mail/delivery/smtpclient/command-handler.ts +343 -0
- package/ts/mail/delivery/smtpclient/connection-manager.ts +289 -0
- package/ts/mail/delivery/smtpclient/constants.ts +145 -0
- package/ts/mail/delivery/smtpclient/create-client.ts +94 -0
- package/ts/mail/delivery/smtpclient/error-handler.ts +141 -0
- package/ts/mail/delivery/smtpclient/index.ts +24 -0
- package/ts/mail/delivery/smtpclient/interfaces.ts +242 -0
- package/ts/mail/delivery/smtpclient/smtp-client.ts +357 -0
- package/ts/mail/delivery/smtpclient/tls-handler.ts +254 -0
- package/ts/mail/delivery/smtpclient/utils/helpers.ts +224 -0
- package/ts/mail/delivery/smtpclient/utils/logging.ts +212 -0
- package/ts/mail/delivery/smtpclient/utils/validation.ts +170 -0
- package/ts/mail/delivery/smtpserver/certificate-utils.ts +398 -0
- package/ts/mail/delivery/smtpserver/command-handler.ts +1340 -0
- package/ts/mail/delivery/smtpserver/connection-manager.ts +1045 -0
- package/ts/mail/delivery/smtpserver/constants.ts +181 -0
- package/ts/mail/delivery/smtpserver/create-server.ts +31 -0
- package/ts/mail/delivery/smtpserver/data-handler.ts +1283 -0
- package/ts/mail/delivery/smtpserver/index.ts +32 -0
- package/ts/mail/delivery/smtpserver/interfaces.ts +655 -0
- package/ts/mail/delivery/smtpserver/secure-server.ts +97 -0
- package/ts/mail/delivery/smtpserver/security-handler.ts +345 -0
- package/ts/mail/delivery/smtpserver/session-manager.ts +557 -0
- package/ts/mail/delivery/smtpserver/smtp-server.ts +804 -0
- package/ts/mail/delivery/smtpserver/starttls-handler.ts +262 -0
- package/ts/mail/delivery/smtpserver/tls-handler.ts +346 -0
- package/ts/mail/delivery/smtpserver/utils/adaptive-logging.ts +514 -0
- package/ts/mail/delivery/smtpserver/utils/helpers.ts +246 -0
- package/ts/mail/delivery/smtpserver/utils/logging.ts +246 -0
- package/ts/mail/delivery/smtpserver/utils/validation.ts +436 -0
- package/ts/mail/index.ts +19 -0
- package/ts/mail/routing/classes.dns.manager.ts +563 -0
- package/ts/mail/routing/classes.dnsmanager.ts +559 -0
- package/ts/mail/routing/classes.domain.registry.ts +139 -0
- package/ts/mail/routing/classes.email.config.ts +82 -0
- package/ts/mail/routing/classes.email.router.ts +575 -0
- package/ts/mail/routing/classes.unified.email.server.ts +1873 -0
- package/ts/mail/routing/index.ts +6 -0
- package/ts/mail/routing/interfaces.ts +202 -0
- package/ts/mail/security/classes.dkimcreator.ts +431 -0
- package/ts/mail/security/classes.dkimverifier.ts +382 -0
- package/ts/mail/security/classes.dmarcverifier.ts +478 -0
- package/ts/mail/security/classes.spfverifier.ts +606 -0
- package/ts/mail/security/index.ts +5 -0
- package/ts/opsserver/classes.opsserver.ts +65 -0
- package/ts/opsserver/handlers/admin.handler.ts +240 -0
- package/ts/opsserver/handlers/config.handler.ts +150 -0
- package/ts/opsserver/handlers/index.ts +5 -0
- package/ts/opsserver/handlers/logs.handler.ts +195 -0
- package/ts/opsserver/handlers/security.handler.ts +208 -0
- package/ts/opsserver/handlers/stats.handler.ts +344 -0
- package/ts/opsserver/helpers/guards.ts +56 -0
- package/ts/opsserver/index.ts +1 -0
- package/ts/paths.ts +48 -0
- package/ts/plugins.ts +94 -0
- package/ts/security/classes.contentscanner.ts +739 -0
- package/ts/security/classes.ipreputationchecker.ts +592 -0
- package/ts/security/classes.securitylogger.ts +299 -0
- package/ts/security/index.ts +21 -0
- package/ts/sms/classes.smsservice.ts +98 -0
- package/ts/sms/config/sms.config.ts +109 -0
- package/ts/sms/config/sms.schema.ts +122 -0
- package/ts/sms/index.ts +1 -0
- package/ts/storage/classes.storagemanager.ts +400 -0
- package/ts/storage/index.ts +2 -0
- package/ts/tspublish.json +3 -0
- package/ts_interfaces/data/auth.ts +8 -0
- package/ts_interfaces/data/index.ts +2 -0
- package/ts_interfaces/data/stats.ts +101 -0
- package/ts_interfaces/index.ts +9 -0
- package/ts_interfaces/plugins.ts +6 -0
- package/ts_interfaces/requests/admin.ts +46 -0
- package/ts_interfaces/requests/config.ts +35 -0
- package/ts_interfaces/requests/index.ts +4 -0
- package/ts_interfaces/requests/logs.ts +44 -0
- package/ts_interfaces/requests/stats.ts +162 -0
- package/ts_interfaces/tspublish.json +3 -0
- package/ts_web/00_commitinfo_data.ts +8 -0
- package/ts_web/appstate.ts +361 -0
- package/ts_web/elements/index.ts +7 -0
- package/ts_web/elements/ops-dashboard.ts +165 -0
- package/ts_web/elements/ops-view-config.ts +268 -0
- package/ts_web/elements/ops-view-logs.ts +207 -0
- package/ts_web/elements/ops-view-overview.ts +222 -0
- package/ts_web/elements/ops-view-security.ts +471 -0
- package/ts_web/elements/ops-view-stats.ts +299 -0
- package/ts_web/elements/shared/css.ts +10 -0
- package/ts_web/elements/shared/index.ts +2 -0
- package/ts_web/elements/shared/ops-sectionheading.ts +42 -0
- package/ts_web/index.ts +9 -0
- package/ts_web/plugins.ts +11 -0
- package/ts_web/tspublish.json +3 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,1873 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import * as paths from '../../paths.js';
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
4
|
+
import { logger } from '../../logger.js';
|
|
5
|
+
import {
|
|
6
|
+
SecurityLogger,
|
|
7
|
+
SecurityLogLevel,
|
|
8
|
+
SecurityEventType
|
|
9
|
+
} from '../../security/index.js';
|
|
10
|
+
import { DKIMCreator } from '../security/classes.dkimcreator.js';
|
|
11
|
+
import { IPReputationChecker } from '../../security/classes.ipreputationchecker.js';
|
|
12
|
+
import {
|
|
13
|
+
IPWarmupManager,
|
|
14
|
+
type IIPWarmupConfig,
|
|
15
|
+
SenderReputationMonitor,
|
|
16
|
+
type IReputationMonitorConfig
|
|
17
|
+
} from '../../deliverability/index.js';
|
|
18
|
+
import { EmailRouter } from './classes.email.router.js';
|
|
19
|
+
import type { IEmailRoute, IEmailAction, IEmailContext, IEmailDomainConfig } from './interfaces.js';
|
|
20
|
+
import { Email } from '../core/classes.email.js';
|
|
21
|
+
import { DomainRegistry } from './classes.domain.registry.js';
|
|
22
|
+
import { DnsManager } from './classes.dns.manager.js';
|
|
23
|
+
import { BounceManager, BounceType, BounceCategory } from '../core/classes.bouncemanager.js';
|
|
24
|
+
import { createSmtpServer } from '../delivery/smtpserver/index.js';
|
|
25
|
+
import { createPooledSmtpClient } from '../delivery/smtpclient/create-client.js';
|
|
26
|
+
import type { SmtpClient } from '../delivery/smtpclient/smtp-client.js';
|
|
27
|
+
import { MultiModeDeliverySystem, type IMultiModeDeliveryOptions } from '../delivery/classes.delivery.system.js';
|
|
28
|
+
import { UnifiedDeliveryQueue, type IQueueOptions } from '../delivery/classes.delivery.queue.js';
|
|
29
|
+
import { UnifiedRateLimiter, type IHierarchicalRateLimits } from '../delivery/classes.unified.rate.limiter.js';
|
|
30
|
+
import { SmtpState } from '../delivery/interfaces.js';
|
|
31
|
+
import type { EmailProcessingMode, ISmtpSession as IBaseSmtpSession } from '../delivery/interfaces.js';
|
|
32
|
+
import type { DcRouter } from '../../classes.dcrouter.js';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Extended SMTP session interface with route information
|
|
36
|
+
*/
|
|
37
|
+
export interface IExtendedSmtpSession extends ISmtpSession {
|
|
38
|
+
/**
|
|
39
|
+
* Matched route for this session
|
|
40
|
+
*/
|
|
41
|
+
matchedRoute?: IEmailRoute;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Options for the unified email server
|
|
46
|
+
*/
|
|
47
|
+
export interface IUnifiedEmailServerOptions {
|
|
48
|
+
// Base server options
|
|
49
|
+
ports: number[];
|
|
50
|
+
hostname: string;
|
|
51
|
+
domains: IEmailDomainConfig[]; // Domain configurations
|
|
52
|
+
banner?: string;
|
|
53
|
+
debug?: boolean;
|
|
54
|
+
useSocketHandler?: boolean; // Use socket-handler mode instead of port listening
|
|
55
|
+
|
|
56
|
+
// Authentication options
|
|
57
|
+
auth?: {
|
|
58
|
+
required?: boolean;
|
|
59
|
+
methods?: ('PLAIN' | 'LOGIN' | 'OAUTH2')[];
|
|
60
|
+
users?: Array<{username: string, password: string}>;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// TLS options
|
|
64
|
+
tls?: {
|
|
65
|
+
certPath?: string;
|
|
66
|
+
keyPath?: string;
|
|
67
|
+
caPath?: string;
|
|
68
|
+
minVersion?: string;
|
|
69
|
+
ciphers?: string;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// Limits
|
|
73
|
+
maxMessageSize?: number;
|
|
74
|
+
maxClients?: number;
|
|
75
|
+
maxConnections?: number;
|
|
76
|
+
|
|
77
|
+
// Connection options
|
|
78
|
+
connectionTimeout?: number;
|
|
79
|
+
socketTimeout?: number;
|
|
80
|
+
|
|
81
|
+
// Email routing rules
|
|
82
|
+
routes: IEmailRoute[];
|
|
83
|
+
|
|
84
|
+
// Global defaults for all domains
|
|
85
|
+
defaults?: {
|
|
86
|
+
dnsMode?: 'forward' | 'internal-dns' | 'external-dns';
|
|
87
|
+
dkim?: IEmailDomainConfig['dkim'];
|
|
88
|
+
rateLimits?: IEmailDomainConfig['rateLimits'];
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// Outbound settings
|
|
92
|
+
outbound?: {
|
|
93
|
+
maxConnections?: number;
|
|
94
|
+
connectionTimeout?: number;
|
|
95
|
+
socketTimeout?: number;
|
|
96
|
+
retryAttempts?: number;
|
|
97
|
+
defaultFrom?: string;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// Rate limiting (global limits, can be overridden per domain)
|
|
101
|
+
rateLimits?: IHierarchicalRateLimits;
|
|
102
|
+
|
|
103
|
+
// Deliverability options
|
|
104
|
+
ipWarmupConfig?: IIPWarmupConfig;
|
|
105
|
+
reputationMonitorConfig?: IReputationMonitorConfig;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Extended SMTP session interface for UnifiedEmailServer
|
|
111
|
+
*/
|
|
112
|
+
export interface ISmtpSession extends IBaseSmtpSession {
|
|
113
|
+
/**
|
|
114
|
+
* User information if authenticated
|
|
115
|
+
*/
|
|
116
|
+
user?: {
|
|
117
|
+
username: string;
|
|
118
|
+
[key: string]: any;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Matched route for this session
|
|
123
|
+
*/
|
|
124
|
+
matchedRoute?: IEmailRoute;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Authentication data for SMTP
|
|
129
|
+
*/
|
|
130
|
+
import type { ISmtpAuth } from '../delivery/interfaces.js';
|
|
131
|
+
export type IAuthData = ISmtpAuth;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Server statistics
|
|
135
|
+
*/
|
|
136
|
+
export interface IServerStats {
|
|
137
|
+
startTime: Date;
|
|
138
|
+
connections: {
|
|
139
|
+
current: number;
|
|
140
|
+
total: number;
|
|
141
|
+
};
|
|
142
|
+
messages: {
|
|
143
|
+
processed: number;
|
|
144
|
+
delivered: number;
|
|
145
|
+
failed: number;
|
|
146
|
+
};
|
|
147
|
+
processingTime: {
|
|
148
|
+
avg: number;
|
|
149
|
+
max: number;
|
|
150
|
+
min: number;
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Unified email server that handles all email traffic with pattern-based routing
|
|
156
|
+
*/
|
|
157
|
+
export class UnifiedEmailServer extends EventEmitter {
|
|
158
|
+
private dcRouter: DcRouter;
|
|
159
|
+
private options: IUnifiedEmailServerOptions;
|
|
160
|
+
private emailRouter: EmailRouter;
|
|
161
|
+
private domainRegistry: DomainRegistry;
|
|
162
|
+
private servers: any[] = [];
|
|
163
|
+
private stats: IServerStats;
|
|
164
|
+
|
|
165
|
+
// Add components needed for sending and securing emails
|
|
166
|
+
public dkimCreator: DKIMCreator;
|
|
167
|
+
private ipReputationChecker: IPReputationChecker; // TODO: Implement IP reputation checks in processEmailByMode
|
|
168
|
+
private bounceManager: BounceManager;
|
|
169
|
+
private ipWarmupManager: IPWarmupManager;
|
|
170
|
+
private senderReputationMonitor: SenderReputationMonitor;
|
|
171
|
+
public deliveryQueue: UnifiedDeliveryQueue;
|
|
172
|
+
public deliverySystem: MultiModeDeliverySystem;
|
|
173
|
+
private rateLimiter: UnifiedRateLimiter; // TODO: Implement rate limiting in SMTP server handlers
|
|
174
|
+
private dkimKeys: Map<string, string> = new Map(); // domain -> private key
|
|
175
|
+
private smtpClients: Map<string, SmtpClient> = new Map(); // host:port -> client
|
|
176
|
+
|
|
177
|
+
constructor(dcRouter: DcRouter, options: IUnifiedEmailServerOptions) {
|
|
178
|
+
super();
|
|
179
|
+
this.dcRouter = dcRouter;
|
|
180
|
+
|
|
181
|
+
// Set default options
|
|
182
|
+
this.options = {
|
|
183
|
+
...options,
|
|
184
|
+
banner: options.banner || `${options.hostname} ESMTP UnifiedEmailServer`,
|
|
185
|
+
maxMessageSize: options.maxMessageSize || 10 * 1024 * 1024, // 10MB
|
|
186
|
+
maxClients: options.maxClients || 100,
|
|
187
|
+
maxConnections: options.maxConnections || 1000,
|
|
188
|
+
connectionTimeout: options.connectionTimeout || 60000, // 1 minute
|
|
189
|
+
socketTimeout: options.socketTimeout || 60000 // 1 minute
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// Initialize DKIM creator with storage manager
|
|
193
|
+
this.dkimCreator = new DKIMCreator(paths.keysDir, dcRouter.storageManager);
|
|
194
|
+
|
|
195
|
+
// Initialize IP reputation checker with storage manager
|
|
196
|
+
this.ipReputationChecker = IPReputationChecker.getInstance({
|
|
197
|
+
enableLocalCache: true,
|
|
198
|
+
enableDNSBL: true,
|
|
199
|
+
enableIPInfo: true
|
|
200
|
+
}, dcRouter.storageManager);
|
|
201
|
+
|
|
202
|
+
// Initialize bounce manager with storage manager
|
|
203
|
+
this.bounceManager = new BounceManager({
|
|
204
|
+
maxCacheSize: 10000,
|
|
205
|
+
cacheTTL: 30 * 24 * 60 * 60 * 1000, // 30 days
|
|
206
|
+
storageManager: dcRouter.storageManager
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Initialize IP warmup manager
|
|
210
|
+
this.ipWarmupManager = IPWarmupManager.getInstance(options.ipWarmupConfig || {
|
|
211
|
+
enabled: true,
|
|
212
|
+
ipAddresses: [],
|
|
213
|
+
targetDomains: []
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// Initialize sender reputation monitor with storage manager
|
|
217
|
+
this.senderReputationMonitor = SenderReputationMonitor.getInstance(
|
|
218
|
+
options.reputationMonitorConfig || {
|
|
219
|
+
enabled: true,
|
|
220
|
+
domains: []
|
|
221
|
+
},
|
|
222
|
+
dcRouter.storageManager
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
// Initialize domain registry
|
|
226
|
+
this.domainRegistry = new DomainRegistry(options.domains, options.defaults);
|
|
227
|
+
|
|
228
|
+
// Initialize email router with routes and storage manager
|
|
229
|
+
this.emailRouter = new EmailRouter(options.routes || [], {
|
|
230
|
+
storageManager: dcRouter.storageManager,
|
|
231
|
+
persistChanges: true
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// Initialize rate limiter
|
|
235
|
+
this.rateLimiter = new UnifiedRateLimiter(options.rateLimits || {
|
|
236
|
+
global: {
|
|
237
|
+
maxConnectionsPerIP: 10,
|
|
238
|
+
maxMessagesPerMinute: 100,
|
|
239
|
+
maxRecipientsPerMessage: 50,
|
|
240
|
+
maxErrorsPerIP: 10,
|
|
241
|
+
maxAuthFailuresPerIP: 5,
|
|
242
|
+
blockDuration: 300000 // 5 minutes
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Initialize delivery components
|
|
247
|
+
const queueOptions: IQueueOptions = {
|
|
248
|
+
storageType: 'memory', // Default to memory storage
|
|
249
|
+
maxRetries: 3,
|
|
250
|
+
baseRetryDelay: 300000, // 5 minutes
|
|
251
|
+
maxRetryDelay: 3600000 // 1 hour
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
this.deliveryQueue = new UnifiedDeliveryQueue(queueOptions);
|
|
255
|
+
|
|
256
|
+
const deliveryOptions: IMultiModeDeliveryOptions = {
|
|
257
|
+
globalRateLimit: 100, // Default to 100 emails per minute
|
|
258
|
+
concurrentDeliveries: 10,
|
|
259
|
+
processBounces: true,
|
|
260
|
+
bounceHandler: {
|
|
261
|
+
processSmtpFailure: this.processSmtpFailure.bind(this)
|
|
262
|
+
},
|
|
263
|
+
onDeliverySuccess: async (item, _result) => {
|
|
264
|
+
// Record delivery success event for reputation monitoring
|
|
265
|
+
const email = item.processingResult as Email;
|
|
266
|
+
const senderDomain = email.from.split('@')[1];
|
|
267
|
+
|
|
268
|
+
if (senderDomain) {
|
|
269
|
+
this.recordReputationEvent(senderDomain, {
|
|
270
|
+
type: 'delivered',
|
|
271
|
+
count: email.to.length
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
this.deliverySystem = new MultiModeDeliverySystem(this.deliveryQueue, deliveryOptions, this);
|
|
278
|
+
|
|
279
|
+
// Initialize statistics
|
|
280
|
+
this.stats = {
|
|
281
|
+
startTime: new Date(),
|
|
282
|
+
connections: {
|
|
283
|
+
current: 0,
|
|
284
|
+
total: 0
|
|
285
|
+
},
|
|
286
|
+
messages: {
|
|
287
|
+
processed: 0,
|
|
288
|
+
delivered: 0,
|
|
289
|
+
failed: 0
|
|
290
|
+
},
|
|
291
|
+
processingTime: {
|
|
292
|
+
avg: 0,
|
|
293
|
+
max: 0,
|
|
294
|
+
min: 0
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
// We'll create the SMTP servers during the start() method
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Get or create an SMTP client for the given host and port
|
|
303
|
+
* Uses connection pooling for efficiency
|
|
304
|
+
*/
|
|
305
|
+
public getSmtpClient(host: string, port: number = 25): SmtpClient {
|
|
306
|
+
const clientKey = `${host}:${port}`;
|
|
307
|
+
|
|
308
|
+
// Check if we already have a client for this destination
|
|
309
|
+
let client = this.smtpClients.get(clientKey);
|
|
310
|
+
|
|
311
|
+
if (!client) {
|
|
312
|
+
// Create a new pooled SMTP client
|
|
313
|
+
client = createPooledSmtpClient({
|
|
314
|
+
host,
|
|
315
|
+
port,
|
|
316
|
+
secure: port === 465,
|
|
317
|
+
connectionTimeout: this.options.outbound?.connectionTimeout || 30000,
|
|
318
|
+
socketTimeout: this.options.outbound?.socketTimeout || 120000,
|
|
319
|
+
maxConnections: this.options.outbound?.maxConnections || 10,
|
|
320
|
+
maxMessages: 1000, // Messages per connection before reconnect
|
|
321
|
+
pool: true,
|
|
322
|
+
debug: false
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
this.smtpClients.set(clientKey, client);
|
|
326
|
+
logger.log('info', `Created new SMTP client pool for ${clientKey}`);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return client;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Start the unified email server
|
|
334
|
+
*/
|
|
335
|
+
public async start(): Promise<void> {
|
|
336
|
+
logger.log('info', `Starting UnifiedEmailServer on ports: ${(this.options.ports as number[]).join(', ')}`);
|
|
337
|
+
|
|
338
|
+
try {
|
|
339
|
+
// Initialize the delivery queue
|
|
340
|
+
await this.deliveryQueue.initialize();
|
|
341
|
+
logger.log('info', 'Email delivery queue initialized');
|
|
342
|
+
|
|
343
|
+
// Start the delivery system
|
|
344
|
+
await this.deliverySystem.start();
|
|
345
|
+
logger.log('info', 'Email delivery system started');
|
|
346
|
+
|
|
347
|
+
// Set up DKIM for all domains
|
|
348
|
+
await this.setupDkimForDomains();
|
|
349
|
+
logger.log('info', 'DKIM configuration completed for all domains');
|
|
350
|
+
|
|
351
|
+
// Create DNS manager and ensure all DNS records are created
|
|
352
|
+
const dnsManager = new DnsManager(this.dcRouter);
|
|
353
|
+
await dnsManager.ensureDnsRecords(this.domainRegistry.getAllConfigs(), this.dkimCreator);
|
|
354
|
+
logger.log('info', 'DNS records ensured for all configured domains');
|
|
355
|
+
|
|
356
|
+
// Apply per-domain rate limits
|
|
357
|
+
this.applyDomainRateLimits();
|
|
358
|
+
logger.log('info', 'Per-domain rate limits configured');
|
|
359
|
+
|
|
360
|
+
// Check and rotate DKIM keys if needed
|
|
361
|
+
await this.checkAndRotateDkimKeys();
|
|
362
|
+
logger.log('info', 'DKIM key rotation check completed');
|
|
363
|
+
|
|
364
|
+
// Skip server creation in socket-handler mode
|
|
365
|
+
if (this.options.useSocketHandler) {
|
|
366
|
+
logger.log('info', 'UnifiedEmailServer started in socket-handler mode (no port listening)');
|
|
367
|
+
this.emit('started');
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Ensure we have the necessary TLS options
|
|
372
|
+
const hasTlsConfig = this.options.tls?.keyPath && this.options.tls?.certPath;
|
|
373
|
+
|
|
374
|
+
// Prepare the certificate and key if available
|
|
375
|
+
let key: string | undefined;
|
|
376
|
+
let cert: string | undefined;
|
|
377
|
+
|
|
378
|
+
if (hasTlsConfig) {
|
|
379
|
+
try {
|
|
380
|
+
key = plugins.fs.readFileSync(this.options.tls.keyPath!, 'utf8');
|
|
381
|
+
cert = plugins.fs.readFileSync(this.options.tls.certPath!, 'utf8');
|
|
382
|
+
logger.log('info', 'TLS certificates loaded successfully');
|
|
383
|
+
} catch (error) {
|
|
384
|
+
logger.log('warn', `Failed to load TLS certificates: ${error.message}`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Create a SMTP server for each port
|
|
389
|
+
for (const port of this.options.ports as number[]) {
|
|
390
|
+
// Create a reference object to hold the MTA service during setup
|
|
391
|
+
const mtaRef = {
|
|
392
|
+
config: {
|
|
393
|
+
smtp: {
|
|
394
|
+
hostname: this.options.hostname
|
|
395
|
+
},
|
|
396
|
+
security: {
|
|
397
|
+
checkIPReputation: false,
|
|
398
|
+
verifyDkim: true,
|
|
399
|
+
verifySpf: true,
|
|
400
|
+
verifyDmarc: true
|
|
401
|
+
}
|
|
402
|
+
},
|
|
403
|
+
// These will be implemented in the real integration:
|
|
404
|
+
dkimVerifier: {
|
|
405
|
+
verify: async () => ({ isValid: true, domain: '' })
|
|
406
|
+
},
|
|
407
|
+
spfVerifier: {
|
|
408
|
+
verifyAndApply: async () => true
|
|
409
|
+
},
|
|
410
|
+
dmarcVerifier: {
|
|
411
|
+
verify: async () => ({}),
|
|
412
|
+
applyPolicy: () => true
|
|
413
|
+
},
|
|
414
|
+
processIncomingEmail: async (email: Email) => {
|
|
415
|
+
// Process email using the new route-based system
|
|
416
|
+
await this.processEmailByMode(email, {
|
|
417
|
+
id: 'session-' + Math.random().toString(36).substring(2),
|
|
418
|
+
state: SmtpState.FINISHED,
|
|
419
|
+
mailFrom: email.from,
|
|
420
|
+
rcptTo: email.to,
|
|
421
|
+
emailData: email.toRFC822String(), // Use the proper method to get the full email content
|
|
422
|
+
useTLS: false,
|
|
423
|
+
connectionEnded: true,
|
|
424
|
+
remoteAddress: '127.0.0.1',
|
|
425
|
+
clientHostname: '',
|
|
426
|
+
secure: false,
|
|
427
|
+
authenticated: false,
|
|
428
|
+
envelope: {
|
|
429
|
+
mailFrom: { address: email.from, args: {} },
|
|
430
|
+
rcptTo: email.to.map(recipient => ({ address: recipient, args: {} }))
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
return true;
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
// Create server options
|
|
439
|
+
const serverOptions = {
|
|
440
|
+
port,
|
|
441
|
+
hostname: this.options.hostname,
|
|
442
|
+
key,
|
|
443
|
+
cert
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
// Create and start the SMTP server
|
|
447
|
+
const smtpServer = createSmtpServer(mtaRef as any, serverOptions);
|
|
448
|
+
this.servers.push(smtpServer);
|
|
449
|
+
|
|
450
|
+
// Start the server
|
|
451
|
+
await new Promise<void>((resolve, reject) => {
|
|
452
|
+
try {
|
|
453
|
+
// Leave this empty for now, smtpServer.start() is handled by the SMTPServer class internally
|
|
454
|
+
// The server is started when it's created
|
|
455
|
+
logger.log('info', `UnifiedEmailServer listening on port ${port}`);
|
|
456
|
+
|
|
457
|
+
// Event handlers are managed internally by the SmtpServer class
|
|
458
|
+
// No need to access the private server property
|
|
459
|
+
|
|
460
|
+
resolve();
|
|
461
|
+
} catch (err) {
|
|
462
|
+
if ((err as any).code === 'EADDRINUSE') {
|
|
463
|
+
logger.log('error', `Port ${port} is already in use`);
|
|
464
|
+
reject(new Error(`Port ${port} is already in use`));
|
|
465
|
+
} else {
|
|
466
|
+
logger.log('error', `Error starting server on port ${port}: ${err.message}`);
|
|
467
|
+
reject(err);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
logger.log('info', 'UnifiedEmailServer started successfully');
|
|
474
|
+
this.emit('started');
|
|
475
|
+
} catch (error) {
|
|
476
|
+
logger.log('error', `Failed to start UnifiedEmailServer: ${error.message}`);
|
|
477
|
+
throw error;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Handle a socket from smartproxy in socket-handler mode
|
|
483
|
+
* @param socket The socket to handle
|
|
484
|
+
* @param port The port this connection is for (25, 587, 465)
|
|
485
|
+
*/
|
|
486
|
+
public async handleSocket(socket: plugins.net.Socket | plugins.tls.TLSSocket, port: number): Promise<void> {
|
|
487
|
+
if (!this.options.useSocketHandler) {
|
|
488
|
+
logger.log('error', 'handleSocket called but useSocketHandler is not enabled');
|
|
489
|
+
socket.destroy();
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
logger.log('info', `Handling socket for port ${port}`);
|
|
494
|
+
|
|
495
|
+
// Create a temporary SMTP server instance for this connection
|
|
496
|
+
// We need a full server instance because the SMTP protocol handler needs all components
|
|
497
|
+
const smtpServerOptions = {
|
|
498
|
+
port,
|
|
499
|
+
hostname: this.options.hostname,
|
|
500
|
+
key: this.options.tls?.keyPath ? plugins.fs.readFileSync(this.options.tls.keyPath, 'utf8') : undefined,
|
|
501
|
+
cert: this.options.tls?.certPath ? plugins.fs.readFileSync(this.options.tls.certPath, 'utf8') : undefined
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
// Create the SMTP server instance
|
|
505
|
+
const smtpServer = createSmtpServer(this, smtpServerOptions);
|
|
506
|
+
|
|
507
|
+
// Get the connection manager from the server
|
|
508
|
+
const connectionManager = (smtpServer as any).connectionManager;
|
|
509
|
+
|
|
510
|
+
if (!connectionManager) {
|
|
511
|
+
logger.log('error', 'Could not get connection manager from SMTP server');
|
|
512
|
+
socket.destroy();
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Determine if this is a secure connection
|
|
517
|
+
// Port 465 uses implicit TLS, so the socket is already secure
|
|
518
|
+
const isSecure = port === 465 || socket instanceof plugins.tls.TLSSocket;
|
|
519
|
+
|
|
520
|
+
// Pass the socket to the connection manager
|
|
521
|
+
try {
|
|
522
|
+
await connectionManager.handleConnection(socket, isSecure);
|
|
523
|
+
} catch (error) {
|
|
524
|
+
logger.log('error', `Error handling socket connection: ${error.message}`);
|
|
525
|
+
socket.destroy();
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Stop the unified email server
|
|
531
|
+
*/
|
|
532
|
+
public async stop(): Promise<void> {
|
|
533
|
+
logger.log('info', 'Stopping UnifiedEmailServer');
|
|
534
|
+
|
|
535
|
+
try {
|
|
536
|
+
// Clear the servers array - servers will be garbage collected
|
|
537
|
+
this.servers = [];
|
|
538
|
+
|
|
539
|
+
// Stop the delivery system
|
|
540
|
+
if (this.deliverySystem) {
|
|
541
|
+
await this.deliverySystem.stop();
|
|
542
|
+
logger.log('info', 'Email delivery system stopped');
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Shut down the delivery queue
|
|
546
|
+
if (this.deliveryQueue) {
|
|
547
|
+
await this.deliveryQueue.shutdown();
|
|
548
|
+
logger.log('info', 'Email delivery queue shut down');
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Close all SMTP client connections
|
|
552
|
+
for (const [clientKey, client] of this.smtpClients) {
|
|
553
|
+
try {
|
|
554
|
+
await client.close();
|
|
555
|
+
logger.log('info', `Closed SMTP client pool for ${clientKey}`);
|
|
556
|
+
} catch (error) {
|
|
557
|
+
logger.log('warn', `Error closing SMTP client for ${clientKey}: ${error.message}`);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
this.smtpClients.clear();
|
|
561
|
+
|
|
562
|
+
logger.log('info', 'UnifiedEmailServer stopped successfully');
|
|
563
|
+
this.emit('stopped');
|
|
564
|
+
} catch (error) {
|
|
565
|
+
logger.log('error', `Error stopping UnifiedEmailServer: ${error.message}`);
|
|
566
|
+
throw error;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Process email based on routing rules
|
|
576
|
+
*/
|
|
577
|
+
public async processEmailByMode(emailData: Email | Buffer, session: IExtendedSmtpSession): Promise<Email> {
|
|
578
|
+
// Convert Buffer to Email if needed
|
|
579
|
+
let email: Email;
|
|
580
|
+
if (Buffer.isBuffer(emailData)) {
|
|
581
|
+
// Parse the email data buffer into an Email object
|
|
582
|
+
try {
|
|
583
|
+
const parsed = await plugins.mailparser.simpleParser(emailData);
|
|
584
|
+
email = new Email({
|
|
585
|
+
from: parsed.from?.value[0]?.address || session.envelope.mailFrom.address,
|
|
586
|
+
to: session.envelope.rcptTo[0]?.address || '',
|
|
587
|
+
subject: parsed.subject || '',
|
|
588
|
+
text: parsed.text || '',
|
|
589
|
+
html: parsed.html || undefined,
|
|
590
|
+
attachments: parsed.attachments?.map(att => ({
|
|
591
|
+
filename: att.filename || '',
|
|
592
|
+
content: att.content,
|
|
593
|
+
contentType: att.contentType
|
|
594
|
+
})) || []
|
|
595
|
+
});
|
|
596
|
+
} catch (error) {
|
|
597
|
+
logger.log('error', `Error parsing email data: ${error.message}`);
|
|
598
|
+
throw new Error(`Error parsing email data: ${error.message}`);
|
|
599
|
+
}
|
|
600
|
+
} else {
|
|
601
|
+
email = emailData;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// First check if this is a bounce notification email
|
|
605
|
+
// Look for common bounce notification subject patterns
|
|
606
|
+
const subject = email.subject || '';
|
|
607
|
+
const isBounceLike = /mail delivery|delivery (failed|status|notification)|failure notice|returned mail|undeliverable|delivery problem/i.test(subject);
|
|
608
|
+
|
|
609
|
+
if (isBounceLike) {
|
|
610
|
+
logger.log('info', `Email subject matches bounce notification pattern: "${subject}"`);
|
|
611
|
+
|
|
612
|
+
// Try to process as a bounce
|
|
613
|
+
const isBounce = await this.processBounceNotification(email);
|
|
614
|
+
|
|
615
|
+
if (isBounce) {
|
|
616
|
+
logger.log('info', 'Successfully processed as bounce notification, skipping regular processing');
|
|
617
|
+
return email;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
logger.log('info', 'Not a valid bounce notification, continuing with regular processing');
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// Find matching route
|
|
624
|
+
const context: IEmailContext = { email, session };
|
|
625
|
+
const route = await this.emailRouter.evaluateRoutes(context);
|
|
626
|
+
|
|
627
|
+
if (!route) {
|
|
628
|
+
// No matching route - reject
|
|
629
|
+
throw new Error('No matching route for email');
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Store matched route in session
|
|
633
|
+
session.matchedRoute = route;
|
|
634
|
+
|
|
635
|
+
// Execute action based on route
|
|
636
|
+
await this.executeAction(route.action, email, context);
|
|
637
|
+
|
|
638
|
+
// Return the processed email
|
|
639
|
+
return email;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Execute action based on route configuration
|
|
644
|
+
*/
|
|
645
|
+
private async executeAction(action: IEmailAction, email: Email, context: IEmailContext): Promise<void> {
|
|
646
|
+
switch (action.type) {
|
|
647
|
+
case 'forward':
|
|
648
|
+
await this.handleForwardAction(action, email, context);
|
|
649
|
+
break;
|
|
650
|
+
|
|
651
|
+
case 'process':
|
|
652
|
+
await this.handleProcessAction(action, email, context);
|
|
653
|
+
break;
|
|
654
|
+
|
|
655
|
+
case 'deliver':
|
|
656
|
+
await this.handleDeliverAction(action, email, context);
|
|
657
|
+
break;
|
|
658
|
+
|
|
659
|
+
case 'reject':
|
|
660
|
+
await this.handleRejectAction(action, email, context);
|
|
661
|
+
break;
|
|
662
|
+
|
|
663
|
+
default:
|
|
664
|
+
throw new Error(`Unknown action type: ${(action as any).type}`);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Handle forward action
|
|
670
|
+
*/
|
|
671
|
+
private async handleForwardAction(_action: IEmailAction, email: Email, context: IEmailContext): Promise<void> {
|
|
672
|
+
if (!_action.forward) {
|
|
673
|
+
throw new Error('Forward action requires forward configuration');
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
const { host, port = 25, auth, addHeaders } = _action.forward;
|
|
677
|
+
|
|
678
|
+
logger.log('info', `Forwarding email to ${host}:${port}`);
|
|
679
|
+
|
|
680
|
+
// Add forwarding headers
|
|
681
|
+
if (addHeaders) {
|
|
682
|
+
for (const [key, value] of Object.entries(addHeaders)) {
|
|
683
|
+
email.headers[key] = value;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// Add standard forwarding headers
|
|
688
|
+
email.headers['X-Forwarded-For'] = context.session.remoteAddress || 'unknown';
|
|
689
|
+
email.headers['X-Forwarded-To'] = email.to.join(', ');
|
|
690
|
+
email.headers['X-Forwarded-Date'] = new Date().toISOString();
|
|
691
|
+
|
|
692
|
+
// Get SMTP client
|
|
693
|
+
const client = this.getSmtpClient(host, port);
|
|
694
|
+
|
|
695
|
+
try {
|
|
696
|
+
// Send email
|
|
697
|
+
await client.sendMail(email);
|
|
698
|
+
|
|
699
|
+
logger.log('info', `Successfully forwarded email to ${host}:${port}`);
|
|
700
|
+
|
|
701
|
+
SecurityLogger.getInstance().logEvent({
|
|
702
|
+
level: SecurityLogLevel.INFO,
|
|
703
|
+
type: SecurityEventType.EMAIL_FORWARDING,
|
|
704
|
+
message: 'Email forwarded successfully',
|
|
705
|
+
ipAddress: context.session.remoteAddress,
|
|
706
|
+
details: {
|
|
707
|
+
sessionId: context.session.id,
|
|
708
|
+
routeName: context.session.matchedRoute?.name,
|
|
709
|
+
targetHost: host,
|
|
710
|
+
targetPort: port,
|
|
711
|
+
recipients: email.to
|
|
712
|
+
},
|
|
713
|
+
success: true
|
|
714
|
+
});
|
|
715
|
+
} catch (error) {
|
|
716
|
+
logger.log('error', `Failed to forward email: ${error.message}`);
|
|
717
|
+
|
|
718
|
+
SecurityLogger.getInstance().logEvent({
|
|
719
|
+
level: SecurityLogLevel.ERROR,
|
|
720
|
+
type: SecurityEventType.EMAIL_FORWARDING,
|
|
721
|
+
message: 'Email forwarding failed',
|
|
722
|
+
ipAddress: context.session.remoteAddress,
|
|
723
|
+
details: {
|
|
724
|
+
sessionId: context.session.id,
|
|
725
|
+
routeName: context.session.matchedRoute?.name,
|
|
726
|
+
targetHost: host,
|
|
727
|
+
targetPort: port,
|
|
728
|
+
error: error.message
|
|
729
|
+
},
|
|
730
|
+
success: false
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
// Handle as bounce
|
|
734
|
+
for (const recipient of email.getAllRecipients()) {
|
|
735
|
+
await this.bounceManager.processSmtpFailure(recipient, error.message, {
|
|
736
|
+
sender: email.from,
|
|
737
|
+
originalEmailId: email.headers['Message-ID'] as string
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
throw error;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Handle process action
|
|
746
|
+
*/
|
|
747
|
+
private async handleProcessAction(action: IEmailAction, email: Email, context: IEmailContext): Promise<void> {
|
|
748
|
+
logger.log('info', `Processing email with action options`);
|
|
749
|
+
|
|
750
|
+
// Apply scanning if requested
|
|
751
|
+
if (action.process?.scan) {
|
|
752
|
+
// Use existing content scanner
|
|
753
|
+
// Note: ContentScanner integration would go here
|
|
754
|
+
logger.log('info', 'Content scanning requested');
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// Note: DKIM signing will be applied at delivery time to ensure signature validity
|
|
758
|
+
|
|
759
|
+
// Queue for delivery
|
|
760
|
+
const queue = action.process?.queue || 'normal';
|
|
761
|
+
await this.deliveryQueue.enqueue(email, 'process', context.session.matchedRoute!);
|
|
762
|
+
|
|
763
|
+
logger.log('info', `Email queued for delivery in ${queue} queue`);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Handle deliver action
|
|
768
|
+
*/
|
|
769
|
+
private async handleDeliverAction(_action: IEmailAction, email: Email, context: IEmailContext): Promise<void> {
|
|
770
|
+
logger.log('info', `Delivering email locally`);
|
|
771
|
+
|
|
772
|
+
// Queue for local delivery
|
|
773
|
+
await this.deliveryQueue.enqueue(email, 'mta', context.session.matchedRoute!);
|
|
774
|
+
|
|
775
|
+
logger.log('info', 'Email queued for local delivery');
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* Handle reject action
|
|
780
|
+
*/
|
|
781
|
+
private async handleRejectAction(action: IEmailAction, email: Email, context: IEmailContext): Promise<void> {
|
|
782
|
+
const code = action.reject?.code || 550;
|
|
783
|
+
const message = action.reject?.message || 'Message rejected';
|
|
784
|
+
|
|
785
|
+
logger.log('info', `Rejecting email with code ${code}: ${message}`);
|
|
786
|
+
|
|
787
|
+
SecurityLogger.getInstance().logEvent({
|
|
788
|
+
level: SecurityLogLevel.WARN,
|
|
789
|
+
type: SecurityEventType.EMAIL_PROCESSING,
|
|
790
|
+
message: 'Email rejected by routing rule',
|
|
791
|
+
ipAddress: context.session.remoteAddress,
|
|
792
|
+
details: {
|
|
793
|
+
sessionId: context.session.id,
|
|
794
|
+
routeName: context.session.matchedRoute?.name,
|
|
795
|
+
rejectCode: code,
|
|
796
|
+
rejectMessage: message,
|
|
797
|
+
from: email.from,
|
|
798
|
+
to: email.to
|
|
799
|
+
},
|
|
800
|
+
success: false
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
// Throw error with SMTP code and message
|
|
804
|
+
const error = new Error(message);
|
|
805
|
+
(error as any).responseCode = code;
|
|
806
|
+
throw error;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* Handle email in MTA mode (programmatic processing)
|
|
811
|
+
*/
|
|
812
|
+
private async _handleMtaMode(email: Email, session: IExtendedSmtpSession): Promise<void> {
|
|
813
|
+
logger.log('info', `Handling email in MTA mode for session ${session.id}`);
|
|
814
|
+
|
|
815
|
+
try {
|
|
816
|
+
// Apply MTA rule options if provided
|
|
817
|
+
if (session.matchedRoute?.action.options?.mtaOptions) {
|
|
818
|
+
const options = session.matchedRoute.action.options.mtaOptions;
|
|
819
|
+
|
|
820
|
+
// Apply DKIM signing if enabled
|
|
821
|
+
if (options.dkimSign && options.dkimOptions) {
|
|
822
|
+
// Sign the email with DKIM
|
|
823
|
+
logger.log('info', `Signing email with DKIM for domain ${options.dkimOptions.domainName}`);
|
|
824
|
+
|
|
825
|
+
try {
|
|
826
|
+
// Ensure DKIM keys exist for the domain
|
|
827
|
+
await this.dkimCreator.handleDKIMKeysForDomain(options.dkimOptions.domainName);
|
|
828
|
+
|
|
829
|
+
// Convert Email to raw format for signing
|
|
830
|
+
const rawEmail = email.toRFC822String();
|
|
831
|
+
|
|
832
|
+
// Create headers object
|
|
833
|
+
const headers = {};
|
|
834
|
+
for (const [key, value] of Object.entries(email.headers)) {
|
|
835
|
+
headers[key] = value;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// Sign the email
|
|
839
|
+
const signResult = await plugins.dkimSign(rawEmail, {
|
|
840
|
+
canonicalization: 'relaxed/relaxed',
|
|
841
|
+
algorithm: 'rsa-sha256',
|
|
842
|
+
signTime: new Date(),
|
|
843
|
+
signatureData: [
|
|
844
|
+
{
|
|
845
|
+
signingDomain: options.dkimOptions.domainName,
|
|
846
|
+
selector: options.dkimOptions.keySelector || 'mta',
|
|
847
|
+
privateKey: (await this.dkimCreator.readDKIMKeys(options.dkimOptions.domainName)).privateKey,
|
|
848
|
+
algorithm: 'rsa-sha256',
|
|
849
|
+
canonicalization: 'relaxed/relaxed'
|
|
850
|
+
}
|
|
851
|
+
]
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
// Add the DKIM-Signature header to the email
|
|
855
|
+
if (signResult.signatures) {
|
|
856
|
+
email.addHeader('DKIM-Signature', signResult.signatures);
|
|
857
|
+
logger.log('info', `Successfully added DKIM signature for ${options.dkimOptions.domainName}`);
|
|
858
|
+
}
|
|
859
|
+
} catch (error) {
|
|
860
|
+
logger.log('error', `Failed to sign email with DKIM: ${error.message}`);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// Get email content for logging/processing
|
|
866
|
+
const subject = email.subject;
|
|
867
|
+
const recipients = email.getAllRecipients().join(', ');
|
|
868
|
+
|
|
869
|
+
logger.log('info', `Email processed by MTA: ${subject} to ${recipients}`);
|
|
870
|
+
|
|
871
|
+
SecurityLogger.getInstance().logEvent({
|
|
872
|
+
level: SecurityLogLevel.INFO,
|
|
873
|
+
type: SecurityEventType.EMAIL_PROCESSING,
|
|
874
|
+
message: 'Email processed by MTA',
|
|
875
|
+
ipAddress: session.remoteAddress,
|
|
876
|
+
details: {
|
|
877
|
+
sessionId: session.id,
|
|
878
|
+
ruleName: session.matchedRoute?.name || 'default',
|
|
879
|
+
subject,
|
|
880
|
+
recipients
|
|
881
|
+
},
|
|
882
|
+
success: true
|
|
883
|
+
});
|
|
884
|
+
} catch (error) {
|
|
885
|
+
logger.log('error', `Failed to process email in MTA mode: ${error.message}`);
|
|
886
|
+
|
|
887
|
+
SecurityLogger.getInstance().logEvent({
|
|
888
|
+
level: SecurityLogLevel.ERROR,
|
|
889
|
+
type: SecurityEventType.EMAIL_PROCESSING,
|
|
890
|
+
message: 'MTA processing failed',
|
|
891
|
+
ipAddress: session.remoteAddress,
|
|
892
|
+
details: {
|
|
893
|
+
sessionId: session.id,
|
|
894
|
+
ruleName: session.matchedRoute?.name || 'default',
|
|
895
|
+
error: error.message
|
|
896
|
+
},
|
|
897
|
+
success: false
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
throw error;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
/**
|
|
905
|
+
* Handle email in process mode (store-and-forward with scanning)
|
|
906
|
+
*/
|
|
907
|
+
private async _handleProcessMode(email: Email, session: IExtendedSmtpSession): Promise<void> {
|
|
908
|
+
logger.log('info', `Handling email in process mode for session ${session.id}`);
|
|
909
|
+
|
|
910
|
+
try {
|
|
911
|
+
const route = session.matchedRoute;
|
|
912
|
+
|
|
913
|
+
// Apply content scanning if enabled
|
|
914
|
+
if (route?.action.options?.contentScanning && route.action.options.scanners && route.action.options.scanners.length > 0) {
|
|
915
|
+
logger.log('info', 'Performing content scanning');
|
|
916
|
+
|
|
917
|
+
// Apply each scanner
|
|
918
|
+
for (const scanner of route.action.options.scanners) {
|
|
919
|
+
switch (scanner.type) {
|
|
920
|
+
case 'spam':
|
|
921
|
+
logger.log('info', 'Scanning for spam content');
|
|
922
|
+
// Implement spam scanning
|
|
923
|
+
break;
|
|
924
|
+
|
|
925
|
+
case 'virus':
|
|
926
|
+
logger.log('info', 'Scanning for virus content');
|
|
927
|
+
// Implement virus scanning
|
|
928
|
+
break;
|
|
929
|
+
|
|
930
|
+
case 'attachment':
|
|
931
|
+
logger.log('info', 'Scanning attachments');
|
|
932
|
+
|
|
933
|
+
// Check for blocked extensions
|
|
934
|
+
if (scanner.blockedExtensions && scanner.blockedExtensions.length > 0) {
|
|
935
|
+
for (const attachment of email.attachments) {
|
|
936
|
+
const ext = this.getFileExtension(attachment.filename);
|
|
937
|
+
if (scanner.blockedExtensions.includes(ext)) {
|
|
938
|
+
if (scanner.action === 'reject') {
|
|
939
|
+
throw new Error(`Blocked attachment type: ${ext}`);
|
|
940
|
+
} else { // tag
|
|
941
|
+
email.addHeader('X-Attachment-Warning', `Potentially unsafe attachment: ${attachment.filename}`);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
break;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
// Apply transformations if defined
|
|
952
|
+
if (route?.action.options?.transformations && route.action.options.transformations.length > 0) {
|
|
953
|
+
logger.log('info', 'Applying email transformations');
|
|
954
|
+
|
|
955
|
+
for (const transform of route.action.options.transformations) {
|
|
956
|
+
switch (transform.type) {
|
|
957
|
+
case 'addHeader':
|
|
958
|
+
if (transform.header && transform.value) {
|
|
959
|
+
email.addHeader(transform.header, transform.value);
|
|
960
|
+
}
|
|
961
|
+
break;
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
logger.log('info', `Email successfully processed in store-and-forward mode`);
|
|
967
|
+
|
|
968
|
+
SecurityLogger.getInstance().logEvent({
|
|
969
|
+
level: SecurityLogLevel.INFO,
|
|
970
|
+
type: SecurityEventType.EMAIL_PROCESSING,
|
|
971
|
+
message: 'Email processed and queued',
|
|
972
|
+
ipAddress: session.remoteAddress,
|
|
973
|
+
details: {
|
|
974
|
+
sessionId: session.id,
|
|
975
|
+
ruleName: route?.name || 'default',
|
|
976
|
+
contentScanning: route?.action.options?.contentScanning || false,
|
|
977
|
+
subject: email.subject
|
|
978
|
+
},
|
|
979
|
+
success: true
|
|
980
|
+
});
|
|
981
|
+
} catch (error) {
|
|
982
|
+
logger.log('error', `Failed to process email: ${error.message}`);
|
|
983
|
+
|
|
984
|
+
SecurityLogger.getInstance().logEvent({
|
|
985
|
+
level: SecurityLogLevel.ERROR,
|
|
986
|
+
type: SecurityEventType.EMAIL_PROCESSING,
|
|
987
|
+
message: 'Email processing failed',
|
|
988
|
+
ipAddress: session.remoteAddress,
|
|
989
|
+
details: {
|
|
990
|
+
sessionId: session.id,
|
|
991
|
+
ruleName: session.matchedRoute?.name || 'default',
|
|
992
|
+
error: error.message
|
|
993
|
+
},
|
|
994
|
+
success: false
|
|
995
|
+
});
|
|
996
|
+
|
|
997
|
+
throw error;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
/**
|
|
1002
|
+
* Get file extension from filename
|
|
1003
|
+
*/
|
|
1004
|
+
private getFileExtension(filename: string): string {
|
|
1005
|
+
return filename.substring(filename.lastIndexOf('.')).toLowerCase();
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
|
|
1009
|
+
|
|
1010
|
+
/**
|
|
1011
|
+
* Set up DKIM configuration for all domains
|
|
1012
|
+
*/
|
|
1013
|
+
private async setupDkimForDomains(): Promise<void> {
|
|
1014
|
+
const domainConfigs = this.domainRegistry.getAllConfigs();
|
|
1015
|
+
|
|
1016
|
+
if (domainConfigs.length === 0) {
|
|
1017
|
+
logger.log('warn', 'No domains configured for DKIM');
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
for (const domainConfig of domainConfigs) {
|
|
1022
|
+
const domain = domainConfig.domain;
|
|
1023
|
+
const selector = domainConfig.dkim?.selector || 'default';
|
|
1024
|
+
|
|
1025
|
+
try {
|
|
1026
|
+
// Check if DKIM keys already exist for this domain
|
|
1027
|
+
let keyPair: { privateKey: string; publicKey: string };
|
|
1028
|
+
|
|
1029
|
+
try {
|
|
1030
|
+
// Try to read existing keys
|
|
1031
|
+
keyPair = await this.dkimCreator.readDKIMKeys(domain);
|
|
1032
|
+
logger.log('info', `Using existing DKIM keys for domain: ${domain}`);
|
|
1033
|
+
} catch (error) {
|
|
1034
|
+
// Generate new keys if they don't exist
|
|
1035
|
+
keyPair = await this.dkimCreator.createDKIMKeys();
|
|
1036
|
+
// Store them for future use
|
|
1037
|
+
await this.dkimCreator.createAndStoreDKIMKeys(domain);
|
|
1038
|
+
logger.log('info', `Generated new DKIM keys for domain: ${domain}`);
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// Store the private key for signing
|
|
1042
|
+
this.dkimKeys.set(domain, keyPair.privateKey);
|
|
1043
|
+
|
|
1044
|
+
// DNS record creation is now handled by DnsManager
|
|
1045
|
+
logger.log('info', `DKIM keys loaded for domain: ${domain} with selector: ${selector}`);
|
|
1046
|
+
} catch (error) {
|
|
1047
|
+
logger.log('error', `Failed to set up DKIM for domain ${domain}: ${error.message}`);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
|
|
1053
|
+
/**
|
|
1054
|
+
* Apply per-domain rate limits from domain configurations
|
|
1055
|
+
*/
|
|
1056
|
+
private applyDomainRateLimits(): void {
|
|
1057
|
+
const domainConfigs = this.domainRegistry.getAllConfigs();
|
|
1058
|
+
|
|
1059
|
+
for (const domainConfig of domainConfigs) {
|
|
1060
|
+
if (domainConfig.rateLimits) {
|
|
1061
|
+
const domain = domainConfig.domain;
|
|
1062
|
+
const rateLimitConfig: any = {};
|
|
1063
|
+
|
|
1064
|
+
// Convert domain-specific rate limits to the format expected by UnifiedRateLimiter
|
|
1065
|
+
if (domainConfig.rateLimits.outbound) {
|
|
1066
|
+
if (domainConfig.rateLimits.outbound.messagesPerMinute) {
|
|
1067
|
+
rateLimitConfig.maxMessagesPerMinute = domainConfig.rateLimits.outbound.messagesPerMinute;
|
|
1068
|
+
}
|
|
1069
|
+
// Note: messagesPerHour and messagesPerDay would need additional implementation in rate limiter
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
if (domainConfig.rateLimits.inbound) {
|
|
1073
|
+
if (domainConfig.rateLimits.inbound.messagesPerMinute) {
|
|
1074
|
+
rateLimitConfig.maxMessagesPerMinute = domainConfig.rateLimits.inbound.messagesPerMinute;
|
|
1075
|
+
}
|
|
1076
|
+
if (domainConfig.rateLimits.inbound.connectionsPerIp) {
|
|
1077
|
+
rateLimitConfig.maxConnectionsPerIP = domainConfig.rateLimits.inbound.connectionsPerIp;
|
|
1078
|
+
}
|
|
1079
|
+
if (domainConfig.rateLimits.inbound.recipientsPerMessage) {
|
|
1080
|
+
rateLimitConfig.maxRecipientsPerMessage = domainConfig.rateLimits.inbound.recipientsPerMessage;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
// Apply the rate limits if we have any
|
|
1085
|
+
if (Object.keys(rateLimitConfig).length > 0) {
|
|
1086
|
+
this.rateLimiter.applyDomainLimits(domain, rateLimitConfig);
|
|
1087
|
+
logger.log('info', `Applied rate limits for domain ${domain}:`, rateLimitConfig);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
/**
|
|
1094
|
+
* Check and rotate DKIM keys if needed
|
|
1095
|
+
*/
|
|
1096
|
+
private async checkAndRotateDkimKeys(): Promise<void> {
|
|
1097
|
+
const domainConfigs = this.domainRegistry.getAllConfigs();
|
|
1098
|
+
|
|
1099
|
+
for (const domainConfig of domainConfigs) {
|
|
1100
|
+
const domain = domainConfig.domain;
|
|
1101
|
+
const selector = domainConfig.dkim?.selector || 'default';
|
|
1102
|
+
const rotateKeys = domainConfig.dkim?.rotateKeys || false;
|
|
1103
|
+
const rotationInterval = domainConfig.dkim?.rotationInterval || 90;
|
|
1104
|
+
const keySize = domainConfig.dkim?.keySize || 2048;
|
|
1105
|
+
|
|
1106
|
+
if (!rotateKeys) {
|
|
1107
|
+
logger.log('debug', `DKIM key rotation disabled for ${domain}`);
|
|
1108
|
+
continue;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
try {
|
|
1112
|
+
// Check if keys need rotation
|
|
1113
|
+
const needsRotation = await this.dkimCreator.needsRotation(domain, selector, rotationInterval);
|
|
1114
|
+
|
|
1115
|
+
if (needsRotation) {
|
|
1116
|
+
logger.log('info', `DKIM keys need rotation for ${domain} (selector: ${selector})`);
|
|
1117
|
+
|
|
1118
|
+
// Rotate the keys
|
|
1119
|
+
const newSelector = await this.dkimCreator.rotateDkimKeys(domain, selector, keySize);
|
|
1120
|
+
|
|
1121
|
+
// Update the domain config with new selector
|
|
1122
|
+
domainConfig.dkim = {
|
|
1123
|
+
...domainConfig.dkim,
|
|
1124
|
+
selector: newSelector
|
|
1125
|
+
};
|
|
1126
|
+
|
|
1127
|
+
// Re-register DNS handler for new selector if internal-dns mode
|
|
1128
|
+
if (domainConfig.dnsMode === 'internal-dns' && this.dcRouter.dnsServer) {
|
|
1129
|
+
// Get new public key
|
|
1130
|
+
const keyPair = await this.dkimCreator.readDKIMKeysForSelector(domain, newSelector);
|
|
1131
|
+
const publicKeyBase64 = keyPair.publicKey
|
|
1132
|
+
.replace(/-----BEGIN PUBLIC KEY-----/g, '')
|
|
1133
|
+
.replace(/-----END PUBLIC KEY-----/g, '')
|
|
1134
|
+
.replace(/\s/g, '');
|
|
1135
|
+
|
|
1136
|
+
const ttl = domainConfig.dns?.internal?.ttl || 3600;
|
|
1137
|
+
|
|
1138
|
+
// Register new selector
|
|
1139
|
+
this.dcRouter.dnsServer.registerHandler(
|
|
1140
|
+
`${newSelector}._domainkey.${domain}`,
|
|
1141
|
+
['TXT'],
|
|
1142
|
+
() => ({
|
|
1143
|
+
name: `${newSelector}._domainkey.${domain}`,
|
|
1144
|
+
type: 'TXT',
|
|
1145
|
+
class: 'IN',
|
|
1146
|
+
ttl: ttl,
|
|
1147
|
+
data: `v=DKIM1; k=rsa; p=${publicKeyBase64}`
|
|
1148
|
+
})
|
|
1149
|
+
);
|
|
1150
|
+
|
|
1151
|
+
logger.log('info', `DKIM DNS handler registered for new selector: ${newSelector}._domainkey.${domain}`);
|
|
1152
|
+
|
|
1153
|
+
// Store the updated public key in storage
|
|
1154
|
+
await this.dcRouter.storageManager.set(
|
|
1155
|
+
`/email/dkim/${domain}/public.key`,
|
|
1156
|
+
keyPair.publicKey
|
|
1157
|
+
);
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
// Clean up old keys after grace period (async, don't wait)
|
|
1161
|
+
this.dkimCreator.cleanupOldKeys(domain, 30).catch(error => {
|
|
1162
|
+
logger.log('warn', `Failed to cleanup old DKIM keys for ${domain}: ${error.message}`);
|
|
1163
|
+
});
|
|
1164
|
+
|
|
1165
|
+
} else {
|
|
1166
|
+
logger.log('debug', `DKIM keys for ${domain} are up to date`);
|
|
1167
|
+
}
|
|
1168
|
+
} catch (error) {
|
|
1169
|
+
logger.log('error', `Failed to check/rotate DKIM keys for ${domain}: ${error.message}`);
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
|
|
1175
|
+
/**
|
|
1176
|
+
* Generate SmartProxy routes for email ports
|
|
1177
|
+
*/
|
|
1178
|
+
public generateProxyRoutes(portMapping?: Record<number, number>): any[] {
|
|
1179
|
+
const routes: any[] = [];
|
|
1180
|
+
const defaultPortMapping = {
|
|
1181
|
+
25: 10025,
|
|
1182
|
+
587: 10587,
|
|
1183
|
+
465: 10465
|
|
1184
|
+
};
|
|
1185
|
+
|
|
1186
|
+
const actualPortMapping = portMapping || defaultPortMapping;
|
|
1187
|
+
|
|
1188
|
+
// Generate routes for each configured port
|
|
1189
|
+
for (const externalPort of this.options.ports) {
|
|
1190
|
+
const internalPort = actualPortMapping[externalPort] || externalPort + 10000;
|
|
1191
|
+
|
|
1192
|
+
let routeName = 'email-route';
|
|
1193
|
+
let tlsMode = 'passthrough';
|
|
1194
|
+
|
|
1195
|
+
// Configure based on port
|
|
1196
|
+
switch (externalPort) {
|
|
1197
|
+
case 25:
|
|
1198
|
+
routeName = 'smtp-route';
|
|
1199
|
+
tlsMode = 'passthrough'; // STARTTLS
|
|
1200
|
+
break;
|
|
1201
|
+
case 587:
|
|
1202
|
+
routeName = 'submission-route';
|
|
1203
|
+
tlsMode = 'passthrough'; // STARTTLS
|
|
1204
|
+
break;
|
|
1205
|
+
case 465:
|
|
1206
|
+
routeName = 'smtps-route';
|
|
1207
|
+
tlsMode = 'terminate'; // Implicit TLS
|
|
1208
|
+
break;
|
|
1209
|
+
default:
|
|
1210
|
+
routeName = `email-port-${externalPort}-route`;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
routes.push({
|
|
1214
|
+
name: routeName,
|
|
1215
|
+
match: {
|
|
1216
|
+
ports: [externalPort]
|
|
1217
|
+
},
|
|
1218
|
+
action: {
|
|
1219
|
+
type: 'forward',
|
|
1220
|
+
target: {
|
|
1221
|
+
host: 'localhost',
|
|
1222
|
+
port: internalPort
|
|
1223
|
+
},
|
|
1224
|
+
tls: {
|
|
1225
|
+
mode: tlsMode
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
return routes;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
/**
|
|
1235
|
+
* Update server configuration
|
|
1236
|
+
*/
|
|
1237
|
+
public updateOptions(options: Partial<IUnifiedEmailServerOptions>): void {
|
|
1238
|
+
// Stop the server if changing ports
|
|
1239
|
+
const portsChanged = options.ports &&
|
|
1240
|
+
(!this.options.ports ||
|
|
1241
|
+
JSON.stringify(options.ports) !== JSON.stringify(this.options.ports));
|
|
1242
|
+
|
|
1243
|
+
if (portsChanged) {
|
|
1244
|
+
this.stop().then(() => {
|
|
1245
|
+
this.options = { ...this.options, ...options };
|
|
1246
|
+
this.start();
|
|
1247
|
+
});
|
|
1248
|
+
} else {
|
|
1249
|
+
// Update options without restart
|
|
1250
|
+
this.options = { ...this.options, ...options };
|
|
1251
|
+
|
|
1252
|
+
// Update domain registry if domains changed
|
|
1253
|
+
if (options.domains) {
|
|
1254
|
+
this.domainRegistry = new DomainRegistry(options.domains, options.defaults || this.options.defaults);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
// Update email router if routes changed
|
|
1258
|
+
if (options.routes) {
|
|
1259
|
+
this.emailRouter.updateRoutes(options.routes);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
/**
|
|
1265
|
+
* Update email routes
|
|
1266
|
+
*/
|
|
1267
|
+
public updateEmailRoutes(routes: IEmailRoute[]): void {
|
|
1268
|
+
this.options.routes = routes;
|
|
1269
|
+
this.emailRouter.updateRoutes(routes);
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
/**
|
|
1273
|
+
* Get server statistics
|
|
1274
|
+
*/
|
|
1275
|
+
public getStats(): IServerStats {
|
|
1276
|
+
return { ...this.stats };
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
/**
|
|
1280
|
+
* Get domain registry
|
|
1281
|
+
*/
|
|
1282
|
+
public getDomainRegistry(): DomainRegistry {
|
|
1283
|
+
return this.domainRegistry;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
/**
|
|
1287
|
+
* Update email routes dynamically
|
|
1288
|
+
*/
|
|
1289
|
+
public updateRoutes(routes: IEmailRoute[]): void {
|
|
1290
|
+
this.emailRouter.setRoutes(routes);
|
|
1291
|
+
logger.log('info', `Updated email routes with ${routes.length} routes`);
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
/**
|
|
1295
|
+
* Send an email through the delivery system
|
|
1296
|
+
* @param email The email to send
|
|
1297
|
+
* @param mode The processing mode to use
|
|
1298
|
+
* @param rule Optional rule to apply
|
|
1299
|
+
* @param options Optional sending options
|
|
1300
|
+
* @returns The ID of the queued email
|
|
1301
|
+
*/
|
|
1302
|
+
public async sendEmail(
|
|
1303
|
+
email: Email,
|
|
1304
|
+
mode: EmailProcessingMode = 'mta',
|
|
1305
|
+
route?: IEmailRoute,
|
|
1306
|
+
options?: {
|
|
1307
|
+
skipSuppressionCheck?: boolean;
|
|
1308
|
+
ipAddress?: string;
|
|
1309
|
+
isTransactional?: boolean;
|
|
1310
|
+
}
|
|
1311
|
+
): Promise<string> {
|
|
1312
|
+
logger.log('info', `Sending email: ${email.subject} to ${email.to.join(', ')}`);
|
|
1313
|
+
|
|
1314
|
+
try {
|
|
1315
|
+
// Validate the email
|
|
1316
|
+
if (!email.from) {
|
|
1317
|
+
throw new Error('Email must have a sender address');
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
if (!email.to || email.to.length === 0) {
|
|
1321
|
+
throw new Error('Email must have at least one recipient');
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
// Check if any recipients are on the suppression list (unless explicitly skipped)
|
|
1325
|
+
if (!options?.skipSuppressionCheck) {
|
|
1326
|
+
const suppressedRecipients = email.to.filter(recipient => this.isEmailSuppressed(recipient));
|
|
1327
|
+
|
|
1328
|
+
if (suppressedRecipients.length > 0) {
|
|
1329
|
+
// Filter out suppressed recipients
|
|
1330
|
+
const originalCount = email.to.length;
|
|
1331
|
+
const suppressed = suppressedRecipients.map(recipient => {
|
|
1332
|
+
const info = this.getSuppressionInfo(recipient);
|
|
1333
|
+
return {
|
|
1334
|
+
email: recipient,
|
|
1335
|
+
reason: info?.reason || 'Unknown',
|
|
1336
|
+
until: info?.expiresAt ? new Date(info.expiresAt).toISOString() : 'permanent'
|
|
1337
|
+
};
|
|
1338
|
+
});
|
|
1339
|
+
|
|
1340
|
+
logger.log('warn', `Filtering out ${suppressedRecipients.length} suppressed recipient(s)`, { suppressed });
|
|
1341
|
+
|
|
1342
|
+
// If all recipients are suppressed, throw an error
|
|
1343
|
+
if (suppressedRecipients.length === originalCount) {
|
|
1344
|
+
throw new Error('All recipients are on the suppression list');
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
// Filter the recipients list to only include non-suppressed addresses
|
|
1348
|
+
email.to = email.to.filter(recipient => !this.isEmailSuppressed(recipient));
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
// IP warmup handling
|
|
1353
|
+
let ipAddress = options?.ipAddress;
|
|
1354
|
+
|
|
1355
|
+
// If no specific IP was provided, use IP warmup manager to find the best IP
|
|
1356
|
+
if (!ipAddress) {
|
|
1357
|
+
const domain = email.from.split('@')[1];
|
|
1358
|
+
|
|
1359
|
+
ipAddress = this.getBestIPForSending({
|
|
1360
|
+
from: email.from,
|
|
1361
|
+
to: email.to,
|
|
1362
|
+
domain,
|
|
1363
|
+
isTransactional: options?.isTransactional
|
|
1364
|
+
});
|
|
1365
|
+
|
|
1366
|
+
if (ipAddress) {
|
|
1367
|
+
logger.log('info', `Selected IP ${ipAddress} for sending based on warmup status`);
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
// If an IP is provided or selected by warmup manager, check its capacity
|
|
1372
|
+
if (ipAddress) {
|
|
1373
|
+
// Check if the IP can send more today
|
|
1374
|
+
if (!this.canIPSendMoreToday(ipAddress)) {
|
|
1375
|
+
logger.log('warn', `IP ${ipAddress} has reached its daily sending limit, email will be queued for later delivery`);
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
// Check if the IP can send more this hour
|
|
1379
|
+
if (!this.canIPSendMoreThisHour(ipAddress)) {
|
|
1380
|
+
logger.log('warn', `IP ${ipAddress} has reached its hourly sending limit, email will be queued for later delivery`);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
// Record the send for IP warmup tracking
|
|
1384
|
+
this.recordIPSend(ipAddress);
|
|
1385
|
+
|
|
1386
|
+
// Add IP header to the email
|
|
1387
|
+
email.addHeader('X-Sending-IP', ipAddress);
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
// Check if the sender domain has DKIM keys and sign the email if needed
|
|
1391
|
+
if (mode === 'mta' && route?.action.options?.mtaOptions?.dkimSign) {
|
|
1392
|
+
const domain = email.from.split('@')[1];
|
|
1393
|
+
await this.handleDkimSigning(email, domain, route.action.options.mtaOptions.dkimOptions?.keySelector || 'mta');
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
// Generate a unique ID for this email
|
|
1397
|
+
const id = plugins.uuid.v4();
|
|
1398
|
+
|
|
1399
|
+
// Queue the email for delivery
|
|
1400
|
+
await this.deliveryQueue.enqueue(email, mode, route);
|
|
1401
|
+
|
|
1402
|
+
// Record 'sent' event for domain reputation monitoring
|
|
1403
|
+
const senderDomain = email.from.split('@')[1];
|
|
1404
|
+
if (senderDomain) {
|
|
1405
|
+
this.recordReputationEvent(senderDomain, {
|
|
1406
|
+
type: 'sent',
|
|
1407
|
+
count: email.to.length
|
|
1408
|
+
});
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
logger.log('info', `Email queued with ID: ${id}`);
|
|
1412
|
+
return id;
|
|
1413
|
+
} catch (error) {
|
|
1414
|
+
logger.log('error', `Failed to send email: ${error.message}`);
|
|
1415
|
+
throw error;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
/**
|
|
1420
|
+
* Handle DKIM signing for an email
|
|
1421
|
+
* @param email The email to sign
|
|
1422
|
+
* @param domain The domain to sign with
|
|
1423
|
+
* @param selector The DKIM selector
|
|
1424
|
+
*/
|
|
1425
|
+
private async handleDkimSigning(email: Email, domain: string, selector: string): Promise<void> {
|
|
1426
|
+
try {
|
|
1427
|
+
// Ensure we have DKIM keys for this domain
|
|
1428
|
+
await this.dkimCreator.handleDKIMKeysForDomain(domain);
|
|
1429
|
+
|
|
1430
|
+
// Get the private key
|
|
1431
|
+
const { privateKey } = await this.dkimCreator.readDKIMKeys(domain);
|
|
1432
|
+
|
|
1433
|
+
// Convert Email to raw format for signing
|
|
1434
|
+
const rawEmail = email.toRFC822String();
|
|
1435
|
+
|
|
1436
|
+
// Sign the email
|
|
1437
|
+
const signResult = await plugins.dkimSign(rawEmail, {
|
|
1438
|
+
canonicalization: 'relaxed/relaxed',
|
|
1439
|
+
algorithm: 'rsa-sha256',
|
|
1440
|
+
signTime: new Date(),
|
|
1441
|
+
signatureData: [
|
|
1442
|
+
{
|
|
1443
|
+
signingDomain: domain,
|
|
1444
|
+
selector: selector,
|
|
1445
|
+
privateKey: privateKey,
|
|
1446
|
+
algorithm: 'rsa-sha256',
|
|
1447
|
+
canonicalization: 'relaxed/relaxed'
|
|
1448
|
+
}
|
|
1449
|
+
]
|
|
1450
|
+
});
|
|
1451
|
+
|
|
1452
|
+
// Add the DKIM-Signature header to the email
|
|
1453
|
+
if (signResult.signatures) {
|
|
1454
|
+
email.addHeader('DKIM-Signature', signResult.signatures);
|
|
1455
|
+
logger.log('info', `Successfully added DKIM signature for ${domain}`);
|
|
1456
|
+
}
|
|
1457
|
+
} catch (error) {
|
|
1458
|
+
logger.log('error', `Failed to sign email with DKIM: ${error.message}`);
|
|
1459
|
+
// Continue without DKIM rather than failing the send
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
/**
|
|
1464
|
+
* Process a bounce notification email
|
|
1465
|
+
* @param bounceEmail The email containing bounce notification information
|
|
1466
|
+
* @returns Processed bounce record or null if not a bounce
|
|
1467
|
+
*/
|
|
1468
|
+
public async processBounceNotification(bounceEmail: Email): Promise<boolean> {
|
|
1469
|
+
logger.log('info', 'Processing potential bounce notification email');
|
|
1470
|
+
|
|
1471
|
+
try {
|
|
1472
|
+
// Process as a bounce notification (no conversion needed anymore)
|
|
1473
|
+
const bounceRecord = await this.bounceManager.processBounceEmail(bounceEmail);
|
|
1474
|
+
|
|
1475
|
+
if (bounceRecord) {
|
|
1476
|
+
logger.log('info', `Successfully processed bounce notification for ${bounceRecord.recipient}`, {
|
|
1477
|
+
bounceType: bounceRecord.bounceType,
|
|
1478
|
+
bounceCategory: bounceRecord.bounceCategory
|
|
1479
|
+
});
|
|
1480
|
+
|
|
1481
|
+
// Notify any registered listeners about the bounce
|
|
1482
|
+
this.emit('bounceProcessed', bounceRecord);
|
|
1483
|
+
|
|
1484
|
+
// Record bounce event for domain reputation tracking
|
|
1485
|
+
if (bounceRecord.domain) {
|
|
1486
|
+
this.recordReputationEvent(bounceRecord.domain, {
|
|
1487
|
+
type: 'bounce',
|
|
1488
|
+
hardBounce: bounceRecord.bounceCategory === BounceCategory.HARD,
|
|
1489
|
+
receivingDomain: bounceRecord.recipient.split('@')[1]
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
// Log security event
|
|
1494
|
+
SecurityLogger.getInstance().logEvent({
|
|
1495
|
+
level: SecurityLogLevel.INFO,
|
|
1496
|
+
type: SecurityEventType.EMAIL_VALIDATION,
|
|
1497
|
+
message: `Bounce notification processed for recipient`,
|
|
1498
|
+
domain: bounceRecord.domain,
|
|
1499
|
+
details: {
|
|
1500
|
+
recipient: bounceRecord.recipient,
|
|
1501
|
+
bounceType: bounceRecord.bounceType,
|
|
1502
|
+
bounceCategory: bounceRecord.bounceCategory
|
|
1503
|
+
},
|
|
1504
|
+
success: true
|
|
1505
|
+
});
|
|
1506
|
+
|
|
1507
|
+
return true;
|
|
1508
|
+
} else {
|
|
1509
|
+
logger.log('info', 'Email not recognized as a bounce notification');
|
|
1510
|
+
return false;
|
|
1511
|
+
}
|
|
1512
|
+
} catch (error) {
|
|
1513
|
+
logger.log('error', `Error processing bounce notification: ${error.message}`);
|
|
1514
|
+
|
|
1515
|
+
SecurityLogger.getInstance().logEvent({
|
|
1516
|
+
level: SecurityLogLevel.ERROR,
|
|
1517
|
+
type: SecurityEventType.EMAIL_VALIDATION,
|
|
1518
|
+
message: 'Failed to process bounce notification',
|
|
1519
|
+
details: {
|
|
1520
|
+
error: error.message,
|
|
1521
|
+
subject: bounceEmail.subject
|
|
1522
|
+
},
|
|
1523
|
+
success: false
|
|
1524
|
+
});
|
|
1525
|
+
|
|
1526
|
+
return false;
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
/**
|
|
1531
|
+
* Process an SMTP failure as a bounce
|
|
1532
|
+
* @param recipient Recipient email that failed
|
|
1533
|
+
* @param smtpResponse SMTP error response
|
|
1534
|
+
* @param options Additional options for bounce processing
|
|
1535
|
+
* @returns Processed bounce record
|
|
1536
|
+
*/
|
|
1537
|
+
public async processSmtpFailure(
|
|
1538
|
+
recipient: string,
|
|
1539
|
+
smtpResponse: string,
|
|
1540
|
+
options: {
|
|
1541
|
+
sender?: string;
|
|
1542
|
+
originalEmailId?: string;
|
|
1543
|
+
statusCode?: string;
|
|
1544
|
+
headers?: Record<string, string>;
|
|
1545
|
+
} = {}
|
|
1546
|
+
): Promise<boolean> {
|
|
1547
|
+
logger.log('info', `Processing SMTP failure for ${recipient}: ${smtpResponse}`);
|
|
1548
|
+
|
|
1549
|
+
try {
|
|
1550
|
+
// Process the SMTP failure through the bounce manager
|
|
1551
|
+
const bounceRecord = await this.bounceManager.processSmtpFailure(
|
|
1552
|
+
recipient,
|
|
1553
|
+
smtpResponse,
|
|
1554
|
+
options
|
|
1555
|
+
);
|
|
1556
|
+
|
|
1557
|
+
logger.log('info', `Successfully processed SMTP failure for ${recipient} as ${bounceRecord.bounceCategory} bounce`, {
|
|
1558
|
+
bounceType: bounceRecord.bounceType
|
|
1559
|
+
});
|
|
1560
|
+
|
|
1561
|
+
// Notify any registered listeners about the bounce
|
|
1562
|
+
this.emit('bounceProcessed', bounceRecord);
|
|
1563
|
+
|
|
1564
|
+
// Record bounce event for domain reputation tracking
|
|
1565
|
+
if (bounceRecord.domain) {
|
|
1566
|
+
this.recordReputationEvent(bounceRecord.domain, {
|
|
1567
|
+
type: 'bounce',
|
|
1568
|
+
hardBounce: bounceRecord.bounceCategory === BounceCategory.HARD,
|
|
1569
|
+
receivingDomain: bounceRecord.recipient.split('@')[1]
|
|
1570
|
+
});
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
// Log security event
|
|
1574
|
+
SecurityLogger.getInstance().logEvent({
|
|
1575
|
+
level: SecurityLogLevel.INFO,
|
|
1576
|
+
type: SecurityEventType.EMAIL_VALIDATION,
|
|
1577
|
+
message: `SMTP failure processed for recipient`,
|
|
1578
|
+
domain: bounceRecord.domain,
|
|
1579
|
+
details: {
|
|
1580
|
+
recipient: bounceRecord.recipient,
|
|
1581
|
+
bounceType: bounceRecord.bounceType,
|
|
1582
|
+
bounceCategory: bounceRecord.bounceCategory,
|
|
1583
|
+
smtpResponse
|
|
1584
|
+
},
|
|
1585
|
+
success: true
|
|
1586
|
+
});
|
|
1587
|
+
|
|
1588
|
+
return true;
|
|
1589
|
+
} catch (error) {
|
|
1590
|
+
logger.log('error', `Error processing SMTP failure: ${error.message}`);
|
|
1591
|
+
|
|
1592
|
+
SecurityLogger.getInstance().logEvent({
|
|
1593
|
+
level: SecurityLogLevel.ERROR,
|
|
1594
|
+
type: SecurityEventType.EMAIL_VALIDATION,
|
|
1595
|
+
message: 'Failed to process SMTP failure',
|
|
1596
|
+
details: {
|
|
1597
|
+
recipient,
|
|
1598
|
+
smtpResponse,
|
|
1599
|
+
error: error.message
|
|
1600
|
+
},
|
|
1601
|
+
success: false
|
|
1602
|
+
});
|
|
1603
|
+
|
|
1604
|
+
return false;
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
/**
|
|
1609
|
+
* Check if an email address is suppressed (has bounced previously)
|
|
1610
|
+
* @param email Email address to check
|
|
1611
|
+
* @returns Whether the email is suppressed
|
|
1612
|
+
*/
|
|
1613
|
+
public isEmailSuppressed(email: string): boolean {
|
|
1614
|
+
return this.bounceManager.isEmailSuppressed(email);
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
/**
|
|
1618
|
+
* Get suppression information for an email
|
|
1619
|
+
* @param email Email address to check
|
|
1620
|
+
* @returns Suppression information or null if not suppressed
|
|
1621
|
+
*/
|
|
1622
|
+
public getSuppressionInfo(email: string): {
|
|
1623
|
+
reason: string;
|
|
1624
|
+
timestamp: number;
|
|
1625
|
+
expiresAt?: number;
|
|
1626
|
+
} | null {
|
|
1627
|
+
return this.bounceManager.getSuppressionInfo(email);
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
/**
|
|
1631
|
+
* Get bounce history information for an email
|
|
1632
|
+
* @param email Email address to check
|
|
1633
|
+
* @returns Bounce history or null if no bounces
|
|
1634
|
+
*/
|
|
1635
|
+
public getBounceHistory(email: string): {
|
|
1636
|
+
lastBounce: number;
|
|
1637
|
+
count: number;
|
|
1638
|
+
type: BounceType;
|
|
1639
|
+
category: BounceCategory;
|
|
1640
|
+
} | null {
|
|
1641
|
+
return this.bounceManager.getBounceInfo(email);
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
/**
|
|
1645
|
+
* Get all suppressed email addresses
|
|
1646
|
+
* @returns Array of suppressed email addresses
|
|
1647
|
+
*/
|
|
1648
|
+
public getSuppressionList(): string[] {
|
|
1649
|
+
return this.bounceManager.getSuppressionList();
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
/**
|
|
1653
|
+
* Get all hard bounced email addresses
|
|
1654
|
+
* @returns Array of hard bounced email addresses
|
|
1655
|
+
*/
|
|
1656
|
+
public getHardBouncedAddresses(): string[] {
|
|
1657
|
+
return this.bounceManager.getHardBouncedAddresses();
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
/**
|
|
1661
|
+
* Add an email to the suppression list
|
|
1662
|
+
* @param email Email address to suppress
|
|
1663
|
+
* @param reason Reason for suppression
|
|
1664
|
+
* @param expiresAt Optional expiration time (undefined for permanent)
|
|
1665
|
+
*/
|
|
1666
|
+
public addToSuppressionList(email: string, reason: string, expiresAt?: number): void {
|
|
1667
|
+
this.bounceManager.addToSuppressionList(email, reason, expiresAt);
|
|
1668
|
+
logger.log('info', `Added ${email} to suppression list: ${reason}`);
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
/**
|
|
1672
|
+
* Remove an email from the suppression list
|
|
1673
|
+
* @param email Email address to remove from suppression
|
|
1674
|
+
*/
|
|
1675
|
+
public removeFromSuppressionList(email: string): void {
|
|
1676
|
+
this.bounceManager.removeFromSuppressionList(email);
|
|
1677
|
+
logger.log('info', `Removed ${email} from suppression list`);
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
/**
|
|
1681
|
+
* Get the status of IP warmup process
|
|
1682
|
+
* @param ipAddress Optional specific IP to check
|
|
1683
|
+
* @returns Status of IP warmup
|
|
1684
|
+
*/
|
|
1685
|
+
public getIPWarmupStatus(ipAddress?: string): any {
|
|
1686
|
+
return this.ipWarmupManager.getWarmupStatus(ipAddress);
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
/**
|
|
1690
|
+
* Add a new IP address to the warmup process
|
|
1691
|
+
* @param ipAddress IP address to add
|
|
1692
|
+
*/
|
|
1693
|
+
public addIPToWarmup(ipAddress: string): void {
|
|
1694
|
+
this.ipWarmupManager.addIPToWarmup(ipAddress);
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
/**
|
|
1698
|
+
* Remove an IP address from the warmup process
|
|
1699
|
+
* @param ipAddress IP address to remove
|
|
1700
|
+
*/
|
|
1701
|
+
public removeIPFromWarmup(ipAddress: string): void {
|
|
1702
|
+
this.ipWarmupManager.removeIPFromWarmup(ipAddress);
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
/**
|
|
1706
|
+
* Update metrics for an IP in the warmup process
|
|
1707
|
+
* @param ipAddress IP address
|
|
1708
|
+
* @param metrics Metrics to update
|
|
1709
|
+
*/
|
|
1710
|
+
public updateIPWarmupMetrics(
|
|
1711
|
+
ipAddress: string,
|
|
1712
|
+
metrics: { openRate?: number; bounceRate?: number; complaintRate?: number }
|
|
1713
|
+
): void {
|
|
1714
|
+
this.ipWarmupManager.updateMetrics(ipAddress, metrics);
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
/**
|
|
1718
|
+
* Check if an IP can send more emails today
|
|
1719
|
+
* @param ipAddress IP address to check
|
|
1720
|
+
* @returns Whether the IP can send more today
|
|
1721
|
+
*/
|
|
1722
|
+
public canIPSendMoreToday(ipAddress: string): boolean {
|
|
1723
|
+
return this.ipWarmupManager.canSendMoreToday(ipAddress);
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
/**
|
|
1727
|
+
* Check if an IP can send more emails in the current hour
|
|
1728
|
+
* @param ipAddress IP address to check
|
|
1729
|
+
* @returns Whether the IP can send more this hour
|
|
1730
|
+
*/
|
|
1731
|
+
public canIPSendMoreThisHour(ipAddress: string): boolean {
|
|
1732
|
+
return this.ipWarmupManager.canSendMoreThisHour(ipAddress);
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
/**
|
|
1736
|
+
* Get the best IP to use for sending an email based on warmup status
|
|
1737
|
+
* @param emailInfo Information about the email being sent
|
|
1738
|
+
* @returns Best IP to use or null
|
|
1739
|
+
*/
|
|
1740
|
+
public getBestIPForSending(emailInfo: {
|
|
1741
|
+
from: string;
|
|
1742
|
+
to: string[];
|
|
1743
|
+
domain: string;
|
|
1744
|
+
isTransactional?: boolean;
|
|
1745
|
+
}): string | null {
|
|
1746
|
+
return this.ipWarmupManager.getBestIPForSending(emailInfo);
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
/**
|
|
1750
|
+
* Set the active IP allocation policy for warmup
|
|
1751
|
+
* @param policyName Name of the policy to set
|
|
1752
|
+
*/
|
|
1753
|
+
public setIPAllocationPolicy(policyName: string): void {
|
|
1754
|
+
this.ipWarmupManager.setActiveAllocationPolicy(policyName);
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
/**
|
|
1758
|
+
* Record that an email was sent using a specific IP
|
|
1759
|
+
* @param ipAddress IP address used for sending
|
|
1760
|
+
*/
|
|
1761
|
+
public recordIPSend(ipAddress: string): void {
|
|
1762
|
+
this.ipWarmupManager.recordSend(ipAddress);
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
/**
|
|
1766
|
+
* Get reputation data for a domain
|
|
1767
|
+
* @param domain Domain to get reputation for
|
|
1768
|
+
* @returns Domain reputation metrics
|
|
1769
|
+
*/
|
|
1770
|
+
public getDomainReputationData(domain: string): any {
|
|
1771
|
+
return this.senderReputationMonitor.getReputationData(domain);
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
/**
|
|
1775
|
+
* Get summary reputation data for all monitored domains
|
|
1776
|
+
* @returns Summary data for all domains
|
|
1777
|
+
*/
|
|
1778
|
+
public getReputationSummary(): any {
|
|
1779
|
+
return this.senderReputationMonitor.getReputationSummary();
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
/**
|
|
1783
|
+
* Add a domain to the reputation monitoring system
|
|
1784
|
+
* @param domain Domain to add
|
|
1785
|
+
*/
|
|
1786
|
+
public addDomainToMonitoring(domain: string): void {
|
|
1787
|
+
this.senderReputationMonitor.addDomain(domain);
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
/**
|
|
1791
|
+
* Remove a domain from the reputation monitoring system
|
|
1792
|
+
* @param domain Domain to remove
|
|
1793
|
+
*/
|
|
1794
|
+
public removeDomainFromMonitoring(domain: string): void {
|
|
1795
|
+
this.senderReputationMonitor.removeDomain(domain);
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
/**
|
|
1799
|
+
* Record an email event for domain reputation tracking
|
|
1800
|
+
* @param domain Domain sending the email
|
|
1801
|
+
* @param event Event details
|
|
1802
|
+
*/
|
|
1803
|
+
public recordReputationEvent(domain: string, event: {
|
|
1804
|
+
type: 'sent' | 'delivered' | 'bounce' | 'complaint' | 'open' | 'click';
|
|
1805
|
+
count?: number;
|
|
1806
|
+
hardBounce?: boolean;
|
|
1807
|
+
receivingDomain?: string;
|
|
1808
|
+
}): void {
|
|
1809
|
+
this.senderReputationMonitor.recordSendEvent(domain, event);
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
/**
|
|
1813
|
+
* Check if DKIM key exists for a domain
|
|
1814
|
+
* @param domain Domain to check
|
|
1815
|
+
*/
|
|
1816
|
+
public hasDkimKey(domain: string): boolean {
|
|
1817
|
+
return this.dkimKeys.has(domain);
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
/**
|
|
1821
|
+
* Record successful email delivery
|
|
1822
|
+
* @param domain Sending domain
|
|
1823
|
+
*/
|
|
1824
|
+
public recordDelivery(domain: string): void {
|
|
1825
|
+
this.recordReputationEvent(domain, {
|
|
1826
|
+
type: 'delivered',
|
|
1827
|
+
count: 1
|
|
1828
|
+
});
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
/**
|
|
1832
|
+
* Record email bounce
|
|
1833
|
+
* @param domain Sending domain
|
|
1834
|
+
* @param receivingDomain Receiving domain that bounced
|
|
1835
|
+
* @param bounceType Type of bounce (hard/soft)
|
|
1836
|
+
* @param reason Bounce reason
|
|
1837
|
+
*/
|
|
1838
|
+
public recordBounce(domain: string, receivingDomain: string, bounceType: 'hard' | 'soft', reason: string): void {
|
|
1839
|
+
// Record bounce in bounce manager
|
|
1840
|
+
const bounceRecord = {
|
|
1841
|
+
id: `bounce_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
|
|
1842
|
+
recipient: `user@${receivingDomain}`,
|
|
1843
|
+
sender: `user@${domain}`,
|
|
1844
|
+
domain: domain,
|
|
1845
|
+
bounceType: bounceType === 'hard' ? BounceType.INVALID_RECIPIENT : BounceType.TEMPORARY_FAILURE,
|
|
1846
|
+
bounceCategory: bounceType === 'hard' ? BounceCategory.HARD : BounceCategory.SOFT,
|
|
1847
|
+
timestamp: Date.now(),
|
|
1848
|
+
smtpResponse: reason,
|
|
1849
|
+
diagnosticCode: reason,
|
|
1850
|
+
statusCode: bounceType === 'hard' ? '550' : '450',
|
|
1851
|
+
processed: false
|
|
1852
|
+
};
|
|
1853
|
+
|
|
1854
|
+
// Process the bounce
|
|
1855
|
+
this.bounceManager.processBounce(bounceRecord);
|
|
1856
|
+
|
|
1857
|
+
// Record reputation event
|
|
1858
|
+
this.recordReputationEvent(domain, {
|
|
1859
|
+
type: 'bounce',
|
|
1860
|
+
count: 1,
|
|
1861
|
+
hardBounce: bounceType === 'hard',
|
|
1862
|
+
receivingDomain
|
|
1863
|
+
});
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
/**
|
|
1867
|
+
* Get the rate limiter instance
|
|
1868
|
+
* @returns The unified rate limiter
|
|
1869
|
+
*/
|
|
1870
|
+
public getRateLimiter(): UnifiedRateLimiter {
|
|
1871
|
+
return this.rateLimiter;
|
|
1872
|
+
}
|
|
1873
|
+
}
|