现在的位置: 首页 > 综合 > 正文

使用libevent异步解析dns

2017年07月30日 ⁄ 综合 ⁄ 共 2869字 ⁄ 字号 评论关闭

    libevent 自带 dns 解析库,支持同步、异步两种方式解析域名。因 libevent 本身是异步事件驱动型类库,我们在基于它做应用时,也多数是使用异步模型,因此这里介绍一下如何使用 libevent 异步解析域名。 libevent 官网有文章专门介绍 DNS 功能,请参考《Using DNS with Libevent》。

    我这里的例子和官网稍有不同,主要体现在对 DNS 服务器的配置上,区分了多平台。因为我发现在 Android (安卓)平台上, libevent 在获取 DNS 服务器时有问题,具体请参考另一篇博文《libevent 在 Android 上的一个改进》。

    我使用 libevent-2.1.3-alpha (请到官网下载)和 Qt 5.2 。关于 Qt 环境搭建,参考《Windows下Qt 5.2 for Android开发入门》。

    下面是所有代码:

struct event_base * g_evbase = 0;
struct evdns_base * g_dnsbase = 0;

static void _dns_callback(int errcode, struct evutil_addrinfo *addr, void *ptr)
{
    if (errcode)
    {
        printf("%s -> %s\n", (char*)ptr, evutil_gai_strerror(errcode));
    }
    else
    {
        struct evutil_addrinfo *ai;
        char ip[128];
        printf("dns resolved,hostname - %s, ip :\n", (char*)ptr);
        for (ai = addr; ai; ai = ai->ai_next)
        {
            const char *s = NULL;
            if (ai->ai_family == AF_INET)
            {
                struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr;
                s = evutil_inet_ntop(AF_INET, &sin->sin_addr, ip, 128);
            }
            else if (ai->ai_family == AF_INET6)
            {
                struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr;
                s = evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, ip, 128);
            }
            if(s)
            {
                printf("  %s\n", s);
            }
        }
    }

    if(addr)evutil_freeaddrinfo(addr);
}


struct evdns_base * setup_evdns_base(struct event_base *base)
{
    if(g_dnsbase)
    {
        return g_dnsbase;
    }
    else
    {
        struct evdns_base * dnsbase = 0;
#if defined(_WIN32)
        dnsbase = evdns_base_new(base, 0);
        evdns_base_nameserver_ip_add(dnsbase, "8.8.8.8");
#elif defined(ANDROID)
        dnsbase = evdns_base_new(base, 0);
        {
            int ret = 0;
            int contains_default = 0;
            char buf[PROP_VALUE_MAX];
            ret = __system_property_get("net.dns1", buf);
            if(ret >= 7)
            {
                if(!strncmp("8.8.8.8", buf, 7)) contains_default = 1;
                evdns_base_nameserver_ip_add(dnsbase, buf);
            }
            ret = __system_property_get("net.dns2", buf);
            if(ret >= 7)
            {
                if(!strncmp("8.8.8.8", buf, 7)) contains_default = 1;
                evdns_base_nameserver_ip_add(dnsbase, buf);
            }
            if(!contains_default)
            {
                evdns_base_nameserver_ip_add(dnsbase, "8.8.8.8");
            }
        }
#else
        dnsbase = evdns_base_new(base, 1);
#endif
        printf(" dns server count : %d\n", evdns_base_count_nameservers(dnsbase));

        g_dnsbase = dnsbase;
        return dnsbase;
    }
}


static int lookup_host(const char * host)
{
    struct evutil_addrinfo hints;
    struct evdns_getaddrinfo_request *req;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_flags = EVUTIL_AI_CANONNAME;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    req = evdns_getaddrinfo(g_dnsbase, host, NULL ,
                &hints, _dns_callback, (void*)host);
    if (req == NULL)
    {
        printf("    [request for %s returned immediately]\n", host);
        return -1;
    }
    return 0;
}



int main(int argc, char **argv)
{
#ifdef WIN32
    WORD wVersionRequested;
    WSADATA wsaData;

    wVersionRequested = MAKEWORD(2, 2);

    (void) WSAStartup(wVersionRequested, &wsaData);
#endif

    if(argc < 2)
    {
        printf("Usage: \n    dns_resolv hostname\n");
        return 0;
    }

    g_evbase = event_base_new();
    setup_evdns_base(g_evbase);
    if(lookup_host(argv[1]) == 0)
    {
        event_base_loop(g_evbase, EVLOOP_NO_EXIT_ON_EMPTY);
    }

    event_base_free(g_evbase);
    evdns_base_free(g_dnsbase, 1);

#ifdef WIN32
    (void) WSACleanup();
#endif
    return 0;
}

    代码比较简单,从命令行获取待解析域名进行解析。通过 WIN32 ,ANDROID 这样一些宏来区分不同平台。

    需要说明一点,在我的 Win7 环境下,通过 evdns_base_new(base, 1) 这种方式无法读取到域名服务器,所以我配置了 google 的 8.8.8.8 来使用。

抱歉!评论已关闭.