Lurgle.Alerting v1.3.1 - Multiple mail hosts, DNS delivery, and fine grained TLS options!

I've recently been contributing some code to the Seq Email+ (Seq.App.HtmlEmail) repo to allow for enhanced capabilities, such as delivering email using DNS (by querying the MX record for domains) and using fallback mail hosts, along with adding To/CC/BCC and other options. These additions will, if merged, make for a more powerful Seq app.

In the meantime, I've circled back to add capabilities to Lurgle.Alerting, to allow my FluentEmail implementation to use multiple mailhosts and DNS delivery, and also have fine-grained control over TLS (when using MailKit), using what I've learned by my contributions to Email+.

The result is Lurgle.Alerting v1.3.1. This has a couple of breaking changes to the constructor if you use that to build your config, by virtue of adding the two new options.

These are predominantly config-based additions:

    <add key="MailHost" value="mailhost1.domain.com,mailhost2.domain.com"/>
  <add key="MailUseDns" value="true"/>
    <add key="MailTlsOptions" value="Auto" />

or in the constructor:

Alerting.SetConfig(new AlertConfig(Alerting.Config, mailHost="mailhost1.domain.com,mailhost2.domain.com", mailUseDns: true, mailTlsOptions: TlsOptions.Auto));

Multiple mail hosts and DNS delivery

In short - the additions for MailHost mean that you can specify multiple hosts as a comma-delimited string. If delivery to one host fails, the next will be attempted, and so on. This means that you have inbuilt mail fallback capabilities.

If MailHost is empty and MailUseDns is true, Lurgle.Alerting will deliver using DNS, by looking up the MX records for each To, CC, and BCC email address. It will attempt to deliver using each MX record found for each domain.

If MailHost has entries and MailUseDns is true, then if all mail host deliveries fail, Lurgle.Alerting will then attempt delivery via DNS, for the ultimate in fallback capabilities! 

I have moved Lurgle.Alerting from using FluentEmail.Core.Models.SendResponse to a MailResult class, which preserves the Successful, ErrorMessages, and MessageId properties. This should preserve compatibility in existing code, but now reflects the last delivery attempt. Lurgle now also  provides MailResult.DeliveryType and MailResult.MailHost. These fields reflect the last delivery attempt's type and the mail server attempted.

    public class MailResult
  {
      /// <summary>
      /// Overall Success / Failure
      /// </summary>
        public bool Successful { get; set; }

      /// <summary>
      /// Last send's delivery type
      /// </summary>
        public DeliveryType DeliveryType { get; set; }

      /// <summary>
      /// Last send's mail host
      /// </summary>
        public string MailHost { get; set; }

      /// <summary>
      /// Last send's ErrorMessages for backward compatibility
      /// </summary>
        public IList<string> ErrorMessages { get; set; }

      /// <summary>
      /// Last send's MessageId for backward compatibility
      /// </summary>
        public string MessageId { get; set; }

      /// <summary>
      /// List of all attempts
      /// </summary>
      public List<DeliveryAttempt> DeliveryAttempts { get; set; }
    }

You can explore all delivery attempts using the MailResult.DeliveryAttempts list, which contains the result of each delivery attempt!

The DeliveryType enum is as follows;

    public enum DeliveryType
    {
        /// <summary>
        /// Delivery via mailhost
        /// </summary>
        MailHost,
        /// <summary>
        /// Delivery via mailhost fallback
        /// </summary>
        MailFallback,
        /// <summary>
        /// Delivery via DNS
        /// </summary>
        Dns,
        /// <summary>
        /// Delivery via DNS fallback
        /// </summary>
        DnsFallback,
        /// <summary>
        /// Delivery via Mailhost DNS fallback
        /// </summary>
        HostDnsFallback,
        /// <summary>
        /// N/A
        /// </summary>
        None = -1
    }

and the DeliveryAttempt class in MailResult.DeliveryAttempts is;

    public class DeliveryAttempt
    {
        /// <summary>
        /// Type of delivery
        /// </summary>
        public DeliveryType DeliveryType { get; set; }
        /// <summary>
        /// Host attempted
        /// </summary>
        public string MailHost { get; set; }
        /// <summary>
        /// Send response
        /// </summary>
        public SendResponse Result { get; set; }
    }


Fine grained TLS with MailKit

MailTlsOptions offers finer grained control over that offered by the MailUseTls boolean's "on or off" functionality. It uses a TlsOptions enum which inherits the same values as MailKit's SecureSocketOptions enum. This allows TlsOptions to be referenced within code, without needing to import the MailKit.Security reference. 

    public enum TlsOptions
  {
      /// <summary>
      /// None
      /// </summary>
        None = SecureSocketOptions.None,

      /// <summary>
      /// Auto
      /// </summary>
        Auto = SecureSocketOptions.Auto,

      /// <summary>
      /// Implicit TLS
      /// </summary>
        SslOnConnect = SecureSocketOptions.SslOnConnect,

      /// <summary>
      /// Explicit TLS
      /// </summary>
        StartTls = SecureSocketOptions.StartTls,

      /// <summary>
      /// Optional TLS
      /// </summary>
      StartTlsWhenAvailable = SecureSocketOptions.StartTlsWhenAvailable
    }

This will only work with MailKit, and setting MailTlsOptions will override MailUseTls. Lurgle.Alerting will also ensure that the correct behaviour is used if you configure TCP port 465 for SMTP:

            switch (MailTlsOptions)
            {
                case null when MailUseTls && MailPort == 465: //Implicit TLS
                case TlsOptions.None when MailPort == 465:
                case TlsOptions.Auto when MailPort == 465:
                case TlsOptions.StartTlsWhenAvailable when MailPort == 465:
                    MailTlsOptions = TlsOptions.SslOnConnect;
                    break;
                case null when MailUseTls:
                    MailTlsOptions = TlsOptions.StartTls; //Explicit TLS
                    break;
                case null:
                    MailTlsOptions = TlsOptions.Auto;
                    break;
            }

This enforces implicit TLS to be used in this scenario.

Lurgle the day away

Lurgle.Alerting is now even more powerful, and this builds on the effort to make common log, alert, and even date libraries using the Lurgle name.  Download Lurgle.Alerting now, and check out the other Lurgles!

 

 

Comments

You may also like:

Lurgle.Alerting v1.2.0 released - Consistent attachment content types!

I've released a small update to Lurgle.Alerting which adds automatic determination of the attachment content type using the MimeMapping library. I've raised the version to v1.2.0 to align with Lurgle.Logging's current releases. This specifically addresses an issue when sending attachments with MailKit as the SMTP sender. The FluentEmail implementation was...

Lurgle.Alerting v1.1.10 and Lurgle.Logging v1.1.15 Released

I've just pushed out an update to Lurgle.Alerting on Nuget. This release adds a Handlebars template option, based on the implementation by Matthew Turner at FluentEmail.Handlebars (github.com). When I came across the FluentEmail.Handlebars package, I was keen to use it, but it was only compiled against .NET Standard 2.1, and...

Lurgle.Alerting - a standardised FluentEmail implementation with extra goodies!

Another Lurgle Around the time that I tackled my original Serilog logging implementation, I also looked at our email alerting. Emails can be used for a variety of reasons, and it's not uncommon that they are sent as a simple string that concatenates or formats variables. In this scenario, the...