Skip to content

SMTP Router v1.0.0.2

Choose a tag to compare

@diassoft diassoft released this 23 Apr 04:20
· 28 commits to master since this release
c38c374

Smtp Router - v1.0.0.2

nuget
GitHub release
NuGet
license

The SMTP Router is a intermediate SMTP server useful to intercept messages and route it to another smtp.

It contains a Listener to capture Smtp messages and a Router that will route the message to another Smtp Server when it matches certain RoutingRules.

These are some of the uses of an SMTP Router:

  • SMTP Relay Server
  • Switch the destination SMTP server based on the incoming message. This is especially useful when you have systems that can only accept one single SMTP configuration and you want to use more than one SMTP.
  • Use Multiple SMTPs to send messages, due to daily our hourly limits defined by the email provider (in development)

If you wish to contact the creator of this component, you can make it thru the Nuget.org page or by email olavodias@gmail.com.

In this repository

Additional Repositories

There are other interesting repositories regarding the SMTPRouter Project.

Repository Description Nuget
SMTPRouter.Windows Contains types to allow the use of the SMTPRouter Component in a .NET Framework. It also contains an implementation of the SMTPRouter as a Windows Service nuget

We are currently working on a solution to have it run in other OSs

How To Use It

There are two different ways to use the SMTP Router.

  • You can initialize a SmtpRouter.Server object or;
  • You can initialize a Listener and a Router manually.

The Listener opens a SmtpServer and listens to messages. Once a messages arrives, it will raise the MessageReceived event, with the MimeKit.MimeMessage received.

The Router runs in a separated thread from the Listener. The Router itself is the component which checks the RoutingRules and routes the message to the proper SMTP.

The Router creates a folder structure containing the following queues:

Queue Description
Outgoing Emails that need to be routed
Sent Emails that were routed successfully
Retry Emails that were not sent yet, but are still under the MessageLifespan. Those messages are sent back to the Outgoing queue.
Error Emails that were not sent and the MessageLifespan is expired. Those messages are no longer sent unless they are moved manually to the Outgoing queue.

Prerequisites

When you choose to use the SmtpRouter Nuget Package, you will also be required to install the following packages:

Package Nuget Link Author
SmtpServer nuget cosullivan
MimeKit nuget jstedfast
MailKit nuget jstedfast

Using the Server Object

The easiest and recommended way to implement the SmtpRouter is by initializing an instance of the Server class and call the StartAsync to start listening to messages route them.

The code below demonstrates how to instantiate a server:

// Creates the Server
var server = new SMTPRouter.Server("localhost", 25, false, false, "SMTPRouter", "C:\\SMTPRouter\\Queues")
{
    MessageLifespan = new TimeSpan(0, 15, 0),
    RoutingRules = new List<Models.RoutingRule>()
    {
        new Models.MailFromDomainRoutingRule(10, "gmail.com", "gmail"),
        new Models.MailFromDomainRoutingRule(20, "hotmail.com", "hotmail")
    },
    DestinationSmtps = new Dictionary<string, Models.SmtpConfiguration>
    {
        { "gmail", new Models.SmtpConfiguration()
            {
                Host = "smtp.gmail.com",
                Description = "Google Mail SMTP",
                Port = 587,
                RequiresAuthentication = true,
                User = "user@gmail.com",
                Password = "",
            }
        },
        { "hotmail", new Models.SmtpConfiguration()
            {
                Host = "smtp.live.com",
                Description = "Hotmail SMTP",
                Port = 587,
                RequiresAuthentication = true,
                User = "user@hotmail.com",
                Password = "",
            }
        }
    },
};

// Initialize Services
Task.WhenAll(server.StartAsync(CancellationToken.None)).ConfigureAwait(false);

The Server class triggers events when certain activities happen. You can hook to the events by using the code below:

// Hook Events
server.SessionCreated += Server_SessionCreated;
server.SessionCommandExecuting += Server_SessionCommandExecuting;
server.SessionCompleted += Server_SessionCompleted;
server.ListeningStarted += Server_ListeningStarted;
server.MessageReceived += Server_MessageReceived;
server.MessageRoutedSuccessfully += Server_MessageRoutedSuccessfully;
server.MessageNotRouted += Server_MessageNotRouted;

For more information, refer to the Server Class in the documentation.

Using the Listener and Router Individually

It is not necessary to instantiate the Listener and Router manually, but if you need to have more control, you can do it.

If you want to have the Router run in a different Service Instance, perhaps having both objects initialized separately would be the best option.

The code below demonstrates how to instantiate a Listener, a Router and hook them together using events.

