找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 5373|回复: 4

【C】又造了一个轮子——自己实现通过UDP发送DNS查询,并简单分析服务器返回的内容

[复制链接]
发表于 2017-2-6 03:54:02 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
所谓DNS查询,就是查询一个域名的相关信息,一般情况下就是查域名解析的IP地址了。
举个例子,我的浏览器要通过TCP/IP协议访问www.baidu.com的80端口并发送HTTP请求,但我要连接到百度的服务器的话,我必须先知道百度服务器的IP地址。此时一般情况下直接调用getaddrinfo()就可以取得百度服务器的IP地址了,但这个函数又是怎么知道百度服务器的IP地址的呢?它靠的是DNS查询(以及读DNS缓存、hosts等)。

DNS查询的过程,通常是通过UDP 53端口发送一个请求报文到DNS服务器上,然后等服务器发送回执,最后分析回执内容。非常简单。
不过UDP发送包裹如果超过了512字节,你就必须手动截断,否则服务器不收。这是服务器的一种自我保护手段,同时也是为了保证不丢包。

每个包裹的结构,无论DNS查询和响应,都是下面的格式:
  • DNS报文头
  • 问题
  • 回答记录
  • 权威记录
  • 额外记录


其中DNS报文头必不可少,它的结构用C语言表达就是这样的:
  1. #include<stdint.h>

  2. typedef struct
  3. {
  4.         uint16_t Id; // 会话ID
  5.         uint16_t Params; // 参数
  6.         uint16_t NumQuestions; // 问题数
  7.         uint16_t NumAnswers; // 回答数
  8.         uint16_t NumAuthority; // 权威记录数
  9.         uint16_t NumAdditional; // 额外记录数
  10. }dns_query_t, *dns_query_p;
复制代码
其中,“参数”的内容,如下:
第15位:QR:这个DNS报文是一条响应时为1,是一条查询时为0
第14,13,12,11位:Opcode:操作码,决定报文是被用来做啥的,情况如下:
  • 0000 一个标准的查询
  • 0001 反向查询,此功能已过时,现在没几个服务器会管它。请看RFC 3425
  • 0010 查询服务器的状态
  • 0100 通知,由主DNS服务器通知底下的子DNS服务器数据区域发生改变,让子DNS服务器更新自己的数据区域。看RFC 1996
  • 0101 DDNS(动态域名解析)提示服务器更新域名解析。看RFC 2136
第10位:AA:由DNS服务器发送响应时设置,为1时表示这条DNS回执来自于一个权威服务器的数据。
第9位:TC:设置为1的时候,表示包裹长度超过512字节,已经被截断。TCP没有消息长度限制,UDP有。
第8位:RD:客户端发送DNS请求时,要求DNS服务器进行一次递归查询,也就是说,如果DNS服务器没有相应记录,那么这个DNS服务器就会发送查询到别的DNS服务器,然后返回查询的结果。
第7位:RA:服务端发送回执的时候,设为1表示服务器支持递归查询,0则表示不支持。
第6,5,4位:Z:无用,设为0
第3,2,1,0位:RCode:由服务器返回,表示查询的状态,有以下的几个数值:
  • 0:一切正常
  • 1:包裹格式异常
  • 2:服务器出错
  • 3:没有查询结果
  • 4:服务器没有指定的功能
  • 5:拒绝服务
  • 6:本来应该没有的域名,但是有
  • 7:有一个资源记录集存在,本来不应该有
  • 8:有一个资源记录集应该有,但它没有
  • 9:查询的服务器并不权威
  • 10:相关的信息并不是当前域存在的


接下来说一下DNS查询中,“问题”的格式。
  • 编码为FQDN的域名,所谓FQDN就是“fully qualified domain name”,它的格式,是把域名中的字符串按照句点“.”拆分开,变成多个字符串,并每个字符串的前面用一个字节存储它的长度,然后在最后一个字符串的末尾追加'\0'结尾,举个例子,“www.0xaa55.com”这个域名,转换为FQDN格式后,就是“[3]www[6]0xaa55[3]com[0]”(其中用方括号框起来的,表示一个字节的数值)
    另外注意,表示字符串长度的那个字节的数值,只有在它高2位都是0的时候,它才表示长度,否则它是另一个含义——指针。
    这个“指针”表示域名的后半部分在整个DNS报文的指定偏移处存储,并且指针的数值(也就是相对于报文起始的偏移量)由当前字节的低6位和下一个字节共同组成一个14位整数。
  • 一个16位整数,查询资源类型,数值如下:
    • a = 1     地址
    • ns = 2    域名服务器地址
    • cname = 5 别名
    • soa = 6   权威服务器的起始
    • wks = 11  知名源
    • ptr = 12  指针
    • mx = 15   邮件交换
    • txt = 16  文本信息
    • aaaa = 28 IPv6地址
    • srv = 33  服务
    • all = 255 全部
  • 一个16位整数,查询类,一般为htons(0x0001)(也就是“IN”,而“IN”表示“Internet”,因特网)


举个例子,一个DNS报文头应该是什么样子的?看下面的截图。
send.png
其中ID为0x00DD,参数中RD为1,要求DNS服务器进行一次递归查询,提问数为1,其它都没有。
然后是经过FQDN编码的域名,exmainland.com,查询资源类型为255,也就是“全部”。
最后是查询类,1表示IN

接下来我们看一下“回答”的格式,按照介绍是这样的:
  • 编码为FQDN的域名。
  • 一个16位整数,回答的资源类型
  • 一个16位整数,资源类
  • 一个32位整数,TTL,缓存时间
  • 一个16位整数,资源数据的长度,也就是回答的答案的长度
  • 资源数据,也就是答案,长度取决于资源类型


那么我们实际测试,收到的回执的报文是什么样子的呢?如下图:
recv.png
首先可以看出ID和我们之前的提问是一样的。
其次是,回执报文也包含了“问题”,而不仅仅是“回答”。
这里面共有4条回答,一条A记录,两条NS记录,一条SOA记录。

