域名系统( 续)

关于 DNS 的更多信息


典型的 DNS 查询 和 DNS 回应通过 UDP 传输, 端口号 53.
如果查询( 更有可能是回应) 太大, 以至于一个 UDP 包装不下, 也会使用 TCP.
这种被称为 TCP fallback 或 DNS transport over TCP, 但大多数情况下使用的是 UDP.

关于 DNS 的更多信息, 查看:

DNS 消息格式


Header 头部, 关于消息的各种信息
Question 向域名服务器查询的问题
Answer 对问题的回答, 答案可能不止一个
Authority 指针, 指向权威服务器
Additional 更多信息

DNS 消息头部格式


0123 4567 891011 12131415
ID
QR Opcode AA TC RD RA Z RCODE
QDCOUNT
ANCOUNT
NSCOUNT
ARCOUNT

DNS 查询 和 DNS 回应的消息头部的大小都是 12 字节, 包含 13 个字段.

ID16 位标识 DNS 消息. 客户将任意值填充进 DNS 查询, DNS 服务器复制相同的值到 DNS 回应的 ID.
QR1 位0 代表 DNS 查询, 1 代表 DNS 回应.
Opcode4 位0 代表标准查询, 1 代表反查询( 解析 IP 地址到域名), 2 代表服务器状态请求, 其他值保留.
AA1 位表明一个权威答案(authoritative answer).
TC1 位表明消息是否被截断. 如果被截断, 就应该使用 TCP 重发.
RD1 位需要递归, 即需要联系其他服务器, 直至完成请求.
RA1 位在回应消息中表明服务器是否支持递归.
Z3 位未使用, 应置为 0.
RCODE4 位在 DNS 回应中表明错误情况.
0无错误, no error
1格式错误, format error
2服务器错误, server failure
3名称错误, name failure
4未实现, not implemented
5被拒绝, refused
**更多出错信息, 查看 RFC 1035

QDCOUNT, ANCOUNT, NSCOUNT, ARCOUNT, 消息对应部分的数量.
QDCOUNT, 16 位, Qustion 数量, 只能为 1, 一条消息只能有一个问题.
ANCOUNT, 16 位, Answer 数量, DNS 服务器做回应时, 在一条消息中包含多个答案是很常见的.

Question 格式


0123 4567 891011 12131415
NAME
...
QTYPE
QCLASS

QTYPE, 16 位, 表明要请求的记录的类型
QCLASS, 16 位, 置为 1, 表明 class 为 互联网(internet)

NAME 字段的编码


域名先被分成几个标签, 如 www.xyz123.com 被分为 3 个标签, www, xyz123 和 com.
每个标签前面添加 1 个字节, 用于表明标签的长度.
在末尾添加 0, 作为域名结束标识.

0123 4567 891011 12131415
3 'w'
'w' 'w'
6 'x'
'y' 'z'
'1' '2'
'3' 3
'c' 'o'
'm' 0

Answer 格式


0123 4567 891011 12131415
NAME
TYPE
CLASS
TTL
TTL (续)
RDLENGTH
RDATA
...

TTL, 32 位, 缓存时长, 秒数
RDLENGTH, 16位, RDATA 字段的长度

DNS 消息的 Question 字段中包含了 NAME 信息.
所以, 大多数服务器在 Answer 字段的 NAME 中保存的是一个指针.
16 位值 11xx xxxx xxxx xxxx(x 为 0 或 1, 即最高 2 位置 1), 表明存储着一个指针.
指针的后 14 位表明指向的位置, 相对于消息开始处的偏移量.

DNS 消息在存储和传输时, 使用 大端序.

简单的 DNS 查询消息


