前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >libev中解析DNS

libev中解析DNS

原创
作者头像
8菠萝
发布2023-09-04 14:23:50
3020
发布2023-09-04 14:23:50
举报
文章被收录于专栏:菠萝上市没有

系统环境:

gcc 8.3

GLIBC 2.28

前置环境

1. 编译libev

代码语言:txt
复制
1. git clone https://github.com/enki/libev.git
2. cd libev 
3. ./configure --enable-static=true --prefix=/root/newev   #指定个安装目录,使用静态库
4. make && make install

2. 安装c-ares

代码语言:txt
复制
sudo apt-get install libc-ares2 libc-ares-dev

例程

1. 项目使用cmake,编写CMakeList.txt

代码语言:txt
复制
project(libev_dns)

# 设置 C 编译器
set(CMAKE_C_COMPILER gcc)
link_directories(/libev/test/libcork/build/lib)

# 设置生成的可执行文件的输出路径

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

include_directories(../)
include_directories(.)

link_directories(../lib)
include_directories(../include)
include_directories(/usr/include)
add_executable(dns_query dns_main.cpp common.cpp)

# 连接
target_link_libraries(dns_query ev cares)

2. dns_main.cpp

代码语言:txt
复制
#include <iostream>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <ares.h>
#include "ev.h"
#include "common.h"

#define IO_COUNT 5
struct resolv_ctx
{
    struct ev_io ios[IO_COUNT];
    ares_channel channel;
    struct ares_options options;
};

struct resolv_query
{
    int requests[2];
    size_t response_count;
    struct sockaddr **responses;
    void (*client_cb)(struct sockaddr *, void *);
    void (*free_cb)(void *);
    uint16_t port;
    void *data;
};


// 全局变量
static struct ev_loop *default_loop;
static struct resolv_ctx default_ctx;

// dns状态更新时
void resolv_sock_state_cb(void *data, ares_socket_t socket_fd, int readable, int writable)
{
    struct resolv_ctx *ctx = (struct resolv_ctx *)data;
    int events = (readable ? EV_READ : 0) | (writable ? EV_WRITE : 0);

    int i = 0;
    int ffi = -1;
    for (; i < IO_COUNT; ++i)
    {
        if (ctx->ios[i].fd == socket_fd)
        {
            break;
        }
        if (ffi < 0 && ctx->ios[i].fd == -1)
        {
            ffi = i;
        }
    }

    if (i < IO_COUNT)
    {
        ev_io_stop(default_loop, &ctx->ios[i]);
    }
    else if (ffi > -1)
    {
        i = ffi;
    }
    else
    {
        i = 0;
        ev_io_stop(default_loop, &ctx->ios[i]);
    }
    if (events)
    {
        ev_io_set(&ctx->ios[i], socket_fd, events);
        ev_io_start(default_loop, &ctx->ios[i]);
    }
    else
    {
        ev_io_set(&ctx->ios[i], -1, 0);
    }
}

// ev io 事件回调函数
static void resolv_sock_cb(EV_P_ ev_io *w, int revents)
{
    ares_socket_t rfd = ARES_SOCKET_BAD, wfd = ARES_SOCKET_BAD;

    if (revents & EV_READ)
        rfd = w->fd;
    if (revents & EV_WRITE)
        wfd = w->fd;
    ares_process_fd(default_ctx.channel, rfd, wfd);
}

// 查询完成后的通知函数
void process_client_callback(struct resolv_query *query)
{

    struct sockaddr *best_address = nullptr;

    for (int i = 0; i < query->response_count; ++i)
    {
        if (query->responses[i]->sa_family == AF_INET)
        {
            best_address = query->responses[i];
            break;
        }
        best_address = query->responses[i];
    }

    query->client_cb(best_address, query->data);

    // clear context
    for (int i = 0; i < query->response_count; i++)
        ss_free(query->responses[i]);
    ss_free(query->responses);
    if (query->free_cb != NULL)
        query->free_cb(query->data);
    else
        ss_free(query->data);
    ss_free(query);
}

