evdns: add ability to get CNAME

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.
This commit is contained in:
Sergey Matveychuk 2021-04-07 20:39:18 +03:00 committed by Azat Khuzhin
parent 087bbc572c
commit 19b3fd0bf0
4 changed files with 40 additions and 11 deletions

31
evdns.c
View File

@ -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);

View 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

View 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)

View 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));
}