查询 xyz123.com 的 IP.

  1. char dns_query[] = {
  2. 0xAA, 0xBB, /* ID */
  3. 0x01, 0x00, /* 递归查询 */
  4. 0x00, 0x01, /* QDCOUNT */
  5. 0x00, 0x00, /* ANCOUNT */
  6. 0x00, 0x00, /* NSCOUNT */
  7. 0x00, 0x00, /* ARCOUNT */
  8. 6, 'x', 'y', 'z', '1', '2', '3', /* 标签 */
  9. 3, 'c', 'o', 'm', /* 标签 */
  10. 0, /* NAME 结束 */
  11. 0x00, 0x01, /* QTYPE = 1 */
  12. 0x00, 0x01 /* QCLASS = 1 */
  13. };

DNS 查询程序示例


  1. /*
  2. query.dns.c
  3. 编译, 运行
  4. **$ gcc query.dns.c -o query
  5. **$ ./query aliyun.com a
  6. **$ ./query aliyun.com aaaa
  7. */
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include "network.h"
  12. #define HEADER_LEN 12
  13. const char * print_name(
  14. const unsigned char * msg,
  15. const unsigned char * p,
  16. const unsigned char * end
  17. );
  18. void print_dns_message(const char * msg, int msg_length);
  19. int main(int argc, char ** argv) {
  20. if (argc != 3) {
  21. printf("Usage : %s hostname type\n", argv[0]);
  22. printf("Example: %s aliyun.com aaaa\n", argv[0]);
  23. return EXIT_SUCCESS;
  24. }
  25. if (strlen(argv[1]) > 255) {
  26. fprintf(stderr, "Hostname is too long ...\n");
  27. return EXIT_FAILURE;
  28. }
  29. int type;
  30. if (strcmp(argv[2], "a") == 0) {
  31. type = 1;
  32. }
  33. else if (strcmp(argv[2], "mx") == 0) {
  34. type = 15;
  35. }
  36. else if (strcmp(argv[2], "txt") == 0) {
  37. type = 16;
  38. }
  39. else if (strcmp(argv[2], "aaaa") == 0) {
  40. type = 28;
  41. }
  42. else if (strcmp(argv[2], "any") == 0) {
  43. type = 255;
  44. }
  45. else {
  46. fprintf(
  47. stderr,
  48. "Unkown type '%s'. Use 'a', 'aaaa', 'mx', 'txt', or 'any'.\n",
  49. argv[2]
  50. );
  51. return EXIT_FAILURE;
  52. }
  53. printf("Configuring remote address ...\n");
  54. struct addrinfo hints;
  55. memset(&hints, 0, sizeof(hints));
  56. hints.ai_socktype = SOCK_DGRAM;
  57. struct addrinfo * peer_addr;
  58. /* 223.5.5.5, 阿里公共 DNS 服务器 */
  59. if (getaddrinfo("223.5.5.5", "53", &hints, &peer_addr)) {
  60. fprintf(stderr, "getaddrinfo() failed, errno: %d, %s\n", errno, strerror(errno));
  61. return EXIT_FAILURE;
  62. }
  63. printf("Creating socket ...\n");
  64. int peer_sock = socket(
  65. peer_addr->ai_family, peer_addr->ai_socktype, peer_addr->ai_protocol
  66. );
  67. if (peer_sock < 0) {
  68. fprintf(stderr, "socket() failed, errno: %d, %s\n", errno, strerror(errno));
  69. return EXIT_FAILURE;
  70. }
  71. printf("Constructing message ...\n");
  72. char query[1024] = {
  73. 0xAA, 0xBB, /* ID */
  74. 0x01, 0x00, /* Set recursion */
  75. 0x00, 0x01, /* QDCOUNT */
  76. 0x00, 0x00, /* ANCOUNT */
  77. 0x00, 0x00, /* NSCOUNT */
  78. 0x00, 0x00, /* ARCOUNT */
  79. };
  80. char * p = query + 12;
  81. char * q = argv[1];
  82. while (*q) {
  83. char * len = p;
  84. ++p;
  85. if (q != argv[1]) ++q;
  86. while (*q && *q != '.') *p++ = *q++;
  87. *len = p - len - 1;
  88. }
  89. *p++ = 0;
  90. *p++ = 0x00; *p++ = type; /* QTYPE */
  91. *p++ = 0x00; *p++ = 0x01; /* QCLASS */
  92. const int query_size = p - query;
  93. int bytes_sent = sendto(
  94. peer_sock,
  95. query, query_size,
  96. 0,
  97. peer_addr->ai_addr, peer_addr->ai_addrlen
  98. );
  99. printf("Sent %d bytes ...\n", bytes_sent);
  100. print_dns_message(query, query_size);
  101. char read_buff[1024];
  102. int bytes_recv = recvfrom(
  103. peer_sock,
  104. read_buff, sizeof(read_buff),
  105. 0,
  106. NULL, NULL
  107. );
  108. printf("Received %d bytes ...\n", bytes_recv);
  109. print_dns_message(read_buff, sizeof(read_buff));
  110. printf("\n");
  111. freeaddrinfo(peer_addr);
  112. close(peer_sock);
  113. return EXIT_SUCCESS;
  114. }
  115. const char * print_name(
  116. const unsigned char * msg,
  117. const unsigned char * p,
  118. const unsigned char * end
  119. ) {
  120. if (p + 2 > end) {
  121. fprintf(stderr, "End of message ...\n");
  122. exit(EXIT_FAILURE);
  123. }
  124. if ((*p & 0xC0) == 0xC0) {
  125. const int k = ((*p & 0x3F) << 8) + p[1];
  126. p += 2;
  127. printf(" (pointer %d) ", k);
  128. print_name(msg, msg+k, end);
  129. return p;
  130. }
  131. else {
  132. const int len = *p++;
  133. if (p + len + 1 > end) {
  134. fprintf(stderr, "End of message ...\n");
  135. exit(EXIT_FAILURE);
  136. }
  137. printf("%.*s", len, p);
  138. p += len;
  139. if (*p) {
  140. printf(".");
  141. return print_name(msg, p, end);
  142. }
  143. else
  144. return p + 1;
  145. }
  146. }
  147. void print_dns_message(const char * message, int msg_length) {
  148. if (msg_length < HEADER_LEN) {
  149. fprintf(stderr, "Message is too short ...\n");
  150. exit(EXIT_FAILURE);
  151. }
  152. const unsigned char * msg = (const unsigned char *) message;
  153. int i, j;
  154. for (i = 0; i < msg_length; ++i) {
  155. unsigned char r = msg[i];
  156. printf("%02d: %02X %03d '%c'\n", i, r, r, r);
  157. }
  158. printf("\n");
  159. printf("ID = %0X %0X\n", msg[0], msg[1]);
  160. const int qr = (msg[2] & 0x80) >> 7;
  161. printf("QR = %d %s\n", qr, qr == 0 ? "query" : "response");
  162. const int opcode = (msg[2] & 0x78) >> 3;
  163. printf("OPCODE = %d ", opcode);
  164. switch(opcode) {
  165. case 0: printf("standard\n"); break;
  166. case 1: printf("reverse\n"); break;
  167. case 2: printf("status\n"); break;
  168. default: printf("?\n"); break;
  169. }
  170. const int aa = (msg[2] & 0x04) >> 2;
  171. printf("AA = %d %s\n", aa, aa == 0 ? "" : "authoritative");
  172. const int tc = (msg[2] & 0x02) >> 1;
  173. printf("TC = %d %s\n", tc, tc == 0 ? "" : "message truncated");
  174. const int rd = msg[2] & 0x01;
  175. printf("RD = %d %s\n", rd, rd == 0 ? "" : "recursion desired");
  176. if (qr) {
  177. const int rcode = msg[3] & 0x07;
  178. printf("RCODE = %d ", rcode);
  179. switch(rcode) {
  180. case 0: printf("success\n"); break;
  181. case 1: printf("format error\n"); break;
  182. case 2: printf("server failure\n"); break;
  183. case 3: printf("name error\n"); break;
  184. case 4: printf("not implemented\n"); break;
  185. case 5: printf("refused\n"); break;
  186. default: printf("?\n"); break;
  187. }
  188. if (rcode != 0) return;
  189. }
  190. const int qdcount = (msg[4] << 8) + msg[5];
  191. const int ancount = (msg[6] << 8) + msg[7];
  192. const int nscount = (msg[8] << 8) + msg[9];
  193. const int arcount = (msg[10] << 8) + msg[11];
  194. printf("QDCOUNT = %d\n", qdcount);
  195. printf("ANCOUNT = %d\n", ancount);
  196. printf("NSCOUNT = %d\n", nscount);
  197. printf("ARCOUNT = %d\n", arcount);
  198. const unsigned char * p = msg + 12;
  199. const unsigned char * end = msg + msg_length;
  200. if (qdcount) {
  201. for (i = 0; i < qdcount; ++i) {
  202. if (p >= end) {
  203. fprintf(stderr, "End of message ...\n");
  204. exit(EXIT_FAILURE);
  205. }
  206. printf("Query %2d\n", i + 1);
  207. printf(" name: ");
  208. p = print_name(msg, p, end);
  209. printf("\n");
  210. if (p + 4 > end) {
  211. fprintf(stderr, "End of message ...\n");
  212. exit(EXIT_FAILURE);
  213. }
  214. const int type = (p[0] << 8) + p[1];
  215. printf(" type: %d\n", type);
  216. p += 2;
  217. const int qclass = (p[0] << 8) + p[1];
  218. printf(" class: %d\n", qclass);
  219. p += 2;
  220. }
  221. }
  222. if (ancount || nscount || arcount) {
  223. for (i = 0; i < ancount + nscount + arcount; ++i) {
  224. if (p >= end) {
  225. fprintf(stderr, "End of message ...\n");
  226. exit(EXIT_FAILURE);
  227. }
  228. printf("Answer: %2d\n", i + 1);
  229. printf(" name: ");
  230. p = print_name(msg, p, end);
  231. printf("\n");
  232. if (p + 10 > end) {
  233. fprintf(stderr, "End of message ...\n");
  234. exit(EXIT_FAILURE);
  235. }
  236. const int type = (p[0] << 8) + p[1];
  237. printf(" type: %d\n", type);
  238. p += 2;
  239. const int qclass = (p[0] << 8) + p[1];
  240. printf(" class: %d\n", qclass);
  241. p += 2;
  242. const unsigned int ttl = (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
  243. printf(" ttl: %u\n", ttl);
  244. p += 4;
  245. const int rdlen = (p[0] << 8) + p[1];
  246. printf(" rdlen: %d\n", rdlen);
  247. p += 2;
  248. if (p + rdlen > end) {
  249. fprintf(stderr, "End of message ...\n");
  250. exit(EXIT_FAILURE);
  251. }
  252. if (rdlen == 4 && type == 1) {
  253. printf("Address ");
  254. printf("%d.%d.%d.%d\n", p[0], p[1], p[2], p[3]);
  255. }
  256. else if (type == 15 && rdlen > 3) {
  257. const int preference = (p[0] << 8) + p[1];
  258. printf(" pref: %d\n", preference);
  259. printf("Mx: ");
  260. print_name(msg, p+2, end);
  261. printf("\n");
  262. }
  263. else if (rdlen == 16 && type == 28) {
  264. printf("Address ");
  265. for (j = 0; j < rdlen; j += 2) {
  266. printf("%02x%02x", p[j], p[j+1]);
  267. if (j + 2 < rdlen) printf(":");
  268. }
  269. printf("\n");
  270. }
  271. else if (type == 16) {
  272. printf("TXT: '%.*s'\n", rdlen-1, p+1);
  273. }
  274. else if (type == 5) {
  275. printf("CNAME: ");
  276. print_name(msg, p, end);
  277. printf("\n");
  278. }
  279. p += rdlen;
  280. }
  281. }
  282. if (p != end) {
  283. printf("There is some unread data left over ...\n");
  284. }
  285. printf("\n");
  286. }

《C 语言网络编程实践》 (《Hands-On Network Programming With C》)