// 获取ipv4结果
void dns_query_v4_cb(void *arg, int status, int timeouts, struct hostent *he)
{
    struct resolv_query *query = (struct resolv_query *)arg;
    if (he == nullptr || status != ARES_SUCCESS)
    {
        printf("failed to lookup v4 address %s, status:%d\n", ares_strerror(status), status);
    }
    else
    {
        int i = 0;
        int n = 0;
        printf("found address name v4 address %s\n", he->h_name);
        n = 0;
        while (he->h_addr_list[n])
        {
            n++;
        }
        if (n > 0)
        {

            struct sockaddr **new_response = (struct sockaddr **)
                ss_realloc(query->responses, (query->response_count + n) * sizeof(struct sockaddr *));

            if (new_response == nullptr)
            {
                perror("failed to allocate memory for additional DNS responses\n");
            }
            else
            {
                query->responses = new_response;
                for (int i = 0; i < n; ++i)
                {
                    struct sockaddr_in *sa = (struct sockaddr_in *)ss_malloc(sizeof(struct sockaddr_in));
                    memset(sa, 0, sizeof(struct sockaddr_in));
                    sa->sin_family = AF_INET;
                    sa->sin_port = query->port;
                    memcpy(&sa->sin_addr, he->h_addr_list[i], he->h_length);
                    query->responses[query->response_count] = (struct sockaddr *)sa;
                    if (query->responses[query->response_count] == nullptr)
                    {
                        perror("failed to allocate memory for DNS query result address\n");
                    }
                    else
                    {
                        query->response_count++;
                    }
                }
            }
        }
    }
    query->requests[0] = 0;
    if (query->requests[0] == 0 && query->requests[1] == 0)
    {
        process_client_callback(query);
    }
}

// 获取ipv6结果
void dns_query_v6_cb(void *arg, int status, int timeouts, struct hostent *he)
{
    int i = 0;
    int n = 0;
    struct resolv_query *query = (struct resolv_query *)arg;
    if (!he || status != ARES_SUCCESS)
    {
        printf("failed to lookup v6 address %s\n", ares_strerror(status));
    }
    else
    {
        printf("found address name v6 address %s\n", he->h_name);
        n = 0;
        while (he->h_addr_list[n])
        {
            n++;
        }
        if (n > 0)
        {
            struct sockaddr **new_responses = (struct sockaddr **)ss_realloc(query->requests, (query->response_count + n) * sizeof(struct sockaddr *));

            if (new_responses == nullptr)
            {
                perror("failed to allocate memory for additional DNS responses");
            }
            else
            {
                query->responses = new_responses;
                if (query->responses[query->response_count] == nullptr)
                {
                    perror("failed to allocate memory for DNS query result address");
                }
                else
                {
                    query->response_count++;
                }
            }
        }
    }
    query->requests[1] = 0;
    if (query->requests[0] == 0 && query->requests[1] == 0)
    {
        process_client_callback(query);
    }
}

// 初始化
int resolv_init(struct ev_loop *loop, char *nameserver)
{
    default_loop = loop;
    int status = ares_library_init(ARES_LIB_INIT_ALL);
    if (status != ARES_SUCCESS)
    {
        return -1;
    }

    memset(&default_ctx, 0, sizeof(struct resolv_ctx));
    // ev_io 绑定
    default_ctx.options.sock_state_cb = resolv_sock_state_cb;
    default_ctx.options.sock_state_cb_data = &default_ctx;
    default_ctx.options.timeout = 3000; // 30s 超时
    default_ctx.options.tries = 2;      // 重试2次

    status = ares_init_options(&default_ctx.channel, &default_ctx.options, ARES_OPT_NOROTATE | ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES | ARES_OPT_SOCK_STATE_CB);

    if (status != ARES_SUCCESS)
    {
        perror("failed to initialize c-ares");
        return -1;
    }
    if (nameserver != nullptr)
    {
        status = ares_set_servers_ports_csv(default_ctx.channel, nameserver);
    }

    if (status != ARES_SUCCESS)
    {
        perror("failed to set nameservers");
        return -1;
    }
	// 绑定io事件
    for (int i = 0; i < IO_COUNT; ++i)
    {
        ev_io_init(&default_ctx.ios[i], resolv_sock_cb, -1, 0);
    }
    return 0;
}


