Makale Özeti

Bu makalemde bir mail adresinin gerçekten varolup olmadığının yazım kuralı kontrolü ile değilde sunucuda bulunan MX kayıtları sayesınde online olarak nasıl öğrenilebileceğini inceleyeceğiz. Bunun için TcpClient kullanarak Mx üzerinde mail komutları ile çeşitli işlemler yapacağız. Bu makalede anlatılan tüm kodları telnet arabirimindede uygulayabilirsiniz.

Makale

          Merhabalar,

          Bu makalemde bir mail adresinin gerçekten varolup olmadığının yazım kuralı kontrolü ile değilde sunucuda bulunan MX kayıtları sayesınde online olarak nasıl öğrenilebileceğini inceleyeceğiz. Bunun için TcpClient kullanarak Mx üzerinde mail komutları ile çeşitli işlemler yapacağız. Bu makalede anlatılan tüm kodları telnet arabirimindede uygulayabilirsiniz.

          Öncelikle bu yöntemin bazı sunucuların sorgulamaya kapalı olması veya bazı sunucularda her mail adresinin sunucuda var gibi gösterilmesi sonucu 100% performanslı çalışmadığı ama yinede önemsenecek bir yüzdede doğru sonuç verdiğini hatırlatmak isterim. Normal Smtp sorgusu ile sorgulayamayacağınız hotmail, yahoo gibi smtp desteği vermeyen mail sunucularınıda bu yöntemle sorgulayabilirsiniz. Bu yöntem genellikle kullanıcıların sisteme kaydolurken doğru mail yazdıklarından emin olmak amacıyla değilde belirli bir mail listesinde bulunan kullanıcıların email adreslerinin doğruluğu nun kontrolü amacıyla kullanılır. Yöntem Hotmail,Yahoo,Gmail üzerinde sorunsuz çalışmaktadır.Şimdi isterseniz bir mail yollama işleminin nasıl gerçekleştiğini inceleyelim.



          Bu şemeda mailin gideceği kullanıcı internet üzerindeki 2 tane mx sunucusundan desteklenmektedir. Belirli bir domainde bulunan mx kayıt adreslerini çözmek için DNS sorgusu yapmak gereklidir. Çünkü mx sunucusunun adı herzaman mail.domain.com şeklinde olmayabiliyor veya bazı büyük sunuclarda birden fazla mx sunucusu bulunabliyor. Bunun için dnsapi isimli api'yi kullanarak dns leri sorgulayacağız.
 
using System;
using System.Collections;
using System.ComponentModel;
using System.Runtime.InteropServices;
 
