
- 1 前言
- 2 知识点分析
- 2.1 网络分层
- 2.2 DNS协议
- 3 代码实现
- 4 测试案例验证
- 5 更多分享
1 前言
相信大家在平时的网络开发中,对域名的接触一定非常多。
对于域名的定义,
域名(英语:Domain Name),又称网域,是由一串用点分隔的名字组成的Internet上某一台计算机或计算机组的名称,用于在数据传输时对计算机的定位标识(有时也指地理位置)。
通俗来说,某个域名就是代表网络上的某个服务器主机,而根据网络协议分层只有IP层,并没有域名层,所以这里就涉及到域名到IP地址的转换,也就是我们常说的域名解析。
在平时开发中,我们都是直接调用系统接口实现域名解析,那么我们有没有方法直接根据网络协议组包来完成域名解析呢?
本文就这个问题,将会给出答案。
我们熟知的网络七层协议模型如下图所示:
每一层都都有自己的独立的作用:
在我们这章节中重点关注网络层。
DNs协议的定义:
我们常说的DNS解析,实则就是通过DNS协议,发起解析请求,从域名服务器上取得响应的。
DNS报文分为查询请求报文和查询相应报文,两类报文结构基本相同,结构如下:
详细可参见这里。
直接上实现代码:
#include#include #include "dns.h" //#define DNS_PLATFORM_WINDOWS 1 //表示windows平台 #define DNS_PLATFORM_LINUX 1 //表示linux平台 #define DNS_SERVER_PORT 53 #define DNS_MAX_IP_LEN 16 // only for IPV4 #define DNS_MAX_IP_CNT 1 #define DNS_MAX_TRY_CNT 1 #define DNS_TIMEOUTS 5 // 5 seconds #define DNS_PACKET_ID 0x55AA #define DNS_DEBUG_ENABLE 0 // debug enable #if (DNS_DEBUG_ENABLE) #define DNS_PRINTF(fmt, arg...) MSG_PRINTF(LOG_INFO, fmt, ##arg) #define DNS_ORG_PRINTF(fmt, arg...) MSG_ORG_PRINTF(LOG_INFO, fmt, ##arg) #define DNS_HEXDUMP(title, buf, len) MSG_INFO_ARRAY(title, buf, len) #else #define DNS_PRINTF(fmt, arg...) do {} while(0) #define DNS_ORG_PRINTF(fmt, arg...) do {} while(0) #define DNS_HEXDUMP(title, buf, len) do {} while(0) #endif #ifndef ARRAY_SIZE #define ARRAY_SIZE(a) sizeof(a)/sizeof(a[0]) #endif static const char *g_dns_server_list[] = { "114.114.114.114", // 114 DNS "8.8.8.8", // Google DNS "208.67.222.222", // OpenDNS }; #if defined (DNS_PLATFORM_LINUX) #include #include #include #include #include #include #endif #if defined (DNS_PLATFORM_WINDOWS) #include #endif #if defined (DNS_PLATFORM_WINDOWS) #pragma comment(lib,"ws2_32.lib") #endif #if defined(DNS_PLATFORM_WINDOWS) #pragma pack(push) #pragma pack(1) #endif struct DnsHeader { unsigned short id; unsigned char rd:1; unsigned char tc:1; unsigned char aa:1; unsigned char opcode:4; unsigned char qr:1; unsigned char rcode:4; unsigned char z:3; unsigned char ra:1; unsigned short qdCount; unsigned short anCount; unsigned short nsCount; unsigned short arCount; } #if defined (DNS_PLATFORM_LINUX) __attribute__((packed)) #endif ; #if defined(DNS_PLATFORM_WINDOWS) #pragma pack(pop) #endif #define COMMENT_MAX 64 #define SYMBOL_MAX 8 struct QueryType { unsigned short q_class; unsigned char mnemonic_symbol[SYMBOL_MAX]; unsigned char comment[COMMENT_MAX]; }; static unsigned short packet_id = DNS_PACKET_ID; static inline void exchangeByteOrder(unsigned char *byte) { unsigned char temp; temp = byte[0]; byte[0] = byte[1]; byte[1] = temp; } static inline void exchangeWordOrder(unsigned int *word) { unsigned char temp; unsigned char *ptr[4]; ptr[0] = (unsigned char *)(word); ptr[1] = ((unsigned char *)(word))+1; ptr[2] = ((unsigned char *)(word))+2; ptr[3] = ((unsigned char *)(word))+3; temp = *ptr[0]; *ptr[0] = *ptr[3]; *ptr[3] = temp; temp = *ptr[1]; *ptr[1] = *ptr[2]; *ptr[2] = temp; } static int dns_packet_hdr_construct(struct DnsHeader *header) { header->id = packet_id; header->rd = 1; header->tc = 0; header->aa = 0; header->opcode = 0; header->qr = 0; header->rcode = 0; header->z = 0; header->ra = 0; header->qdCount = 1; header->anCount = 0; header->nsCount = 0; header->arCount = 0; exchangeByteOrder((unsigned char *)&header->anCount); exchangeByteOrder((unsigned char *)&header->qdCount); exchangeByteOrder((unsigned char *)&header->nsCount); exchangeByteOrder((unsigned char *)&header->arCount); return sizeof(struct DnsHeader); } static int dns_packet_body_construct(char *packet_body, const char *buf) { const char *domain_ptr = buf ; int i = 0, j = 0; while(*domain_ptr != '') { if(*domain_ptr != '.') { packet_body[i+1] = *domain_ptr; j++; } else { packet_body[i-j] = j; j = 0; } i++; domain_ptr++; } packet_body[i-j] = j; packet_body[++i] = 0; packet_body[++i] = 0x00; packet_body[++i] = 0x01; packet_body[++i] = 0x00; packet_body[++i] = 0x01; return i; } static const char *dns_packet_question_resovle(const struct DnsHeader *packet, const char *body) { const char *ptr; unsigned char cnt = 0; unsigned char len = 0; (void)len; DNS_PRINTF("the queston: n"); DNS_PRINTF("Name : "); ptr = body; while(*ptr != '') { cnt = *ptr++; while(cnt--) { DNS_ORG_PRINTF("%c", *ptr); ptr++; } if(*ptr != '') { DNS_ORG_PRINTF("."); } } DNS_ORG_PRINTF("n"); ptr++; DNS_PRINTF("Type : "); if(*ptr == 0x0 && *(ptr+1) == 0x1) { DNS_ORG_PRINTF("Host Addressn"); }else{ DNS_ORG_PRINTF("%02x%02xn",*ptr, *(ptr+1)); } ptr += 2; DNS_PRINTF("Class : "); if(*ptr == 0x0 && *(ptr+1) == 0x01) { DNS_ORG_PRINTF("INn"); } else { DNS_ORG_PRINTF("%02x%02xn", *ptr, *(ptr+1)); } ptr += 2; return ptr; } static const char *dns_packet_name_resovle(const struct DnsHeader *packet, const char *name) { const char *p; unsigned char cnt,len; p = name; len = 0; (void)len; while(*p != '') { if(((*p & 0xC0) >> 6) == 0x3) { p = (const char *)packet + (((*p & 0x3F)<<8) | *(p+1)); p = dns_packet_name_resovle(packet, p); } else { cnt = *p++; while(cnt--) { DNS_ORG_PRINTF("%c",*p); p++; } if(*p != '') { DNS_ORG_PRINTF("."); } } } return p; } static const char *dns_packet_rr_resovle(const struct DnsHeader *packet, const char *rr, unsigned short rlen, char *ipout, int *iplen) { const char *p; unsigned char cnt = 0; if (*iplen) { *iplen = -1; } if(rlen == 0x04) { DNS_ORG_PRINTF("%d.%d.%d.%dn", (unsigned char)rr[0],(unsigned char)rr[1], (unsigned char)rr[2],(unsigned char)rr[3]); if (ipout && iplen) { snprintf(ipout, *iplen, "%d.%d.%d.%d", (unsigned char)rr[0],(unsigned char)rr[1], (unsigned char)rr[2],(unsigned char)rr[3]); *iplen = strlen(ipout); } return (rr += 4); } while(rlen > 0) { if(((*rr & 0xC0) >> 6) == 0x03) { p = (const char *)packet + ((*rr & 0x3F)<<8 | (*(rr+1))); rlen -= 2; cnt = *p++; rr += 2; } else { p = rr; cnt = *p++; rlen -= ( cnt + 1 ); rr += ( cnt + 1 ); } while(cnt--) { DNS_ORG_PRINTF("%c", *p); p++; } if(rlen != 0) { DNS_ORG_PRINTF("."); } } DNS_ORG_PRINTF("n"); return rr; } static const char *dns_packet_response_resovle(const struct DnsHeader *packet, const char *body, char *ipout, int *iplen) { const char *ptr; unsigned char cnt = 0; unsigned char len = 0; unsigned int ttl = 0; unsigned short rs_len = 0; (void)cnt; (void)len; DNS_PRINTF("the answer:n"); DNS_PRINTF("Name : "); ptr = dns_packet_name_resovle(packet, body); DNS_ORG_PRINTF("%sn", ptr); ptr = body + 2; DNS_PRINTF("Type : "); if((*ptr == 0x00) && (*(ptr+1) == 0x01)) { DNS_ORG_PRINTF("Host Addressn"); } else if ((*ptr == 0x00) && (*(ptr+1) == 0x05)) { DNS_ORG_PRINTF("CNAME (Canonical name for an alias)n"); } else { DNS_ORG_PRINTF("%02x%02xn",*ptr, *(ptr+1)); } ptr += 2; DNS_PRINTF("Class : "); if(*ptr == 0x00 && *(ptr+1) == 0x01) { DNS_ORG_PRINTF("INn"); } else { DNS_ORG_PRINTF("%02x%02xn", *ptr, *(ptr+1)); } ptr += 2; ttl = *(unsigned int *)(ptr); exchangeWordOrder(&ttl); DNS_PRINTF("TTL(Time To live): +++++++%dn", ttl); ptr += 4; rs_len = *(unsigned short*)(ptr); DNS_PRINTF("rs_len=%d=0x%04Xn", rs_len, rs_len); exchangeByteOrder((unsigned char *)&rs_len); DNS_PRINTF("Data length: +++++++++++++%dn", rs_len ); ptr += 2; DNS_PRINTF("data: "); ptr = dns_packet_rr_resovle(packet, ptr, rs_len, ipout, iplen); return ptr; } static const char * dns_packet_authority_resovle(const struct DnsHeader *packet, const char *body) { return 0; } static const char * dns_packet_addtional_resovle(const struct DnsHeader *packet, const char *body) { return 0; } static int dns_packet_body_resolve(const char *packet, char ipout[][DNS_MAX_IP_LEN]) { const struct DnsHeader *header; const char *ptr; unsigned short cnt; header = (struct DnsHeader *)packet; ptr = (const char *)header+sizeof(struct DnsHeader); DNS_PRINTF("Queston count is %dn", header->qdCount); DNS_PRINTF("Answer count is %dn", header->anCount); DNS_PRINTF("Authoritative count is %dn", header->nsCount); DNS_PRINTF("Addtional count is %dn", header->arCount); if(header->qdCount > 0) { cnt = header->qdCount; while(cnt--){ DNS_PRINTF("n=================question_resovle %d==============n", header->qdCount-cnt); ptr = dns_packet_question_resovle(header, ptr); } } if(header->anCount > 0) { char tmpip[DNS_MAX_IP_LEN] = {0}; int tmpiplen = sizeof(tmpip); int ipindex = 0; cnt = header->anCount; while(cnt--) { DNS_PRINTF("n=================response_resovle %d=============n", header->anCount-cnt); ptr = dns_packet_response_resovle(header, ptr, tmpip, &tmpiplen); if (tmpiplen > 0 && ipindex < DNS_MAX_IP_CNT) { strcpy(ipout[ipindex++], tmpip); } } } if(header->nsCount > 0) { cnt = header->nsCount; while(cnt--) { DNS_PRINTF("n================authority_resovle %d=============n", header->nsCount-cnt); ptr = dns_packet_authority_resovle(header, ptr); } } if(header->arCount > 0) { cnt = header->arCount; while(cnt--) { DNS_PRINTF("n================addtional_resovle %d=============n", header->arCount-cnt); ptr = dns_packet_addtional_resovle(header, ptr); } } return 0; } static int dns_packet_hdr_resolve(char *rbuf, int rlen, char ipout[][DNS_MAX_IP_LEN]) { struct DnsHeader *header; header = (struct DnsHeader *)(rbuf); if(header->id != packet_id) { DNS_PRINTF("Packet don't match usn"); return -1; } if(header->qr != 1) { DNS_PRINTF("Not a response packetn"); return -1; } exchangeByteOrder((unsigned char *)&header->anCount); exchangeByteOrder((unsigned char *)&header->qdCount); exchangeByteOrder((unsigned char *)&header->nsCount); exchangeByteOrder((unsigned char *)&header->arCount); if(header->anCount == 0) { DNS_PRINTF("Answer count is zeron"); return -1; } DNS_PRINTF("Now ,we get a valid DNS response, Resovle Now.......n"); dns_packet_body_resolve((char *)header, ipout); return 0; } static int set_recv_timeout(int sockfd, int timeouts) { struct timeval tv; int ret = 0; (void)ret; tv.tv_sec = timeouts; tv.tv_usec = 0; ret = setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval)); DNS_PRINTF("%s, timeouts=%d, sockfd=%d, ret=%dn", __func__, timeouts, sockfd, ret); return ret; } static int get_ip_by_dns(const char *dns_server, const char *dns_addr, char ipout[DNS_MAX_IP_CNT][DNS_MAX_IP_LEN], int timeouts, int max_try_cnt) { #if defined (DNS_PLATFORM_WINDOWS) SOCKET soc; SOCKADDR_IN addr,raddr; WSADATA wsa; #endif #if defined (DNS_PLATFORM_LINUX) int soc; struct sockaddr_in addr,raddr; #endif char rbuf[BUFSIZ],sbuf[BUFSIZ]; struct DnsHeader header; int len = 0; int rlen = 0, addr_len,try_cnt = 0; int wlen = 0; int ret = -1; #if defined (DNS_PLATFORM_WINDOWS) if(WSAStartup(MAKEWORD(2,2), &wsa) != 0) { DNS_PRINTF("WSAStartup : Error code %dn",WSAGetLastError()); ret = -1; goto exit_entry; } #endif if((soc = socket(AF_INET, SOCK_DGRAM, 0)) <= 0) { DNS_PRINTF("Create socket fail!n"); ret = -2; goto exit_entry; } set_recv_timeout(soc, timeouts); addr_len = sizeof(raddr); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(dns_server); addr.sin_port = htons(DNS_SERVER_PORT); dns_packet_hdr_construct(&header); memcpy(sbuf, (char *)&header, sizeof(header)); len = dns_packet_body_construct(sbuf + sizeof(header), dns_addr); while(++try_cnt <= max_try_cnt) { DNS_PRINTF("try to request DNS_server(%s) to resolve the DNS_addr(%s) (%d)n", dns_server, dns_addr, try_cnt); wlen = sendto(soc, sbuf, len + sizeof(header) + 1, 0, (struct sockaddr *)&addr, sizeof(addr)); DNS_PRINTF("DNS send to ret = %dn", wlen); DNS_HEXDUMP("dns send: ", sbuf, len + sizeof(header) + 1); if(wlen < 0) { #if defined (DNS_PLATFORM_WINDOWS) DNS_PRINTF("sendto Error(%d)n", WSAGetLastError()); #endif #if defined (DNS_PLATFORM_LINUX) DNS_PRINTF("sendto Error(%d)=%sn", errno, strerror(errno)); #endif ret = -3; continue; } DNS_PRINTF("waiting to response... BUFSIZ=%dn", BUFSIZ); #if defined (DNS_PLATFORM_WINDOWS) rlen = recvfrom(soc, rbuf, BUFSIZ, 0, (struct sockaddr *)&raddr, &addr_len); DNS_PRINTF("DNS recv from ret = %dn", rlen); DNS_HEXDUMP("dns recv: ", rbuf, rlen); if(rlen < 0){ DNS_PRINTF("recvfrom Error Error(%d)n", WSAGetLastError()); ret = -4; continue; } #endif #if defined (DNS_PLATFORM_LINUX) rlen = recvfrom(soc, rbuf, BUFSIZ, 0, (struct sockaddr *)&raddr, (socklen_t *)&addr_len); DNS_PRINTF("DNS recv from ret = %dn", rlen); if(rlen < 0) { DNS_PRINTF("recvfrom Error Error(%d)=%sn", errno, strerror(errno)); ret = -4; continue; } #endif else { DNS_HEXDUMP("dns recv: ", rbuf, rlen); if(dns_packet_hdr_resolve(rbuf, rlen, ipout) == 0) { DNS_PRINTF("OK!!! DNS request successfullyn"); ret = 0; goto exit_entry; } } } if(try_cnt == max_try_cnt) { DNS_PRINTF("try %d times total, but still can't resolve the [%s]n", DNS_MAX_TRY_CNT, dns_addr); ret = -5; goto exit_entry; } exit_entry: #if defined (DNS_PLATFORM_WINDOWS) WSACleanup(); //clean up Ws2_32.dll #endif #if defined (DNS_PLATFORM_LINUX) shutdown(soc,SHUT_RDWR); close(soc); #endif return ret; } //send dns parse request packet by udp !!! static int get_host_ip_by_url_default_dns_server(const char *url, char *ip, int max_try_cnt) { int i = 0; int j = 0; int cnt = ARRAY_SIZE(g_dns_server_list); const char *dns_server = NULL; const char *dns_addr = url; char ip_out[DNS_MAX_IP_CNT][DNS_MAX_IP_LEN] = {0}; int ret = -1; for (i = 0; i < cnt; i++) { memset(ip_out, 0, sizeof(ip_out)); dns_server = g_dns_server_list[i]; ret = get_ip_by_dns(dns_server, dns_addr, ip_out, DNS_TIMEOUTS, max_try_cnt); if (ret == 0) { for (j = 0; j < DNS_MAX_IP_CNT; j++) { DNS_PRINTF("ip out %d: %sn", j, ip_out[j]); } if (strlen(ip_out[0]) != 0) { DNS_PRINTF("get valid ip addr %s...n", ip_out[0]); strcpy(ip, ip_out[0]); } break; } DNS_PRINTF("dns ret=%dn", ret); } return ret; } static int get_host_ip_by_url_fixed_dns_server(const char *dns_server, const char *url, char *ip, int max_try_cnt) { int i = 0; const char *dns_addr = url; char ip_out[DNS_MAX_IP_CNT][DNS_MAX_IP_LEN] = {0}; int ret = -1; DNS_PRINTF("%s() ... fixed dns-server=%sn", __func__, dns_server); memset(ip_out, 0, sizeof(ip_out)); ret = get_ip_by_dns(dns_server, dns_addr, ip_out, DNS_TIMEOUTS, max_try_cnt); if (ret == 0) { for (i = 0; i < DNS_MAX_IP_CNT; i++) { DNS_PRINTF("ip out %d: %sn", i, ip_out[i]); } if (strlen(ip_out[0]) != 0) { DNS_PRINTF("get valid ip addr %s...n", ip_out[0]); strcpy(ip, ip_out[0]); } } DNS_PRINTF("dns ret=%dn", ret); return ret; } static int get_host_ip_by_url(const char *dns_server, const char *url, char *ip) { if (dns_server) { return get_host_ip_by_url_fixed_dns_server(dns_server, url, ip, DNS_MAX_TRY_CNT); } else { return get_host_ip_by_url_default_dns_server(url, ip, DNS_MAX_TRY_CNT); } } typedef unsigned int TCPIP_IPADDR_T; static struct hostent *my_gethostbyname_inner(const char *dns_server, const char *name) { //TCPIP_HOST_HANDLE host_handle; static TCPIP_IPADDR_T ipaddr = 0; static char sipaddr[40] = {0}, *psipaddr[2] = {(char *) &ipaddr, 0}, *paliases[2] = {0, 0}; static struct hostent hent = {0}; struct hostent *h = NULL; int ret; ret = get_host_ip_by_url(dns_server, name, sipaddr); if (ret) { DNS_PRINTF("dns failrn"); return NULL; } ipaddr = (TCPIP_IPADDR_T)inet_addr((const char *)sipaddr); DNS_PRINTF("ipaddr:%d ==> sipaddr:%srn", ipaddr, sipaddr); hent.h_name = (char *)name; hent.h_addrtype = AF_INET; hent.h_length = 4; hent.h_addr_list = (char **)&psipaddr[0]; hent.h_aliases = (char **)&paliases[0]; h = &hent; return h; } struct hostent *my_gethostbyname(const char *name) { return my_gethostbyname_inner(NULL, name); } struct hostent *my_gethostbyname_with_dns_server(const char *dns_server, const char *name) { return my_gethostbyname_inner(dns_server, name); }
对于导出的头文件定义:
#ifndef __DNS_H__
#define __DNS_H__
#ifdef __cplusplus
extern "C" {
#endif
#include
#include
extern struct hostent *my_gethostbyname(const char *name);
extern struct hostent *my_gethostbyname_with_dns_server(const char *dns_server, const char *name);
#ifdef __cplusplus
}
#endif
#endif
4 测试案例验证
本代码在Ubuntu x64平台使用gcc编译、验证,测试代码如下:
#include#include #include #include #include #include "dns.h" #ifndef ARRAY_SIZE #define ARRAY_SIZE(a) sizeof(a)/sizeof(a[0]) #endif void http_get_ip_addr(const char *dns_server, const char *domain, char *ip_addr) { int32_t i; struct hostent *host = NULL; if (dns_server) { host = (struct hostent *)my_gethostbyname_with_dns_server(dns_server, domain); } else { host = (struct hostent *)my_gethostbyname(domain); } for (i = 0; host->h_addr_list[i]; i++) { strcpy(ip_addr, inet_ntoa( * (struct in_addr*) host->h_addr_list[i])); break; } } int main(int argc, const char *argv[]) { const char *dns_server_list[] = { "", "8.8.8.8", "114.114.114.114", }; const char *domain_list[] = { "www.baidu.com", "www.hao123.com", }; const char *dns_server = NULL; char ip_addr[128] = {0}; int i, j; for (i = 0; i < ARRAY_SIZE(domain_list); i++) { for (j = 0; j < ARRAY_SIZE(dns_server_list); j++) { if (!strlen(dns_server_list[j])) { dns_server = NULL; } else { dns_server = dns_server_list[j]; } memset(ip_addr, 0, sizeof(ip_addr)); http_get_ip_addr(dns_server, domain_list[i], ip_addr); printf("DNS resolve %-32s by %-32s => %-32sn", domain_list[i], dns_server ? dns_server : "default", ip_addr); } } return 0; }
测试代码中使用了不同的域名服务器解析不同的域名主机,均得到了正常的响应。
5 更多分享欢迎关注我的github仓库01workstation,日常分享一些开发笔记和项目实战,欢迎指正问题。
同时也非常欢迎关注我的CSDN主页和专栏:
【http://yyds.recan-li.cn】
【C/C++语言编程专栏】
【GCC专栏】
【信息安全专栏】
【网络编程专利】
【RT-Thread开发笔记】
【freeRTOS开发笔记】
有问题的话,可以跟我讨论,知无不答,谢谢大家。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)