Advanced SMTP connections (proxy, extensions, custom hello, commands and ports)

MailBee.NET supports a number of advanced methods and options which allow you to tune your SMTP connections in different ways, send non-standard SMTP commands, enable or disable various ESMTP extensions, and so on.

This guide also demonstrates how you can examine the server's response and status codes, use ESMTP extensions which are not natively supported by MailBee.NET (such as ENHANCEDSTATUSCODES), etc.


Connect via proxy server (SOCKS4, SOCKS5, HTTP)

If you're behind a firewall and the network is configured to make it possible to use SMTP only through a proxy server, you can do it as follows:

using System;
using MailBee;
using MailBee.SmtpMail;
using MailBee.Proxy;

class Sample
{
    static void Main(string[] args)
    {
        Smtp mailer = new Smtp();

        // See in the log file what happens when proxy is on.
        mailer.Log.Enabled = true;
        mailer.Log.Filename = "C:\\Temp\\log.txt";
        mailer.Log.Clear();

        // Use SMTP relay with ESMTP authentication.
        SmtpServer server = mailer.SmtpServers.Add(
            "mail.domain.com", "jdoe", "secret");

        // We want to connect via this proxy server.
        server.Proxy.Name = "proxy.server.com";

        // SOCKS5 proxy.
        server.Proxy.Protocol = ProxyProtocol.Socks5;

        // Or, SOCKS4 proxy.
        // server.Proxy.Protocol = ProxyProtocol.Socks4;

        // Or, HTTP proxy.
        // server.Proxy.Protocol = ProxyProtocol.Http;
        // server.Proxy.Port = 808;

        // Authentication is used with SOCKS5 and HTTP.
        // Not required with SOCKS4.
        server.Proxy.AccountName = "proxy.user";
        server.Proxy.Password = "proxy.password";

        // Compose and send simple e-mail though the proxy.
        mailer.From.Email = "jdoe@domain.com";
        mailer.To.Add("jane@example.com");
        mailer.Subject = "Test message";
        mailer.Send();
    }
}
Imports System
Imports MailBee
Imports MailBee.SmtpMail
Imports MailBee.Proxy

Module Module1
    Sub Main()
        Dim mailer As New Smtp()

        ' See in the log file what happens when proxy is on.
        mailer.Log.Enabled = True
        mailer.Log.Filename = "C:\Temp\log.txt"
        mailer.Log.Clear()

        ' Use SMTP relay with ESMTP authentication.
        Dim server As SmtpServer = mailer.SmtpServers.Add( _
            "mail.domain.com", "jdoe", "secret")

        ' We want to connect via this proxy server.
        server.Proxy.Name = "proxy.server.com"

        ' SOCKS5 proxy.
        server.Proxy.Protocol = ProxyProtocol.Socks5

        ' Or, SOCKS4 proxy.
        ' server.Proxy.Protocol = ProxyProtocol.Socks4

        ' Or, HTTP proxy.
        ' server.Proxy.Protocol = ProxyProtocol.Http
        ' server.Proxy.Port = 808

        ' Authentication is used with SOCKS5 and HTTP.
        ' Not required with SOCKS4.
        server.Proxy.AccountName = "proxy.user"
        server.Proxy.Password = "proxy.password"

        ' Compose and send simple e-mail though the proxy.
        mailer.From.Email = "jdoe@domain.com"
        mailer.To.Add("jane@example.com")
        mailer.Subject = "Test message"
        mailer.Send()
    End Sub
End Module

Here and below, we assume that MailBee.NET SMTP license key is already set in app.config, web.config, or Windows Registry. See Import namespaces and set license key topic for details.

If you haven't specified the port, 1080 will be used (the default for SOCKS4 and SOCKS5). HTTP proxies usually operate at port 808 so you'll need to specify the port manually in that case.


Connect to non-standard SMTP port

You'll typically need to change the port in case if you want to send to connect to a dedicated SSL port (usually, 465) or alternate non-SSL port (usually, 587).

If you want to use SSL, refer to Send if server requires SSL (like Gmail) topic. Alternate non-SSL port 587 is supported by some e-mail providers to let you send e-mails through in case if port 25 is blocked by your ISP.