// 查询入口
void resolv_start(const char *hostname, uint16_t port, void (*client_cb)(struct sockaddr *, void *), void (*free_cb)(void *), void *data)
{
    struct resolv_query *query = (resolv_query *)ss_malloc(sizeof(struct resolv_query));
    memset(query, 0, sizeof(struct resolv_query));

    query->port = port;
    query->client_cb = client_cb;
    query->response_count = 0;
    query->responses = nullptr;
    query->data = data;
    query->free_cb = free_cb;

    query->requests[0] = AF_INET;
    query->requests[1] = AF_INET6;
    ares_gethostbyname(default_ctx.channel, hostname, AF_INET, dns_query_v4_cb, query);
    ares_gethostbyname(default_ctx.channel, hostname, AF_INET6, dns_query_v6_cb, query);
}

// 退出
void resolv_shutdown(struct ev_loop *loop)
{
    for (int i = 0; i < IO_COUNT; i++)
        ev_io_stop(loop, &default_ctx.ios[i]);

    ares_cancel(default_ctx.channel);
    ares_destroy(default_ctx.channel);
    ares_library_cleanup();

    printf("resolv_shutdown");
}

// 获取查询结果
void resolv_cb(struct sockaddr *addr, void *data)
{
    char ip[INET6_ADDRSTRLEN];

    if (addr->sa_family == AF_INET)
    {
        const sockaddr_in *sa_in = reinterpret_cast<const sockaddr_in *>(addr);
        inet_ntop(AF_INET, &(sa_in->sin_addr), ip, INET_ADDRSTRLEN);
        std::cout << "ipv4:" << ip << std::endl;
    }
    else if (addr->sa_family == AF_INET6)
    {
        const sockaddr_in6 *sa_in6 = reinterpret_cast<const sockaddr_in6 *>(addr);
        inet_ntop(AF_INET6, &(sa_in6->sin6_addr), ip, INET6_ADDRSTRLEN);
        std::cout << "ipv6:" << ip << std::endl;
    }
}

// 
void resolv_free_cb(void *data)
{
}

int main(int argc, char *argv[])
{
    struct ev_loop *loop = EV_DEFAULT;
    resolv_init(loop, "8.8.8.8");
    if (argc == 2)
    {
        resolv_start(argv[1], 80, resolv_cb, resolv_free_cb, nullptr);
    }
    else
    {
        resolv_start("qq.com", 80, resolv_cb, resolv_free_cb, nullptr);
    }
    ev_run(loop, 0);
    resolv_shutdown(loop);
    return 0;
}

3. 辅助函数 common.cpp

代码语言:txt
复制
void *ss_malloc(size_t size)
{
    void *tmp = malloc(size);
    if (tmp == NULL)
        exit(-1);
    return tmp;
}

void *ss_realloc(void *ptr, size_t new_size)
{
    void *n = realloc(ptr, new_size);
    if (n == NULL)
    {
        free(ptr);
        ptr = NULL;
        exit(EXIT_FAILURE);
    }
    return n;
}

void ss_free(void *ptr)
{
    free(ptr);
    ptr = NULL;
}

4. 编译测试

代码语言:txt
复制
1. make 
2. ./bin/dns_query badiu.com
failed to lookup v6 address DNS server returned answer with no data
found address name v4 address baidu.com
ipv4:39.156.66.10

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前置环境
    • 1. 编译libev
      • 2. 安装c-ares
      • 例程
        • 1. 项目使用cmake,编写CMakeList.txt
          • 2. dns_main.cpp
            • 3. 辅助函数 common.cpp
              • 4. 编译测试
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档