Clion开发STM32之W5500系列(DNS服务封装)

概述

  1. 在w5500基础库中进行封装,通过域名的方式获取实际的ip地址
  2. 用于动态获取ntp的ip地址

DNS封装

头文件

/*******************************************************************************
 Copyright (c) [scl]。保留所有权利。
 ******************************************************************************/
#ifndef F1XX_TEMPLATE_W5500_DNS_H
#define F1XX_TEMPLATE_W5500_DNS_H

#include "socket.h"

#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 MAX_DNS_BUF_SIZE 256 /* maximum size of DNS buffer. */

/**
 * @memberof delay_ms_cb 毫秒延迟回调
 * @memberof dns_server_ip: dns 服务ip
 * @memberof dns_port: dns 端口,默认53
 */
struct dns_conf {
    void (*delay_ms_cb)(uint32_t ms);

    uint8_t dns_server_ip[4];
    uint16_t dns_port;
};

void dns_config_set(struct dns_conf *cnf);
/**
 *
 * @param s socket num
 * @param domain_name 服务域名
 * @param dst_ip [out] 保存解析的数据
 * @param wait_ms [in] 发送请求后等待时间
 * @return
 */
bool do_dns(SOCKET s, uint8_t *domain_name, uint8_t *dst_ip, uint32_t wait_ms);


#endif //F1XX_TEMPLATE_W5500_DNS_H

源文件

/*******************************************************************************
 Copyright (c) [scl]。保留所有权利。
 ******************************************************************************/
#include "w5500_dns.h"

#define DBG_ENABLE
#define DBG_SECTION_NAME "dns"
#define DBG_LEVEL DBG_INFO // DBG_LOG DBG_INFO DBG_WARNING DBG_ERROR

#include "sys_dbg.h"


/* Header for all domain messages */
struct dhdr {
    uint16_t id; /* Identification */
    uint8_t qr;  /* Query/Response */
    uint8_t opcode;
    uint8_t aa;     /* Authoratative answer */
    uint8_t tc;     /* Truncation */
    uint8_t rd;     /* Recursion desired */
    uint8_t ra;     /* Recursion available */
    uint8_t rcode; /* Response code */
    uint16_t qdcount; /* Question count */
    uint16_t ancount; /* Answer count */
    uint16_t nscount; /* Authority (name server) count */
    uint16_t arcount; /* Additional record count */
};

static uint8_t DNS_BUF[MAX_DNS_BUF_SIZE] = {0};
static struct dhdr dhp = {0}; /*定义一个结构体用来包含报文头信息*/
static uint16_t MSG_ID = 0x1122;
struct dns_conf *cnf_ptr = NULL;

static int dns_packet_build(uint16_t op, uint8_t *name, uint8_t *buf, uint16_t len);

static bool parse_DNS_Response(struct dhdr *pdhdr, uint8_t *pbuf, uint8_t *dst_result_ip);

static uint8_t *dns_question(uint8_t *msg, uint8_t *cp);

static uint8_t *dns_answer(uint8_t *msg, uint8_t *cp, uint8_t *dst_result_ip);

static int16_t parse_name(uint8_t *msg, uint8_t *compressed, uint16_t len);

void dns_config_set(struct dns_conf *cnf) {
    cnf_ptr = cnf;
}


/**
 * dns 解析
 * @param s
 * @param domain_name 域名
 * @return
 */
