dotSITE
Шаблоны проектирования Work in Murano Software. Вопросы/Ответы
новости материалы решения форумы группы настройки/о проекте
Логин/Регистрация
Логин:
Пароль:
Запомнить вас:
Регистрация
Забыли пароль?

Комментарии

Работа с DNS в .NET

Работа с DNS в .NET

В одном из моих нынешних проектов как одна из задач существует задача проверки валидности email адресов для рассылок. Листы рассылок довольно-таки большие (от 1000 и больше адресов) плюс проверять нужно без участия пользователей (т.е. эти листы составляются администраторами из каких-нибудь источников и посылка письма пользователю для того, чтобы он на него ответил или еще какие действия произвел, не допускается). Как решение для проверки валидности email адресов был выбран алгоритм проверки через smtp сервер. Данный алгоритм довольно прост - подключаемся к smtp серверу пользователя, пытаемся отправить email по проверяемому адресу и смотрим на ответ сервера. Работа этого алгоритма при известных email и smtp сервере описаны например на http://www.aspfree.com/authors/123aspx/ValidateEmailTutorial.asp

Но когда в исходных данных присутствует только набор email адресов для проверки, возникает вопрос, где брать те самые smtp сервера для применения вышеуказанного алгоритма? Логика подсказывает, что данными сведениями обладает (ну или, по крайней мере, может обладать) DNS сервер. Но, к сожалению, в .NET Framework класс DNS предназначен только для того, чтобы узнать адрес машины по ее имени и наоборот, что явно маловато для нашей задачи.

Ну нет - так нет. Прочитав RFC 1035 (Domain names - implementation and specification), мы можем убедиться, что работа с DNS сервером ничем не отличается от работы с любым другим сетевым объектом - сначала мы посылаем специально сформированный запрос серверу (в нашем случае на порт 53), а потом забираем его ответ и расшифровываем его.

Итак, представляем объект DNSQuery, позволяющий делать запросы к DNS серверу и получать записи интересующего вас типа.

Вся полезная работа выполняется в открытом методе MakeQuery. Ему передаются строка с именем домена и значение перечисления DNSQueryType, определяющее тип записей, которые мы хотели бы узнать. Формируется запрос и отправляется DNS серверу, указанному в свойстве DNSServer. Полученный ответ потом декодируется и сохраняется в скрытой переменной results типа SortedList. Значения возвращаются пользователю через нумератор, что позволит нам в дальнейшем более свободно управлять процессом возвращения запросов без изменения типа доступа к возвращаемым данным.

Ниже приведен пример использования объекта DNSQuery для получения MX записей для домена hotmail.com (dns сервер, на который посылается запрос, фиктивный)
public void listHotmailMXrecords(){
  // Создаем объект dnsquery	
  Dimon.NetQueries.DNSQuery dns = new Dimon.NetQueries.DNSQuery();
  // Нужно же куда-то запросы отправлять - устанавливаем адрес DNS сервера
  dns.DNSServer = "010.010.010.010";
  // Собственно сам запрос
  dns.MakeQuery("hotmail.com", Dimon.NetQueries.DNSQueryType.Qtype_MX);
  // Возвращаем полученные результаты.
  for (int i = 0; i < dns.Count; i++)	{
    Response.Write dns[i] + "";
  }
}

Но MX записи нас обычно волнуют не просто как сами по себе, а отсортированные по их приоритету. Да и возвращаются они нашим методом MakeQuery в виде <приоритет>, <имя сервера>. И вот тут то нам и пригодится наш SortedList. Нужно только добавить некоторый код после вызова MakeQuery. Так появился второй открытый метод в DNSQuery - GetMXRecords(string Name). В нем после вызова MakeQuery, который получает интересующие нас MX записи, нам необходимо немного подчистить возвращенные значения (убрать значения приоритетов, стоящие перед названиями mail серверов). Причем приоритеты мы вставляем в SortedList как ключи (по ним и происходит сортировка), а сами адреса - как значения (их то мы и возвращаем с помощью нумератора). Это делает следующий код:
string val;
for (int i = 0; i < results.Count; i++){
  val = (string)results.GetByIndex(i);
  val = val.Remove(0, val.IndexOf(",") + 1);
  results.SetByIndex(i, val);
}
Теперь listHotmailMXrecords будет выглядеть так:
public void listHotmailMXrecords(){
  // Создаем объект dnsquery
  Dimon.NetQueries.DNSQuery 
  dns = new Dimon.NetQueries.DNSQuery();	
  // Нужно ж куда-то запросы отправлять - далаем адрес DNS сервера	
  dns.DNSServer = "010.010.010.010";	
  // Собственно сам запрос	
  dns.GetMXRecords("hotmail.com");	
  // Возвращаем полученные результаты.	
  for (int i = 0; i < dns.Count; i++)	{		
    Response.Write dns[i] + "";	
  }
}
Наши результаты возвращены в приемлемом для нас виде и отсортированы в нужном нам порядке.