// Create the Listener
var listener = new SMTPRouter.Listener()
{
    ServerName = "localhost",
    Ports = new int[] { 25, 587 },
    RequiresAuthentication = false,
    UseSSL = false
};
listener.SessionCreated += Server_SessionCreated;
listener.SessionCommandExecuting += Server_SessionCommandExecuting;
listener.SessionCompleted += Server_SessionCompleted;
listener.ListeningStarted += Server_ListeningStarted;

// Create the Router
var router = new SMTPRouter.Router("SMTPRouter", "C:\\SMTPRouter\\Queues")
{
    MessageLifespan = new TimeSpan(0, 15, 0),
    RoutingRules = new List<Models.RoutingRule>()
    {
        new Models.MailFromDomainRoutingRule(10, "gmail.com", "gmail"),
        new Models.MailFromDomainRoutingRule(20, "hotmail.com", "hotmail")
    },
    DestinationSmtps = new Dictionary<string, Models.SmtpConfiguration>
    {
        { "gmail", new Models.SmtpConfiguration()
            {
                Host = "smtp.gmail.com",
                Description = "Google Mail SMTP",
                Port = 587,
                RequiresAuthentication = true,
                User = "user@gmail.com",
                Password = "",
            }
        },
        { "hotmail", new Models.SmtpConfiguration()
            {
                Host = "smtp.live.com",
                Description = "Hotmail SMTP",
                Port = 587,
                RequiresAuthentication = true,
                User = "user@hotmail.com",
                Password = "",
            }
        }
    },
};
router.MessageRoutedSuccessfully += Server_MessageRoutedSuccessfully;
router.MessageNotRouted += Server_MessageNotRouted;

// When the Listener have a message, it has to be sent to the Router. This is Automatic when you use the Server class
listener.MessageReceived += (object sender, MessageEventArgs e) =>
{
    // Make sure to enqueue the message otherwise the router doesn't know about anything
    router.Enqueue(e.MimeMessage);
};
listener.MessageReceived += Server_MessageReceived;

// Initialize Services
Task.WhenAll(listener.StartAsync(CancellationToken.None),
             router.StartAsync(CancellationToken.None)).ConfigureAwait(false);

For more information, refer to the Listener Class and the Router Class in the documentation.

Smtp Configuration

The Router has a property named DestinationSmtps, which represents a Dictionary<string, SmtpConfiguration>.

The Smtps to route messages must be cofigured and added to this collection. The Key field is the key to fetch the Smtp.

You can configure the Smtps when initializing the Server or Router, or you can configure a Smtp using the code below:

// Initializes a new Smtp Configuration
var smtpConfiguration = new SmtpConfiguration()
{
    Key = "Gmail",
    Description = "Gmail SMTP",
    Host = "smtp.gmail.com",
    Port = 587,
    RequiresAuthentication = true,
    UseSSL = false,
    User = "user@gmail.com",
    Password = "password"
};

The SmtpConfiguration object derives from an ObservableObject, which makes it compatible with Mvvm Implementations.

For more information, refer to the SmtpConfiguration in the documentation.

Routing Rules

The Router has a property named RoutingRules, which represents a List<RoutingRule>.

A RoutingRule is a Rule that can be applied and, if it matches, the Smtp Configuration for that rule will be used to route the email.

The RoutingRule class itself does not provide any rule, instead, it is a base class that must be inherited from in order to create an actual rule.

By default the SmtpRouter offers two Routing Rules:

  • MailFromDomainRoutingRule, which is a rule that verifies if the domain on the mail from matches the domain configured on the rule.
  • MailFromRegexMatchRoutingRule, which is a rule that runs a System.Text.RegularExpressions to validate the mail from.

The code below demonstrates how to add a MailFromDomainRoutingRule to the Router object:

Router.RoutingRules.Add(new MailFromDomainRoutingRule(10, "mydomain.com", "mydomain"));

You can create your own Routing Rules by inheriting from RoutingRule class, as demonstrated on the code below:

public sealed class MyCustomRoutingRule: RoutingRule
{
    public MyCustomRoutingRule(): this(0)
    {
            
    }

    public MyCustomRoutingRule(int executionSequence, string smtpConfigurationKey): base(executionSequence)
    {
        base.SmtpConfigurationKey = smtpConfigurationKey;
    }

    public override bool Match(MimeMessage mimeMessage)
    {
        return true;
    }
}

You have to override the method Match to provide a algorithm that validades the rule.

The RoutingRule object derives from an ObservableObject, which makes it compatible with Mvvm Implementations.

For more information, refer to the RoutingRule in the documentation.