Filter spam with DNS queries (MX, SPF, DKIM, reverse DNS)


Introduction

MailBee.NET can make use of DNS queries for these tasks:

Besides that, MailBee.NET lets you make various DNS queries directly. Although MailBee.NET SMTP component mainly serves the purposes of sending e-mail, its capabilities of making DNS queries can also be used to verify incoming e-mails in a number of ways (including the validation of the e-mail address and IP address of the message sender).

For instance, if your code needs to perform an extensive anti-spam check against incoming e-mails, verifying just DKIM/DomainKeys signatures might not be enough and you'd also want to make sure sending hosts have valid MX, PTR, and SPF records.

In most cases, you'll perform such checks against the domain part of "From" e-mail address. You can take it as follows:

domain = msg.From.GetDomain();
domain = msg.From.GetDomain()

we assume msg is a MailMessage instance which represents the incoming e-mail message being examined.

However, "From:" e-mail address may be forged. Usually, you'll need to combine different methods to improve the reliability of anti-spam checks, such as to use reverse DNS check which is immune to e-mail address forging. See Get reverse DNS records with DNS PTR query topic for details.

Also, you can perform content filtering with MailBee.NET AntiSpam component which implements self-learning Bayesian filter. This component is included in MailBee.NET assembly. See BayesFilter class for details.


DNS RBL lookup

To make queries to RBL servers, you can use RblFilter class (you can find the sample code there as well).


Get DNS MX records

To request the list of DNS MX names for the given domain, use Smtp.GetMXHosts method. It also properly handles the situation when the host does not have any MX records but has A record (in case if the MX domain for the host is the host itself).

This sample downloads the last e-mail from an account on a POP3 server, then checks if the "From" domain has the required MX records (or A record) to determine if at least the domain part of the "From:" address denotes an existing host. The return value of Smtp.GetMXHosts method isn't used, we just need to know if the DNS MX records for that host are there:

// Get the last message from the POP3 server.
MailMessage msg = Pop3.QuickDownloadMessage(
    "mail.domain.com", "jdoe", "secret", -1);

Smtp mailer = new Smtp();

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

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

bool isDomainValid;
try
{
    // Make DNS query. We don't care for results.
    // If the method succeeds, the domain is OK.
    mailer.GetMXHosts(msg.From.GetDomain());
    isDomainValid = true;
}
catch (MailBeeDnsNameErrorException)
{
    // DNS server does not know this domain.
    isDomainValid = false;
}
catch (MailBeeDnsRecordsDisabledException)
{
    // By default, caching DNS queries is on. If the domain
    // being requested is already in the cache and MailBee.NET
    // already knows it's invalid, no actual query will be made.
    isDomainValid = false;
}

if (isDomainValid)
{
    Console.WriteLine("Valid domain");
}
else
{
    Console.WriteLine("Invalid domain");
}
' Get the last message from the POP3 server.
Dim msg As MailMessage = Pop3.QuickDownloadMessage( _
    "mail.domain.com", "jdoe", "secret", -1)

Dim mailer As New Smtp()

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

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

Dim isDomainValid As Boolean
Try
    ' Make DNS query. We don't care for results.
    ' If the method succeeds, the domain is OK.
    mailer.GetMXHosts(msg.From.GetDomain())
    isDomainValid = True
Catch e As MailBeeDnsNameErrorException
    ' DNS server does not know this domain.
    isDomainValid = False
Catch e As MailBeeDnsRecordsDisabledException
    ' By default, caching DNS queries is on. If the domain
    ' being requested is already in the cache and MailBee.NET
    ' already knows it's invalid, no actual query will be made.
    isDomainValid = False
End Try

If isDomainValid Then
    Console.WriteLine("Valid domain")
Else
    Console.WriteLine("Invalid domain")
End If

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

As you can see, the spam detection efficiency of this method is very low. The spammer can easily forge "From:" address and use an existing domain in it (e.g. fake_from_address@hotmail.com). It's more reliable to use spam filtering methods which examine the sender's IP address instead (as it cannot be forged).

For instance, you can use rDNS query to get the sender's domain name from its IP address and then get MX records for the "From:" domain name. If any of these MX records contain the domain name of the sender, this indicates the "From:" domain is not forged.

The opposite is not correct, however. If the sending host's domain does not appear in MX list of the "From:" domain, it may indicate the relay SMTP server of the sender and its SMTP MX server are different hosts. This is perfectly normal for large providers.

