int InitSimpleDNS(void)
dns_fd = socket(sockaddr.sin_family, SOCK_DGRAM, 0);//创建一个IPV4的socket,基于UDP数据套接字
ret = setsockopt(dns_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) ;
//设置SO_REUSEADDR,调用closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket:
ret = bind(dns_fd, &sockaddr, sizeof(sockaddr)) ;//给socket返回值dns_fd,赋一个地址&sockaddr
pthread_create( &pthrd_id,NULL,(void *) simple_dns_proc,NULL ))//创建一个线程simple_dns_proc
static void *simple_dns_proc(void *handle)
FD_ZERO(&dns_rset);//清空&dns_rset集合
get_answer_ip(&answer_addr);
FD_SET(dns_fd, &dns_rset); //将一个给定的文件描述符(dns_fd)加入集合(dns_rset)之中
ret = dns_rcv_query(&sockaddr);
FD_CLR(dns_fd, &dns_rset);//将一个给定的文件描述符从集合中删除
dns_build_answer(header, &answer_len, &answer_addr);
dns_send_answer((void*)header, answer_len, &sockaddr, &answer_addr);
static int get_answer_ip(struct in_addr* ip_addr)
strncpy(ifr.ifr_name, "rndis0", IF_NAMESIZE);
ioctl(dns_fd, SIOCGIFADDR, &ifr);//获取接口地址
static int dns_rcv_query(struct sockaddr_in* p_src_addr)
select(dns_fd+1, &dns_rset, NULL, NULL, &timeout)//非阻塞,以返回值的不同来反映函数的执行情况,监视dns_rset的变化情况,有可读返回大于0
FD_ISSET(dns_fd, &dns_rset)//检查集合中指定的文件描述符是否可以读写
iov[0].iov_base = dns_buf; //数据指针
iov[0].iov_len = DNS_BUF_SIZE;
msg.msg_control = control_u.control;//附属数据缓冲区
msg.msg_controllen = 0;
msg.msg_flags = 0; //接收信息标记位
msg.msg_name = p_src_addr; //套接口地址
msg.msg_namelen = sizeof(struct sockaddr_in);
msg.msg_iov = iov; // I/O向量
msg.msg_iovlen = 1;
recvmsg(dns_fd, &msg, 0) //从套接字上接收一个消息
static int dns_build_answer(HEADER *header, unsigned short* answer_len, struct in_addr* answer_addr)
p = (unsigned char *)(header+1)
nameoffset = p - (unsigned char *)header;
GETSHORT(qtype, p); //getshort函数从指定地址读取16bit网络字节顺序的数据, 并将其转换成little-endian的顺序返回
GETSHORT(qclass, p);
PUTSHORT(nameoffset | 0xc000, p);
PUTSHORT(qtype, p);
PUTSHORT(qclass, p);
PUTLONG(0, p); /* TTL */
PUTSHORT(4, p);
p += INADDRSZ;
header->qr = 1; //* response */答复
header->aa = 0; //* authoritive - only hosts and DHCP derived names. */权威,仅仅主机和DHCP衍生名
header->ra = 1; //* recursion if available */如果可获取就递归查找
header->tc = 0; //* truncation */截尾
header->rcode = NOERROR; /* no error */
header->ancount = htons(1);//将一个无符号短整型的主机数值转换为网络字节顺序,即大尾顺序(big-endian)
header->nscount = htons(0);//header内容见下面图文说明
header->arcount = htons(0);
*answer_len = p -(unsigned char *)header;
static int dns_send_answer(void* data, unsigned short data_len, struct sockaddr_in* to, struct in_addr* answer_addr)
iov[0].iov_base = (void*)data;
iov[0].iov_len = data_len;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
msg.msg_name = to;
msg.msg_namelen = sizeof(struct sockaddr_in);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
sendmsg(dns_fd, &msg, 0)//成功执行时,返回已发送的字节数。失败返回-1
Packet header structure
ID - A 16-bit identifier assigned by the program that generates any kind of query.
QR - Query/Response.
OPCODE - A 4-bit field that specifies the kind of query in this message. This value is set by the originator of a query and copied into the response. This specification defines the behavior of standard queries and responses (opcode value of zero). Future specifications may define the use of other opcodes with LLMNR.
C - Conflict.
TC - TrunCation.
T - Tentative.
Z - Reserved for future use.
RCODE - Response code.
QDCOUNT - An unsigned 16-bit integer specifying the number of entries in the question section.
ANCOUNT - An unsigned 16-bit integer specifying the number of resource records in the answer section.(资源记录数)
NSCOUNT - An unsigned 16-bit integer specifying the number of name server resource records in the authority records section.(名称服务器资源记录数)
ARCOUNT - An unsigned 16-bit integer specifying the number of resource records in the additional records section.(附加档案记录数)