网络通讯开发系列如何使用C语言编程通过UDP通讯解析域名
Posted 架构师李肯
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络通讯开发系列如何使用C语言编程通过UDP通讯解析域名相关的知识,希望对你有一定的参考价值。
1 前言
相信大家在平时的网络开发中,对域名的接触一定非常多。
对于域名
的定义,
域名(英语:Domain Name),又称网域,是由一串用点分隔的名字组成的Internet上某一台计算机或计算机组的名称,用于在数据传输时对计算机的定位标识(有时也指地理位置)
。
通俗来说,某个域名就是代表网络上的某个服务器主机,而根据网络协议分层只有IP层,并没有域名层
,所以这里就涉及到域名到IP地址的转换
,也就是我们常说的域名解析
。
在平时开发中,我们都是直接调用系统接口实现域名解析,那么我们有没有方法直接根据网络协议组包来完成域名解析呢?
本文就这个问题,将会给出答案。
2 知识点分析
2.1 网络分层
我们熟知的网络七层协议模型
如下图所示:
每一层都都有自己的独立的作用:
在我们这章节中重点关注网络层。
2.2 DNS协议
DNs协议的定义:
我们常说的DNS解析,实则就是通过DNS协议,发起解析请求,从域名服务器上取得响应的。
DNS报文分为查询请求报文和查询相应报文,两类报文结构基本相同,结构如下:
详细可参见这里。
3 代码实现
直接上实现代码:
#include <stdio.h>
#include <stdint.h>
#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
/*
Add your dns server here, but considering the IP white list !
*/
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 <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#endif
#if defined (DNS_PLATFORM_WINDOWS)
#include <winsock2.h>
#endif
#if defined (DNS_PLATFORM_WINDOWS)
#pragma comment(lib,"ws2_32.lib")
#endif
/* 域名查询请求报文头定义,1字节对齐*/
#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; /* 0:查询,1:响应 */
unsigned char rcode:4; /* 响应吗:0没有出错 */
unsigned char z:3; /* 保留将来使用 */
unsigned char ra:1; /* DNS服务器是否支持递归解析 */
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;
/* 构造 DNS 请求报文包头(12 bytes) */
static int dns_packet_hdr_construct(struct DnsHeader *header)
/* 定义DNS查询报文请求头,没有使用的字段必须设置为0 */
header->id = packet_id; /* 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;
/* 16 bits 的本地格式转换为网络格式 */
exchangeByteOrder((unsigned char *)&header->anCount);
exchangeByteOrder((unsigned char *)&header->qdCount);
exchangeByteOrder((unsigned char *)&header->nsCount);
exchangeByteOrder((unsigned char *)&header->arCount);
return sizeof(struct DnsHeader);
/* 构造 NDS 请求报文内容 */
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 != '\\0')
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; /* null */
packet_body[++i] = 0x00;
packet_body[++i] = 0x01; /* 查询类型 为 1 : IPv4地址 */
packet_body[++i] = 0x00; /* 查询类 为 1 : Inetnet数据 */
packet_body[++i] = 0x01;
return i;
/**
* 解析 问题
* packet:数据包头部指针
* body: 指向问题域开始部分
*/
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 != '\\0')
cnt = *ptr++;
while(cnt--)
DNS_ORG_PRINTF("%c", *ptr);
ptr++;
if(*ptr != '\\0')
DNS_ORG_PRINTF(".");
DNS_ORG_PRINTF("\\n");
ptr++; /* skip null */
DNS_PRINTF("Type : ");
if(*ptr == 0x0 && *(ptr+1) == 0x1)
DNS_ORG_PRINTF("Host Address\\n");
else
DNS_ORG_PRINTF("%02x%02x\\n",*ptr, *(ptr+1));
ptr += 2; /* skip Type area */
DNS_PRINTF("Class : ");
if(*ptr == 0x0 && *(ptr+1) == 0x01)
DNS_ORG_PRINTF("IN\\n");
else
DNS_ORG_PRINTF("%02x%02x\\n", *ptr, *(ptr+1));
ptr += 2; /* skip Class area */
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 != '\\0')
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 != '\\0')
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) /* *clear ip length */
*iplen = -1;
if(rlen == 0x04) /* IP addr */
DNS_ORG_PRINTF("%d.%d.%d.%d\\n", (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;
/**
* 解析 应答
* packet:数据包头部指针
* body: 指向应答域开始部分
*/
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("%s\\n", ptr);
ptr = body + 2; /* skip null ,Get Type (2 bytes) */
DNS_PRINTF("Type : ");
if((*ptr == 0x00) && (*(ptr+1) == 0x01))
DNS_ORG_PRINTF("Host Address\\n");
else if ((*ptr == 0x00) && (*(ptr+1) == 0x05))
DNS_ORG_PRINTF("CNAME (Canonical name for an alias)\\n");
else
DNS_ORG_PRINTF("%02x%02x\\n",*ptr, *(ptr+1));
ptr += 2; /* skip Type area , Get Class (2 bytes) */
DNS_PRINTF("Class : ");
if(*ptr == 0x00 && *(ptr+1) == 0x01)
DNS_ORG_PRINTF("IN\\n");
MicroPython ESP32 入网和udp数据接收通讯示例