原创声明: 本文分析属于 tiandimz 所有,转载请注明出处!谢谢!
本文源码版本为 LWIP 1.4.0
下面的代码是 dns.c 的全部代码!主要完成ipv4的客户端域名解析部分,不涉及复杂的服务器端的处理过程。
下面进行简要分析,由于LWIP是轻量级的IP协议栈,所以对于DNS的要求也很简单,主要是针对客户端测的实现,能够提供API接口,通过调用接口可以实现DNS查询请求,并能够解析DNS响应,并建立本地缓存!
主要涉及的文件有 dns.c dns.h netdb.c netdb.h ip_addr.h ip_addr.c
对于复杂的域名解析服务器的程序,可以参考 bind 9.
dns报文格式请见:
#include "lwip/opt.h" //LWIP的配置选项文件
#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
#include "lwip/udp.h"
#include "lwip/mem.h"
#include "lwip/memp.h"
#include "lwip/dns.h" //dns的头文件
#include <string.h>
/** DNS server IP address opendns 是一个开放dns服务,我们的请求也是发往这个服务器地址!*/
#ifndef DNS_SERVER_ADDRESS
#define DNS_SERVER_ADDRESS(ipaddr) (ip4_addr_set_u32(ipaddr, ipaddr_addr("208.67.222.222"))) /* resolver1.opendns.com */
#endif
/** DNS server port address */
#ifndef DNS_SERVER_PORT
#define DNS_SERVER_PORT 53 // DNS通过 udp承载,主要利用 53端口
#endif
/** DNS maximum number of retries when asking for a name, before "timeout". */
#ifndef DNS_MAX_RETRIES
#define DNS_MAX_RETRIES 4 //dns请求超时的重试最大次数 4次
#endif
/** DNS resource record max. TTL (one week as default) */
#ifndef DNS_MAX_TTL
#define DNS_MAX_TTL 604800 //源记录的最大生存时间
#endif
/* DNS protocol flags DNS的标记位 */
#define DNS_FLAG1_RESPONSE 0x80
#define DNS_FLAG1_OPCODE_STATUS 0x10
#define DNS_FLAG1_OPCODE_INVERSE 0x08
#define DNS_FLAG1_OPCODE_STANDARD 0x00
#define DNS_FLAG1_AUTHORATIVE 0x04
#define DNS_FLAG1_TRUNC 0x02
#define DNS_FLAG1_RD 0x01
#define DNS_FLAG2_RA 0x80
#define DNS_FLAG2_ERR_MASK 0x0f
#define DNS_FLAG2_ERR_NONE 0x00
#define DNS_FLAG2_ERR_NAME 0x03
/* DNS protocol states */
#define DNS_STATE_UNUSED 0
#define DNS_STATE_NEW 1
#define DNS_STATE_ASKING 2
#define DNS_STATE_DONE 3
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/bpstruct.h"
#endif
PACK_STRUCT_BEGIN
/** DNS message header DNS的消息头部 根据DNS的RFC协议 1035 描述进行设计的,采用结构体和位域来实现*/
struct dns_hdr {
PACK_STRUCT_FIELD(u16_t id);
PACK_STRUCT_FIELD(u8_t flags1);
PACK_STRUCT_FIELD(u8_t flags2);
PACK_STRUCT_FIELD(u16_t numquestions);
PACK_STRUCT_FIELD(u16_t numanswers);
PACK_STRUCT_FIELD(u16_t numauthrr);
PACK_STRUCT_FIELD(u16_t numextrarr);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/epstruct.h"
#endif
#define SIZEOF_DNS_HDR 12
/** DNS query message structure.
No packing needed: only used locally on the stack. dns消息查询结构体 */
struct dns_query {
/* DNS query record starts with either a domain name or a pointer
to a name already present somewhere in the packet. */
u16_t type;
u16_t cls;
};
#define SIZEOF_DNS_QUERY 4
/** DNS answer message structure.
No packing needed: only used locally on the stack. DNS应答消息报文格式 */
struct dns_answer {
/* DNS answer record starts with either a domain name or a pointer
to a name already present somewhere in the packet. */
u16_t type;
u16_t cls;
u32_t ttl;
u16_t len;
};
#define SIZEOF_DNS_ANSWER 10
/** DNS table entry DNS的表格 */
struct dns_table_entry {
u8_t state;
u8_t numdns;
u8_t tmr;
u8_t retries;
u8_t seqno;
u8_t err;
u32_t ttl;
char name[DNS_MAX_NAME_LENGTH];//DNS_MAX_NAME_LENGTH 256,域名字符串最大长度 256
ip_addr_t ipaddr;
/* pointer to callback on DNS query done callback 函数指针*/
dns_found_callback found;
void *arg;
};
#if DNS_LOCAL_HOSTLIST
#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
/** Local host-list. For hostnames in this list, no
* external name resolution is performed */
static struct local_hostlist_entry *local_hostlist_dynamic; //本地的主机列表
#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
/** Defining this allows the local_hostlist_static to be placed in a different
* linker section (e.g. FLASH) */
#ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE
#define DNS_LOCAL_HOSTLIST_STORAGE_PRE static
#endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */
/** Defining this allows the local_hostlist_static to be placed in a different
* linker section (e.g. FLASH) */
#ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST
#define DNS_LOCAL_HOSTLIST_STORAGE_POST
#endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */
DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[]
DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT;
#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
static void dns_init_local(); //dns初始化本地列表
#endif /* DNS_LOCAL_HOSTLIST */
/* forward declarations 函数声明包括:dns接受响应报文处理函数和dns检查本地列表函数*/
static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
static void dns_check_entries(void);
/*-----------------------------------------------------------------------------
* Globales 一些全局变量
*----------------------------------------------------------------------------*/
/* DNS variables */
static struct udp_pcb *dns_pcb; //因为用UDP承载,所有需要在UDP 的pcb中记录
static u8_t dns_seqno; //
static struct dns_table_entry dns_table[DNS_TABLE_SIZE]; //DNS_TABLE_SIZE 为 4 ,只存4个历史域名记录
static ip_addr_t dns_servers[DNS_MAX_SERVERS]; //DNS_MAX_SERVERS为 2 ,主DNS和辅DNS
/** Contiguous buffer for processing responses 处理响应的缓存 */
static u8_t dns_payload_buffer[LWIP_MEM_ALIGN_BUFFER(DNS_MSG_SIZE)];
static u8_t* dns_payload;
/**
* Initialize the resolver: set up the UDP pcb and configure the default server
* (DNS_SERVER_ADDRESS).初始化解析,建立UDP pcb 配置默认的服务器地址
*/
void
dns_init()
{
ip_addr_t dnsserver; /* 声明dns服务器地址 */
dns_payload = (u8_t *)LWIP_MEM_ALIGN(dns_payload_buffer); /* 分配内存并对齐 */
/* initialize default DNS server address */
DNS_SERVER_ADDRESS(&dnsserver); /* 设置DNS服务器地址 */
LWIP_DEBUGF(DNS_DEBUG, ("d