namespace MailAddressChecker
{
    public class Mx
    {
        public Mx()
        {
        }
        [DllImport("dnsapi", EntryPoint = "DnsQuery_W", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
        private static extern int DnsQuery([MarshalAs(UnmanagedType.VBByRefStr)]ref string pszName, QueryTypes wType, QueryOptions options, int aipServers, ref IntPtr ppQueryResults, int pReserved);
 
        [DllImport("dnsapi", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern void DnsRecordListFree(IntPtr pRecordList, int FreeType);
 
        public static string[] GetMXRecords(string domain)
        {
 
            IntPtr ptr1 = IntPtr.Zero;
            IntPtr ptr2 = IntPtr.Zero;
            MXRecord recMx;
            if (Environment.OSVersion.Platform != PlatformID.Win32NT)
            {
                throw new NotSupportedException();
            }
            ArrayList list1 = new ArrayList();
            int num1 = Mx.DnsQuery(ref domain, QueryTypes.DNS_TYPE_MX, QueryOptions.DNS_QUERY_BYPASS_CACHE, 0, ref ptr1, 0);
            if (num1 != 0)
            {
                throw new Win32Exception(num1);
            }
            for (ptr2 = ptr1; !ptr2.Equals(IntPtr.Zero); ptr2 = recMx.pNext)
            {
                recMx = (MXRecord)Marshal.PtrToStructure(ptr2, typeof(MXRecord));
                if (recMx.wType == 15)
                {
                    string text1 = Marshal.PtrToStringAuto(recMx.pNameExchange);
                    list1.Add(text1);
                }
            }
            Mx.DnsRecordListFree(ptr2, 0);
            return (string[])list1.ToArray(typeof(string));
        }
 
        private enum QueryOptions
        {
            DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE = 1,
            DNS_QUERY_BYPASS_CACHE = 8,
            DNS_QUERY_DONT_RESET_TTL_VALUES = 0x100000,
            DNS_QUERY_NO_HOSTS_FILE = 0x40,
            DNS_QUERY_NO_LOCAL_NAME = 0x20,
            DNS_QUERY_NO_NETBT = 0x80,
            DNS_QUERY_NO_RECURSION = 4,
            DNS_QUERY_NO_WIRE_QUERY = 0x10,
            DNS_QUERY_RESERVED = -16777216,
            DNS_QUERY_RETURN_MESSAGE = 0x200,
            DNS_QUERY_STANDARD = 0,
            DNS_QUERY_TREAT_AS_FQDN = 0x1000,
            DNS_QUERY_USE_TCP_ONLY = 2,
            DNS_QUERY_WIRE_ONLY = 0x100
        }
 
        private enum QueryTypes
        {
            DNS_TYPE_MX = 15
        }
 
        [StructLayout(LayoutKind.Sequential)]
        private struct MXRecord
        {
            public IntPtr pNext;
            public string pName;
            public short wType;
            public short wDataLength;
            public int flags;
            public int dwTtl;
            public int dwReserved;
            public IntPtr pNameExchange;
            public short wPreference;
            public short Pad;
        }
    }
}

          Burda yapılan temel iş GetMxRecords metodunda Api ile gelen DnsQuery metodunu çağırarak bu metoddan belirtilen domain'e ait olan mx kayıtlarının getirilmesini sağlamaktır. Daha sonra bu dönene değerler bir string array olarak bize geri dönmektedir. Diğer enum'lar ise apide bulunan metodların çalışması için gerekli parametresel verileri taşımakla yükümlüdürler. Şimdi isterseniz esas işlemleri yapacak olan class'ımızı yazmaya başlayalım.
 
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
 
namespace MailAddressChecker
{
    public class MailValidator
    {
        private bool mResult = false;
        public bool Result
        {
            get { return mResult; }
            set { mResult = value; }
        }
        private string mError = "";
        public string Error
        {
            get { return mError; }
            set { mError = value; }
        }
        private string mMailAddress = "";
        public string MailAddress
        {
            get { return mMailAddress; }
            set { mMailAddress = value; }
        }
          Class'ımızın ismini MailValidator olarak belirliyoruz ve içine değişkenlerimizle bu değişkenleri encapsule eden propertylerimizi yazmaya başlıyoruz. Burada Result propertysi yapılan içlem sonucunda mail adresinin gerçekte val olup olmadığı sonucunu saklayacağımız property, Error propertysi ise işlem sırasında mx sunucuya bağlanılamaması gibi herhangi bir hata mesajı aldığımızda bunu saklayacağımız property, ve EmailAddress propertysi ise kontrol edilecek mail adresini saklayacağımız propertydir. Biraz sonra yazacağımız kontrol metodunda ve yapıcı metodda bu propertylere gerekli değerlerini atayarak daha sonra propgram tarafından erişeceğiz.
 
        public MailValidator(string mailAddress)
        {
            mMailAddress = mailAddress;
        }
          Bu yapıcı metodda ise classımızın bir örneği yaratılırken mail adresi belirtilmesini zorunlu tutuyoruz. İsterseniz esas kontrolü yapacak metodumuzu yazmaya başlamadan önce bu metodumuzda bulunan komtları telnet üzerinden uygulayabileceğimiz için ilk önce komutlarımızı telnette deneyelim.

" Yandaki animasyonda olan bir mail adresinin telnet ile sorgulanması sırasında geçilen adımlar ve bu adımlarda yazılan komutlara server'dan dönen sonuçlar gözükmektedir. Burada bizim için önemli olan son adımda 250 Ok mesajının dönmesidir.
" Yandaki animasyonda ise mevcut olmayan bir mail adresinni telnet sorgulaması ile nasıl algılanabileceğini görüyoruz. Burda dikkat etmemiz gereken son adımda dönen mailbox not available mesajıdır. 

        public bool MailExists()
        {
            string domainName = mMailAddress.Substring(mMailAddress.IndexOf("@") + 1);
            foreach (string str in Mx.GetMXRecords(domainName))
            {
                TcpClient insTcpClient=null;
                try
                {
                    //MessageBox.Show(str);
                    insTcpClient = new TcpClient(str, 25);
                    NetworkStream ns = insTcpClient.GetStream();
 
                    byte[] recievedMessage = new byte[insTcpClient.ReceiveBufferSize];
                    ns.Read(recievedMessage, 0, insTcpClient.ReceiveBufferSize);
                    //MessageBox.Show(Encoding.ASCII.GetString(recievedMessage));
 
                    string messageToSend = "HELO\r\n";
                    byte[] buffer = System.Text.Encoding.GetEncoding(1254).GetBytes(messageToSend);
                    ns.Write(buffer, 0, buffer.Length);
 
                    recievedMessage = new byte[insTcpClient.ReceiveBufferSize];
                    ns.Read(recievedMessage, 0, insTcpClient.ReceiveBufferSize);
                    //MessageBox.Show(Encoding.ASCII.GetString(recievedMessage));
 
                    messageToSend = "MAIL FROM:<deneme@" + domainName + ">\r\n";
                    buffer = System.Text.Encoding.GetEncoding(1254).GetBytes(messageToSend);
                    ns.Write(buffer, 0, buffer.Length);
 
                    recievedMessage = new byte[insTcpClient.ReceiveBufferSize];
                    ns.Read(recievedMessage, 0, insTcpClient.ReceiveBufferSize);
                    //MessageBox.Show(Encoding.ASCII.GetString(recievedMessage));
 
                    messageToSend = "RCPT TO:<" + mMailAddress + ">\r\n";
                    buffer = System.Text.Encoding.GetEncoding(1254).GetBytes(messageToSend);
                    ns.Write(buffer, 0, buffer.Length);
 
                    recievedMessage = new byte[insTcpClient.ReceiveBufferSize];
                    ns.Read(recievedMessage, 0, insTcpClient.ReceiveBufferSize);
                    //MessageBox.Show(Encoding.ASCII.GetString(recievedMessage));
                    if (Encoding.ASCII.GetString(recievedMessage).StartsWith("250"))
                    {
                        mResult = true;
 
                    }
                    insTcpClient.GetStream().Close();
                    insTcpClient.Close();
                }
                catch (Exception ex)
                {
                    mError += str + ": " + ex.Message; ;
                }
            }
            return mResult;
        }
    }
}
          MailExists metodumuz işlemleri yapacağımız temel metodumuzdur ve bize geriye mail adresinin bulunup bulunmadığı bilgisini döndürür. Bu metodun ilk adımında MailAddress propertysinde bulunan mail adresinin @'den sonraki kısmını alıyoruz ve Daha öncesinde yazdığımız Api kodu barındıran MX class'ının GetMxRecords metoduna bu domain'i yolluyoruz ve bize bu domaine ait mx sunucuları string dizisi olarak geliyor. Burdan sonra açıklayacağımız kod dönen her mx sunucu adı için geçerlidir. İlk önce TcpClient nesnesinin bir örneğini yaratıyoruz ve mx sunucusuna 25. port üzerinden bağlantı açıyoruz. Ve TcpClient tan gelen cevabı GetStream metodu ile NetworkStream olarak kaydediyor ve bunu string e çevirerek gelen cevabı okuyabiliyoruz. Daha sonrasında ise NetworkStrem in write metodu ile sunucuya HELO komutunu gönderiyoruz. Bu komut gönderilmeden herhang bir işlem yapmamız mümkün olmayacaktır. Buradan gelen cevaıda okuduktan sonra sunucuya MAIL FROM:<DENEME@DOMAIN.COM> seklinde bir komut gönderiyoruz. Burada yazdığımız mail adresi tamamen serbesttir ve mail gönderme işleminin hangi adres üzerinden yapılacağını temsil eder. Buradan OK cevabını akdıktan sonra ise sunucuya RCPT TO:<KONTROLEDILECEK@DOMAIN.COM> şeklinde kontrol etmek istediğimiz mail adresini barındıran komutu yolluyoruz. Burada ise bize sunucudan iki çeşit cevap dönmesi beklenir. Bunlardan biri 250 OK cevabıdır ve bu cevap bize mail adresinin sunucunun MX kayıtlarında olduğunu bildirir. Diğer bir cevap ise Mailbox is not available mesajıdır ki buda bize böyle bir mail adresinin bulunmadığını bildirir. Bu metodda OK cevabını aldıktan sonra diğer MX sunucularını kontrol etmeye gerek olmadığından burada döngümüzü sonlandırabiliriz.

         Şimdi ise bu yazdığımız class'ı basit bir programdan nasıl çağıracağımızı ve kullanacağımızı inceleyen ufak bir örnek yapalım.
 

         Programın görünüşü bu iken Buttonun arkasında bulunan kodu inceleyelim.
        private void btnCheck_Click(object sender, EventArgs e)
        {
            MailValidator mv = new MailValidator(txtMailAddress.Text);
            if (mv.MailExists())
            {
                MessageBox.Show("Mail Adresi Gecerli");
            }
            else
            {
                MessageBox.Show(mv.Error);
            }
        }
          Görüldüğü gibi bu yazmış olduğumuz Class'ın insatance'sini alırken kullanıcının textbox a yazmış olduğu mail adresini veriyoruz ve MailExists metodundan bize geriye mail adresinin olup olmadığına dair bilgi dönüyor.

         Bu makalede TxpClient kullanarak bir mx sunucusunun nasıl sorgulanabileceğini inceledik. Bu sorgulama sonucunda maili alacak kişiyi o sunucudan seçersen sunucunun bize bu mail adresinin olup olmadığı bilgisini anında döndürmesinden faydalanarak mail adresinin varlığını kontrol etmiş oluyoruz.

oztamer@hotmail.com
tamer.oz@yazgelistir.com
oztamer@hotmail.com
Örnek Kodlar