bool do_dns(SOCKET s, uint8_t *domain_name, uint8_t *dst_ip, uint32_t wait_ms) {
    if (cnf_ptr == NULL) {
        LOG_E("dns_config_set not set");
        goto _exit_false;
    }
    if (udp_client_init(s, cnf_ptr->dns_port)) {
        uint16_t len = dns_packet_build(0, domain_name, DNS_BUF, MAX_DNS_BUF_SIZE);
        sendto(s, DNS_BUF, len, cnf_ptr->dns_server_ip, cnf_ptr->dns_port); /*发送DNS请求报文给DNS服务器*/
        for (int i = 0; i < wait_ms / 2; ++i) {
            len = w5500_socket_rx_size_read(s);
            if (len > 0) break;
            cnf_ptr->delay_ms_cb(2);
        }
        if (len > 0) {
            if (len > MAX_DNS_BUF_SIZE) len = MAX_DNS_BUF_SIZE;
            recvfrom_simple(s, DNS_BUF, len);
            if (parse_DNS_Response(&dhp, DNS_BUF, dst_ip)) {
                close(s);/*关闭socket*/
                return true;/*返回DNS解析成功域名信息*/
            } else {
                LOG_W("parse_DNS_Response err");
            }
        } else {
            LOG_D("w5500_socket_rx_size_read not rec data");
        }
    } else {
        LOG_W("udp_client_init error");
    }
    _exit_false:
    return false;
}


static int dns_packet_build(uint16_t op, uint8_t *name, uint8_t *buf, uint16_t len) {
    uint8_t *cp = buf;
    uint8_t *cp1;
    uint8_t *dname;
    uint16_t p;
    uint16_t dlen;
    MSG_ID++;
    *(uint16_t *) &cp[0] = htons(MSG_ID);
    p = (op << 11) | 0x0100;
    *(uint16_t *) &cp[2] = htons(p);
    *(uint16_t *) &cp[4] = htons(1);
    *(uint16_t *) &cp[6] = htons(0);
    *(uint16_t *) &cp[8] = htons(0);
    *(uint16_t *) &cp[10] = htons(0);
    cp += sizeof(uint16_t) * 6;
    dname = name;
    dlen = strlen((char *) dname);
    for (;;) {
        /* Look for next dot */
        cp1 = (unsigned char *) strchr((char *) dname, '.');

        if (cp1) {
            /* More to come */
            len = cp1 - dname;
        } else {
            len = dlen; /* Last component */
        }
        *cp++ = len;   /* Write length of component */
        if (len == 0) break;
        strncpy((char *) cp, (char *) dname, len);/* Copy component up to (but not including) dot */
        cp += len;
        if (!cp1) {
            *cp++ = 0;                                                                                                                    /* Last one; write null and finish */
            break;
        }
        dname += len + 1;
        dlen -= len + 1;
    }
    *(uint16_t *) &cp[0] = htons(0x0001);    /* type */
    *(uint16_t *) &cp[2] = htons(0x0001);    /* class */
    cp += sizeof(uint16_t) * 2;
    return ((int) ((uint32_t) (cp) - (uint32_t) (buf)));

}


