写一个与DNS通信获取MX记录的C++类

上一篇文章用telnet发送电子邮件提到通过nslookup获取域名的MX记录。
如果在程序里实现SMTP协议就不可能依赖于nslookup程序了。写一个与DNS通信获取MX记录的C++类。
效果如图所示:

DNS通信协议请参见RFC1035

实现代码如下:

  1. #include <stdio.h>  
  2. #ifdef __WIN32__  
  3. #include <winsock.h>  
  4. #include <wininet.h>  
  5. #define SHUT_RDWR SD_BOTH  
  6. #else  
  7. #include <sys/socket.h>  
  8. #include <arpa/inet.h>  
  9. #include <netdb.h>  
  10. #define closesocket close  
  11. #endif  
  12. #include <assert.h>  
  13. #include <string.h>  
  14. #include <string>  
  15. #include <vector>  
  16. using namespace std;  
  17.   
  18. struct DnsMxRecord {  
  19.     int     preference;  
  20.     string  address;  
  21. };  
  22.   
  23. class DnsQuery  
  24. {  
  25. private:  
  26.     int     socketHandle;  
  27.     string  nameServer;  
  28.     char    buffer[512+4];  
  29.     int     bufferPos;  
  30.     int     errorCode;  
  31.     vector<DnsMxRecord>   mxRecords;  
  32.   
  33.     string  parseName(int &pos) {  
  34.         string name;  
  35.         char temp[256];  
  36.         while(buffer[pos]) {  
  37.             int length = (unsigned char)buffer[pos ++];  
  38.             if(length == 0xC0) {  
  39.                 int newPos = (unsigned char)buffer[pos ++];  
  40.                 if(name.empty())  
  41.                     name = parseName(newPos);  
  42.                 else  
  43.                     name = name + "." + parseName(newPos);  
  44.                 break;  
  45.             } else {  
  46.                 memcpy(temp, &buffer[pos], length);  
  47.                 pos += length;  
  48.                 temp[length] = '\0';  
  49.                 if(name.empty())  
  50.                     name = temp;  
  51.                 else  
  52.                     name = name + "." + temp;  
  53.             }  
  54.         }  
  55.         return name;  
  56.     }  
  57. public:  
  58.     DnsQuery() {  
  59.         socketHandle = -1;  
  60.     }  
  61.     ~DnsQuery() {  
  62.         if(socketHandle != -1)  
  63.             closesocket(socketHandle);  
  64.     }  
  65.   
  66.     void    setNameServer(string ns) {  
  67.         this->nameServer = ns;  
  68.     }  
  69.   
  70.     string  getErrorString() {  
  71.         static char errors[][32] = {  
  72.             "No error.",    "Incorrect message format.",    "DNS error.",  
  73.             "Domain name not found.",   "Unimplemented command.",   "Denied."  
  74.         };  
  75.         if(errorCode < 6)  
  76.             return errors[errorCode];  
  77.         else  
  78.             return "Unknown error.";  
  79.     }  
  80.   
  81.     bool    connect() {  
  82.         /* Create a UDP socket */  
  83.         socketHandle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);  
  84.         assert(socketHandle != -1);  
  85.         /* Connect to the name server */  
  86.         struct sockaddr_in addr = {0,};  
  87.         addr.sin_family = AF_INET;  
  88.         addr.sin_addr.s_addr = inet_addr(nameServer.c_str());  
  89.         addr.sin_port = htons(53);  
  90.         return (0 == ::connect(socketHandle, (sockaddr*)&addr, sizeof(addr)));  
  91.     }  
  92.   
  93.     bool    queryMx(string name) {  
  94.         printf("queryMx: %s\n", name.c_str());  
  95.         errorCode = 0;  
  96.         /* Initialize MX query header */  
  97.         static const char queryHeader[] = {0x00,0x01,0x01,0x00,0x00,0x01,  
  98.                                            0x00,0x00,0x00,0x00,0x00,0x00  
  99.                                           };  
  100.         memcpy(buffer, queryHeader, sizeof(queryHeader));  
  101.         bufferPos = sizeof(queryHeader);  
  102.         /* put domain name */  
  103.         for(int next, current = 0; current<name.length(); ) {  
  104.             /* divide by dot */  
  105.             next = name.find('.', current);  
  106.             if(next == -1)  
  107.                 next = name.length();  
  108.             /* length of part */  
  109.             int length = next - current;  
  110.             buffer[bufferPos ++] = length;  
  111.             memcpy(buffer + bufferPos, name.c_str() + current, length);  
  112.             bufferPos += length;  
  113.             current = next + 1;  
  114.         }  
  115.         buffer[bufferPos ++] = '\0';  
  116.         /* query type and class */  
  117.         *((unsigned int*)&buffer[bufferPos]) = 0x01000F00;  
  118.         bufferPos += sizeof(unsigned int);  
  119.         /* send & wait for reply */  
  120.         send(this->socketHandle, buffer, bufferPos, 0);  
  121.         int ret = recv(this->socketHandle, buffer, 512, 0);  
  122.         if(ret < bufferPos) {  
  123.             errorCode = 2;  
  124.             return false;  
  125.         }  
  126.         if((errorCode = buffer[3] & 0xf) != 0)  
  127.             return false;  
  128.         /* Parse MX record data */  
  129.         mxRecords.clear();  
  130.         int mxCount = ntohs(*((unsigned short*)&buffer[6]));  
  131.         for(int i=0; i<mxCount; i++) {  
  132.             string domain = parseName(bufferPos);  
  133.             int type = ntohs(*((unsigned short*)&buffer[bufferPos]));  
  134.             /* pass class & ttl */  
  135.             bufferPos += 8;  
  136.             int rdLength = ntohs(*((unsigned short*)&buffer[bufferPos]));  
  137.             bufferPos += 2;  
  138.             if(type == 0xf) {  
  139.                 DnsMxRecord item;  
  140.                 item.preference = ntohs(*((unsigned short*)&buffer[bufferPos]));  
  141.                 bufferPos += 2;  
  142.                 item.address = parseName(bufferPos);  
  143.                 mxRecords.push_back(item);  
  144.                 printf("found MX record: %s, preference: %d\n", item.address.c_str(), item.preference);  
  145.             } else {  
  146.                 bufferPos += rdLength;  
  147.             }  
  148.         }  
  149.         return true;  
  150.     }  
  151. };  
  152.   
  153. int main(int argc, char **argv)  
  154. {  
  155. #ifdef __WIN32__  
  156.     WSADATA wsaData;  
  157.     assert(WSAStartup(MAKEWORD(2,1), static_cast<WSADATA*>(&wsaData)) == 0);  
  158. #endif  
  159.     DnsQuery query;  
  160.     query.setNameServer("8.8.8.8");  
  161.     if(!query.connect()) {  
  162.         perror("failed to connect to name server.");  
  163.         _exit(1);  
  164.     }  
  165.     if(!query.queryMx("gmail.com")) {  
  166.         printf("failed to get mx records: %s\n", query.getErrorString().c_str());  
  167.         _exit(1);  
  168.     }  
  169.     return 0;  
  170. }  

写一个与DNS通信获取MX记录的C++类》有10个想法

        1. Xiaoxia 文章作者

          嗯,你说得有道理~!
          感觉我这样写,是让connect这个步骤显得累赘了。
          哈哈,向你学习了!

          回复

回复 Xiaoxia 取消回复

您的邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据