STM32F103RC W5500 DNS Client端实现
程序员文章站
2024-02-23 22:30:22
...
DNS即域名解析服务。
当我们想用W5500访问一个接口的时候,只给了接口的URL地址,但W5500实际通讯的时候需要用到IP地址,那么通过DNS域名解析服务,就可以实现将域名解析成接口对应的IP地址。
DNS是基于UDP通信基础上封装了自己的协议报文的。关于DNS的协议报文,请参考 《DNS(域名解析协议)详解》。
STM32 W5500实现DNS Client 需要以下几点:
1、STM32 W5500基础配置,使得 W5500入网,可以PING通。《STM32F103RC驱动W5500入网,并可ping通》
2、STM32 W5500 UDP发送数据测试没问题。《STM32 W5500 UDP Client 发送数据》
3、DNS协议报文包装及UDP发送(DNS Server 端口固定为53,DNS Server的IP地址可以选择一个合适的,我选的是 114.114.114.114),以及UDP接收DNS Server返回的报文并解析。
DNS Client的C代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "stm32f10x.h"
#include "utility.h"
#include "w5500.h"
#include "socket.h"
#include "dns.h"
u16 MSG_ID = 0x1122;
int dns_makequery(u16 op, u8 * name, u8 * buf, u16 len)
{
u8 *cp;
u8 *cp1;
// int8 sname[MAX_DNS_BUF_SIZE];
u8 *dname;
u16 p;
u16 dlen;
cp = buf;
MSG_ID++;
*(u16*)&cp[0] = htons(MSG_ID);
p = (op << 11) | 0x0100; /* Recursion desired */
*(u16*)&cp[2] = htons(p);
*(u16*)&cp[4] = htons(1);
*(u16*)&cp[6] = htons(0);
*(u16*)&cp[8] = htons(0);
*(u16*)&cp[10]= htons(0);
cp += sizeof(u16)*6;
// strcpy(sname, name);
dname = name;
dlen = strlen((char*)dname);
for (;;)
{
/* Look for next dot */
cp1 = (unsigned char*)strchr((char*)dname, '.');
if (cp1) len = cp1 - dname; /* More to come */
else len = dlen; /* Last component */
*cp++ = len; /* Write length of component */
if (len == 0) break;
/* Copy component up to (but not including) dot */
strncpy((char *)cp, (char*)dname, len);
cp += len;
if (!cp1)
{
*cp++ = 0; /* Last one; write null and finish */
break;
}
dname += len+1;
dlen -= len+1;
}
*(u16*)&cp[0] = htons(0x0001); /* type */
*(u16*)&cp[2] = htons(0x0001); /* class */
cp += sizeof(u16)*2;
return ((int)((u32)(cp) - (u32)(buf)));
}
int parse_name(u8 * msg, u8 * compressed, /*char * buf,*/ u16 len)
{
u16 slen; /* Length of current segment */
u8 * cp;
u16 clen = 0; /* Total length of compressed name */
u16 indirect = 0; /* Set if indirection encountered */
u16 nseg = 0; /* Total number of segments in name */
u8 name[MAX_DNS_BUF_SIZE];
u8 *buf;
buf = name;
cp = compressed;
for (;;)
{
slen = *cp++; /* Length of this segment */
if (!indirect) clen++;
if ((slen & 0xc0) == 0xc0)
{
if (!indirect)
clen++;
indirect = 1;
/* Follow indirection */
cp = &msg[((slen & 0x3f)<<8) + *cp];
slen = *cp++;
}
if (slen == 0) /* zero length == all done */
break;
len -= slen + 1;
if (len <= 0) return -1;
if (!indirect) clen += slen;
while (slen-- != 0) *buf++ = (int8)*cp++;
*buf++ = '.';
nseg++;
}
if (nseg == 0)
{
/* Root name; represent as single dot */
*buf++ = '.';
len--;
}
*buf++ = '\0';
len--;
return clen; /* Length of compressed message */
}
u8 * dns_question(u8 * msg, u8 * cp)
{
int16 len;
// int8 xdata name[MAX_DNS_BUF_SIZE];
len = parse_name(msg, cp, /*name,*/ MAX_DNS_BUF_SIZE);
if (len == -1) return 0;
cp += len;
cp += 2; /* type */
cp += 2; /* class */
return cp;
}
u8 * dns_answer(u8 * msg, u8 * cp, u8 *out_ip)
{
int16 len, type;
// int8 xdata name[MAX_DNS_BUF_SIZE];
len = parse_name(msg, cp, /*name,*/ MAX_DNS_BUF_SIZE);
if (len == -1) return 0;
cp += len;
type = ntohs(*((u16*)&cp[0]));
cp += 2; /* type */
cp += 2; /* class */
cp += 4; /* ttl */
cp += 2; /* len */
switch (type)
{
case TYPE_A:
out_ip[0] = *cp++;
out_ip[1] = *cp++;
out_ip[2] = *cp++;
out_ip[3] = *cp++;
break;
case TYPE_CNAME:
case TYPE_MB:
case TYPE_MG:
case TYPE_MR:
case TYPE_NS:
case TYPE_PTR:
/* These types all consist of a single domain name */
/* convert it to ascii format */
len = parse_name(msg, cp, /*name,*/ MAX_DNS_BUF_SIZE);
if (len == -1) return 0;
cp += len;
break;
case TYPE_HINFO:
len = *cp++;
cp += len;
len = *cp++;
cp += len;
break;
case TYPE_MX:
cp += 2;
/* Get domain name of exchanger */
len = parse_name(msg, cp,/* name,*/ MAX_DNS_BUF_SIZE);
if (len == -1) return 0;
cp += len;
break;
case TYPE_SOA:
/* Get domain name of name server */
len = parse_name(msg, cp,/* name,*/ MAX_DNS_BUF_SIZE);
if (len == -1) return 0;
cp += len;
/* Get domain name of responsible person */
len = parse_name(msg, cp,/* name,*/ MAX_DNS_BUF_SIZE);
if (len == -1) return 0;
cp += len;
cp += 4;
cp += 4;
cp += 4;
cp += 4;
cp += 4;
break;
case TYPE_TXT:
/* Just stash */
break;
default:
/* Ignore */
break;
}
return cp;
}
u8 parseMSG(struct dhdr * pdhdr, u8 * pbuf, u8 *out_ip)
{
u16 tmp;
u16 i;
u8 * msg;
u8 * cp;
msg = pbuf;
memset(pdhdr, 0, sizeof(pdhdr));
pdhdr->id = ntohs(*((u16*)&msg[0]));
tmp = ntohs(*((u16*)&msg[2]));
if (tmp & 0x8000) pdhdr->qr = 1;
pdhdr->opcode = (tmp >> 11) & 0xf;
if (tmp & 0x0400) pdhdr->aa = 1;
if (tmp & 0x0200) pdhdr->tc = 1;
if (tmp & 0x0100) pdhdr->rd = 1;
if (tmp & 0x0080) pdhdr->ra = 1;
pdhdr->rcode = tmp & 0xf;
pdhdr->qdcount = ntohs(*((u16*)&msg[4]));
pdhdr->ancount = ntohs(*((u16*)&msg[6]));
pdhdr->nscount = ntohs(*((u16*)&msg[8]));
pdhdr->arcount = ntohs(*((u16*)&msg[10]));
/* Now parse the variable length sections */
cp = &msg[12];
/* Question section */
for (i = 0; i < pdhdr->qdcount; i++)
{
cp = dns_question(msg, cp);
}
/* Answer section */
for (i = 0; i < pdhdr->ancount; i++)
{
cp = dns_answer(msg, cp, out_ip);
}
/* Name server (authority) section */
for (i = 0; i < pdhdr->nscount; i++)
{
;
}
/* Additional section */
for (i = 0; i < pdhdr->arcount; i++)
{
;
}
if(pdhdr->rcode == 0) return 1; // No error
else return 0;
}
u8 func_dns_query(u8* buf_dns, u16 len_buf, u8 *domain_name, u16 query_timeout_ms, u8 sock, u8 *dns_server_ip, u8 *out_ip)
{
u8 dns_ip[4];
u16 len, port, cnt = 0;
struct dhdr dhp;
if(!memcmp(dns_server_ip, "\x00\x00\x00\x00", 4))
{
return 1;//dns server ip not set
}
memset(buf_dns, 0, len_buf);
for(;;)
{
if(getSn_SR(sock) == SOCK_CLOSED)
{
socket(sock, Sn_MR_UDP, 3000, 0);
}
len = dns_makequery(0, domain_name, buf_dns, MAX_DNS_BUF_SIZE);
sendto(sock, buf_dns, len, dns_server_ip, IPPORT_DOMAIN);
if(getSn_IR(sock) & Sn_IR_RECV)
{
setSn_IR(sock, Sn_IR_RECV);
}
if ((len = getSn_RX_RSR(sock)) > 0)
{
len = recvfrom(sock, buf_dns, len, dns_ip, &port);
if(parseMSG(&dhp, buf_dns, out_ip))
{
close(sock);
return 0;
}
}
cnt += 10;
if(cnt >= query_timeout_ms)
{
break;
}
delay_ms(10);
}
close(sock);
return 3;
}
DNS Client的头文件:
#ifndef _DNS_H_
#define _DNS_H_
#include "types.h"
#include "w5500_conf.h"
extern u8 BUFPUB_DNS[1024];
#define DNS_RESPONSE_TIMEOUT 3 // 3 seconds
#define DNS_RETRY 3//3 times
#define DNS_RET_PROGRESS 0
#define DNS_RET_FAIL 1
#define DNS_RET_SUCCESS 2
#define MAX_DNS_BUF_SIZE 256 /* maximum size of DNS buffer. */
#define INITRTT 2000L /* Initial smoothed response time */
#define MAXCNAME 10 /* Maximum amount of cname recursion */
#define TYPE_A 1 /* Host address */
#define TYPE_NS 2 /* Name server */
#define TYPE_MD 3 /* Mail destination (obsolete) */
#define TYPE_MF 4 /* Mail forwarder (obsolete) */
#define TYPE_CNAME 5 /* Canonical name */
#define TYPE_SOA 6 /* Start of Authority */
#define TYPE_MB 7 /* Mailbox name (experimental) */
#define TYPE_MG 8 /* Mail group member (experimental) */
#define TYPE_MR 9 /* Mail rename name (experimental) */
#define TYPE_NULL 10 /* Null (experimental) */
#define TYPE_WKS 11 /* Well-known sockets */
#define TYPE_PTR 12 /* Pointer record */
#define TYPE_HINFO 13 /* Host information */
#define TYPE_MINFO 14 /* Mailbox information (experimental)*/
#define TYPE_MX 15 /* Mail exchanger */
#define TYPE_TXT 16 /* Text strings */
#define TYPE_ANY 255/* Matches any type */
#define CLASS_IN 1 /* The ARPA Internet */
/* Round trip timing parameters */
#define AGAIN 8 /* Average RTT gain = 1/8 */
#define LAGAIN 3 /* Log2(AGAIN) */
#define DGAIN 4 /* Mean deviation gain = 1/4 */
#define LDGAIN 2 /* log2(DGAIN) */
#define IPPORT_DOMAIN 53
/* Header for all domain messages */
struct dhdr
{
u16 id; /* Identification */
u8 qr; /* Query/Response */
#define QUERY 0
#define RESPONSE 1
u8 opcode;
#define IQUERY 1
u8 aa; /* Authoratative answer */
u8 tc; /* Truncation */
u8 rd; /* Recursion desired */
u8 ra; /* Recursion available */
u8 rcode; /* Response code */
#define NO_ERROR 0
#define FORMAT_ERROR 1
#define SERVER_FAIL 2
#define NAME_ERROR 3
#define NOT_IMPL 4
#define REFUSED 5
u16 qdcount; /* Question count */
u16 ancount; /* Answer count */
u16 nscount; /* Authority (name server) count */
u16 arcount; /* Additional record count */
};
u8 func_dns_query(u8* buf_dns, u16 len_buf, u8 *domain_name, u16 query_timeout_ms, u8 sock, u8 *dns_server_ip, u8 *out_ip);
#endif /* _DNS_H_ */
测试的主函数代码,以解析百度的域名为例:
#ifndef __STM32F10X_H
#define __STM32F10X_H
#include "stm32f10x.h"
#endif
#ifndef __Z_UTIL_TIME_H
#define __Z_UTIL_TIME_H
#include "z_util_time.h"
#endif
#ifndef __Z_HARDWARE_LED_H
#define __Z_HARDWARE_LED_H
#include "z_hardware_led.h"
#endif
#ifndef __Z_HARDWARE_SPI_H
#define __Z_HARDWARE_SPI_H
#include "z_hardware_spi.h"
#endif
#ifndef __Z_HARDWARE_USART2_H
#define __Z_HARDWARE_USART2_H
#include "z_hardware_usart2.h"
#endif
#include "w5500.h"
#include "socket.h"
#include "w5500_conf.h"
#include "dhcp.h"
#include "dns.h"
int main(void)
{
DHCP_Get dhcp_get;
uint8 buffer[1024];
u8 mac[6]={0, 0, 0, 0, 0, 0};
u8 *dn_baidu = (uint8 *)"www.baidu.com";
u8 dns_server_ip[] = {114, 114, 114, 114};
u8 out_ip[4];
init_led();
init_hardware_usart2_dma(9600);
init_system_spi();
func_w5500_reset();
getMacByLockCode(mac);
setSHAR(mac);
sysinit(txsize, rxsize);
setRTR(2000);
setRCR(3);
//DHCP
for(;func_dhcp_get_ip_sub_gw(buffer, sizeof(buffer), 0, mac, &dhcp_get, 1000) != 0;);
if(func_dhcp_get_ip_sub_gw(buffer, sizeof(buffer), 0, mac, &dhcp_get, 1000) == 0)
{
func_usart2_dma_send_bytes(dhcp_get.lip, 4);
setSUBR(dhcp_get.dns);
setGAR(dhcp_get.gw);
setSIPR(dhcp_get.lip);
}
for(;;)
{
delay_ms(500);
//DNS
if(func_dns_query(buffer, sizeof(buffer), dn_baidu, 500, 1, dns_server_ip, out_ip) == 0)
{
func_usart2_dma_send_bytes(out_ip, 4);
}
func_led1_on();
delay_ms(500);
func_led1_off();
delay_ms(500);
}
}
代码中,我先做了DHCP动态获取IP地址《STM32F103RCT6 W5500 DHCP 实现过程分析》
解析百度的域名后,通过串口将获取到的IP地址打印出来(16进制的),如图:
发现,百度的域名解析后,有两个IP地址,转成十进制后,分别是 182.61.200.6 和 182.61.200.7 ,将两个IP地址输入到浏览器地址栏中,就可以访问百度的网页。
推荐阅读
-
STM32F103RC W5500 DNS Client端实现
-
STM32F103RC W5500 NTP获取网络时间实现
-
asp.net core IdentityServer4 实现 Client credentials(客户端凭证)
-
从零开始学习Node.js系列教程四:多页面实现数学运算的client端和server端示例
-
springcloud——客户端Eureka Client实现负载均衡 Ribbon
-
asp.net core IdentityServer4 实现 Client credentials(客户端凭证)
-
golang 实现tcp server端和client端,并计算RTT时间操作
-
Hadoop RPC Client端的简单实现
-
linux网络编程客户端实现client.c
-
从零开始学习Node.js系列教程四:多页面实现数学运算的client端和server端示例