]> granicus.if.org Git - libevent/commitdiff
evdns: add ability to get CNAME
authorSergey Matveychuk <sem33@yandex-team.ru>
Wed, 7 Apr 2021 17:39:18 +0000 (20:39 +0300)
committerAzat Khuzhin <azat@libevent.org>
Mon, 24 May 2021 18:03:45 +0000 (21:03 +0300)
Add new flag (DNS_CNAME_CALLBACK) for
evdns_base_resolve_ipv4()/evdns_base_resolve_ipv6().

If set, you will get one more callback with type == DNS_CNAME and CNAME
in addrs argument.

evdns.c
include/event2/dns.h
test/regress_dns.c
test/regress_testutils.c

diff --git a/evdns.c b/evdns.c
index 52077064339593684b006c429c023da78062772f..0f1c359582e3dcbde3cf5288d0577feaf421c2bc 100644 (file)
--- a/evdns.c
+++ b/evdns.c
@@ -209,6 +209,7 @@ struct request {
        u16 trans_id;  /* the transaction id */
        unsigned request_appended :1;   /* true if the request pointer is data which follows this struct */
        unsigned transmit_me :1;  /* needs to be transmitted */
+       unsigned need_cname :1;   /* make a separate callback for CNAME */
 
        /* XXXX This is a horrible hack. */
        char **put_cname_in_ptr; /* store the cname here if we get one. */
@@ -228,6 +229,7 @@ struct reply {
                char *ptr_name;
                void *raw;
        } data;
+       char *cname;
 };
 
 enum tcp_state {
@@ -986,12 +988,15 @@ reply_run_callback(struct event_callback *d, void *user_pointer)
 
        switch (cb->request_type) {
        case TYPE_A:
-               if (cb->have_reply)
+               if (cb->have_reply) {
                        cb->user_callback(DNS_ERR_NONE, DNS_IPv4_A,
                            cb->reply.rr_count, cb->ttl,
                            cb->reply.data.a,
                            user_pointer);
-               else
+                       if (cb->reply.cname)
+                               cb->user_callback(DNS_ERR_NONE, DNS_CNAME, 1,
+                                   cb->ttl, cb->reply.cname, user_pointer);
+               } else
                        cb->user_callback(cb->err, 0, 0, cb->ttl, NULL, user_pointer);
                break;
        case TYPE_PTR:
@@ -1004,12 +1009,15 @@ reply_run_callback(struct event_callback *d, void *user_pointer)
                }
                break;
        case TYPE_AAAA:
-               if (cb->have_reply)
+               if (cb->have_reply) {
                        cb->user_callback(DNS_ERR_NONE, DNS_IPv6_AAAA,
                            cb->reply.rr_count, cb->ttl,
                            cb->reply.data.aaaa,
                            user_pointer);
-               else
+                       if (cb->reply.cname)
+                               cb->user_callback(DNS_ERR_NONE, DNS_CNAME, 1,
+                                   cb->ttl, cb->reply.cname, user_pointer);
+               } else
                        cb->user_callback(cb->err, 0, 0, cb->ttl, NULL, user_pointer);
                break;
        default:
@@ -1024,6 +1032,10 @@ reply_run_callback(struct event_callback *d, void *user_pointer)
                mm_free(cb->reply.data.raw);
        }
 
+       if (cb->reply.cname) {
+               mm_free(cb->reply.cname);
+       }
+
        mm_free(cb);
 }
 
