[嵌入式開發模塊]DNS客戶端模塊(基於wiz/W5500官方io庫)

前言

出於同樣的項目需要的原因,我把我的DNS模塊也進一步進行了封裝。基於W5500官方IO庫,提供完成一次域名解析IPv4的整個過程的接口。

先複製黏貼好DNS模塊的代碼:
https://blog.csdn.net/lin_strong/article/details/101915670

代碼中遇到的依賴之類的問題都可以在NTPClient模塊中找到:
https://blog.csdn.net/lin_strong/article/details/100807679

接下來直接上源碼:

代碼

DNSClient.h

/*
******************************************************************************************
*
*                                   DNS CLIENT MODULE
*
* File : DNSClient.h
* By   : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date:  2019/10/15
* Version: V1.1
* History: 2019/09/16  V1.0
*          2019/10/15  V1.1 add bufSize parameter to DnsClient_QueryIPv4 interface
*                           fix serverPort parameter from uint8_t to uint16_t
* NOTE(s): the module is to provide DNS client function based on wiznet io-library.
******************************************************************************************
*/

#ifndef _DNSCLIENT_H
#define _DNSCLIENT_H

/*
*******************************************************************************************
*                                       INCLUDE
*******************************************************************************************
*/

#include "DNS.h"

/*
*******************************************************************************************
*                                     CONFIGURATION
*******************************************************************************************
*/

// to enable debug message
//#define DNSC_DEBUG

// set the wait time of a request
#ifndef DNSC_TIMEOUT_TIME_MS
#define DNSC_TIMEOUT_TIME_MS   1000
#endif

/*
*******************************************************************************************
*                                    PUBLIC INTERFACES
*******************************************************************************************
*/

// description: launch a dns ipv4 standard query(A record) on given socketNum and return the respond if any.
// parameter  : socketNum   the socket number for dns client
//              buf         the buffer for dns client.
//              bufSize     the size of buffer. Should be bigger enough to hold the request
//                          and respond message, or it will fail.
//              serverIp    ip of the dns server.
//              serverPort  udp port of the dns server. 0 if default port(i.e. DNSS_PORT)
//              DName       domain name to query
//              ret         to return the result if success, uint8_t[4]
// return     : TRUE        if success
//              FALSE       if fail
// note       : 
BOOL DnsClient_QueryIPv4(uint8_t socketNum, uint8_t *buf, uint16_t bufSize, uint8_t serverIp[4], 
                  uint16_t serverPort, const char *DName,uint8_t *ret);

#endif

DNSClient.c

/*
******************************************************************************************
*
*                                   DNS CLIENT MODULE
*
* File : DNSClient.c
* By   : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date:  2019/10/15
* Version: V1.1
* History: 
* NOTE(s): 
******************************************************************************************
*/


/*
*******************************************************************************************
*                                       INCLUDES
*******************************************************************************************
*/
#include <string.h>
#include "DNSClient.h"
#include "socket.h"
#include "MyOS.h"

#ifndef DNSC_DEBUG
#undef _DEBUG
#endif

#include "DebugMsg.h"
 
/*
*******************************************************************************************
*                                     CONFIGURATION
*******************************************************************************************
*/

#ifndef DNSC_POLL_INTERVAL_MS
#define DNSC_POLL_INTERVAL_MS  10
#endif
#define DNSC_POLL_CNT ((DNSC_TIMEOUT_TIME_MS) / (DNSC_POLL_INTERVAL_MS))

#define ANYNUM   0x3453

/*
*******************************************************************************************
*                                 LOCAL FUNCTION DECLARATION
*******************************************************************************************
*/

#define socketNum_valid(socketNum)  ((socketNum) < _WIZCHIP_SOCK_NUM_)

/*
*******************************************************************************************
*                                 LOCAL VARIABLE DECLARATION
*******************************************************************************************
*/

static uint16_t _id = ANYNUM;

/*
*******************************************************************************************
*                                       DEBUG STRINGS
*******************************************************************************************
*/

static const char *_str_DNSCErrFormat = "DNSC Err: %s.\r\n";

/*
*******************************************************************************************
*                             PUBLIC INTERFACE IMPLEMENTATIONS
*******************************************************************************************
*/