Рабочий пример можно посмотреть на http://aspx.securedomains.com/dimon/DNSQ.aspx

Ниже приведен исходный код класса, осуществляеющего DNS запросы:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Globalization;

namespace Dimon.NetQueries {
  /// <summary>
  ///    Summary description for Class1.
  /// </summary>
  /// 
  public enum DNSQueryType {
    Qtype_A     =1,
    Qtype_NS    =2,
    Qtype_MD    =3,
    Qtype_MF    =4,
    Qtype_CNAME =5,
    Qtype_SOA   =6,
    Qtype_MB    =7,
    Qtype_MG    =8,
    Qtype_MR    =9,
    Qtype_NULL  =10,
    Qtype_WKS   =11,  //
    Qtype_PTR   =12,
    Qtype_HINFO =13,
    Qtype_MINFO =14,
    Qtype_MX    =15,
    Qtype_TXT   =16,
    Qtype_RP    =17,
    Qtype_AFSDB =18,
    Qtype_X25   =19,
    Qtype_ISDN  =20,
    Qtype_RT    =21,
    Qtype_NSAP  =22,
    Qtype_NSAPPTR=23,
    Qtype_SIG   =24, //RFC-2065
    Qtype_KEY   =25, //RFC-2065
    Qtype_PX    =26,
    Qtype_GPOS  =27,
    Qtype_AAAA  =28, //IP6 Address [Susan Thomson]
    Qtype_LOC   =29, //RFC-1876
    Qtype_NXT   =30, //RFC-2065
    Qtype_SRV   =33, //RFC-2052
    Qtype_NAPTR =35, //RFC-2168
    Qtype_KX    =36,
    Qtype_AXFR  =252, //
    Qtype_MAILB =253, //
    Qtype_MAILA =254, //
    Qtype_ALL   =255 //
  }
  public class DNSQuery {
    string DNSHost;
    byte[] Buffer;
    Socket sock;
    int BufferPos = 0;
    System.Collections.SortedList results;
    public string DNSServer {
      get {
        return DNSHost;
      }
      set {
        DNSHost = value;
      }
    }
    public int Count {
      get {
        return results.Count;
      }
    }
    public string this[int Index] {
      get {
        return (string)results.GetByIndex(Index);
      }
    }