This code snippet sends a simple e-mail through a non-SSL port 587:

Smtp mailer = new Smtp();

// Use SMTP relay with authentication.
SmtpServer server = mailer.SmtpServers.Add(
    "mail.provider.com", "jdoe@here.com", "secret");

// Use alternate SMTP port.
server.Port = 587;

mailer.SmtpServers.Add(server);

// Compose and send simple e-mail.
mailer.From.Email = "jdoe@here.com";
mailer.To.Add("jane@example.com");
mailer.Subject = "Test message";
mailer.Send();
Dim mailer As New Smtp()

' Use SMTP relay with authentication.
Dim server As SmtpServer = mailer.SmtpServers.Add( _
    "mail.provider.com", "jdoe@here.com", "secret")

' Use alternate SMTP port.
server.Port = 587

' Compose and send simple e-mail.
mailer.From.Email = "jdoe@here.com"
mailer.To.Add("jane@example.com")
mailer.Subject = "Test message"
mailer.Send()

Here and below, we assume MailBee.SmtpMail namespace is already imported in your code.


Set local end point in case of multiple network adapters or firewall

If the machine you're sending from is equipped with multiple networks adapters (each with its own IP address assigned), you may let MailBee.NET know which adapter to use. This may improve the overall performance is the appropriate adapter is used.

Another case is when you want to send from a particular local port (for instance, if your firewall requires this).

To set a local end point, assign SmtpServer.LocalEndPoint property. In case if you are doing "direct send" rather than "send through a relay server", use DirectSendServerConfig.LocalEndPoint property.

The sample below assigns a local end point and sends an e-mail to the SMTP relay server:

Smtp mailer = new Smtp();

// Use SMTP relay with ESMTP authentication.
SmtpServer server = mailer.SmtpServers.Add(
    "smtp.company.com", "jdoe@company.com", "secret");

// Send from the adapter which has 192.168.1.2 address,
// and use 20051 as the local port.
server.LocalEndPoint = new System.Net.IPEndPoint(
    System.Net.IPAddress.Parse("192.168.1.2"), 20051);

// Compose and send simple e-mail.
mailer.From.Email = "jdoe@company.com";
mailer.To.Add("jane@example.com");
mailer.Subject = "Test message";
mailer.Send();
Dim mailer As New Smtp()

' Use SMTP relay with ESMTP authentication.
Dim server As SmtpServer = mailer.SmtpServers.Add( _
    "smtp.company.com", "jdoe@company.com", "secret")

' Send from the adapter which has 192.168.1.2 address,
' and use 20051 as the local port.
server.LocalEndPoint = New System.Net.IPEndPoint( _
    System.Net.IPAddress.Parse("192.168.1.2"), 20051)

' Compose and send simple e-mail.
mailer.From.Email = "jdoe@company.com"
mailer.To.Add("jane@example.com")
mailer.Subject = "Test message"
mailer.Send()

In case of "direct send" mode, the same local end point can be specified as:

Smtp mailer = new Smtp();

// Tell MailBee.NET which DNS servers to use for DNS MX lookup.
mailer.DnsServers.Autodetect();

// Send from the adapter which has 192.168.1.2 address,
// and use 20051 as the local port.
mailer.DirectSendDefaults.LocalEndPoint =
    new System.Net.IPEndPoint(
        System.Net.IPAddress.Parse("192.168.1.2"), 20051);

// Compose and send simple e-mail.
mailer.From.Email = "jdoe@company.com";
mailer.To.Add("jane@example.com");
mailer.Subject = "Test message";
mailer.Send();
Dim mailer As New Smtp()

' Tell MailBee.NET which DNS servers to use for DNS MX lookup.
mailer.DnsServers.Autodetect()

' Send from the adapter which has 192.168.1.2 address,
' and use 20051 as the local port.
mailer.DirectSendDefaults.LocalEndPoint = _
    New System.Net.IPEndPoint( _
        System.Net.IPAddress.Parse("192.168.1.2"), 20051)

' Compose and send simple e-mail.
mailer.From.Email = "jdoe@company.com"
mailer.To.Add("jane@example.com")
mailer.Subject = "Test message"
mailer.Send()