BOOL DnsClient_QueryIPv4(uint8_t socketNum, uint8_t *buf, uint16_t bufSize, uint8_t serverIp[4],
              uint16_t serverPort, const char *DName,uint8_t *ret){
  int32_t len;
  uint8_t dip[4];
  uint16_t dport;
  uint16_t id;
  DNSHdr hdr;
  DNSRR QueOrRR;
  const uint8_t *p;
  int i;
  BOOL rst = FALSE;
  int remainPollCnt = DNSC_POLL_CNT;
  id = ++_id;
  if(serverIp == NULL || ret == NULL || buf == NULL || DName == NULL || !socketNum_valid(socketNum)){
    _dbg_printf1(_str_DNSCErrFormat, "bad param");
    return FALSE;
  }
  serverPort = (serverPort != 0)? serverPort: DNSS_PORT;
  if(getSn_SR(socketNum) != SOCK_UDP)
    if(socketNum != socket(socketNum, Sn_MR_UDP, 0, 0x00)){
      _dbg_printf1(_str_DNSCErrFormat, "Open socket");
      goto bail;
    };
  len = (int32_t)DnsMsg_printStandardQueryIPv4Message(buf, bufSize, id, DName);
  if(len <= 0){
    _dbg_printf1(_str_DNSCErrFormat, "printf query");
    goto bail;
  }
  len = sendto(socketNum, buf, (uint16_t)len, serverIp, serverPort);
  if(len < 0){
    _dbg_printf1(_str_DNSCErrFormat, "sendto");
    goto bail;
  }
  _dbg_printf0("DNSC :wating for respond\r\n");
  do{
    MyOS_DlyHMSM(0,0,0,DNSC_POLL_INTERVAL_MS);
    if(getSn_RX_RSR(socketNum) <= 0)
      continue;
    len = recvfrom(socketNum, buf, bufSize, dip, &dport);
    if(len <= 0){
      _dbg_printf1(_str_DNSCErrFormat, "socket mode");
      goto bail;
    }
    // intentionly only check server ip. considering the situation that respond with another port;
    if(*(uint32_t *)dip != *(uint32_t *)serverIp)
      continue;
    p = DNSMsg_scanHeader(buf, (uint16_t)len, &hdr);
    if(p == NULL || hdr.QR != 1 || id != hdr.TransID)
      continue;
    // we have confirm that this message is the response, so stop loop in this time.
    // jumpoff query section
    for(i = 0; i < hdr.Questions; i++){
      if((p = DNSMsg_scanQuery(buf, (uint16_t)len, p, (DNSQue *)&QueOrRR)) == NULL){
        _dbg_printf1(_str_DNSCErrFormat, "resolve query");
        goto bail;
      }
      DNSMsg_freeQueReturnByScan((DNSQue *)&QueOrRR);
    }
    for(i = 0; i < hdr.AnswerRRs; i++){
      if((p = DNSMsg_scanRR(buf, (uint16_t)len, p, (DNSRR *)&QueOrRR)) == NULL){
        _dbg_printf1(_str_DNSCErrFormat, "resolve RR");
        goto bail;
      }
      // id對上了就默認回答的A記錄一定是那個域名的了。就不浪費時間覈對域名了。因爲有的時候有別名就特麻煩。
      if(QueOrRR.Type == DNSQT_A){
        rst = TRUE;
        (void)memcpy(ret, QueOrRR.RData.A.addr, 4);
        DNSMsg_freeRRReturnByScan((DNSRR *)&QueOrRR);
        _dbg_printf4("DNSC :got IPv4 addr %u.%u.%u.%u\r\n", ret[0], ret[1], ret[2], ret[3]);
        goto bail;
      }
      DNSMsg_freeRRReturnByScan((DNSRR *)&QueOrRR);
    }
    _dbg_printf1(_str_DNSCErrFormat, "no answer");
    goto bail;
  }while(remainPollCnt-- > 0);
  // timeout
  _dbg_printf1(_str_DNSCErrFormat, "timeout");
bail:
  close(socketNum);
  return rst;
}

使用示例

如下就可以從192.168.1.1處的DNS服務器處要到www.baidu.com的IP地址:

static uint8_t dnsBuf[200];
static uint8_t dnsServerIP[] = {192,168,1,1};
static const char * dnameForQuery = "www.baidu.com";
void DnsTask(void *p_arg){
  uint8_t ip[4];
  // 已省去不重要的代碼
  ...
  printf("launch dns query for %s to %u.%u.%u.%u on socket %u\r\n", dnameForQuery, 
        dnsServerIP[0], dnsServerIP[1], dnsServerIP[2], dnsServerIP[3],DNS_SOCKET);
  if(DnsClient_QueryIPv4(DNS_SOCKET, dnsBuf, sizeof(dnsBuf), (uint8_t *)dnsServerIP, 0, dnameForQuery,ip) == TRUE){
    printf("%s's ip is %u.%u.%u.%u\r\n", dnameForQuery, ip[0], ip[1], ip[2], ip[3]);
  }else{
    printf("query dns fail\r\n");
  }
  ...
}

更新歷史

2020/06/04 更新到V1.1 修復V1.0中端口號使用uint8_t的問題,應該使用uint16_t;爲接口添加bufSize參數以避免超出緩衝區;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章