详细的,发送报文、接收并分析报文的C语言程序源码,请看下面:
  1. //=============================================================================
  2. //作者:0xAA55
  3. //网站:http://www.0xaa55.com
  4. //请保留原作者信息
  5. //-----------------------------------------------------------------------------

  6. #include<stdio.h>
  7. #include<errno.h>
  8. #include<stdint.h>
  9. #include<stdlib.h>
  10. #include<stdarg.h>
  11. #include<malloc.h>
  12. #include<memory.h>
  13. #include<time.h>

  14. #define DNS_LOOKUP_PORT 53

  15. #ifdef WIN32
  16. #include<WinSock2.h>
  17. #include<Ws2tcpip.h>

  18. #define sockerr_native_errno WSAGetLastError()

  19. //=============================================================================
  20. //函数:sockerr_param
  21. //描述:判断socket出错是不是因为参数不对
  22. //-----------------------------------------------------------------------------
  23. static int sockerr_param()
  24. {
  25.         switch(sockerr_native_errno)
  26.         {
  27.         case WSANOTINITIALISED: case WSAEAFNOSUPPORT:
  28.         case WSAEPROTOTYPE: case WSAEINVAL:
  29.         case WSAESOCKTNOSUPPORT: case WSAENOTSOCK:
  30.         case WSAEADDRNOTAVAIL: case WSAEFAULT:
  31.         case WSAEDESTADDRREQ:
  32.                 return 1;
  33.         default:
  34.                 return 0;
  35.         }
  36. }

  37. //=============================================================================
  38. //函数:sockerr_system
  39. //描述:判断socket出错是不是因为系统的问题
  40. //-----------------------------------------------------------------------------
  41. static int sockerr_system()
  42. {
  43.         switch(sockerr_native_errno)
  44.         {
  45.         case WSAENETDOWN: case WSAEMFILE:
  46.         case WSAEINVALIDPROVIDER: case WSAEINVALIDPROCTABLE:
  47.         case WSAENOBUFS: case WSAEPROTONOSUPPORT:
  48.         case WSAEPROVIDERFAILEDINIT: case WSAEACCES:
  49.                 return 1;
  50.         default:
  51.                 return 0;
  52.         }
  53. }

  54. //=============================================================================
  55. //函数:sockerr_addrinuse
  56. //描述:判断socket出错是不是因为地址已经被占用
  57. //-----------------------------------------------------------------------------
  58. static int sockerr_addrinuse()
  59. {
  60.         switch(sockerr_native_errno)
  61.         {
  62.         case WSAEADDRINUSE:
  63.                 return 1;
  64.         default:
  65.                 return 0;
  66.         }
  67. }

  68. //=============================================================================
  69. //函数:sockerr_wouldblock
  70. //描述:判断socket出错是不是因为它需要再来一遍
  71. //-----------------------------------------------------------------------------
  72. static int sockerr_wouldblock()
  73. {
  74.         switch(sockerr_native_errno)
  75.         {
  76.         case WSAEWOULDBLOCK:
  77.                 return 1;
  78.         default:
  79.                 return 0;
  80.         }
  81. }

  82. //=============================================================================
  83. //函数:sockerr_notconn
  84. //描述:判断socket出错是不是因为它没有连接上
  85. //-----------------------------------------------------------------------------
  86. static int sockerr_notconn()
  87. {
  88.         switch(sockerr_native_errno)
  89.         {
  90.         case WSAENOTCONN:
  91.                 return 1;
  92.         default:
  93.                 return 0;
  94.         }
  95. }

  96. //=============================================================================
  97. //函数:sockerr_connbreak
  98. //描述:判断socket出错是不是因为它的连接断开了
  99. //-----------------------------------------------------------------------------
  100. static int sockerr_connbreak()
  101. {
  102.         switch(sockerr_native_errno)
  103.         {
  104.         case WSAECONNABORTED:
  105.         case WSAECONNREFUSED:
  106.         case WSAECONNRESET:
  107.                 return 1;
  108.         default:
  109.                 return 0;
  110.         }
  111. }

  112. //=============================================================================
  113. //函数:close_socket
  114. //描述:关闭套接字,Windows的socket不是文件描述符
  115. //-----------------------------------------------------------------------------
  116. static void close_socket(int sockfd)
  117. {
  118.         closesocket(sockfd);
  119. }

  120. //=============================================================================
  121. //函数:relax
  122. //描述:释放CPU
  123. //-----------------------------------------------------------------------------
  124. static void relax()
  125. {
  126.         Sleep(0);
  127. }

  128. #else
  129. #include<unistd.h>
  130. #include<sys/socket.h>
  131. #include<netinet/in.h>
  132. #include<arpa/inet.h>

  133. #define sockerr_native_errno errno

  134. //=============================================================================
  135. //函数:sockerr_param
  136. //描述:判断socket出错是不是因为参数不对
  137. //-----------------------------------------------------------------------------
  138. static int sockerr_param()
  139. {
  140.         switch(sockerr_native_errno)
  141.         {
  142.         case EBADF: case ENOTSOCK: case EAFNOSUPPORT:
  143.         case EINVAL: case EPROTONOSUPPORT:
  144.         case EADDRNOTAVAIL: case EFAULT:
  145.         case ENAMETOOLONG: case EDESTADDRREQ:
  146.                 return 1;
  147.         default:
  148.                 return 0;
  149.         }
  150. }

  151. //=============================================================================
  152. //函数:sockerr_system
  153. //描述:判断socket出错是不是因为系统的问题
  154. //-----------------------------------------------------------------------------
  155. static int sockerr_system()
  156. {
  157.         switch(sockerr_native_errno)
  158.         {
  159.         case EACCES: case EMFILE: case ENFILE:
  160.         case ENOBUFS: case ENOMEM: case ELOOP:
  161.         case ENOENT: case ENOTDIR: case EROFS:
  162.                 return 1;
  163.         default:
  164.                 return 0;
  165.         }
  166. }

  167. //=============================================================================
  168. //函数:sockerr_addrinuse
  169. //描述:判断socket出错是不是因为地址已经被占用
  170. //-----------------------------------------------------------------------------
  171. static int sockerr_addrinuse()
  172. {
  173.         switch(sockerr_native_errno)
  174.         {
  175.         case EADDRINUSE:
  176.                 return 1;
  177.         default:
  178.                 return 0;
  179.         }
  180. }

  181. //=============================================================================
  182. //函数:sockerr_wouldblock
  183. //描述:判断socket出错是不是因为它需要再来一遍
  184. //-----------------------------------------------------------------------------
  185. static int sockerr_wouldblock()
  186. {
  187.         switch(sockerr_native_errno)
  188.         {
  189. #if EAGAIN != EWOULDBLOCK
  190.         case EAGAIN:
  191. #endif
  192.         case EWOULDBLOCK:
  193.                 return 1;
  194.         default:
  195.                 return 0;
  196.         }
  197. }

  198. //=============================================================================
  199. //函数:sockerr_notconn
  200. //描述:判断socket出错是不是因为它没有连接上
  201. //-----------------------------------------------------------------------------
  202. static int sockerr_notconn()
  203. {
  204.         switch(sockerr_native_errno)
  205.         {
  206.         case ENOTCONN:
  207.                 return 1;
  208.         default:
  209.                 return 0;
  210.         }
  211. }

  212. //=============================================================================
  213. //函数:sockerr_connbreak
  214. //描述:判断socket出错是不是因为它的连接断开了
  215. //-----------------------------------------------------------------------------
  216. static int sockerr_connbreak()
  217. {
  218.         switch(sockerr_native_errno)
  219.         {
  220.         case ECONNRESET:
  221.                 return 1;
  222.         default:
  223.                 return 0;
  224.         }
  225. }

  226. //=============================================================================
  227. //函数:relax
  228. //描述:释放CPU
  229. //-----------------------------------------------------------------------------
  230. static void relax()
  231. {
  232.         sleep(0);
  233. }

  234. //=============================================================================
  235. //函数:close_socket
  236. //描述:关闭套接字,Linux的socket是一个文件描述符
  237. //-----------------------------------------------------------------------------
  238. static void close_socket(int sockfd)
  239. {
  240.         close(sockfd);
  241. }
  242. #endif

  243. typedef struct
  244. {
  245.         uint16_t sa_family; // AF_INET
  246.         uint16_t sa_port;        // htons(port_no)
  247.         uint32_t sa_addr;        // 127.0.0.1 = 0x0100007F
  248.         uint8_t  sa_reserved[8]; // = {0}
  249. }sockaddr_v4_t, *sockaddr_v4_p;

  250. typedef struct
  251. {
  252.         int sockfd;
  253.         struct sockaddr dns_server_addr;
  254.         void *msgtail;
  255.         void *userdata; // 这个变量留着给用户存自己的东西
  256.        
  257.         int is_tcp;
  258.         int conn_status;
  259.        
  260.         size_t cbmsg;
  261.         size_t cbsend;
  262.         uint8_t sendbuf[8192];

  263.         size_t cbrecv;
  264.         uint8_t recvbuf[8192];
  265. }dns_lookup_t, *dns_lookup_p;

  266. #define MAX_SEND_UDP 512

  267. typedef struct
  268. {
  269.         uint16_t Id;
  270.         uint16_t Params;
  271.         uint16_t NumQuestions;
  272.         uint16_t NumAnswers;
  273.         uint16_t NumAuthority;
  274.         uint16_t NumAdditional;
  275. }dns_query_t, *dns_query_p;

  276. typedef struct
  277. {
  278.         dns_query_t query;
  279.         char body[1];
  280. }dns_msg_t, *dns_msg_p;

  281. #define dnsp_response        0x8000

  282. #define dnsp_stdquery        0x0000
  283. #define dnsp_invquery        0x0800 // 过时
  284. #define dnsp_status                0x1000
  285. #define        dnsp_notify                0x2000
  286. #define dnsp_update                0x2800 // DDNS更新

  287. #define dnsp_aa                        0x0400 // 权威
  288. #define dnsp_tc                        0x0200 // 截断
  289. #define dnsp_rd                        0x0100 // 请求用递归查询(客户端发送)
  290. #define dnsp_ra                        0x0080 // 可以用递归查询(服务器返回)

  291. typedef enum
  292. {
  293.         dnsperr_format_err                = 0x0001, // 格式错误
  294.         dnsperr_server_fail                = 0x0002, // 服务器gg
  295.         dnsperr_name_err                = 0x0003, // 没查到
  296.         dnsperr_not_implemented        = 0x0004, // 功能不存在
  297.         dnsperr_refused                        = 0x0005, // 拒绝服务
  298.         dnsperr_yx_domain                = 0x0006, // 本来应该没有的域名,但是有
  299.         dnsperr_yx_rr_set                = 0x0007, // 有一个资源记录集存在,本来不应该有
  300.         dnsperr_nx_rr_set                = 0x0008, // 有一个资源记录集应该有,但它没有
  301.         dnsperr_notauth                        = 0x0009, // 查询的服务器并不权威
  302.         dnsperr_notzone                        = 0x000a, // 相关的信息并不是当前域存在的
  303. }dnsp_err_t, *dnsp_err_p;

  304. typedef enum
  305. {
  306.         dns_rr_a = 1, // 地址
  307.         dns_rr_ns = 2, // 域名服务器地址
  308.         dns_rr_cname = 5, // 别名
  309.         dns_rr_soa = 6, // 权威服务器的起始
  310.         dns_rr_wks = 11, // 知名源
  311.         dns_rr_ptr = 12, // 指针
  312.         dns_rr_mx = 15, // 邮件交换
  313.         dns_rr_txt = 16, // 文本信息
  314.         dns_rr_aaaa = 28, // IPv6地址
  315.         dns_rr_srv = 33, // 服务
  316.         dns_rr_all = 255, // 全部
  317. }dns_rr_t, *dns_rr_p;

  318. typedef enum
  319. {
  320.         dpr_success        = 0, // 分析成功
  321.         dpr_not_received, // 没有接收到包裹
  322.         dpr_incomplete_package, // 没有接收全包裹,TCP的话需要继续接收,UDP的话
  323.         // 则表明,这个回答太长了,应该换成TCP(虽然不是所有的DNS服务器都支持TCP)
  324.         dpr_no_answers, // 服务器表示无可奉告
  325.         dpr_bad_package, // 坏包裹——得到的回答并不对应我们的提问
  326. }dns_parse_result_t, *dns_parse_result_p;

  327. //=============================================================================
  328. //函数:_TimeStr
  329. //描述:返回当前时间的字符串地址,HH:MM:SS,非线程安全,永远返回同一个字符串缓
  330. //      冲区,配合_Log使用。禁止多线程使用它。
  331. //-----------------------------------------------------------------------------
  332. static char*_TimeStr()
  333. {
  334.         static char _TimeBuf[9];
  335.         struct tm*pTime;
  336.         time_t _Time;
  337.         time(&_Time);
  338.         pTime = gmtime(&_Time);
  339.         sprintf(_TimeBuf, "%.2d:%.2d:%.2d",
  340.                 pTime->tm_hour, pTime->tm_min, pTime->tm_sec);
  341.         return _TimeBuf;
  342. }

  343. //=============================================================================
  344. //函数:_Log
  345. //描述:写入日志到stderr
  346. //-----------------------------------------------------------------------------
  347. static void _Log(const char*szFormat, ...)
  348. {
  349.         va_list ap;

  350.         va_start(ap, szFormat);
  351.         vfprintf(stderr, szFormat, ap);
  352.         va_end(ap);
  353. }

  354. //=============================================================================
  355. //函数:_LogSockErr
  356. //描述:记录错误原因
  357. //-----------------------------------------------------------------------------
  358. static void _LogSockErr(const char *szSource)
  359. {
  360.         _Log("[%s]: %s: %d(%s)\n",
  361.                 _TimeStr(), szSource, sockerr_native_errno,
  362.                 sockerr_system()?"system issue":
  363.                 sockerr_addrinuse()?"address already in use":
  364.                 sockerr_connbreak()?"disconnected":
  365.                 sockerr_notconn()?"not connected":
  366.                 sockerr_param()?"bad parameters":
  367.                 sockerr_wouldblock()?"not replied":
  368.                 "unknown");
  369. }

  370. //=============================================================================
  371. //函数:_new_nonblocking_socket
  372. //描述:创建一个非阻塞IO的socket
  373. //-----------------------------------------------------------------------------
  374. static int _new_nonblocking_socket(int af, int type, int protocol)
  375. {
  376.         int sockfd;
  377. #ifdef WIN32
  378.         sockfd = socket(af, type, protocol);
  379. #else
  380.         sockfd = socket(af, type | SOCK_NONBLOCK, protocol);
  381. #endif
  382.         if(sockfd < 0)
  383.         {
  384.                 _LogSockErr("Create socket failed");
  385.                 return sockfd;
  386.         }
  387.        
  388. #ifdef WIN32
  389.         {
  390.                 unsigned long mode = 1;
  391.                 if(ioctlsocket(sockfd, FIONBIO, &mode) != 0)
  392.                 {
  393.                         _LogSockErr("Failed to set a socket to non-blocking mode");
  394.                         close_socket(sockfd);
  395.                         return -1;
  396.                 }
  397.         }
  398. #endif

  399.         return sockfd;
  400. }

  401. //=============================================================================
  402. //函数:_bind_ipv4_port
  403. //描述:绑定IPV4端口
  404. //-----------------------------------------------------------------------------
  405. static int _bind_ipv4_port(int sockfd, uint16_t Port)
  406. {
  407.         sockaddr_v4_t sav4 = {0};
  408.         int ret;

  409.         sav4.sa_family = AF_INET;
  410.         sav4.sa_port = htons(Port);

  411.         ret = bind(sockfd, (struct sockaddr*)&sav4, sizeof(sav4));
  412.         if(ret < 0)
  413.         {
  414.                 _LogSockErr("Failed to bind port");
  415.         }
  416.         return ret;
  417. }

  418. //=============================================================================
  419. //函数:dl_delete
  420. //描述:擦屁股
  421. //-----------------------------------------------------------------------------
  422. void dl_delete(dns_lookup_p pdl)
  423. {
  424.         if(pdl)
  425.         {
  426.                 if(pdl->sockfd >= 0)
  427.                         close_socket(pdl->sockfd);
  428.                 free(pdl);
  429.         }
  430. }

  431. //=============================================================================
  432. //函数:dl_set_dns_server_v4
  433. //描述:设置DNS服务器的地址
  434. //-----------------------------------------------------------------------------
  435. void dl_set_dns_server_v4(dns_lookup_p dl, const char *pszAddr)
  436. {
  437.         dl->dns_server_addr.sa_family = AF_INET;
  438.         ((sockaddr_v4_p)&dl->dns_server_addr)->sa_port = htons(DNS_LOOKUP_PORT);
  439.         ((sockaddr_v4_p)&dl->dns_server_addr)->sa_addr = inet_addr(pszAddr);
  440. }

  441. //=============================================================================
  442. //函数:dl_create_tcp
  443. //描述:创建一个TCP协议的查询器,初始化它,以用于准备查询
  444. //-----------------------------------------------------------------------------
  445. dns_lookup_p dl_create_tcp()
  446. {
  447.         dns_lookup_p pRet = /* VS的编辑器会给右边加波浪线 */malloc(sizeof(dns_lookup_t));
  448.         if(!pRet)
  449.         {
  450.                 _Log("[%s]: malloc() failed: %d.\n", _TimeStr(), errno);
  451.                 return NULL;
  452.         }
  453.         memset(pRet, 0, sizeof(dns_lookup_t));

  454.         pRet->is_tcp = 1;

  455.         pRet->sockfd = _new_nonblocking_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  456.         if(pRet->sockfd < 0)
  457.                 goto ErrReturn;

  458.         //_bind_ipv4_port(pRet->sockfd, DNS_LOOKUP_PORT);

  459.         dl_set_dns_server_v4(pRet, "8.8.4.4");

  460.         return pRet;
  461. ErrReturn:

  462.         dl_delete(pRet);
  463.         return NULL;
  464. }

  465. //=============================================================================
  466. //函数:dl_create_udp
  467. //描述:创建一个UDP协议的查询器,初始化它,以用于准备查询
  468. //-----------------------------------------------------------------------------
  469. dns_lookup_p dl_create_udp()
  470. {
  471.         dns_lookup_p pRet =
  472.                 /* VS的编辑器会给右边加该死的波浪线 */malloc(sizeof(dns_lookup_t));
  473.         if(!pRet)
  474.         {
  475.                 _Log("[%s]: malloc() failed: %d.\n", _TimeStr(), errno);
  476.                 return NULL;
  477.         }
  478.         memset(pRet, 0, sizeof(dns_lookup_t));

  479.         pRet->sockfd = _new_nonblocking_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  480.         if(pRet->sockfd < 0)
  481.                 goto ErrReturn;

  482.         {
  483.                 long v = 1;
  484.                 if(setsockopt(pRet->sockfd, SOL_SOCKET, SO_REUSEADDR,
  485.                         (char*)&v, sizeof(v)) < 0)
  486.                 {
  487.                         _Log("[%s]: setsockopt(SO_REUSEADDR) failed: %d.\n", _TimeStr(),
  488.                                 sockerr_native_errno);
  489.                 }
  490.         }

  491.         _bind_ipv4_port(pRet->sockfd, DNS_LOOKUP_PORT);

  492.         dl_set_dns_server_v4(pRet, "8.8.4.4");

  493.         return pRet;
  494. ErrReturn:

  495.         dl_delete(pRet);
  496.         return NULL;
  497. }

  498. //=============================================================================
  499. //函数:dl_start_form
  500. //描述:开始构造查询
  501. //-----------------------------------------------------------------------------
  502. void dl_start_form(dns_lookup_p dl)
  503. {
  504.         dns_msg_p pmsg = (dns_msg_p)dl->sendbuf;
  505.        
  506.         memset(dl->sendbuf, 0, sizeof(dl->sendbuf));

  507.         srand(clock());
  508.         pmsg->query.Id = htons(rand());
  509.         pmsg->query.Params = htons(dnsp_rd);

  510.         dl->msgtail = pmsg->body;
  511.         dl->cbmsg = sizeof(dns_query_t);
  512. }

  513. //=============================================================================
  514. //函数:_to_fqdn
  515. //描述:分析域名,改变格式
  516. //-----------------------------------------------------------------------------
  517. static size_t _to_fqdn(uint8_t *buf, size_t cbbuf, const char *pszDomain)
  518. {
  519.         char*delim = buf++;
  520.         size_t cb = 1;
  521.         *delim = 0;
  522.         while(cbbuf)
  523.         {
  524.                 switch(*pszDomain)
  525.                 {
  526.                 case '.':
  527.                         delim = buf++;
  528.                         cbbuf--; cb++;
  529.                         *delim = 0;
  530.                         pszDomain++;
  531.                         break;
  532.                 case '\0':
  533.                         delim = buf;
  534.                         *delim = 0;
  535.                         return ++cb;
  536.                 default:
  537.                         *buf++ = *pszDomain++;
  538.                         cbbuf--; cb++;
  539.                         (*delim)++;
  540.                         break;
  541.                 }
  542.         }
  543.         return 0;
  544. }

  545. //=============================================================================
  546. //函数:_from_fqdn
  547. //描述:把FQDN转换为用句点隔开的域名
  548. //-----------------------------------------------------------------------------
  549. static size_t _from_fqdn(char *buf, size_t cbbuf, const uint8_t *fqdn)
  550. {
  551.         size_t cb = 0;
  552.         for(;*fqdn;)
  553.         {
  554.                 if(*fqdn < cbbuf)
  555.                         memcpy(buf, fqdn+1, *fqdn);
  556.                 else
  557.                         return 0;
  558.                 cbbuf -= *fqdn;
  559.                 buf += *fqdn;
  560.                 *buf++ = '.';
  561.                 cb += *fqdn + 1;
  562.                 fqdn += *fqdn + 1;
  563.         }
  564.         *buf = '\0';
  565.         return cb;
  566. }

  567. //=============================================================================
  568. //函数:dl_add_domain
  569. //描述:添加一个域名到查询表格,失败返回0
  570. //-----------------------------------------------------------------------------
  571. int dl_add_domain
  572. (
  573.         dns_lookup_p dl,
  574.         const char *pszDomain,
  575.         dns_rr_t rr,
  576.         int rrclass
  577. )
  578. {
  579.         dns_msg_p pmsg = (dns_msg_p)dl->sendbuf;
  580.         size_t cbdomain;
  581.        
  582.         if(sizeof(dl->sendbuf) - dl->cbmsg < 4)
  583.         {
  584.                 _Log("[%s]: No enough space to add a new domain query for %s\n",
  585.                         _TimeStr(), pszDomain);
  586.                 return 0;
  587.         }

  588.         cbdomain = _to_fqdn(dl->msgtail, sizeof(dl->sendbuf) - dl->cbmsg - 4, pszDomain);

  589.         if(!cbdomain)
  590.         {
  591.                 _Log("[%s]: No enough space to add a new domain query for %s\n",
  592.                         _TimeStr(), pszDomain);
  593.                 return 0;
  594.         }

  595.         dl->msgtail = (uint8_t*)dl->msgtail + cbdomain;
  596.         dl->cbmsg += cbdomain;
  597.        
  598.         *((uint16_t*)dl->msgtail) = htons(rr);
  599.         dl->msgtail = (uint16_t*)dl->msgtail + 1;
  600.         *((uint16_t*)dl->msgtail) = htons(rrclass);
  601.         dl->msgtail = (uint16_t*)dl->msgtail + 1;

  602.         dl->cbmsg += 4;

  603.         pmsg->query.NumQuestions = htons(htons(pmsg->query.NumQuestions) + 1);
  604.         return 1;
  605. }

  606. //=============================================================================
  607. //函数:dl_try_send_query_udp
  608. //描述:发送一个查询到dns服务器,通过UDP协议。
  609. //失败返回-1,没发出去返回0(需再来一遍),成功返回1
  610. //-----------------------------------------------------------------------------
  611. static int dl_try_send_query_udp(dns_lookup_p dl)
  612. {
  613.         int sr;

  614.         dl->cbsend = 0;
  615.         dl->cbrecv = 0;
  616.        
  617.         // 判断截断
  618.         if(dl->cbmsg > MAX_SEND_UDP)
  619.         {
  620.                 dl->cbmsg = MAX_SEND_UDP;
  621.                 ((dns_query_p)dl->sendbuf)->Params |= dnsp_tc;
  622.         }

  623.         sr = sendto(dl->sockfd, dl->sendbuf, dl->cbmsg, 0,
  624.                 &dl->dns_server_addr, sizeof(dl->dns_server_addr));

  625.         if(sr == dl->cbmsg)
  626.         {
  627.                 dl->cbsend = sr;
  628.                 return 1; // 发送成功
  629.         }
  630.         else
  631.         {
  632.                 if(sockerr_wouldblock())
  633.                         return 0;
  634.                 else
  635.                 {
  636.                         _LogSockErr("Send DNS loop up query failed");
  637.                         return-1;
  638.                 }
  639.         }
  640. }

  641. //=============================================================================
  642. //函数:dl_try_send_query_tcp
  643. //描述:尝试发送一个查询到dns服务器,通过TCPIP协议。
  644. //失败返回-1,需要再次调用返回0,成功返回1
  645. //-----------------------------------------------------------------------------
  646. static int dl_try_send_query_tcp(dns_lookup_p dl)
  647. {
  648.         int cr;
  649.         int sr;
  650.        
  651.         switch(dl->conn_status)
  652.         {
  653.         case 0:
  654.                 cr = connect(dl->sockfd, &dl->dns_server_addr, sizeof(dl->dns_server_addr));
  655.                 dl->conn_status = 1;
  656.                 if(cr < 0)
  657.                 {
  658.                         if(sockerr_wouldblock())
  659.                                 return 0;
  660.                         else
  661.                         {
  662.                                 _LogSockErr("Send DNS loop up query failed");
  663.                                 return-1;
  664.                         }
  665.                 }
  666.                 dl->cbsend = 0;
  667.                 dl->cbrecv = 0;
  668.                 //return 0;
  669.         case 1:
  670.                 sr = send(dl->sockfd, dl->sendbuf + dl->cbsend, dl->cbmsg - dl->cbsend, 0);
  671.                 if(sr > 0)
  672.                 {
  673.                         dl->cbsend += sr;
  674.                         if(dl->cbsend >= dl->cbmsg)
  675.                                 return 1;
  676.                         else
  677.                                 return 0;
  678.                 }
  679.                 else
  680.                 {
  681.                         if(sockerr_wouldblock() || sockerr_notconn())
  682.                                 return 0;
  683.                         else
  684.                         {
  685.                                 _LogSockErr("Send DNS loop up query failed");
  686.                                 return-1;
  687.                         }
  688.                 }
  689.         default:
  690.                 _Log("Unknown error.\n");
  691.                 return-1;
  692.         }
  693. }

  694. //=============================================================================
  695. //函数:dl_try_send_query
  696. //描述:尝试发送一个查询到dns服务器。
  697. //失败返回-1,需要再次调用返回0,成功返回1
  698. //-----------------------------------------------------------------------------
  699. int dl_try_send_query(dns_lookup_p dl)
  700. {
  701.         if(dl->is_tcp)
  702.                 return dl_try_send_query_tcp(dl);
  703.         else
  704.                 return dl_try_send_query_udp(dl);
  705. }

  706. //=============================================================================
  707. //函数:dl_send_query
  708. //描述:发送一个查询到dns服务器,失败返回0
  709. //-----------------------------------------------------------------------------
  710. int dl_send_query(dns_lookup_p dl, clock_t timeout_ms)
  711. {
  712.         clock_t c_start = clock();
  713.         for(;;)
  714.         {
  715.                 switch(dl_try_send_query(dl))
  716.                 {
  717.                 case 1:
  718.                         return 1;
  719.                 case-1:
  720.                         return 0;
  721.                 }

  722.                 if(clock() - c_start > (timeout_ms * CLOCKS_PER_SEC) / 1000)
  723.                         return 0;
  724.         }
  725. }

  726. //=============================================================================
  727. //函数:dl_poll_reply_udp
  728. //描述:检查DNS服务器是否返回了回复,有回复时返回1,没回复时返回0,出错返回-1
  729. //-----------------------------------------------------------------------------
  730. static int dl_poll_reply_udp(dns_lookup_p dl)
  731. {
  732.         int fromlen = sizeof(dl->dns_server_addr);
  733.         int rr = recvfrom(dl->sockfd, dl->recvbuf, sizeof(dl->recvbuf), 0,
  734.                 &dl->dns_server_addr, &fromlen);
  735.         if(rr > 0)
  736.         {
  737.                 dl->cbrecv = rr;
  738.                 return 1;
  739.         }
  740.         else
  741.         {
  742.                 if(sockerr_wouldblock())
  743.                         return 0;
  744.                 else
  745.                 {
  746.                         _LogSockErr("Failed to receive reply");
  747.                         return-1;
  748.                 }
  749.         }
  750. }

  751. //=============================================================================
  752. //函数:dl_poll_reply_tcp
  753. //描述:检查DNS服务器是否返回了回复,有回复时返回1,没回复时返回0,出错返回-1
  754. //-----------------------------------------------------------------------------
  755. static int dl_poll_reply_tcp(dns_lookup_p dl)
  756. {
  757.         int rr;
  758.        
  759.         if(dl->cbrecv >= sizeof(dl->recvbuf))
  760.                 return 1;

  761.         rr = recv(dl->sockfd, dl->recvbuf + dl->cbrecv,
  762.                 sizeof(dl->recvbuf) - dl->cbrecv, 0);
  763.         if(rr > 0)
  764.         {
  765.                 dl->cbrecv += rr;
  766.                 return 1;
  767.         }
  768.         else if(rr == 0)
  769.         {
  770.                 return 0;
  771.         }
  772.         else
  773.         {
  774.                 if(sockerr_wouldblock())
  775.                         return 0;
  776.                 else
  777.                 {
  778.                         _LogSockErr("Failed to receive reply");
  779.                         return-1;
  780.                 }
  781.         }
  782. }

  783. //=============================================================================
  784. //函数:dl_poll_reply
  785. //描述:检查DNS服务器是否返回了回复,有回复时返回1,没回复时返回0,出错返回-1
  786. //-----------------------------------------------------------------------------
  787. int dl_poll_reply(dns_lookup_p dl)
  788. {
  789.         if(dl->is_tcp)
  790.                 return dl_poll_reply_tcp(dl);
  791.         else
  792.                 return dl_poll_reply_udp(dl);
  793. }

  794. //=============================================================================
  795. //函数:dl_parse_name
  796. //描述:分析DNS服务器发回的回复信息中的QNAME,返回一个字符串
  797. //-----------------------------------------------------------------------------
  798. const char *dl_parse_name
  799. (
  800.         dns_lookup_p dl,
  801.         size_t name_offset,
  802.         size_t*srcbytes
  803. )
  804. {
  805.         static char name[256] = {0};
  806.         size_t cbsource = 0;
  807.         size_t cbname = 0;
  808.         const char *pch;
  809.        
  810.         if(name_offset >= dl->cbrecv)
  811.                 return NULL;

  812. Parse_From_Entry:

  813.         pch = dl->recvbuf + name_offset;
  814.         for(;*pch;)
  815.         {
  816.                 if(((*pch) & 0xC0) == 0xC0)
  817.                 {
  818.                         // 发现“指针”
  819.                         size_t ptr = ntohs(*(uint16_t*)pch) & 0x3FFF; // 取指针
  820.                         if(ptr >= dl->cbrecv)
  821.                                 return NULL;
  822.                         // 记录源长度就此为止
  823.                         cbsource += 2;
  824.                         if(srcbytes)
  825.                                 *srcbytes = cbsource;
  826.                         // 跳到指针的位置
  827.                         name_offset = ptr;
  828.                         srcbytes = NULL;
  829.                         // 继续分析
  830.                         goto Parse_From_Entry;
  831.                 }
  832.                 else if(((*pch) & 0xC0) == 0x00)
  833.                 {
  834.                         name_offset += *pch + 1;
  835.                         if(name_offset >= dl->cbrecv)
  836.                                 return NULL;
  837.                         cbsource += *pch + 1;
  838.                         memcpy(name + cbname, pch+1, *pch);
  839.                         cbname += *pch;
  840.                         name[cbname++] = '.';
  841.                         pch += *pch + 1;
  842.                 }
  843.                 else
  844.                         return NULL;
  845.         }
  846.         name[--cbname] = '\0';
  847.         cbsource ++;
  848.         if(srcbytes)
  849.                 *srcbytes = cbsource;
  850.         return name;
  851. }

  852. //=============================================================================
  853. //函数:dl_parse_reply
  854. //描述:分析DNS服务器发回的回复信息
  855. //-----------------------------------------------------------------------------
  856. dns_parse_result_t dl_parse_reply
  857. (
  858.         dns_lookup_p dl,
  859.         void(*on_get_answer)
  860.                 (
  861.                         dns_lookup_p dl,
  862.                         const char *name, // 对应的域名(已经转换为用句点分隔的字符串)
  863.                         dns_rr_t resource_record_type, // 记录类型
  864.                         uint16_t _class, // 类,一般为1(IN)
  865.                         uint32_t ttl, // 缓存时间
  866.                         uint16_t record_length, // 记录长度
  867.                         const char *record // 记录内容
  868.                 )
  869. )
  870. {
  871.         char*pch;
  872.         char*pQuestionSend;
  873.         char*pQuestionRecv;
  874.         char*pAnswer;
  875.         size_t cbdomain = 0;
  876.         size_t cbparse = 0;
  877.         size_t i;
  878.         dns_msg_p pSendHead = (dns_msg_p)dl->sendbuf;
  879.         dns_msg_p pRecvHead = (dns_msg_p)dl->recvbuf;
  880.         uint16_t numQuestions, numAnswers;

  881.         // 判断是否收到包裹了
  882.         if(!dl->cbrecv)
  883.                 return dpr_not_received;

  884.         if(dl->cbrecv <= sizeof(dns_query_t))
  885.                 return dpr_incomplete_package;

  886.         cbparse += sizeof(dns_query_t);

  887.         // 判断ID是否对应
  888.         if(pRecvHead->query.Id != pSendHead->query.Id)
  889.                 return dpr_bad_package;

  890.         // 判断对方是否给了回答
  891.         if(!(pRecvHead->query.Params & dnsp_response))
  892.                 return dpr_bad_package; // 被反问了

  893.         numQuestions = ntohs(pRecvHead->query.NumQuestions);
  894.         numAnswers = ntohs(pRecvHead->query.NumAnswers);

  895.         // 对方举一反三了……
  896.         if(numQuestions > ntohs(pSendHead->query.NumQuestions))
  897.                 return dpr_bad_package;

  898.         // 判断回答
  899.         if(!numAnswers)
  900.                 return dpr_no_answers; // 对方表示无可奉告

  901.         // 遍历检查所有的提问
  902.         pQuestionSend = pSendHead->body;
  903.         pQuestionRecv = pRecvHead->body;
  904.         for(i = 0; i < numQuestions; i++)
  905.         {
  906.                 cbdomain = 0;
  907.                 for(pch = pQuestionRecv; *pch; pch += *pch + 1)
  908.                         cbdomain += *pch + 1;
  909.                 cbdomain ++; // 有个'\0'结尾

  910.                 cbparse += cbdomain + 4;

  911.                 if(cbparse > dl->cbrecv)
  912.                         return dpr_incomplete_package;
  913.                
  914.                 // 判断是不是答非所问
  915.                 if(memcmp(pQuestionSend, pQuestionRecv, cbdomain + 4))
  916.                         return dpr_bad_package;

  917.                 pQuestionSend += cbdomain + 4;
  918.                 pQuestionRecv += cbdomain + 4;
  919.         }

  920.         if(cbparse + 12 > dl->cbrecv)
  921.                 return dpr_incomplete_package;

  922.         for(i = 0; i < numAnswers; i++)
  923.         {
  924.                 static char ans_domain[256] = {0};
  925.                 static char name_record[256] = {0};
  926.                 struct
  927.                 {
  928.                         uint16_t Type;
  929.                         uint16_t Class;
  930.                         uint32_t TTL;
  931.                         uint16_t RDLength;
  932.                         char RData[1];
  933.                 } *answer_struct;
  934.                 size_t cbname;
  935.                 const char*pszname;

  936.                 // NAME
  937.                 pszname = dl_parse_name(dl, cbparse, &cbname);
  938.                 if(!pszname)
  939.                         return dpr_bad_package;
  940.                 strcpy(ans_domain, pszname);
  941.                 cbparse += cbname;
  942.                
  943.                 if(cbparse + 10 > dl->cbrecv)
  944.                         return dpr_incomplete_package;

  945.                 answer_struct = (void*)(dl->recvbuf + cbparse);
  946.                 cbparse += 10;
  947.                
  948.                 cbparse += ntohs(answer_struct->RDLength);
  949.                 if(cbparse > dl->cbrecv)
  950.                         return dpr_incomplete_package;

  951.                 on_get_answer(dl,
  952.                         ans_domain,
  953.                         ntohs(answer_struct->Type),
  954.                         ntohs(answer_struct->Class),
  955.                         ntohl(answer_struct->TTL),
  956.                         ntohs(answer_struct->RDLength),
  957.                         answer_struct->RData);
  958.         }

  959.         return dpr_success;
  960. }

  961. //=============================================================================
  962. //函数:_print_ipv6_addr
  963. //描述:打印IPV6地址
  964. //-----------------------------------------------------------------------------
  965. void _print_ipv6_addr(FILE*fp, void *addr)
  966. {
  967.         char buf[40];
  968.         inet_ntop(AF_INET6, addr, buf, sizeof(buf));
  969.         fputs(buf, fp);
  970. }

  971. //=============================================================================
  972. //函数:demo_get_answer
  973. //描述:示例回调函数,取得解析结果
  974. //-----------------------------------------------------------------------------
  975. void demo_get_answer
  976. (
  977.         dns_lookup_p dl,
  978.         const char *name, // 对应的域名(已经转换为用句点分隔的字符串)
  979.         dns_rr_t resource_record_type, // 记录类型
  980.         uint16_t _class, // 记录长度
  981.         uint32_t ttl, // 缓存时间
  982.         uint16_t record_length, // 记录长度
  983.         const char *record // 记录内容
  984. )
  985. {
  986.         printf("Name: %s\nType: %u(%s)\nTTL: %u\n",
  987.                 name,
  988.                 resource_record_type,
  989.                 resource_record_type == dns_rr_a?"A":
  990.                 resource_record_type == dns_rr_ns?"NS":
  991.                 resource_record_type == dns_rr_cname?"CNAME":
  992.                 resource_record_type == dns_rr_soa?"SOA":
  993.                 resource_record_type == dns_rr_wks?"WKS":
  994.                 resource_record_type == dns_rr_ptr?"PTR":
  995.                 resource_record_type == dns_rr_mx?"MX":
  996.                 resource_record_type == dns_rr_srv?"SRV":
  997.                 resource_record_type == dns_rr_aaaa?"AAAA":
  998.                 "Unknown",
  999.                 ttl);

  1000.         switch(resource_record_type)
  1001.         {
  1002.         case dns_rr_a:
  1003.                 printf("Value: %u.%u.%u.%u\n",
  1004.                         (uint8_t)record[0],
  1005.                         (uint8_t)record[1],
  1006.                         (uint8_t)record[2],
  1007.                         (uint8_t)record[3]);
  1008.                 break;
  1009.         case dns_rr_cname:
  1010.         case dns_rr_ns:
  1011.         case dns_rr_ptr:
  1012.                 printf("Value: %s\n",
  1013.                         dl_parse_name(dl, (ptrdiff_t)record - (ptrdiff_t)dl->recvbuf, NULL));
  1014.                 break;
  1015.         case dns_rr_aaaa:
  1016.                 printf("Value: ");
  1017.                 _print_ipv6_addr(stdout, (void*)record);
  1018.                 printf("\n");
  1019.                 break;
  1020.         default:
  1021.                 break;
  1022.         }
  1023. }

  1024. //=============================================================================
  1025. //函数:main
  1026. //描述:程序入口点
  1027. //-----------------------------------------------------------------------------
  1028. int main(int argc,char**argv)
  1029. {
  1030.         dns_lookup_p pdl = NULL;
  1031.         int ret = 1;

  1032.         if(argc < 2)
  1033.         {
  1034.                 fprintf(stderr, "Usage: %s domain [dns server ip]\n", argv[0]);
  1035.                 return-1;
  1036.         }

  1037. #ifdef WIN32
  1038.         {
  1039.                 WSADATA wsaData;
  1040.                 if(WSAStartup(WINSOCK_VERSION, &wsaData) < 0)
  1041.                 {
  1042.                         fprintf(stderr, "WSAStartup(WINSOCK_VERSION, &wsaData) failed with %u\n.", WSAGetLastError());
  1043.                         return-1;
  1044.                 }
  1045.         }
  1046. #endif

  1047.         pdl = dl_create_udp();
  1048.         if(!pdl)goto EOP;

  1049.         // dl_set_dns_server_v4(pdl, "119.29.29.29");
  1050.         // 我这的路由器只允许我把DNS查询发到网关
  1051.         // dl_set_dns_server_v4(pdl, "192.168.11.1");
  1052.        
  1053.         if(argc >= 3)
  1054.                 dl_set_dns_server_v4(pdl, argv[2]);

  1055.         dl_start_form(pdl);
  1056.         if(!dl_add_domain(pdl, argv[1], dns_rr_all, 1))
  1057.                 goto EOP;
  1058.        
  1059.         dl_send_query(pdl, 1000);

  1060.         {
  1061.                 clock_t _cstart = clock();
  1062.                 const clock_t timeout = CLOCKS_PER_SEC * 10; // 等10秒

  1063.                 for(;;relax())
  1064.                 {
  1065.                         clock_t _now;

  1066.                         switch(dl_poll_reply(pdl))
  1067.                         {
  1068.                         case 1:
  1069.                                 _cstart = clock();
  1070.                                 fprintf(stderr, "Response received.\n");
  1071.                                 ret = dl_parse_reply(pdl, demo_get_answer);
  1072.                                 if(ret == dpr_no_answers)
  1073.                                         fprintf(stderr, "no answers.\n");
  1074.                                 if(!pdl->is_tcp)
  1075.                                         goto EOP;
  1076.                                 break;
  1077.                         case-1:
  1078.                                 goto EOP;
  1079.                         }
  1080.                        
  1081.                         _now = clock();
  1082.                         if(_now - _cstart >= timeout)
  1083.                         {
  1084.                                 fprintf(stderr, "Timed out, no response.\n");
  1085.                                 goto EOP;
  1086.                         }
  1087.                 }
  1088.         }

  1089.         ret = 0;
  1090. EOP:
  1091.         dl_delete(pdl);
  1092.         pdl = NULL;
  1093.        
  1094. #ifdef WIN32
  1095.         WSACleanup();
  1096. #endif
  1097.         return ret;
  1098. }