You can also specify zero as the second parameter of System.Net.IPEndPoint constructor in case if only care for the IP address and want to let the OS assign the local port number.


Send custom Hello message to server

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

Some 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 (it's not possible if you're behind NAT) and thus your local IP address will be used. You can use SmtpServer.HelloDomain property to overcome this.

Note that if you specify the IP address instead of the domain name (although it's not recommended), 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].

That is, if the relay server you're sending through requires something special to be sent as a parameter of EHLO or HELO (usually, external IP address or the domain of the sending machine), you can set it as below:

// Use SMTP relay with ESMTP authentication.
SmtpServer server = mailer.SmtpServers.Add(
   "mail.company.com", "jdoe@company.com", "secret");

// Set the custom name MailBee.NET will use
// to represent the client to the server.
server.HelloDomain = "workstation.company.com";
' Use SMTP relay with ESMTP authentication.
Dim server As SmtpServer = mailer.SmtpServers.Add( _
    "mail.company.com", "jdoe@company.com", "secret")

' Set the custom name MailBee.NET will use
' to represent the client to the server.
server.HelloDomain = "workstation.company.com"

In case of direct send mode, setting the domain for HELO/EHLO is essential. Use the public domain name of the machine you're sending from. You can specify this parameter as follows:

// Set the custom name MailBee.NET will use to represent
// the sending host to MX servers of end recipients.
mailer.DirectSendDefaults.HelloDomain = "mail.company.com";
' Set the custom name MailBee.NET will use to represent
' the sending host to MX servers of end recipients.
mailer.DirectSendDefaults.HelloDomain = "mail.company.com"

We assume mailer is an Smtp instance. See the previous topic to learn how mailer is created and used with normal send and direct send.


Examine supported ESMTP extensions

If the logic of your code assumes different behavior in case if certain ESMTP extensions are available, you can check which extensions are supported by the given SMTP relay server.

Note that MailBee.NET supports many ESMTP extensions out-of-box. This spares you all the trouble of manual check and activation of the supported extensions. For instance, to check which authentication methods are supported by the given server, you can use Smtp.GetSupportedAuthMethods method.

If you need to determine whether the server supports STARTTLS and start the SSL session it if does, you can let MailBee.NET connect to the server, check if STARTTLS is supported, and issue STARTTLS automatically. SeeConnect to regular port and use STARTTLS to activate SSL topic for details.

However, sometimes you may need to check the supported extensions manually. For instance, Advanced STARTTLS topic contains a sample code which manually determines if STARTTLS is supported but issues this command if it's actually required by the server (not just supported).

DSN (Delivery Status Notification) is another example. MailBee.NET won't set DSN parameters during the SMTP session if the relay server does not support it. To determine if DSN is supported, see ESMTP DSN in detail topic.

The console sample below gets the list of all extensions supported by the server, and also checks the availability of ENHANCEDSTATUSCODES extension (RFC1893 and RFC2034). If it's supported, the code takes advantage of it. The code prints the detailed status of every operation (authentication and sending) and performs some exception handling as well:

using System;
using System.Collections.Specialized;
using MailBee;
using MailBee.SmtpMail;

class Sample
{
    // Reports the status of the last completed SMTP command,
    // including Enhanced Status Code if available.
    static void ExamineCommandResults(Smtp mailer, bool useEnhancedCodes)
    {
        string lastResponse = mailer.GetServerResponse();
        if (lastResponse.EndsWith("\r\n"))
        {
            lastResponse = lastResponse.Substring(0, lastResponse.Length - 2);
        }
        int simpleCode = mailer.GetServerResponseCode();

        // Extract enhanced status code from the response.
        string enhancedCode = null;
        if (useEnhancedCodes && lastResponse.Length >= 9)
        {
            enhancedCode = lastResponse.Substring(4, 5);
        }

        Console.WriteLine("Last response was: " + lastResponse);
        Console.WriteLine("Simple status code: " + simpleCode.ToString());

        if (enhancedCode != null)
        {
            Console.WriteLine("Enhanced status code: " + enhancedCode);
        }
        else
        {
            Console.WriteLine("Enhanced status code not available");
        }

        Console.WriteLine();
    }

    static void Main(string[] args)
    {
        Smtp mailer = new Smtp();

        // Log file can be helpful when dealing with low-level SMTP functions.
        mailer.Log.Enabled = true;
        mailer.Log.Filename = "C:\\Temp\\log.txt";
        mailer.Log.Clear();

        // Use SMTP relay with authentication.
        mailer.SmtpServers.Add("mail.company.com", "john.doe", "secret");

        // Compose a simple e-mail.
        mailer.From.Email = "john.doe@company.com";
        mailer.To.Add("jane@example.com");
        mailer.Subject = "Test message";

        // To determine if certain extension is supported, we need
        // to explicitly connect to the server and say hello. The server
        // responds with its capabilities to that.
        mailer.Connect();
        mailer.Hello();

        // Get the names and values of the supported extensions.
        // Some extensions have no parameters and their value is empty string.
        StringDictionary extensions =
            mailer.GetExtensions();

        if (extensions == null)
        {
            Console.WriteLine(
                "The given SMTP server does not support any ESMTP extensions");
        }
        else
        {
            // Print the list of the supported extensions.
            foreach (string extension in extensions.Keys)
            {
                string val = extensions[extension];
                if (val != string.Empty)
                {
                    // Print extension name and parameters.
                    Console.WriteLine(extension + " " + val);
                }
                else
                {
                    // For extension without parameters, print its name only.
                    Console.WriteLine(extension);
                }
            }
        }

        Console.WriteLine();

        // Check if ENHANCEDSTATUSCODES extension is available.
        bool useEnhancedCodes =
            (mailer.GetExtension("ENHANCEDSTATUSCODES") != null);

        try
        {
            // Try to authenticate.
            mailer.Login();

            // Report the results if succeeded.
            ExamineCommandResults(mailer, useEnhancedCodes);
        }
        catch (MailBeeSmtpNegativeResponseException e)
        {
            // Report the exception's message.
            Console.WriteLine(e.Message);
            Console.WriteLine();

            // Report the results if failed with a negative error code.
            ExamineCommandResults(mailer, useEnhancedCodes);

            throw;
        }

        try
        {
            // Try to send the message.
            mailer.Send();

            // Report the results if succeeded.
            ExamineCommandResults(mailer, useEnhancedCodes);
        }
        catch (MailBeeSmtpNegativeResponseException e)
        {
            // Report the exception's message.
            Console.WriteLine(e.Message);
            Console.WriteLine();

            // Report the results if failed with a negative error code.
            ExamineCommandResults(mailer, useEnhancedCodes);

            throw;
        }

        // Since we connected manually, we need to disconnect manually either.
        mailer.Disconnect();
    }
}
Imports System
Imports System.Collections.Specialized
Imports MailBee
Imports MailBee.SmtpMail

Module Module1
    ' Reports the status of the last completed SMTP command,
    ' including Enhanced Status Code if available.
    Sub ExamineCommandResults(ByVal mailer As Smtp, _
            ByVal useEnhancedCodes As Boolean)
        Dim lastResponse As String = mailer.GetServerResponse()
        If lastResponse.EndsWith(vbCrLf) Then
            lastResponse = lastResponse.Substring(0, lastResponse.Length - 2)
        End If
        Dim simpleCode As Integer = mailer.GetServerResponseCode()

        ' Extract enhanced status code from the response.
        Dim enhancedCode As String = Nothing
        If useEnhancedCodes And lastResponse.Length >= 9 Then
            enhancedCode = lastResponse.Substring(4, 5)
        End If

        Console.WriteLine("Last response was: " & lastResponse)
        Console.WriteLine("Simple status code: " & simpleCode.ToString())

        If Not (enhancedCode Is Nothing) Then
            Console.WriteLine("Enhanced status code: " & enhancedCode)
        Else
            Console.WriteLine("Enhanced status code not available")
        End If

        Console.WriteLine()
    End Sub

    Sub Main()
        Dim mailer As New Smtp()

        ' Log file can be helpful when dealing with low-level SMTP functions.
        mailer.Log.Enabled = True
        mailer.Log.Filename = "C:\Temp\log.txt"
        mailer.Log.Clear()

        ' Use SMTP relay with authentication.
        mailer.SmtpServers.Add("mail.company.com", "john.doe", "secret")

        ' Compose a simple e-mail.
        mailer.From.Email = "john.doe@company.com"
        mailer.To.Add("jane@example.com")
        mailer.Subject = "Test message"

        ' To determine if certain extension is supported, we need
        ' to explicitly connect to the server and say hello. The server
        ' responds with its capabilities to that.
        mailer.Connect()
        mailer.Hello()

        ' Get the names and values of the supported extensions.
        ' Some extensions have no parameters and their value is empty string.
        Dim extensions As StringDictionary = mailer.GetExtensions()

        If extensions Is Nothing Then
            Console.WriteLine( _
                "The given SMTP server does not support any ESMTP extensions")
        Else
            ' Print the list of the supported extensions.
            Dim extension As String
            For Each extension In extensions.Keys
                Dim val As String = extensions(extension)
                If val <> String.Empty Then
                    ' Print extension name and parameters.
                    Console.WriteLine(extension & " " & val)
                Else
                    ' For extension without parameters, print its name only.
                    Console.WriteLine(extension)
                End If
            Next extension
        End If

        Console.WriteLine()

        ' Check if ENHANCEDSTATUSCODES extension is available.
        Dim useEnhancedCodes As Boolean = Not _
            (mailer.GetExtension("ENHANCEDSTATUSCODES") Is Nothing)

        Try
            ' Try to authenticate.
            mailer.Login()

            ' Report the results if succeeded.
            ExamineCommandResults(mailer, useEnhancedCodes)
        Catch e As MailBeeSmtpNegativeResponseException
            ' Report the exception's message.
            Console.WriteLine(e.Message)
            Console.WriteLine()

            ' Report the results if failed with a negative error code.
            ExamineCommandResults(mailer, useEnhancedCodes)

            Throw
        End Try

        Try
            ' Try to send the message.
            mailer.Send()

            ' Report the results if succeeded.
            ExamineCommandResults(mailer, useEnhancedCodes)
        Catch e As MailBeeSmtpNegativeResponseException
            ' Report the exception's message.
            Console.WriteLine(e.Message)
            Console.WriteLine()

            ' Report the results if failed with a negative error code.
            ExamineCommandResults(mailer, useEnhancedCodes)

            Throw
        End Try

        ' Since we connected manually, we need to disconnect manually either.
        mailer.Disconnect()
    End Sub
End Module

Note that you can use the methods like Smtp.GetServerResponse only with a single connection. In case of multiple simultaneous connections or "direct send" mode, such methods make no sense as would not be clear for which of the current connections the method must return the results. If unsure, use Smtp.IsSmtpContext property to determine if you can currently use the methods which can operate in a single-connection mode only.


Disable conflicting ESMTP extensions

Extensions to SMTP protocol can improve performance or add features. However, they are not the essential part of the SMTP standard and may cause issues sometimes. Even though MailBee.NET never enables extensions which are not advertized by the server in EHLO response.

The typical example of a problematic extension is CHUNKING. It should speed up sending performance but it sometimes causes problems with SMTP traffic filters (like e-mail anti-virus) which intercept all the SMTP traffic. If the relay server itself supports CHUNKING and declares this in EHLO response, MailBee.NET then take advantage of it. However, the SMTP filter in the middle might not support CHUNKING and cause the entire transmission to hang or slow down.

Issues with ESMTP extensions can even happen intentionally. MS Exchange supports CHUNKING but works very slowly with it when SMTP Tar Pitting is enabled. MS Exchange does this because CHUNKING is very fast, and that's why many programs for sending spam widely use it, but most desktop e-mail clients still do not. So, if the server sees you're using CHUNKING, it intentionally slows the connection down thinking that you may be a spammer.

See MS Exchange SMTP issues topic on how to disable CHUNKING. Or, just disable SMTP Tar Pitting on the server.

PIPELINING extension allows the client to join several commands in a batch (especially efficient in case of many recipients in the message). If it causes problems with your relay server (although it happens extremely rarely), you can disable it as below:

mailer.SmtpServers.Add("smtp.domain.com""joe""secret").Pipelining = false;
mailer.SmtpServers.Add("smtp.domain.com""joe""secret").Pipelining = False

And for "direct send" mode:

mailer.DirectSendDefaults.Pipelining = false;
mailer.DirectSendDefaults.Pipelining = False

Another case is the very old SMTP servers which don't understand EHLO command and close the connection if they receive an unknown command. Normally, MailBee.NET immediately sends HELO if the server responded to EHLO with a negative reply. But if the server closes the connection instead, you'll have to disable EHLO in MailBee.NET.

The sample below disables EHLO and thus configures the relay server for classic SMTP mode:

// Use SMTP relay without ESMTP authentication, configured
// to use HELO instead of EHLO. All extensions are disabled.
mailer.SmtpServers.Add("mail.server.com").SmtpOptions =
    ExtendedSmtpOptions.ClassicSmtpMode;
' Use SMTP relay without ESMTP authentication, configured
' to use HELO instead of EHLO. All extensions are disabled.
mailer.SmtpServers.Add("mail.server.com").SmtpOptions = _
    ExtendedSmtpOptions.ClassicSmtpMode

Note we haven't specified the username and password for ESMTP authentication. If your server requires authentication but does not support EHLO, you should probably use POP-before-SMTP authentication. See Authenticate with POP-before-SMTP topic for details.

With "direct send" mode, you can disable EHLO using DirectSendServerConfig.SmtpOptions property which is accessible through Smtp.DirectSendDefaults object:

mailer.DirectSendDefaults.SmtpOptions = ExtendedSmtpOptions.ClassicSmtpMode;
mailer.DirectSendDefaults.SmtpOptions = ExtendedSmtpOptions.ClassicSmtpMode

We assume mailer is an Smtp instance.


Send custom command to server

You can send user-defined commands to an SMTP server and examine the server's response.

The sample below sends VRFY command to check if the e-mail address is valid. Unfortunately, most e-mail servers nowadays do not let you actually check if the specified address is valid on the given server (spammers started to use it to collect e-mail addresses) so we use this command just for demonstration purposes:

Smtp mailer = new Smtp();

// Log file can be helpful when dealing with low-level SMTP functions.
mailer.Log.Enabled = true;
mailer.Log.Filename = "C:\\Temp\\log.txt";
mailer.Log.Clear();

// Do not use authentication here. VRFY command does not require us
// to authenticate first. With other commands, that can be different.
mailer.SmtpServers.Add("smtp.domain.com");

mailer.Connect();
mailer.Hello();

try
{
    // Do not forget CRLF at the end of the command!
    mailer.ExecuteCustomCommand("VRFY user@domain.com\r\n");
}
catch (MailBeeSmtpNegativeResponseException e)
{
    // Perhaps, unknown command or unknown user account.
    Console.WriteLine("Oops...");
    Console.WriteLine(e.Message);
    Console.WriteLine();
}

Console.WriteLine("Server replied: " + mailer.GetServerResponse());
Console.WriteLine("Status code is: " + mailer.GetServerResponseCode().ToString());

// Since we connected manually, we need to disconnect manually either.
mailer.Disconnect();
Dim mailer As New Smtp()

' Log file can be helpful when dealing with low-level SMTP functions.
mailer.Log.Enabled = True
mailer.Log.Filename = "C:\Temp\log.txt"
mailer.Log.Clear()

' Do not use authentication here. VRFY command does not require us
' to authenticate first. With other commands, that can be different.
mailer.SmtpServers.Add("smtp.domain.com")

mailer.Connect()
mailer.Hello()

Try
    ' Do not forget CRLF at the end of the command!
    mailer.ExecuteCustomCommand("VRFY user@domain.com" & vbCrLf)
Catch e As MailBeeSmtpNegativeResponseException
    ' Perhaps, unknown command or unknown user account.
    Console.WriteLine("Oops...")
    Console.WriteLine(e.Message)
    Console.WriteLine()
End Try

Console.WriteLine("Server replied: " & mailer.GetServerResponse())
Console.WriteLine("Status code is: " & mailer.GetServerResponseCode().ToString())

' Since we connected manually, we need to disconnect manually either.
mailer.Disconnect()

You can also find the async version of this sample in Smtp.BeginExecuteCustomCommand method documentation.


Send feedback to AfterLogic

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