Thus, you can use the method described above only to confirm that certain e-mail is not spam. If this check is negative, you should try other methods before you can say the e-mail is spam.


Get SPF or DKIM ADSP records with DNS TXT query

MailBee.NET provides built-in support of DomainKeys/DKIM verification with advanced methods of DomainKeys class and a short-hand MailMessage.DomainKeysVerify method.

Still, you may need to get some DNS TXT records manually, including those used with SPF (Sender Policy Framework, RFC 4408) or DKIM ADSP (Author Domain Signing Practices, RFC 5617).

MailBee.NET does not provide built-in mechanisms for SPF and DKIM ADSP but allows you to easily extract their DNS information for a domain. Your application will only need to parse the text string returned.

Although many servers use SPF nowadays, no server implementing DKIM ADSP is known to the moment (at least all major e-mail providers like Gmail, Hotmail and Yahoo still didn't support it at the moment of writing).

The sample below gets TXT records for DKIM ADSP (no exception is thrown in case if not found) and also checks root TXT records of the domain and searches for SPF record there:

Smtp mailer = new Smtp();

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

// Get ready for making DNS queries.
mailer.DnsServers.Autodetect();

string[] records = null;

try
{
    // Get DKIM ADSP record (will likely to fail so ignore exceptions).
    records = mailer.GetTxtData("_adsp._domainkey.domain.com");

    // Some servers do not claim error but return no records. Check this.
    if (records != null)
    {
        Console.Write("DKIM ADSP record:");
        foreach (string rec in records)
        {
            // Should be only one such record (if any).
            Console.WriteLine(rec);
        }
        Console.WriteLine();
    }
}
catch (MailBeeDnsNameErrorException) { }
catch (MailBeeDnsRecordsDisabledException) { }

// Get root TXT records (to filter SPF record from them).
records = mailer.GetTxtData("domain.com");
if (records != null)
{
    foreach (string rec in records)
    {
        if (rec.ToLower().StartsWith("v=spf1"))
        {
            // Should be only one such record (if any).
            Console.Write("SPF record: ");
            Console.WriteLine(rec);
        }
    }
}
Dim mailer As New Smtp()

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

' Get ready for making DNS queries.
mailer.DnsServers.Autodetect()

Dim records As String() = Nothing

Try
    ' Get DKIM ADSP record (will likely to fail so ignore exceptions).
    records = mailer.GetTxtData("_adsp._domainkey.domain.com")

    ' Some servers do not claim error but return no records. Check this.
    If Not (records Is Nothing) Then
        Console.Write("DKIM ADSP record:")
        Dim rec As String
        For Each rec In records
            ' Should be only one such record (if any).
            Console.WriteLine(rec)
        Next rec
        Console.WriteLine()
    End If
Catch e As MailBeeDnsNameErrorException
Catch e As MailBeeDnsRecordsDisabledException
End Try

' Get root TXT records (to filter SPF record from them).
records = mailer.GetTxtData("domain.com")

If Not (records Is Nothing) Then
    Dim rec As String
    For Each rec In records
        If rec.ToLower().StartsWith("v=spf1") Then
            ' Should be only one such record (if any).
            Console.Write("SPF record: ")
            Console.WriteLine(rec)
        End If
    Next rec
End If

You can extend the sample with the analysis of what's in the SPF record (for instance, get an e-mail from a POP3 or IMAP server, then look the SPF record up for the IP address of the sender which you can find inMailMessage.TimeStamps collection).

See the next sample on how to extract IP addresses from time stamps (Received headers). However, these sample represent only very basics of DNS-based spam filtering. For instance, SPF record may contain IP address ranges or special tokens like "mx" which need some additional processing.


Get reverse DNS records with DNS PTR query

Reverse DNS queries (also known as rDNS or DNS PTR) are used in many popular methods of filtering spam. You can also make rDNS queries for other needs, such as network diagnostics.

For a very simple code sample, see Smtp.GetPtrData method documentation.

The next two topics provide the extensive samples of checking rDNS to filter spam. Both samples check the IP address of the sender (if available in the Received header) and perform a DNS PTR query on it. After that, the samples do different things:


Use rDNS to check if sender's IP address matches HELO domain

This method is widely used by SMTP MX servers when they respond to the connection request from the sending party (which can be the relay SMTP server of the sender or the machine which directly sends e-mail).

An SMTP session starts with HELO or HELO command. The sending party issues a command like EHLO sending.host.com where sending.host.com must designate the external IP address or the external host name of the sending computer. In case if it's a host name, the SMTP MX server makes a reverse DNS query to resolve this host name into an IP address and makes sure the obtained IP address matches the actual IP address of the incoming connection.

As your application is not an SMTP MX server and does not accept connections from senders directly, it can only determine the incoming connection's IP address and HELO domain from the message headers if your SMTP MX server added them to the message before putting the message into your inbox. Most servers do this by adding a Received header. Still, the exact format depends on a particular server so you'll need to adapt the code for your particular server.

The console sample below gets the last e-mail in a POP3 e-mail account, extracts the sender's IP address and HELO/EHLO domain, and uses rDNS query to check if they match. The sample also correctly handles the situation when the HELO/EHLO domain is an IP address:

using System;
using System.Net;
using System.Text.RegularExpressions;
using MailBee;
using MailBee.Mime;
using MailBee.Pop3Mail;
using MailBee.SmtpMail;
using MailBee.DnsMX;

class Sample
{
    static string MatchIPAddress(string ipAddrString)
    {
        return Regex.Match(ipAddrString,
            @"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b").ToString();
    }

    // Examples of receivedFrom and the results.
    //
    // receivedFrom: [139.126.112.43] (helo=relay-5.company.com)
    // Results: ip=139.126.112.43, heloDomain=relay-5.company.com, heloIsIP=false.
    //
    // receivedFrom: [127.0.0.1] (account joe@my-pc.office HELO localhost)
    // Results: ip=127.0.0.1, heloDomain=localhost, heloIsIP=false.
    //
    // receivedFrom: [139.126.112.43] (helo=139.126.112.43)
    // Result: ip=139.126.112.43, heloDomain=139.126.112.43, heloIsIP=true.
    //
    // receivedFrom: 139.126.112.43 (helo=[139.126.112.43])
    // Results: ip=139.126.112.43, heloDomain=139.126.112.43, heloIsIP=true.
    static bool GetIPAddressAndHeloValue(string receivedFrom,
        ref string ip, ref string heloDomain, ref bool heloIsIP)
    {
        Match m = Regex.Match(receivedFrom,
            @"((?'ip'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b)|" +
            @"(?i:helo(=| )(?'helo'\[?(\w|-|\.)+\]?))|(.))+");

        if (m.Groups["ip"].Length > 0)
        {
            ip = m.Groups["ip"].ToString();
            if (m.Groups["helo"].Length > 0)
            {
                heloDomain = m.Groups["helo"].ToString();

                // [127.0.0.1] => 127.0.0.1
                if (heloDomain.StartsWith("[") && heloDomain.EndsWith("]"))
                {
                    heloDomain = heloDomain.Substring(1, heloDomain.Length - 2);
                }

                // Is helloDomain value an IP address?
                heloIsIP = (MatchIPAddress(heloDomain).Length > 0);

                // Both IP address and hello domain found.
                return true;
            }
        }

        return false;
    }

    static void Main(string[] args)
    {
        // Get the last message from the POP3 server.
        MailMessage msg = Pop3.QuickDownloadMessage(
            "mail.domain.com", "jdoe", "secret", -1);

        Smtp mailer = new Smtp();

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

        // Get ready for making DNS queries.
        mailer.DnsServers.Autodetect();

        string fromString = null;
        IPAddress ip = null;
        string fromIP = null;
        string fromHelloDomain = null;
        bool helloDomainIsIP = false;

        // Analyze the message we received from the POP3 server.
        if (msg.TimeStamps.Count > 0)
        {
            // Get the "from" part of the most recent Received header.
            fromString = msg.TimeStamps[0].From;

            Console.WriteLine("\"Received: from\" value: " + fromString);
            Console.WriteLine();

            // Get IP address and hello domain.
            if (GetIPAddressAndHeloValue(fromString,
                ref fromIP, ref fromHelloDomain, ref helloDomainIsIP))
            {
                Console.WriteLine("Received from IP: " + fromIP);
                Console.WriteLine("HELO/EHLO domain used by client: " +
                    fromHelloDomain);
                Console.WriteLine("HELO/EHLO domain is IP address: " +
                    helloDomainIsIP.ToString());
                Console.WriteLine();
            }
            else
            {
                Console.WriteLine("IP address and HELO domain not available");
                return;
            }

            // Check if the value is really an IP address.
            if (IPAddress.TryParse(fromIP, out ip))
            {
                Console.WriteLine("Sender's IP address: " + fromIP);
                Console.WriteLine();

                // In case if HELO/EHLO parameter is also an IP address,
                // just make sure both IP addresses match.
                if (helloDomainIsIP)
                {
                    // Is the IP address where the message came from the
                    // same as the IP address in HELO/EHLO parameter?
                    if (fromIP == fromHelloDomain)
                    {
                        Console.WriteLine("Both IP addresses match. Perfect!");
                    }
                    else
                    {
                        Console.WriteLine("IP addresses do NOT match!");
                    }
                }
                else
                {
                    string[] recs = null;

                    try
                    {
                        // Perform reverse DNS check for the sender's IP address.
                        recs = mailer.GetPtrData(fromIP);
                    }
                    catch (MailBeeDnsNameErrorException e)
                    {
                        Console.WriteLine("No rDNS for the host " + e.HostName);
                        return;
                    }
                    catch (MailBeeDnsRecordsDisabledException e)
                    {
                        Console.WriteLine("The local cache states " +
                            "there is no rDNS for the host " + e.Domain);
                        return;
                    }

                    if (recs != null)
                    {
                        Console.WriteLine("Reverse DNS record(s) found.");
                        Console.WriteLine();
                        foreach (string record in recs)
                        {
                            Console.WriteLine(record);

                            if (record.ToLower() == fromHelloDomain.ToLower())
                            {
                                Console.WriteLine("Perfect match!");
                            }
                            else
                            {
                                Console.WriteLine(record +
                                    " does not match " + fromHelloDomain);
                            }
                        }
                    }
                    else
                    {
                        // Can happen if the DNS record exists but empty.
                        Console.WriteLine(
                            "No corresponding rDNS record found for " + fromIP);
                    }
                }
            }
        }
    }
}
Imports System
Imports System.Net
Imports System.Text.RegularExpressions
Imports MailBee
Imports MailBee.Mime
Imports MailBee.Pop3Mail
Imports MailBee.SmtpMail
Imports MailBee.DnsMX

Module Module1
    Function MatchIPAddress(ByVal ipAddrString As String) As String
        Return Regex.Match(ipAddrString, _
            "\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b").ToString()
    End Function

    ' Examples of receivedFrom and the results.
    '
    ' receivedFrom: [139.126.112.43] (helo=relay-5.company.com)
    ' Results: ip=139.126.112.43, heloDomain=relay-5.company.com, heloIsIP=false.
    '
    ' receivedFrom: [127.0.0.1] (account joe@my-pc.office HELO localhost)
    ' Results: ip=127.0.0.1, heloDomain=localhost, heloIsIP=false.
    '
    ' receivedFrom: [139.126.112.43] (helo=139.126.112.43)
    ' Result: ip=139.126.112.43, heloDomain=139.126.112.43, heloIsIP=true.
    '
    ' receivedFrom: 139.126.112.43 (helo=[139.126.112.43])
    ' Results: ip=139.126.112.43, heloDomain=139.126.112.43, heloIsIP=true.
    Function GetIPAddressAndHeloValue(ByVal receivedFrom As String, _
            ByRef ip As String, ByRef heloDomain As String, _
            ByRef heloIsIP As Boolean) As Boolean
        Dim m As Match = Regex.Match(receivedFrom, _
            "((?'ip'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b)|" & _
            "(?i:helo(=| )(?'helo'\[?(\w|-|\.)+\]?))|(.))+")

        If m.Groups("ip").Length > 0 Then
            ip = m.Groups("ip").ToString()
            If m.Groups("helo").Length > 0 Then
                heloDomain = m.Groups("helo").ToString()

                ' [127.0.0.1] => 127.0.0.1
                If heloDomain.StartsWith("[") And heloDomain.EndsWith("]") Then
                    heloDomain = heloDomain.Substring(1, heloDomain.Length - 2)
                End If

                ' Is helloDomain value an IP address?
                heloIsIP = MatchIPAddress(heloDomain).Length > 0

                ' Both IP address and hello domain found.
                Return True
            End If
        End If

        Return False
    End Function

    Sub Main()
        ' Get the last message from the POP3 server.
        Dim msg As MailMessage = Pop3.QuickDownloadMessage( _
            "mail.domain.com", "jdoe", "secret", -1)

        Dim mailer As New Smtp()

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

        ' Get ready for making DNS queries.
        mailer.DnsServers.Autodetect()

        Dim fromString As String = Nothing
        Dim ip As IPAddress = Nothing
        Dim fromIP As String = Nothing
        Dim fromHelloDomain As String = Nothing
        Dim helloDomainIsIP As Boolean = False

        ' Analyze the message we received from the POP3 server.
        If msg.TimeStamps.Count > 0 Then
            ' Get the "from" part of the most recent Received header.
            fromString = msg.TimeStamps(0).From

            Console.WriteLine("""Received: from"" value: " & fromString)
            Console.WriteLine()

            ' Get IP address and hello domain.
            If GetIPAddressAndHeloValue(fromString, _
                    fromIP, fromHelloDomain, helloDomainIsIP) Then
                Console.WriteLine("Received from IP: " & fromIP)
                Console.WriteLine("HELO/EHLO domain used by client: " & _
                    fromHelloDomain)
                Console.WriteLine("HELO/EHLO domain is IP address: " & _
                    helloDomainIsIP.ToString())
                Console.WriteLine()
            Else
                Console.WriteLine("IP address and HELO domain not available")
                Return
            End If

            ' Check if the value is really an IP address.
            If IPAddress.TryParse(fromIP, ip) Then
                Console.WriteLine("Sender's IP address: " & fromIP)
                Console.WriteLine()

                ' In case if HELO/EHLO parameter is also an IP address,
                ' just make sure both IP addresses match.
                If helloDomainIsIP Then
                    ' Is the IP address where the message came from the
                    ' same as the IP address in HELO/EHLO parameter?
                    If fromIP = fromHelloDomain Then
                        Console.WriteLine("Both IP addresses match. Perfect!")
                    Else
                        Console.WriteLine("IP addresses do NOT match!")
                    End If
                Else
                    Dim recs As String() = Nothing

                    Try
                        ' Perform reverse DNS check for the sender's IP address.
                        recs = mailer.GetPtrData(fromIP)
                    Catch e As MailBeeDnsNameErrorException
                        Console.WriteLine("No rDNS for the host " & e.HostName)
                        Return
                    Catch e As MailBeeDnsRecordsDisabledException
                        Console.WriteLine("The local cache states " & _
                            "there is no rDNS for the host " & e.Domain)
                        Return
                    End Try

                    If Not (recs Is Nothing) Then
                        Console.WriteLine("Reverse DNS record(s) found.")
                        Console.WriteLine()
                        Dim record As String
                        For Each record In recs
                            Console.WriteLine(record)

                            If record.ToLower() = fromHelloDomain.ToLower() Then
                                Console.WriteLine("Perfect match!")
                            Else
                                Console.WriteLine(record & _
                                    " does not match " & fromHelloDomain)
                            End If
                        Next record
                    Else
                        ' Can happen if the DNS record exists but empty.
                        Console.WriteLine( _
                            "No corresponding rDNS record found for " & fromIP)
                    End If
                End If
            End If
        End If
    End Sub
End Module

We assume your license key covers not only Smtp class but for Pop3 as well. The sample gets the e-mail to analyze from a POP3 server but you can get it from any source including an IMAP server.


Use rDNS query to find out if sender is home computer or Internet server

As most of spam is being sent from home systems, not Internet servers, it's enough to check if the sending part has a "good-looking" host name. If it doesn't, we probably deal with a personal computer which sends spam.

How can we know that the host name "looks good"? Good question.

Good records are somewhat like "mail.server.net", "smtp20.mail-cluster.mx.domain.com", etc. They should not have IP-address-alike sequences in their names which are "101.92.140.39.dynamic.isp.com" or "cable-78-109-33-05.provider.net" (typical external host names of home systems as assigned by ISPs).

As you can see, it's quite an informal definition which just tries to exploit naming habits of Internet providers which tend to use some common patterns for host names they assign to their customers. That's why it's pretty hard to distinguish between "good" and "bad" hosts, and requires a lot of coding to implement this.

The method discussed in the previous topic is simpler to implement but it's more limited and won't allow detecting spam if a spam program uses the correct host name in EHLO. The method discussed here, however, goes deeper and checks if the host name of the sending party (even if it's correct) has the format which is more typical for home systems rather than for Internet servers.

The spam program can put whatever it wants into EHLO command but it can't change the public IP address and the public host name of the machine it's sending from. And if this name looks suspicious, it can't do anything with it.

By the way, that's why it's so important to have a valid DNS PTR record and a good-looking public host name for your server which sends e-mail. Otherwise, other hosts will consider you a spam source.

The console sample below performs reverse DNS check for the IP address from which the e-mail came to the destination server. Unfortunately, there is no well-defined standard of storing the sender's IP address in the message so that the exact code may differ for your particular server. Moreover, as already said above, there is no strict rule of what is a good DNS record and what is not.

This sample assumes the sender's IP address is contained in the most recent Received header, and "good" DNS record is somewhat like "mx.domain.com" while "56.32.104.9.provider.net" is not. The e-mail message to check is taken from a POP3 server:

using System;
using System.Net;
using System.Text.RegularExpressions;
using MailBee;
using MailBee.Mime;
using MailBee.Pop3Mail;
using MailBee.SmtpMail;
using MailBee.DnsMX;

class Sample
{
    static string MatchIPAddress(string ipAddrString)
    {
        return Regex.Match(ipAddrString,
            @"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b").ToString();
    }

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

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

        // Get ready for making DNS queries.
        mailer.DnsServers.Autodetect();

        // Get last message from the POP3 server.
        MailMessage msg = Pop3.QuickDownloadMessage(
            "mail.domain.com", "jdoe", "secret", -1);

        string ipAddrString = null;
        IPAddress ip = null;

        if (msg.TimeStamps.Count > 0)
        {
            ipAddrString = msg.TimeStamps[0].From;

            // Remove everything but IP address.
            ipAddrString = MatchIPAddress(ipAddrString);

            // Check if the value is an IP address.
            if (IPAddress.TryParse(ipAddrString, out ip))
            {
                try
                {
                    Console.WriteLine(
                        "Sender's IP address is " + ipAddrString);
                    Console.WriteLine();

                    // Perform reverse DNS check for this IP address.
                    // Ignore "name not found" exceptions if failed
                    // (this indicates spam with no valid rDNS).
                    string[] recs = mailer.GetPtrData(ipAddrString);

                    if (recs != null)
                    {
                        Console.WriteLine("Reverse DNS record found.");
                        Console.WriteLine("Will report all records now.");
                        Console.WriteLine("Records:");
                        foreach (string record in recs)
                        {
                            // Display the record found. We'll then check
                            // if the record is "good" in terms of rDNS
                            // spam checks.
                            Console.Write(record);

                            bool isGoodName = false;

                            // If the record contains IP address, no good.
                            ipAddrString = MatchIPAddress(record);

                            if (ipAddrString == string.Empty)
                            {
                                string[] parts = record.Split('.');

                                // If no dot, this is a local name - no good.
                                if (parts.Length > 1)
                                {
                                    // Short name like mail.domain.com is good.
                                    if (parts.Length <= 3)
                                    {
                                        isGoodName = true;
                                    }
                                    else
                                    {
                                        int nonLetterCount = 0;
                                        isGoodName = true;

                                        // Check first two sections for
                                        // IP-address like things and digits.
                                        for (int i = 0; i < 2; i++)
                                        {
                                            // Check if looks like
                                            // "adsl-11-22-33-44".
                                            ipAddrString =
                                                parts[i].Replace('-', '.');

                                            if (MatchIPAddress(ipAddrString) !=
                                                string.Empty)
                                            {
                                                // IP-address-alike is not a
                                                // good record (home system).
                                                nonLetterCount = 0;
                                                isGoodName = false;
                                                break;
                                            }

                                            for (int j = 0;
                                                j < parts[i].Length; j++)
                                            {
                                                if (!char.IsLetter(
                                                    parts[i][j]))
                                                {
                                                    nonLetterCount++;
                                                }
                                            }
                                        }

                                        // Too much digits and dashes - no good.
                                        if (isGoodName && nonLetterCount >= 5)
                                        {
                                            isGoodName = false;
                                        }
                                    }
                                }
                            }

                            if (isGoodName)
                            {
                                Console.WriteLine(" looks good (server host)");
                            }
                            else
                            {
                                Console.WriteLine(" looks bad (home host?)");
                            }
                        }
                    }
                }
                catch (MailBeeDnsNameErrorException) { }
                catch (MailBeeDnsRecordsDisabledException) { }
            }
        }
    }
}
Imports System
Imports System.Net
Imports System.Text.RegularExpressions
Imports MailBee
Imports MailBee.Mime
Imports MailBee.Pop3Mail
Imports MailBee.SmtpMail
Imports MailBee.DnsMX

Module Module1
    Function MatchIPAddress(ByVal ipAddrString As String) As String
        Return Regex.Match(ipAddrString, _
            "\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b").ToString()
    End Function

    Sub Main()
        Dim mailer As New Smtp()

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

        ' Get ready for making DNS queries.
        mailer.DnsServers.Autodetect()

        ' Get last message from the POP3 server.
        Dim msg As MailMessage = Pop3.QuickDownloadMessage( _
            "mail.domain.com", "jdoe", "secret", -1)

        Dim ipAddrString As String = Nothing
        Dim ip As IPAddress = Nothing

        If msg.TimeStamps.Count > 0 Then
            ipAddrString = msg.TimeStamps(0).From

            ' Remove everything but IP address.
            ipAddrString = MatchIPAddress(ipAddrString)

            ' Check if the value is an IP address.
            If IPAddress.TryParse(ipAddrString, ip) Then
                Try
                    Console.WriteLine("Sender's IP address is " & ipAddrString)
                    Console.WriteLine()

                    ' Perform reverse DNS check for this IP address.
                    ' Ignore "name not found" exceptions if failed
                    ' (this indicates spam with no valid rDNS).
                    Dim recs As String() = mailer.GetPtrData(ipAddrString)

                    If Not (recs Is Nothing) Then
                        Console.WriteLine("Reverse DNS record found.")
                        Console.WriteLine("Will report all records now.")
                        Console.WriteLine("Records:")
                        Dim record As String
                        For Each record In recs
                            ' Display the record found. We'll then check
                            ' if the record is "good" in terms of rDNS
                            ' spam checks.
                            Console.Write(record)

                            Dim isGoodName As Boolean = False

                            ' If the record contains IP address, no good.
                            ipAddrString = MatchIPAddress(record)

                            If ipAddrString = String.Empty Then
                                Dim parts As String() = record.Split("."c)

                                ' If no dot, this is a local name - no good.
                                If parts.Length > 1 Then
                                    ' Short name like mail.domain.com is good.
                                    If parts.Length <= 3 Then
                                        isGoodName = True
                                    Else
                                        Dim nonLetterCount As Integer = 0
                                        isGoodName = True

                                        ' Check first two sections for
                                        ' IP-address like things and digits.
                                        Dim i As Integer
                                        For i = 0 To 1
                                            ' Check if looks like
                                            ' "adsl-11-22-33-44".
                                            ipAddrString = _
                                                parts(i).Replace("-"c, "."c)

                                            If MatchIPAddress( _
                                                ipAddrString) <> _
                                                String.Empty Then
                                                ' IP-address-alike is not a
                                                ' good record (home system).
                                                nonLetterCount = 0
                                                isGoodName = False
                                                Exit For
                                            End If

                                            Dim j As Integer
                                            For j = 0 To (parts(i).Length) - 1
                                                If Not _
                                                    Char.IsLetter( _
                                                        parts(i)(j)) Then
                                                    nonLetterCount += 1
                                                End If
                                            Next j
                                        Next i

                                        ' Too much digits and dashes - no good.
                                        If isGoodName And _
                                            nonLetterCount >= 5 Then
                                            isGoodName = False
                                        End If
                                    End If
                                End If
                            End If

                            If isGoodName Then
                                Console.WriteLine(" looks good (server host)")
                            Else
                                Console.WriteLine(" looks bad (home host?)")
                            End If
                        Next record
                    End If
                Catch e As MailBeeDnsNameErrorException
                Catch e As MailBeeDnsRecordsDisabledException
                End Try
            End If
        End If
    End Sub
End Module

We assume your license key covers not only Smtp class but Pop3 as well. The sample gets the e-mail to analyze from a POP3 server but you can get it from any source including an IMAP server.

The idea is to get rDNS records and check what's in there. If the record looks like "12.34.65.111.users.provider.com" or "dsl-134-50-127-9.telecom.net", this is probably home machine (not a network server), and such machine should not try to send e-mail without relay server (and most likely it's a spam source).

Note that this example (though it's big) still just demonstrates the idea, it should not be used in production. Real-world reverse DNS check can be even much more complicated to allow for different formats of "Received" headers from which time stamps are taken, more sophisticated patterns of "good" and "not good" host names, etc.


Send feedback to AfterLogic

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