    public DNSQuery() {
      sock = new Socket(AddressFamily.AfINet, 
        SocketType.SockDgram,
        ProtocolType.ProtUDP);
      DNSHost = "localhost";
      Buffer = new byte[8192];
      results = new System.Collections.SortedList();
    }
    ~DNSQuery() {
      results = null;
      if (sock.Connected) {
        sock.Shutdown(SocketShutdown.SdBoth);
        sock.Close();
      }
      sock = null;
    }
    private int CompressName(string Value, out byte[] ret) {
      byte[] result = new byte[255];
      byte[] temp;
      int i = 0;
      if (Value == "") {
        result[i++] = 0;
      } else {
        string s = "";
        for (int n = 0; n < Value.Length; n++) {
          if (Value[n] == '.') {
            result[i++] = s.Length.ToByte();
            temp = Encoding.ASCII.GetBytes(s);
            for (int t =0; t < temp.Length; t++) {
              result[i++] = temp[t];
            }
            s = "";
          } else {
            s += Value[n];
          }
        }
        if (s != "") {
          result[i++] = s.Length.ToByte();
          temp = Encoding.ASCII.GetBytes(s);
          for (int t = 0; t < temp.Length; t++) {
            result[i++] = temp[t];
          }
        }
      }
      result[i++] = 0;
      ret = result;
      return i;
    }
    private byte[] CodeInt(ushort Value) {
      byte[] retArr = new byte[2];
      retArr[0] = (byte)(Value / 256);
      retArr[1] = (byte)(Value % 256);
      return (retArr);
    }
    private byte[] CodeHeader() {
      byte[] ret = new byte[12];
      Random rnd = new Random(32767);
      CodeInt((ushort)rnd.Next()).CopyTo(ret, 0);

      CodeInt(256).CopyTo(ret, 2);
      CodeInt(1).CopyTo(ret, 4);
      CodeInt(0).CopyTo(ret, 6);
      CodeInt(0).CopyTo(ret, 8);
      CodeInt(0).CopyTo(ret, 10);
      return ret;
    }
    private int CodeQuery(string Name, int Qtype,
        out byte[] ret) {
      byte[] res = new byte[255];
      int i;
      i = CompressName(Name, out res);
      CodeInt((ushort)Qtype).CopyTo(res, i);
      i += 2;
      CodeInt(1).CopyTo(res, i);
      i += 2;
      ret = res;
      return i;
    }
    private string DecodeLabels(ref int From) {
      string ret = "";
      int l, f;
      while (true) {
        l = (int) Buffer[From++];
        if (l == 0) {
          break;
        }
        if (ret != "") {
          ret += ".";
        }
        if ((l & 0xC0) != 0) {
          f = l & 0x3F;
          f = f * 256 + (int) Buffer[From++];
          ret += this.DecodeLabels(ref f);
          break;
        } else {
          ret += Encoding.ASCII.GetString(Buffer, From, l);
          From += l;
        }
      }
      return ret;
    }

