STM32F103RC W5500 DNS Client端实现

12 篇文章 7 订阅
1 篇文章 0 订阅

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地址输入到浏览器地址栏中,就可以访问百度的网页。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值