static bool parse_DNS_Response(struct dhdr *pdhdr, uint8_t *pbuf, uint8_t *dst_result_ip) {
    uint16_t tmp = 0, i = 0;
    uint8_t *msg = NULL, *cp = NULL;
    msg = pbuf;
    memset(pdhdr, 0, sizeof(struct dhdr));
    pdhdr->id = ntohs(*((uint16_t *) &msg[0]));
    tmp = ntohs(*((uint16_t *) &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(*((uint16_t *) &msg[4]));
    pdhdr->ancount = ntohs(*((uint16_t *) &msg[6]));
    pdhdr->nscount = ntohs(*((uint16_t *) &msg[8]));
    pdhdr->arcount = ntohs(*((uint16_t *) &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, dst_result_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;
    else
        return 0;
}

/**
*@brief		 解析回复消息的问询记录
*@param		 msg - 指向回复信息的指针
           cp  - 指向问询记录的指针
*@return	 返回下一个记录指针
*/
uint8_t *dns_question(uint8_t *msg, uint8_t *cp) {
    int16_t len = parse_name(msg, cp, MAX_DNS_BUF_SIZE);
    if (len == -1)return 0;
    cp += len;
    cp += 2; /* type */
    cp += 2; /* class */

    return cp;
}

static uint8_t *dns_answer(uint8_t *msg, uint8_t *cp, uint8_t *dst_result_ip) {
    int16_t len, type;
    len = parse_name(msg, cp, /*name,*/ MAX_DNS_BUF_SIZE);
    if (len == -1)
        return 0;

    cp += len;
    type = ntohs(*((uint16_t *) &cp[0]));
    cp += 2; /* type */
    cp += 2; /* class */
    cp += 4; /* ttl */
    cp += 2; /* len */

    switch (type) {
        case TYPE_A:
            dst_result_ip[0] = *cp++;
            dst_result_ip[1] = *cp++;
            dst_result_ip[2] = *cp++;
            dst_result_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, 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, 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, MAX_DNS_BUF_SIZE);
            if (len == -1)
                return 0;
            cp += len;
            /* Get domain name of responsible person */
            len = parse_name(msg, cp, 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;
}

static int16_t parse_name(uint8_t *msg, uint8_t *compressed, uint16_t len) {
    static int8_t name[MAX_DNS_BUF_SIZE];
    uint16_t slen = 0; /* Length of current segment */
    uint8_t *cp = NULL;
    int16_t clen = 0;     /* Total length of compressed name */
    int16_t indirect = 0; /* Set if indirection encountered */
    int16_t nseg = 0;     /* Total number of segments in name */
    int8_t *buf = NULL;
    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_t) *cp++;
        *buf++ = '.';
        nseg++;
    }

    if (nseg == 0) /* Root name; represent as single dot */
    {
        *buf++ = '.';
        len--;
    }

    *buf++ = '\0';
    len--;

    return clen; /* Length of compressed message */
}

测试

配置文件

/*******************************************************************************
 *  Copyright (c) [scl]。保留所有权利。
 *     本文仅供个人学习和研究使用,禁止用于商业用途。
 ******************************************************************************/

#include "app_conf.h"
#include "w5500_config.h"

#if APP_CONFIG_W5500
#define DBG_ENABLE
#define DBG_SECTION_NAME "w5500"
#define DBG_LEVEL W5500_DBG_LEVEL

#include "sys_dbg.h"
#include "w5500_dns.h"
#include "w5500_ntp.h"

#define W5500_CS stm_port_define(B,12)
#define W5500_RST stm_port_define(C,7)

extern struct net_date_time gb_app_time;


static SPI_HandleTypeDef *w5500_spi = NULL;

static void send_and_rec_bytes(uint8_t *in_dat, uint8_t *out_data, uint16_t len) {
    while (HAL_SPI_GetState(w5500_spi) != HAL_SPI_STATE_READY);
    HAL_SPI_TransmitReceive(w5500_spi, in_dat, out_data, len, 1000);
    while (HAL_SPI_GetState(w5500_spi) != HAL_SPI_STATE_READY);
}

static void send_only(uint8_t *in_data, uint16_t len) {
    HAL_SPI_Transmit(w5500_spi, in_data, len, 1000);
}

static void W5500_RST_HIGH(void) { stm_pin_high(W5500_RST); }

static void W5500_RST_LOW(void) { stm_pin_low(W5500_RST); }

static void W5500_CS_LOW(void) { stm_pin_low(W5500_CS); }

static void W5500_CS_HIGH(void) { stm_pin_high(W5500_CS); }

static void W5500_Driver_MspInit(void) {
    stm32_pin_mode(W5500_CS, pin_mode_output);  /*CS*/
    stm32_pin_mode(W5500_RST, pin_mode_output); /*RST*/
    stm_pin_low(W5500_RST);
    stm_pin_low(W5500_CS);
    /*初始化SPI外设*/
    /*W5500 支持 SPI 模式 0 及模式 3..MOSI 和 MISO 信号无论是接收或发送,均遵从从最高标志位(MSB)到最低标志位(LSB)的传输序列。*/
    bsp_SpiHandleInit(w5500_spi, SPI_BAUDRATEPRESCALER_2, spi_mode_3);
}

module_w5500_t w5500_conf = {
        .base_conf={
                .socket_num = 4,
                .rx_size={4, 4, 4, 4},
                .tx_size={4, 4, 4, 4},
        },
        .net_conf={
                .ip={192, 168, 199, 12},
                .gw={192, 168, 199, 1},
                .sub={255, 255, 255, 0},
        },
        .driver={
                .cs_high = W5500_CS_HIGH,
                .cs_low = W5500_CS_LOW,
                .rst_high= W5500_RST_HIGH,
                .rst_low=W5500_RST_LOW,
                .delay = HAL_Delay,
                .send_and_rec_bytes = send_and_rec_bytes,
                .send_only =send_only
        },
        .api = {
                .msp_init=W5500_Driver_MspInit,
        }
};
uint8_t ntp_domain[] = {"ntp.ntsc.ac.cn"}; /*ntp域名*/
static struct dns_conf net_dns_cnf = { /*dns服务配置*/
        .dns_server_ip={114, 114, 114, 114},
        .dns_port = 53,
        .delay_ms_cb = HAL_Delay
};
static struct ntp_conf net_ntp_conf = {
//        .ntp_server={114, 118, 7, 163},
        .ntp_port = 123,
        .delay_ms_cb = HAL_Delay
};


static void w5500_pre_init(void) {
    /*一般做数据加载,此时系统时钟使用的是内部时钟,如需要使用系统时钟的外设不在此进行初始化*/
    w5500_spi = conv_spi_handle_ptr(handle_get_by_id(spi2_id));
    /*初始化资源*/
    module_w5500_init(&w5500_conf);
    uint32_t uid0 = HAL_GetUIDw0();
    uint32_t uid1 = HAL_GetUIDw1();
    uint32_t uid2 = HAL_GetUIDw2();
    uint8_t mac[6] = {0, uid0 >> 8, uid1, uid1 >> 8, uid2, uid2 >> 8};
    memcpy(w5500_conf.net_conf.mac, mac, sizeof(mac));

}

static void w5500_init(void) {

    w5500_conf.api.msp_init();/*初始化*/
    w5500_conf.net_conf_init();
    uint8_t ip[4];
    w5500_reg_ip_read(ip);
    LOG_D("w5500_reg_ip_read:%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
    w5500_reg_gw_read(ip);
    LOG_D("w5500_reg_gw_read:%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
}


static void w5500_after_init(void) {
    uint8_t try_cnt = 3;// 尝试次数
    SOCKET sn = 1;/*使用的socket*/
#if APP_CONFIG_W5500_DNS // 启用dns
    for (int i = 0; i < try_cnt; ++i) {
        dns_config_set(&net_dns_cnf);
        if (do_dns(sn, ntp_domain, net_ntp_conf.ntp_server, 500)) {
            LOG_D("dns parse result ip:%d.%d.%d.%d",
                  net_ntp_conf.ntp_server[0], net_ntp_conf.ntp_server[1],
                  net_ntp_conf.ntp_server[2], net_ntp_conf.ntp_server[3]
            );
            goto next_step; /*解析成功跳转下一步*/
        }
    }
    LOG_D("do dns try cnt over");
    return;
    next_step:

#endif

#if APP_CONFIG_W5500_NTP // 启用ntp

    ntp_config_set(&net_ntp_conf);
    for (int i = 0; i < try_cnt; ++i) {
        if (ntp_date_time_get(sn, 500, &gb_app_time)) {
            HAL_TIM_Base_Start_IT(handle_get_by_id(tim6_id));
            goto exit_ok;
        }
    }
    LOG_W("ntp_date_time_get time out");
    return;
    exit_ok:
    LOG_D("NTP TIME:%d-%02d-%02d %02d:%02d:%02d",
          gb_app_time.year, gb_app_time.month, gb_app_time.day,
          gb_app_time.hour, gb_app_time.min, gb_app_time.sec
    );
#else
    return;
#endif
}

app_init_export(w5500_net_conf, w5500_pre_init, w5500_init, w5500_after_init);
#endif





结果

Clion开发STM32之W5500系列(DNS服务封装)_第1张图片

你可能感兴趣的:(STM32相关驱动,stm32,网络,linux)