Direct send without SMTP relay server

This guide provides the detailed explanation of "SMTP relay", "SMTP MX" and "DNS MX lookup" terms, the comparison of "direct send" and "send through a relay server" modes, and shows how to send e-mail without an SMTP relay server.


Send with SMTP relay server, explained

Typically, the client sends e-mail through an SMTP relay server. An relay server is a hub which accepts e-mails from clients and relays them to SMTP servers of their intended recipients. These SMTP servers as called MX servers (Mail eXchange). To find out which MX server exchanges e-mail for a particular domain, the relay server makes DNS MX lookup query for that domain.

The diagram below demonstrates sending e-mail VIA an SMTP relay server:

So the entire process in case of "send through a relay server" is:

  1. The client submits the e-mail over SMTP protocol to its SMTP relay server.
  2. The SMTP relay server examines each recipient's e-mail address, gets the domain part of each address and asks the DNS server which MX server is accepting e-mail for the given domain.
  3. Once the SMTP relay got the answers, it then establishes SMTP connection with MX server for every recipient's domain and submits the e-mail message data.
  4. The MX server of every recipient saves the message in the corresponding user's mailbox.

In practice, this process may involve multiple DNS servers for better performance and reliability (and the recipient's domain may have several MX servers assigned) but the idea remains the same.


Direct send via DNS MX lookup, explained

If you do not have an SMTP relay server, it's now the client's task to do all those things which are normally done by an SMTP relay.

The diagram below demonstrates sending e-mail WITHOUT an SMTP relay server:

MailBee.NET is capable of making DNS MX lookup queries and thus can discover recipients' MX servers. After the MX host name has been discovered, the client can send the e-mail to this MX host as if were a relay server.

From this moment, the only difference between an MX and a relay server is that MX server does not require authentication. This is because you send e-mail to a local recipient of this MX server and this MX does not need to relay the e-mail elsewhere (authentication is only needed when you want to send to external domains).

In many cases, SMTP relay and SMTP MX is the same server but from different points of view. When you're the sender, you assume this is your relay server. But it's your local MX for other senders (on other domains) who are willing to send e-mail to you and treat you as their recipient.


Set DNS servers in code, via auto-detection, or in config file

To let MailBee.NET perform DNS MX queries instead of a normal send through an SMTP relay server, fill Smtp.DnsServers collection instead of Smtp.SmtpServers.

You can specify DNS servers manually or rely on automatic detection of the DNS servers which are used by your active network connection. It's recommended to have more than one DNS server (primary and secondary) for better performance and reliability.

To find out which DNS servers are used by your current connection, examine the active TCP/IP network connection properties in Windows Control Panel / Network Connections.

That's how you can set DNS servers manually:

Smtp mailer = new Smtp();
mailer.DnsServers.Add("192.168.0.1");
mailer.DnsServers.Add("192.168.0.2");
Dim mailer As Smtp = New Smtp()
mailer.DnsServers.Add("192.168.0.1")
mailer.DnsServers.Add("192.168.0.2")

Here and below, we assume that MailBee and MailBee.SmtpMail namespaces are already imported and MailBee.Global.LicenseKey is specified (directly in the code, or in app.config or web.config file). To learn more, refer toImport namespaces and set license key topic.

When using direct send, you may also need to import MailBee.DnsMX namespace.

The above method works if you know which DNS servers are available in your network. Another method is to let MailBee.NET auto-detect the available DNS servers itself. However, this method may not work if .NET security system does not allow your application to access NetworkInterface class, WMI or the required Windows Registry branch.

That's how you can auto-detect the DNS servers (assuming the permissions are OK):

Smtp mailer = new Smtp();
mailer.DnsServers.Autodetect();
Dim mailer As Smtp = New Smtp()
mailer.DnsServers.Autodetect()

If the auto-detection of DNS servers does not work in your case (usually happens with ASP.NET web applications lacking permission to access NetworkInterface class), you'll need to change Trust Level of your web application to Full.

If changing Trust Level is not an option, you can specify DNS servers' IP addresses in web.config file:

<?xml version="1.0"?>
<configuration>
  <appSettings>
    <add key="MailBee.DnsMX.DnsServerCollection" value="192.168.0.1,0;192.168.0.2"/>
  </appSettings>

Note that <appSettings> may also appear as <applicationSettings> in your case.

In the first place, DnsServerCollection.Autodetect method uses the values specified in the config file and proceeds to other methods only if no DNS servers were listed there. You can also specify priorities of DNS servers in the config file, see the documentation on DnsAutodetectOptions enumeration for examples.


Direct send example

The code below uses the auto-detection of DNS servers and sends a simple e-mail via DNS MX lookup to several recipients:

Smtp mailer = new Smtp();

// See in the log what exactly happens during direct send.
mailer.Log.Enabled = true;
mailer.Log.Filename = "C:\\Temp\\log.txt";
mailer.Log.Clear();

// Auto-detect DNS servers.
mailer.DnsServers.Autodetect();

// Specify From, To, CC in various ways.
mailer.From.Email = "joe@domain.com";
mailer.From.DisplayName = "Joe";
mailer.To.Add("mike@company1.com");
mailer.To.Add("liz@company1.com", "Liz");
mailer.Cc.Add("bill@company2.com", "Bill");
mailer.Cc.Add(new EmailAddress("kate@company3.com", "Kate"));

// Specify other fields and send the message.
mailer.Subject = "Test message";
mailer.BodyPlainText = "Just a test";
mailer.Send()
Dim mailer As New Smtp()

' See in the log what exactly happens during direct send.
mailer.Log.Enabled = True
mailer.Log.Filename = "C:\Temp\log.txt"
mailer.Log.Clear()

' Auto-detect DNS servers.
mailer.DnsServers.Autodetect()

' Specify From, To, CC in various ways.
mailer.From.Email = "joe@domain.com"
mailer.From.DisplayName = "Joe"
mailer.To.Add("mike@company1.com")
mailer.To.Add("liz@company1.com", "Liz")
mailer.Cc.Add("bill@company2.com", "Bill")
mailer.Cc.Add(New EmailAddress("kate@company3.com", "Kate"))

' Specify other fields and send the message.
mailer.Subject = "Test message"
mailer.BodyPlainText = "Just a test"
mailer.Send()

Note that as one of the recipients is set from EmailAddress object, you'll also need to import MailBee.Mime namespace.

You can also fine-tune a number of settings specific to "direct send" mode using Smtp.DirectSendDefaults property. Some examples of setting "direct send" defaults can also be found throughout Advanced SMTP connections (proxy, extensions, custom hello, commands and ports) guide.

Besides that, Smtp class provides some events specific to "direct send" mode: Smtp.MessageMXLookupDone and Smtp.MessageDirectSendDone.

You can also use DNS queries (including DNS MX lookup) for other purposes besides sending e-mail. For instance, you can validate e-mail addresses and perform various checks to filter spam by examining DNS-related data. To learn more, refer to Validate e-mail address via syntax check and DNS MX lookup topic or Filter spam with DNS queries (MX, SPF, DKIM, reverse DNS) guide respectively.


Set appropriate Hello message to pass reverse DNS check

By default, MailBee.NET uses the IPv4 address of the sending machine in the parameter of EHLO and HELO commands when making connections with MX hosts, or the local host name if the IPv4 address is not available.

Most SMTP MX servers perform reverse DNS check over the domain name or IP address listed in the EHLO/HELO parameter, and do not trust the connections which come from IP addresses not matching those specified in EHLO/HELO. MailBee.NET cannot always determine your external IP address (this is not possible if you're behind NAT) and thus your local IP address will be used. You can use DirectSendServerConfig.HelloDomain property to overcome this.

Note that if you specify the IP address instead of the domain name, you should enclose it in square brackets. Example: [15.16.23.42], so that the command to be sent would be EHLO [15.16.23.42].

Still, using the public (i.e. external) domain name is much more preferred than the IP address. Unfortunately, the public domain name is not known to the system (even if the public IP address is known) so that you can only specify it manually.

Example (assuming mailer is an Smtp instance):

mailer.DirectSendDefaults.HelloDomain = "sending.host.com";
mailer.DirectSendDefaults.HelloDomain = "sending.host.com"

Note that setting Hello message is essential for direct send (whereas it isn't so important when sending through a relay server).


Direct send vs normal send

In short, use normal send whenever possible. Use direct send only if normal send is not available.

Normal send via an SMTP relay server usually provides the optimal performance. SMTP relay sits in the data center with a very fast Internet connection, it can sort and pre-process incoming requests from clients in optimal way, maintain large cache of DNS records to avoid making DNS MX queries for popular domains, retry failed sends later, etc.

Also, if your e-mail is addressed to multiple recipients, there is a great difference between direct send and normal send. When "direct send" mode is used, rejected recipients are always allowed (unless you send to a single recipient only). However, with the SMTP relay and SmtpServer.AllowRefusedRecipients property, you can change this to require that the e-mail is considered as sent only if all recipients have been accepted. This means MailBee.NET will abort the transmission and cancel the e-mail completely - no recipients will receive it.

Such transactional mechanism ("all or none") is not possible with direct send because delivering e-mail to recipients' MXes is not a single transaction. In contrast, submitting recipients to SMTP relay can be assumed as the transaction which can be rolled back.

Even more important vote for SMTP relay is the fact that most e-mail services nowadays do not accept e-mail from hosts which are not full-fledged SMTP servers. This is because "direct send" mode is widely used by spammers' bot networks. A typical e-mail service, after it receives the incoming request to deliver an e-mail to it, makes reverse connection to the client's host, checks if that host is an SMTP server itself, if it has properly configured DNS MX, PTR, and SPF records, and so on.

Due to the above, you as a client may use "direct send" only when you send from the host which has all the DNS records and open SMTP port for reverse connections. Otherwise, many of your e-mails will not be delivered or will just end up in Spam folder of recipients' e-mail accounts.

More meaningful way of using direct send is the fail-over delivery method if the SMTP relay fails. Although MailBee.NET does allow you to use multiple SMTP relays (such as primary and fail-over), it does not help in case if you have only one SMTP relay and no fail-over one. In this case, you can implement fail-over mode only via direct send. Refer to Send through SMTP relay server or use direct send if SMTP relay server is down topic for example.

Last but not least, you can use direct send to verify e-mail addresses for validity (i.e. if certain string denotes an existing e-mail address). Although you can use Smtp.TestSend method with a relay server as well, usually it produces more accurate results with direct send. See the next topic for details.


Validate e-mail address via syntax check and DNS MX lookup

This is the advanced version of Verify e-mail address without sending actual e-mail topic.

To check if an e-mail address is valid, you can:

  1. Perform a syntax check over the address string (for instance, using Smtp.ValidateEmailAddressSyntax static method, or, in custom cases, your own regular expressions, like in the sample below).
  2. Additionally use Smtp.TestSend method to check if the domain name of the e-mail address is valid (and, with some degree of accuracy, the account name).
  3. EmailAddressValidator component is the ultimate tool for e-mail address verification, for both single addresses and (especially) bulks.

The sample below performs both syntax check via custom RegEx pattern and DNX MX lookup check. If the domain is found, MailBee.NET connects to the found MX server of the recipient being tested, and makes an attempt to submit the sender's and the recipient's e-mail addresses. If everything went fine, MailBee.NET closes the connection and reports that the specified e-mail address is valid:

// The e-mail address to check.
string email = "jane.doe@company.com";

string addressPattern =
    @"^(([\w-]+['\.])+[\w-]+|([\w-]+))@" +
    @"((([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?" +
    @"[0-9]{1,2}|25[0-5]|2[0-4][0-9])\." +
    @"([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?" +
    @"[0-9]{1,2}|25[0-5]|2[0-4][0-9])){1}|" +
    @"([a-zA-Z0-9]+[\w-]*\.)+[a-zA-Z]{2,9})$";

bool isCorrectSyntax =
    System.Text.RegularExpressions.Regex.IsMatch(email, addressPattern);

if (!isCorrectSyntax)
{
    Console.WriteLine("Syntax check failed");
}
else
{
    Smtp mailer = new Smtp();

    // See in the log how exactly MailBee.NET validates the address.
    mailer.Log.Enabled = true;
    mailer.Log.Filename = "C:\\Temp\\log.txt";
    mailer.Log.Clear();

    // Get ready for DNS MX lookup.
    mailer.DnsServers.Autodetect();

    // Set sender. It's recommended to use the address
    // from which you'll then be sending the actual e-mail.
    mailer.From.Email = "john.doe@domain.com";

    // Set the recipient's address we're going to verify.
    mailer.To.Add(email);

    // Verify the e-mail address.
    TestSendResult res =
        mailer.TestSend(SendFailureThreshold.AnyRecipientsFailed);

    switch (res)
    {
        case TestSendResult.OK:
            Console.WriteLine("Existing address");
            break;
        case TestSendResult.BadRecipient:
            // E.g. bad-recipient@good-domain.com.
            Console.WriteLine("Invalid account but valid domain");
            break;
        case TestSendResult.NoMXRecord:
            // E.g. bad-recipient@bad-domain.com.
            Console.WriteLine("Invalid account and domain");
            break;
        default:
            Console.WriteLine("Other error");
            break;
    }
}
' The e-mail address to check.
Dim email As String = "jane.doe@company.com"

Dim addressPattern As String = _
    "^(([\w-]+['\.])+[\w-]+|([\w-]+))@" & _
    "((([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?" & _
    "[0-9]{1,2}|25[0-5]|2[0-4][0-9])\." & _
    "([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?" & _
    "[0-9]{1,2}|25[0-5]|2[0-4][0-9])){1}|" & _
    "([a-zA-Z0-9]+[\w-]*\.)+[a-zA-Z]{2,9})$"

Dim isCorrectSyntax As Boolean = _
    System.Text.RegularExpressions.Regex.IsMatch(email, addressPattern)

If Not isCorrectSyntax Then
    Console.WriteLine("Syntax check failed")
Else
    Dim mailer As New Smtp()

    ' See in the log how exactly MailBee.NET validates the address.
    mailer.Log.Enabled = True
    mailer.Log.Filename = "C:\Temp\log.txt"
    mailer.Log.Clear()

    ' Get ready for DNS MX lookup.
    mailer.DnsServers.Autodetect()

    ' Set sender. It's recommended to use the address
    ' from which you'll then be sending the actual e-mail.
    mailer.From.Email = "john.doe@domain.com"

    ' Set the recipient's address we're going to verify.
    mailer.To.Add(email)

    ' Verify the e-mail address.
    Dim res As TestSendResult = _
        mailer.TestSend(SendFailureThreshold.AnyRecipientsFailed)

    Select Case res
        Case TestSendResult.OK
            Console.WriteLine("Existing address")
        Case TestSendResult.BadRecipient
            ' E.g. bad-recipient@good-domain.com.
            Console.WriteLine("Invalid account but valid domain")
        Case TestSendResult.NoMXRecord
            ' E.g. bad-recipient@bad-domain.com.
            Console.WriteLine("Invalid account and domain")
        Case Else
            Console.WriteLine("Other error")
    End Select
End If

As you can see, the sender's address is also required (in addition to the recipient's address being tested).

Smtp.TestSend method returns not just true or false but more than 20 status codes (see TestSendResult documentation) to let the application know what exactly went wrong. For instance, if you got, let's say, TestSendResult.DnsConnectionError, this means there is a problem with the DNS server being used, not with the e-mail address being checked.

You can also verify multiple addresses at once (in this case, failureThreshold parameter denotes whether to allow failed recipients or not). In there is a single recipient only, failureThreshold parameter value is ignored.

Note that it's not always possible to validate an e-mail address with 100% accuracy guarantee. Usually, you can reliably verify the syntax and the domain part (domain.com in user@domain.com).

As for the account name part (user in user@domain.com), the server may accept the e-mail to that address but bounce it back later (so that spammers would be unable to go over the list of popular words and try each word against the server to collect valid e-mail accounts on that server).

Also, even if the e-mail address is indeed correct and the recipient's server has confirmed this, you may be unable to send an actual e-mail to that address. For instance, Gmail allows any client to check if certain e-mail address exists in its domain, but only full-fledged e-mail servers with MX and PTR records can actually send e-mail there (again, to prevent spam). So, if the machine you're sending from is a public server with MX and PTR records and it isn't blacklisted on major DNSRBL's, you shouldn't have any issues in this regard.


Send feedback to AfterLogic

Copyright © 2006-2023 AfterLogic Corporation. All rights reserved.