@@ -1383,13 +1395,13 @@ reply_parse(struct evdns_base *base, u8 *packet, int length)
                        break;
                } else if (type == TYPE_CNAME) {
                        char cname[HOST_NAME_MAX];
-                       if (!req->put_cname_in_ptr || *req->put_cname_in_ptr) {
-                               j += datalength; continue;
-                       }
                        if (name_parse(packet, length, &j, cname,
                                sizeof(cname))<0)
                                goto err;
-                       *req->put_cname_in_ptr = mm_strdup(cname);
+                       if (req->need_cname)
+                               reply.cname = mm_strdup(cname);
+                       if (req->put_cname_in_ptr && !*req->put_cname_in_ptr)
+                               *req->put_cname_in_ptr = mm_strdup(cname);
                } else if (type == TYPE_AAAA && class == CLASS_INET) {
                        int addrcount;
                        if (req->request_type != TYPE_AAAA) {
@@ -3555,6 +3567,9 @@ request_new(struct evdns_base *base, struct evdns_request *handle, int type,
                handle->base = base;
        }
 
+       if (flags & DNS_CNAME_CALLBACK)
+               req->need_cname = 1;
+
        return req;
 err1:
        mm_free(req);
index e6b33afb7663828107a9ea7111a735f102458a51..512646520737f8e49c198f0b7e07e084baf0d88b 100644 (file)
@@ -182,6 +182,7 @@ extern "C" {
 #define DNS_IPv4_A 1
 #define DNS_PTR 2
 #define DNS_IPv6_AAAA 3
+#define DNS_CNAME 4
 
 /** Disable searching for the query. */
 #define DNS_QUERY_NO_SEARCH 0x01
@@ -189,6 +190,8 @@ extern "C" {
 #define DNS_QUERY_USEVC 0x02
 /** Ignore trancation flag in responses (don't fallback to TCP connections). */
 #define DNS_QUERY_IGNTC 0x04
+/** Make a separate callback for CNAME in answer */
+#define DNS_CNAME_CALLBACK 0x80
 
 /* Allow searching */
 #define DNS_OPTION_SEARCH 1
index fff111a8f0410ae074253e85621246bc9edd34bb..8cbb2e6dbb0aa1cd8940c0bdb266b4663368eec8 100644 (file)
@@ -512,7 +512,7 @@ generic_dns_callback(int result, char type, int count, int ttl, void *addresses,
                len = count * 4;
        else if (type == DNS_IPv6_AAAA)
                len = count * 16;
-       else if (type == DNS_PTR)
+       else if (type == DNS_PTR || type == DNS_CNAME)
                len = strlen(addresses)+1;
        else {
                res->addrs_len = len = 0;
@@ -551,6 +551,7 @@ static struct regress_dns_server_table search_table[] = {
        { "hostn.a.example.com", "errsoa", "0", 0, 0 },
        { "hostn.b.example.com", "errsoa", "3", 0, 0 },
        { "hostn.c.example.com", "err", "0", 0, 0 },
+       { "hostc.c.example.com", "CNAME", "cname.c.example.com", 0, 0 },
        { "host", "err", "3", 0, 0 },
        { "host2", "err", "3", 0, 0 },
        { "*", "err", "3", 0, 0 },
@@ -586,7 +587,7 @@ dns_search_test_impl(void *arg, int lower)
        ev_uint16_t portnum = 0;
        char buf[64];
 
-       struct generic_dns_callback_result r[8];
+       struct generic_dns_callback_result r[9];
        size_t i;
 
        for (i = 0; i < ARRAY_SIZE(table); ++i) {
@@ -604,7 +605,7 @@ dns_search_test_impl(void *arg, int lower)
        evdns_base_search_add(dns, "b.example.com");
        evdns_base_search_add(dns, "c.example.com");
 
-       n_replies_left = ARRAY_SIZE(r);
+       n_replies_left = ARRAY_SIZE(r)+1;       /* CNAME gives us 2 callbacks for 1 request */
        exit_base = base;
 
        evdns_base_resolve_ipv4(dns, "host", 0, generic_dns_callback, &r[0]);
@@ -615,6 +616,8 @@ dns_search_test_impl(void *arg, int lower)
        evdns_base_resolve_ipv4(dns, "hostn.a.example.com", DNS_NO_SEARCH, generic_dns_callback, &r[5]);
        evdns_base_resolve_ipv4(dns, "hostn.b.example.com", DNS_NO_SEARCH, generic_dns_callback, &r[6]);
        evdns_base_resolve_ipv4(dns, "hostn.c.example.com", DNS_NO_SEARCH, generic_dns_callback, &r[7]);
+       evdns_base_resolve_ipv4(dns, "hostc.c.example.com", DNS_NO_SEARCH | DNS_CNAME_CALLBACK,
+                               generic_dns_callback, &r[8]);
 
        event_base_dispatch(base);
 
@@ -633,6 +636,9 @@ dns_search_test_impl(void *arg, int lower)
        tt_int_op(r[6].ttl, ==, 42);
        tt_int_op(r[7].result, ==, DNS_ERR_NODATA);
        tt_int_op(r[7].ttl, ==, 0);
+       tt_int_op(r[8].type, ==, DNS_CNAME);
+       tt_int_op(r[8].count, ==, 1);
+       tt_str_op(r[8].addrs, ==, "cname.c.example.com");
 
 end:
        if (dns)
index ece3733b420f8b54a9b3a3b50dea91d8b5cf39d7..5a3f6c1eeef787f0e483f02ea14908084ea45e2b 100644 (file)
@@ -257,6 +257,11 @@ regress_dns_server_cb(struct evdns_server_request *req, void *data)
                }
                evdns_server_request_add_aaaa_reply(req,
                    question, 1, &in6.s6_addr, 100);
+       } else if (!strcmp(tab->anstype, "CNAME")) {
+               struct in_addr in;
+               evutil_inet_pton(AF_INET, "11.22.33.44", &in);
+               evdns_server_request_add_a_reply(req, question, 1, &in, 100);
+               evdns_server_request_add_cname_reply(req, question, tab->ans, 100);
        } else {
                TT_DIE(("Weird table entry with type '%s'", tab->anstype));
        }