    private ushort DecodeInt(byte[] Value, int Index) {
      byte x, y;
      if (Value.Length > Index) {
        x = Value[Index];
      } else {
        x = 0;
      }
      if (Value.Length > Index + 1) {
        y = Value[Index + 1];
      } else {
        y = 0;
      }
      return (ushort) (x * 256 + y);
    }
    private string DecodeResource(ref int i, string Name,
        DNSQueryType Qtype) {
      string Rname;
      int Rtype, len, j, x, n;
      string ret = "";

      Rname = DecodeLabels(ref i);
      Rtype = DecodeInt(Buffer,i);
      i += 8;
      len = DecodeInt(Buffer,i);
      i += 2;
      j = i;
      i += len;   //i point to next record
      if ((Name == Rname) && ((int)Qtype == Rtype)) {
        switch(Qtype) {
          case DNSQueryType.Qtype_A:
            ret = ((byte)(Buffer[j++])).ToString();
            ret += "." + ((byte)(Buffer[j++])).ToString();
            ret += "." + ((byte)(Buffer[j++])).ToString();
            ret += "." + ((byte)(Buffer[j++])).ToString();
            break;
          case DNSQueryType.Qtype_NS:
          case DNSQueryType.Qtype_MD:
          case DNSQueryType.Qtype_MF:
          case DNSQueryType.Qtype_CNAME:
          case DNSQueryType.Qtype_MB:
          case DNSQueryType.Qtype_MG:
          case DNSQueryType.Qtype_MR:
          case DNSQueryType.Qtype_PTR:
          case DNSQueryType.Qtype_X25:
          case DNSQueryType.Qtype_NSAP:
          case DNSQueryType.Qtype_NSAPPTR:
            ret = DecodeLabels(ref j);
            break;

          case DNSQueryType.Qtype_SOA:
            ret =  DecodeLabels(ref j);
            ret += "," + DecodeLabels(ref j);
            for (n = 1; n <= 5; n++) {
              x = DecodeInt(Buffer,j)*65536 + DecodeInt(Buffer,j+2);
              j += 4;
              ret += "," + x.ToString();
            }
            break;
          case DNSQueryType.Qtype_NULL:
          case DNSQueryType.Qtype_WKS:
            break;
          case DNSQueryType.Qtype_HINFO:
          case DNSQueryType.Qtype_MINFO:
          case DNSQueryType.Qtype_RP:
          case DNSQueryType.Qtype_ISDN:
            ret = DecodeLabels(ref j);
            ret += "," + DecodeLabels(ref j);
            break;
          case DNSQueryType.Qtype_MX:
          case DNSQueryType.Qtype_AFSDB:
          case DNSQueryType.Qtype_RT:
          case DNSQueryType.Qtype_KX:
            x = DecodeInt(Buffer,j);
            j += 2;
            ret = x.ToString();
            ret += "," + DecodeLabels(ref j);
            break;
          case DNSQueryType.Qtype_TXT:
            ret = DecodeLabels(ref j);
            break;
          case DNSQueryType.Qtype_GPOS:
            ret = DecodeLabels(ref j);
            ret += "," + DecodeLabels(ref j);
            ret += "," + DecodeLabels(ref j);
            break;
          case DNSQueryType.Qtype_PX:
            x = DecodeInt(Buffer,j);
            j += 2;
            ret = x.ToString();
            ret += "," + DecodeLabels(ref j);
            ret += "," + DecodeLabels(ref j);
        }  
      }
      return ret;
    }
    private bool IsIP(string Value) {
      int x = 0;
      string correctChars = "0123456789.";
      bool ret = true;
      for (int n = 1; n <= Value.Length; n++) {
        if (correctChars.IndexOf(Value[n]) == -1) {
          ret = false;
          break;
        } else {
          if (Value[n] == '.') {
            x++;
          }
        }
      }
      if (x != 3) {
        ret = false;
      }
      return ret;
    }
    private string ReverseIP(string Value) {
      int x;
      string ret = "";
      x = Value.LastIndexOf(".");
      while (x > 0) {
        ret += Value.Substring(x + 1);
        Value.Remove(x, Value.Length - x + 1);
        x = Value.LastIndexOf(".");
      }
      if (ret.Length > 0) {
        if(ret[0] == '.') {
          ret.Remove(0, 1);
        }
      }
      return ret;
    }
    public bool MakeQuery(string Name,
        DNSQueryType QType) {
      int n, i;
      int flag,qdcount, ancount, nscount, arcount;
      string s;
      byte[] tmp;
      bool ret = false;
      results.Clear();
      if (IsIP(Name)) {
        Name = ReverseIP(Name) + ".in-addr.arpa";
      }
      CodeHeader().CopyTo(Buffer, 0);
      BufferPos += 12;
      int len = CodeQuery(Name, (int)QType, out tmp);
      tmp.CopyTo(Buffer, BufferPos);
      BufferPos += len;
      sock.Connect(new IPEndPoint(new IPAddress(DNSHost), 53));
      sock.Send(Buffer, BufferPos, 0);
      int bytes = sock.Receive(Buffer, 8192, 0);
      flag = DecodeInt(Buffer, 2);
      byte RCode = (byte)(flag & 0x000F);
      if (RCode == 0) {
        qdcount = DecodeInt(Buffer, 4);
        ancount = DecodeInt(Buffer, 6);
        nscount = DecodeInt(Buffer, 8);
        arcount = DecodeInt(Buffer, 10);
        i = 13;
        if (qdcount > 0) {
          for(n = 1; n <= qdcount ; n++) {
            while ((Buffer[i] != 0) && 
                ((Buffer[i] & 0xC0) != 0xC0)) {
              i++;
            }
            i += 5;
          }
        }
        if (ancount > 0) {
          for(n = 1; n <= ancount; n++) {
            s = DecodeResource(ref i, Name, QType);
            if (s != "") {
              results.Add(s, s);
            }
          }
        }
        ret = true;
      }
      return ret;
    }
    public bool MakeQuery(string Name, int QType) {
      return MakeQuery(Name, (DNSQueryType) QType);
    }
    public bool GetMXRecords(string Name) {
      bool retValue;
      retValue = MakeQuery(Name, DNSQueryType.Qtype_MX);
      if (retValue) {
        string val;
        for (int i = 0; i < results.Count; i++) {
          val = (string)results.GetByIndex(i);
          val = val.Remove(0, val.IndexOf(",") + 1);
          results.SetByIndex(i, val);
        }
      }
      return retValue;
    }
  }
}
Любая критика ожидаема и принимается.

Далі буде…

Dimon aka Manowar
Редактирование: Андрей Филев(dotSITE team)


Контакт Реклама на сайте Спонсорам Веб мастерам

Лицензионное соглашение - © 2000-2012 dotSITE
Хостинг .NET предоставлен PARKING.RU
Поддержку сайта осуществляет Murano Software Inc., Offshore software development