复制代码
源码下载: dnslkup.c (29.6 KB, 下载次数: 10)

这份代码可以移植到Linux上运行,简单地打印DNS查询的结果。
dnslookup.png

参考资料:
RFC 1034
RFC 1035
RFC 1536
RFC 1996
RFC 2136
RFC 3425
RFC 5966
RFC 7766
http://www.zytrax.com/books/dns/ch15/
http://www.tcpipguide.com/free/t ... onSectionFormat.htm

本帖被以下淘专辑推荐:

回复

使用道具 举报

发表于 2017-2-6 07:31:46 | 显示全部楼层
太牛逼了!
回复

使用道具 举报

发表于 2017-2-13 12:39:18 | 显示全部楼层
查询是否答非所问,这个功能对DNS污染似乎有作用
回复 赞! 靠!

使用道具 举报

发表于 2017-2-25 23:59:22 | 显示全部楼层
DNS服务器只有一个么,// dl_set_dns_server_v4(pdl, "119.29.29.29");   如果这个服务器崩了,那大家都上不了网了,怎么办???
回复 赞! 靠!

使用道具 举报

 楼主| 发表于 2017-9-8 01:55:16 | 显示全部楼层
乘简 发表于 2017-2-25 23:59
DNS服务器只有一个么,// dl_set_dns_server_v4(pdl, "119.29.29.29");   如果这个服务器崩了,那大家都 ...

如果这个崩了,就换一个服务器
回复 赞! 靠!

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-11-25 01:10 , Processed in 0.057340 second(s), 26 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表