Add support for URI(Uniform Resource Identifier) records. (#411)

Add ares_parse_uri_reply() for parsing URI DNS replies.

Fix By: Martin Holeš (@martin-256)
pull/414/head
Martin Holeš 3 years ago committed by GitHub
parent ccebec8303
commit 9ac7efc382
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      docs/Makefile.inc
  2. 2
      docs/adig.1
  3. 4
      docs/ares_free_data.3
  4. 81
      docs/ares_parse_uri_reply.3
  5. 12
      include/ares.h
  6. 1
      src/lib/Makefile.inc
  7. 16
      src/lib/ares_data.c
  8. 2
      src/lib/ares_data.h
  9. 6
      src/lib/ares_nameser.h
  10. 184
      src/lib/ares_parse_uri_reply.c
  11. 17
      src/tools/adig.c
  12. 1
      test/Makefile.inc
  13. 4
      test/ares-test-fuzz.c
  14. 288
      test/ares-test-parse-uri.cc
  15. 25
      test/dns-proto.cc
  16. 10
      test/dns-proto.h

@ -37,6 +37,7 @@ MANPAGES = ares_cancel.3 \
ares_parse_soa_reply.3 \
ares_parse_srv_reply.3 \
ares_parse_txt_reply.3 \
ares_parse_uri_reply.3 \
ares_process.3 \
ares_query.3 \
ares_save_options.3 \

@ -42,7 +42,7 @@ Query records of specified type.
Possible values for type are
A (default), AAAA, AFSDB, ANY, AXFR, CNAME, GPOS, HINFO, ISDN, KEY, LOC, MAILA,
MAILB, MB, MD, MF, MG, MINFO, MR, MX, NAPTR, NS, NSAP, NSAP_PTR, NULL,
PTR, PX, RP, RT, SIG, SOA, SRV, TXT, WKS, X25,
PTR, PX, RP, RT, SIG, SOA, SRV, TXT, URI, WKS, X25,
.TP
\fB\-U\fR port
Use specified UDP port to connect to DNS server.

@ -60,6 +60,10 @@ associated with those structures.
When used to free the data returned by ares_parse_soa_reply(3) this
will free the ares_soa_reply structure, along with any additional storage
associated with those structure.
.B ares_parse_uri_reply(3)
When used to free the data returned by ares_parse_uri_reply(3) this
will free list of ares_uri_reply structures, along with any aditional
storage associated with those structure.
.SH RETURN VALUE
The ares_free_data() function does not return a value.
.SH AVAILABILITY

@ -0,0 +1,81 @@
.\"
.\" Copyright 1998 by the Massachusetts Institute of Technology.
.\"
.\" Permission to use, copy, modify, and distribute this
.\" software and its documentation for any purpose and without
.\" fee is hereby granted, provided that the above copyright
.\" notice appear in all copies and that both that copyright
.\" notice and this permission notice appear in supporting
.\" documentation, and that the name of M.I.T. not be used in
.\" advertising or publicity pertaining to distribution of the
.\" software without specific, written prior permission.
.\" M.I.T. makes no representations about the suitability of
.\" this software for any purpose. It is provided "as is"
.\" without express or implied warranty.
.\"
.TH ARES_PARSE_URI_REPLY 3 "14 August 2020"
.SH NAME
ares_parse_uri_reply \- Parse a reply to a DNS query of type URI
.SH SYNOPSIS
.nf
.B #include <ares.h>
.PP
.B int ares_parse_uri_reply(const unsigned char* \fIabuf\fP, int \fIalen\fP,
.B struct ares_uri_reply** \fIuri_out\fP);
.fi
.SH DESCRIPTION
The
.B ares_parse_uri_reply
function parses the response to a query of type URI into a
linked list of
.I struct ares_uri_reply
The parameters
.I abuf
and
.I alen
give the contents of the response. The result is stored in allocated
memory and a pointer to it stored into the variable pointed to by
.IR uri_out .
It is the caller's responsibility to free the resulting
.IR uri_out
structure when it is no longer needed using the function
.B ares_free_data
.PP
The structure
.I ares_uri_reply
contains the following fields:
.sp
.in +4n
.nf
struct ares_uri_reply {
struct ares_uri_reply *next;
unsigned short weight;
unsigned short priority;
char *uri;
int ttl;
};
.fi
.in
.PP
.SH RETURN VALUES
.B ares_parse_uri_reply
can return any of the following values:
.TP 15
.B ARES_SUCCESS
The response was successfully parsed.
.TP 15
.B ARES_EBADRESP
The response was malformatted.
.TP 15
.B ARES_ENODATA
The response did not contain an answer to the query.
.TP 15
.B ARES_ENOMEM
Memory was exhausted.
.SH AVAILABILITY
.SH SEE ALSO
.BR ares_query (3)
.BR ares_free_data (3)
.SH AUTHOR
Written by Jan Petrasek <petrasek@tes.eu>

@ -588,6 +588,14 @@ struct ares_soa_reply {
unsigned int minttl;
};
struct ares_uri_reply {
struct ares_uri_reply *next;
unsigned short priority;
unsigned short weight;
char *uri;
int ttl;
};
/*
* Similar to addrinfo, but with extra ttl and missing canonname.
*/
@ -685,6 +693,10 @@ CARES_EXTERN int ares_parse_soa_reply(const unsigned char* abuf,
int alen,
struct ares_soa_reply** soa_out);
CARES_EXTERN int ares_parse_uri_reply(const unsigned char* abuf,
int alen,
struct ares_uri_reply** uri_out);
CARES_EXTERN void ares_free_string(void *str);
CARES_EXTERN void ares_free_hostent(struct hostent *host);

@ -39,6 +39,7 @@ CSOURCES = ares__close_sockets.c \
ares_parse_soa_reply.c \
ares_parse_srv_reply.c \
ares_parse_txt_reply.c \
ares_parse_uri_reply.c \
ares_platform.c \
ares_process.c \
ares_query.c \

@ -77,6 +77,14 @@ void ares_free_data(void *dataptr)
ares_free(ptr->data.srv_reply.host);
break;
case ARES_DATATYPE_URI_REPLY:
if (ptr->data.uri_reply.next)
next_data = ptr->data.uri_reply.next;
if (ptr->data.uri_reply.uri)
ares_free(ptr->data.uri_reply.uri);
break;
case ARES_DATATYPE_TXT_REPLY:
case ARES_DATATYPE_TXT_EXT:
@ -174,6 +182,14 @@ void *ares_malloc_data(ares_datatype type)
ptr->data.srv_reply.port = 0;
break;
case ARES_DATATYPE_URI_REPLY:
ptr->data.uri_reply.next = NULL;
ptr->data.uri_reply.priority = 0;
ptr->data.uri_reply.weight = 0;
ptr->data.uri_reply.uri = NULL;
ptr->data.uri_reply.ttl = 0;
break;
case ARES_DATATYPE_TXT_EXT:
ptr->data.txt_ext.record_start = 0;
/* FALLTHROUGH */

@ -23,6 +23,7 @@ typedef enum {
ARES_DATATYPE_MX_REPLY, /* struct ares_mx_reply - introduced in 1.7.2 */
ARES_DATATYPE_NAPTR_REPLY,/* struct ares_naptr_reply - introduced in 1.7.6 */
ARES_DATATYPE_SOA_REPLY, /* struct ares_soa_reply - introduced in 1.9.0 */
ARES_DATATYPE_URI_REPLY, /* struct ares_uri_reply */
#if 0
ARES_DATATYPE_ADDR6TTL, /* struct ares_addrttl */
ARES_DATATYPE_ADDRTTL, /* struct ares_addr6ttl */
@ -67,6 +68,7 @@ struct ares_data {
struct ares_naptr_reply naptr_reply;
struct ares_soa_reply soa_reply;
struct ares_caa_reply caa_reply;
struct ares_uri_reply uri_reply;
} data;
};

@ -138,7 +138,7 @@ typedef enum __ns_type {
ns_t_mailb = 253, /* Transfer mailbox records. */
ns_t_maila = 254, /* Transfer mail agent records. */
ns_t_any = 255, /* Wildcard match. */
ns_t_zxfr = 256, /* BIND-specific, nonstandard. */
ns_t_uri = 256, /* Uniform Resource Identifier (RFC7553) */
ns_t_caa = 257, /* Certification Authority Authorization. */
ns_t_max = 65536
} ns_type;
@ -468,8 +468,8 @@ typedef enum __ns_rcode {
#ifndef T_ANY
# define T_ANY 255 /* ns_t_any */
#endif
#ifndef T_ZXFR
# define T_ZXFR 256 /* ns_t_zxfr */
#ifndef T_URI
# define T_URI 256 /* ns_t_uri */
#endif
#ifndef T_CAA
# define T_CAA 257 /* ns_t_caa */

@ -0,0 +1,184 @@
/* Copyright 1998 by the Massachusetts Institute of Technology.
* Copyright (C) 2009 by Jakub Hrozek <jhrozek@redhat.com>
*
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting
* documentation, and that the name of M.I.T. not be used in
* advertising or publicity pertaining to distribution of the
* software without specific, written prior permission.
* M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is"
* without express or implied warranty.
*/
#include "ares_setup.h"
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_NETDB_H
# include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#include "ares_nameser.h"
#include "ares.h"
#include "ares_dns.h"
#include "ares_data.h"
#include "ares_private.h"
/* AIX portability check */
#ifndef T_URI
# define T_URI 256 /* uri selection */
#endif
int
ares_parse_uri_reply (const unsigned char *abuf, int alen,
struct ares_uri_reply **uri_out)
{
unsigned int qdcount, ancount, i;
const unsigned char *aptr, *vptr;
int status, rr_type, rr_class, rr_len, rr_ttl;
long len;
char *uri_str = NULL, *rr_name = NULL;
struct ares_uri_reply *uri_head = NULL;
struct ares_uri_reply *uri_last = NULL;
struct ares_uri_reply *uri_curr;
/* Set *uri_out to NULL for all failure cases. */
*uri_out = NULL;
/* Give up if abuf doesn't have room for a header. */
if (alen < HFIXEDSZ){
return ARES_EBADRESP;
}
/* Fetch the question and answer count from the header. */
qdcount = DNS_HEADER_QDCOUNT (abuf);
ancount = DNS_HEADER_ANCOUNT (abuf);
if (qdcount != 1) {
return ARES_EBADRESP;
}
if (ancount == 0) {
return ARES_ENODATA;
}
/* Expand the name from the question, and skip past the question. */
aptr = abuf + HFIXEDSZ;
status = ares_expand_name (aptr, abuf, alen, &uri_str, &len);
if (status != ARES_SUCCESS){
return status;
}
if (aptr + len + QFIXEDSZ > abuf + alen)
{
ares_free (uri_str);
return ARES_EBADRESP;
}
aptr += len + QFIXEDSZ;
/* Examine each answer resource record (RR) in turn. */
for (i = 0; i < ancount; i++)
{
/* Decode the RR up to the data field. */
status = ares_expand_name (aptr, abuf, alen, &rr_name, &len);
if (status != ARES_SUCCESS)
{
break;
}
aptr += len;
if (aptr + RRFIXEDSZ > abuf + alen)
{
status = ARES_EBADRESP;
break;
}
rr_type = DNS_RR_TYPE (aptr);
rr_class = DNS_RR_CLASS (aptr);
rr_ttl = DNS_RR_TTL(aptr);
rr_len = DNS_RR_LEN (aptr);
aptr += RRFIXEDSZ;
if (aptr + rr_len > abuf + alen)
{
status = ARES_EBADRESP;
break;
}
/* Check if we are really looking at a URI record */
if (rr_class == C_IN && rr_type == T_URI)
{
/* parse the URI record itself */
if (rr_len < 5)
{
status = ARES_EBADRESP;
break;
}
/* Allocate storage for this URI answer appending it to the list */
uri_curr = ares_malloc_data(ARES_DATATYPE_URI_REPLY);
if (!uri_curr)
{
status = ARES_ENOMEM;
break;
}
if (uri_last)
{
uri_last->next = uri_curr;
}
else
{
uri_head = uri_curr;
}
uri_last = uri_curr;
vptr = aptr;
uri_curr->priority = DNS__16BIT(vptr);
vptr += sizeof(unsigned short);
uri_curr->weight = DNS__16BIT(vptr);
vptr += sizeof(unsigned short);
uri_curr->uri = (char *)ares_malloc(rr_len-3);
if (!uri_curr->uri)
{
status = ARES_ENOMEM;
break;
}
uri_curr->uri = strncpy(uri_curr->uri, vptr, rr_len-4);
uri_curr->uri[rr_len-4]='\0';
uri_curr->ttl = rr_ttl;
if (status != ARES_SUCCESS)
break;
}
/* Don't lose memory in the next iteration */
ares_free (rr_name);
rr_name = NULL;
/* Move on to the next record */
aptr += rr_len;
}
if (uri_str)
ares_free (uri_str);
if (rr_name)
ares_free (rr_name);
/* clean up on error */
if (status != ARES_SUCCESS)
{
if (uri_head)
ares_free_data (uri_head);
return status;
}
/* everything looks fine, return the data */
*uri_out = uri_head;
return ARES_SUCCESS;
}

@ -121,6 +121,7 @@ static const struct nv types[] = {
{ "NSEC", T_NSEC },
{ "DNSKEY", T_DNSKEY },
{ "CAA", T_CAA },
{ "URI", T_URI },
{ "ANY", T_ANY }
};
static const int ntypes = sizeof(types) / sizeof(types[0]);
@ -516,7 +517,7 @@ static const unsigned char *display_rr(const unsigned char *aptr,
const unsigned char *abuf, int alen)
{
const unsigned char *p;
int type, dnsclass, ttl, dlen, status;
int type, dnsclass, ttl, dlen, status, i;
long len;
int vlen;
char addr[46];
@ -737,6 +738,18 @@ static const unsigned char *display_rr(const unsigned char *aptr,
ares_free_string(name.as_char);
break;
case T_URI:
/* The RR data is two two-byte numbers representing the
* priority and weight, followed by a target.
*/
printf("\t%d ", (int)DNS__16BIT(aptr));
printf("%d \t\t", (int)DNS__16BIT(aptr+2));
p = aptr +4;
for (i=0; i <dlen-4; ++i)
printf("%c",p[i]);
break;
case T_NAPTR:
printf("\t%d", (int)DNS__16BIT(aptr)); /* order */
@ -962,7 +975,7 @@ static void print_help_info_adig(void) {
" KEY, LOC, MAILA, MAILB, MB, MD,\n"
" MF, MG, MINFO, MR, MX, NAPTR, NS,\n"
" NSAP, NSAP_PTR, NULL, PTR, PX, RP,\n"
" RT, SIG, SOA, SRV, TXT, WKS, X25\n\n"
" RT, SIG, SOA, SRV, TXT, URI, WKS, X25\n\n"
" -x : For a '-t PTR a.b.c.d' lookup, query for 'd.c.b.a.in-addr.arpa.'\n"
" -xx : As above, but for IPv6, compact the format into a bitstring like\n"
" '[xabcdef00000000000000000000000000].IP6.ARPA.'\n");

@ -14,6 +14,7 @@ TESTSOURCES = ares-test-main.cc \
ares-test-parse-soa-any.cc \
ares-test-parse-srv.cc \
ares-test-parse-txt.cc \
ares-test-parse-uri.cc \
ares-test-misc.cc \
ares-test-live.cc \
ares-test-mock.cc \

@ -51,5 +51,9 @@ int LLVMFuzzerTestOneInput(const unsigned char *data,
ares_parse_caa_reply(data, size, &caa);
if (caa) ares_free_data(caa);
struct ares_uri_reply* uri = NULL;
ares_parse_uri_reply(data, size, &uri);
if (uri) ares_free_data(uri);
return 0;
}

@ -0,0 +1,288 @@
#include "ares-test.h"
#include "dns-proto.h"
#include <sstream>
#include <vector>
namespace ares {
namespace test {
TEST_F(LibraryTest, ParseUriReplyOK) {
DNSPacket pkt;
pkt.set_qid(0x1234).set_response().set_aa()
.add_question(new DNSQuestion("example.com", T_URI))
.add_answer(new DNSUriRR("example.com", 100, 10, 20, "uri.example.com"))
.add_answer(new DNSUriRR("example.com", 200, 11, 21, "uri2.example.com"));
std::vector<byte> data = pkt.data();
struct ares_uri_reply* uri = nullptr;
EXPECT_EQ(ARES_SUCCESS, ares_parse_uri_reply(data.data(), data.size(), &uri));
ASSERT_NE(nullptr, uri);
EXPECT_EQ("uri.example.com", std::string(uri->uri));
EXPECT_EQ(10, uri->priority);
EXPECT_EQ(20, uri->weight);
EXPECT_EQ(100, uri->ttl);
struct ares_uri_reply* uri2 = uri->next;
ASSERT_NE(nullptr, uri2);
EXPECT_EQ("uri2.example.com", std::string(uri2->uri));
EXPECT_EQ(11, uri2->priority);
EXPECT_EQ(21, uri2->weight);
EXPECT_EQ(200, uri2->ttl);
EXPECT_EQ(nullptr, uri2->next);
ares_free_data(uri);
}
TEST_F(LibraryTest, ParseUriReplySingle) {
DNSPacket pkt;
pkt.set_qid(0x1234).set_response().set_aa()
.add_question(new DNSQuestion("example.abc.def.com", T_URI))
.add_answer(new DNSUriRR("example.abc.def.com", 180, 0, 10, "example.abc.def.com"))
.add_auth(new DNSNsRR("abc.def.com", 44, "else1.where.com"))
.add_auth(new DNSNsRR("abc.def.com", 44, "else2.where.com"))
.add_auth(new DNSNsRR("abc.def.com", 44, "else3.where.com"))
.add_auth(new DNSNsRR("abc.def.com", 44, "else4.where.com"))
.add_auth(new DNSNsRR("abc.def.com", 44, "else5.where.com"))
.add_additional(new DNSARR("else2.where.com", 42, {172,19,0,1}))
.add_additional(new DNSARR("else5.where.com", 42, {172,19,0,2}));
std::vector<byte> data = pkt.data();
struct ares_uri_reply* uri = nullptr;
EXPECT_EQ(ARES_SUCCESS, ares_parse_uri_reply(data.data(), data.size(), &uri));
ASSERT_NE(nullptr, uri);
EXPECT_EQ("example.abc.def.com", std::string(uri->uri));
EXPECT_EQ(0, uri->priority);
EXPECT_EQ(10, uri->weight);
EXPECT_EQ(180, uri->ttl);
EXPECT_EQ(nullptr, uri->next);
ares_free_data(uri);
}
TEST_F(LibraryTest, ParseUriReplyMalformed) {
std::vector<byte> data = {
0x12, 0x34, // qid
0x84, // response + query + AA + not-TC + not-RD
0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError
0x00, 0x01, // num questions
0x00, 0x01, // num answer RRs
0x00, 0x00, // num authority RRs
0x00, 0x00, // num additional RRs
// Question
0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
0x03, 'c', 'o', 'm',
0x00,
0x01, 0x00, // type URI
0x00, 0x01, // class IN
// Answer 1
0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
0x03, 'c', 'o', 'm',
0x00,
0x01, 0x00, // RR type
0x00, 0x01, // class IN
0x01, 0x02, 0x03, 0x04, // TTL
0x00, 0x04, // rdata length -- too short
0x02, 0x03, 0x04, 0x05,
};
struct ares_uri_reply* uri = nullptr;
EXPECT_EQ(ARES_EBADRESP, ares_parse_uri_reply(data.data(), data.size(), &uri));
ASSERT_EQ(nullptr, uri);
}
TEST_F(LibraryTest, ParseUriReplyMultiple) {
DNSPacket pkt;
pkt.set_qid(0x1234).set_response().set_ra().set_rd()
.add_question(new DNSQuestion("uri.example.com", T_URI))
.add_answer(new DNSUriRR("uri.example.com", 600, 0, 5, "a1.uri.example.com"))
.add_answer(new DNSUriRR("uri.example.com", 660, 0, 5, "a2.uri.example.com"))
.add_answer(new DNSUriRR("uri.example.com", 720, 0, 5, "a3.uri.example.com"))
.add_auth(new DNSNsRR("example.com", 300, "ns1.example.com"))
.add_auth(new DNSNsRR("example.com", 300, "ns2.example.com"))
.add_auth(new DNSNsRR("example.com", 300, "ns3.example.com"))
.add_additional(new DNSARR("a1.uri.example.com", 300, {172,19,1,1}))
.add_additional(new DNSARR("a2.uri.example.com", 300, {172,19,1,2}))
.add_additional(new DNSARR("a3.uri.example.com", 300, {172,19,1,3}))
.add_additional(new DNSARR("n1.example.com", 300, {172,19,0,1}))
.add_additional(new DNSARR("n2.example.com", 300, {172,19,0,2}))
.add_additional(new DNSARR("n3.example.com", 300, {172,19,0,3}));
std::vector<byte> data = pkt.data();
struct ares_uri_reply* uri0 = nullptr;
EXPECT_EQ(ARES_SUCCESS, ares_parse_uri_reply(data.data(), data.size(), &uri0));
ASSERT_NE(nullptr, uri0);
struct ares_uri_reply* uri = uri0;
EXPECT_EQ("a1.uri.example.com", std::string(uri->uri));
EXPECT_EQ(0, uri->priority);
EXPECT_EQ(5, uri->weight);
EXPECT_EQ(600, uri->ttl);
EXPECT_NE(nullptr, uri->next);
uri = uri->next;
EXPECT_EQ("a2.uri.example.com", std::string(uri->uri));
EXPECT_EQ(0, uri->priority);
EXPECT_EQ(5, uri->weight);
EXPECT_EQ(660, uri->ttl);
EXPECT_NE(nullptr, uri->next);
uri = uri->next;
EXPECT_EQ("a3.uri.example.com", std::string(uri->uri));
EXPECT_EQ(0, uri->priority);
EXPECT_EQ(5, uri->weight);
EXPECT_EQ(720, uri->ttl);
EXPECT_EQ(nullptr, uri->next);
ares_free_data(uri0);
}
TEST_F(LibraryTest, ParseUriReplyCname) {
DNSPacket pkt;
pkt.set_qid(0x1234).set_response().set_aa()
.add_question(new DNSQuestion("example.abc.def.com", T_URI))
.add_answer(new DNSCnameRR("example.abc.def.com", 300, "cname.abc.def.com"))
.add_answer(new DNSUriRR("cname.abc.def.com", 600, 0, 10, "uri.abc.def.com"))
.add_auth(new DNSNsRR("abc.def.com", 44, "else1.where.com"))
.add_auth(new DNSNsRR("abc.def.com", 44, "else2.where.com"))
.add_auth(new DNSNsRR("abc.def.com", 44, "else3.where.com"))
.add_additional(new DNSARR("example.abc.def.com", 300, {172,19,0,1}))
.add_additional(new DNSARR("else1.where.com", 42, {172,19,0,1}))
.add_additional(new DNSARR("else2.where.com", 42, {172,19,0,2}))
.add_additional(new DNSARR("else3.where.com", 42, {172,19,0,3}));
std::vector<byte> data = pkt.data();
struct ares_uri_reply* uri = nullptr;
EXPECT_EQ(ARES_SUCCESS, ares_parse_uri_reply(data.data(), data.size(), &uri));
ASSERT_NE(nullptr, uri);
EXPECT_EQ("uri.abc.def.com", std::string(uri->uri));
EXPECT_EQ(0, uri->priority);
EXPECT_EQ(10, uri->weight);
EXPECT_EQ(600, uri->ttl);
EXPECT_EQ(nullptr, uri->next);
ares_free_data(uri);
}
TEST_F(LibraryTest, ParseUriReplyCnameMultiple) {
DNSPacket pkt;
pkt.set_qid(0x1234).set_response().set_ra().set_rd()
.add_question(new DNSQuestion("query.example.com", T_URI))
.add_answer(new DNSCnameRR("query.example.com", 300, "uri.example.com"))
.add_answer(new DNSUriRR("uri.example.com", 600, 0, 5, "a1.uri.example.com"))
.add_answer(new DNSUriRR("uri.example.com", 660, 0, 5, "a2.uri.example.com"))
.add_answer(new DNSUriRR("uri.example.com", 720, 0, 5, "a3.uri.example.com"))
.add_auth(new DNSNsRR("example.com", 300, "ns1.example.com"))
.add_auth(new DNSNsRR("example.com", 300, "ns2.example.com"))
.add_auth(new DNSNsRR("example.com", 300, "ns3.example.com"))
.add_additional(new DNSARR("a1.uri.example.com", 300, {172,19,1,1}))
.add_additional(new DNSARR("a2.uri.example.com", 300, {172,19,1,2}))
.add_additional(new DNSARR("a3.uri.example.com", 300, {172,19,1,3}))
.add_additional(new DNSARR("n1.example.com", 300, {172,19,0,1}))
.add_additional(new DNSARR("n2.example.com", 300, {172,19,0,2}))
.add_additional(new DNSARR("n3.example.com", 300, {172,19,0,3}));
std::vector<byte> data = pkt.data();
struct ares_uri_reply* uri0 = nullptr;
EXPECT_EQ(ARES_SUCCESS, ares_parse_uri_reply(data.data(), data.size(), &uri0));
ASSERT_NE(nullptr, uri0);
struct ares_uri_reply* uri = uri0;
EXPECT_EQ("a1.uri.example.com", std::string(uri->uri));
EXPECT_EQ(0, uri->priority);
EXPECT_EQ(5, uri->weight);
EXPECT_EQ(600, uri->ttl);
EXPECT_NE(nullptr, uri->next);
uri = uri->next;
EXPECT_EQ("a2.uri.example.com", std::string(uri->uri));
EXPECT_EQ(0, uri->priority);
EXPECT_EQ(5, uri->weight);
EXPECT_EQ(660, uri->ttl);
EXPECT_NE(nullptr, uri->next);
uri = uri->next;
EXPECT_EQ("a3.uri.example.com", std::string(uri->uri));
EXPECT_EQ(0, uri->priority);
EXPECT_EQ(5, uri->weight);
EXPECT_EQ(720, uri->ttl);
EXPECT_EQ(nullptr, uri->next);
ares_free_data(uri0);
}
TEST_F(LibraryTest, ParseUriReplyErrors) {
DNSPacket pkt;
pkt.set_qid(0x1234).set_response().set_aa()
.add_question(new DNSQuestion("example.abc.def.com", T_URI))
.add_answer(new DNSUriRR("example.abc.def.com", 180, 0, 10, "example.abc.def.com"));
std::vector<byte> data;
struct ares_uri_reply* uri = nullptr;
// No question.
pkt.questions_.clear();
data = pkt.data();
EXPECT_EQ(ARES_EBADRESP, ares_parse_uri_reply(data.data(), data.size(), &uri));
pkt.add_question(new DNSQuestion("example.abc.def.com", T_URI));
#ifdef DISABLED
// Question != answer
pkt.questions_.clear();
pkt.add_question(new DNSQuestion("Axample.com", T_URI));
data = pkt.data();
EXPECT_EQ(ARES_ENODATA, ares_parse_uri_reply(data.data(), data.size(), &uri));
pkt.questions_.clear();
pkt.add_question(new DNSQuestion("example.com", T_URI));
#endif
// Two questions.
pkt.add_question(new DNSQuestion("example.abc.def.com", T_URI));
data = pkt.data();
EXPECT_EQ(ARES_EBADRESP, ares_parse_uri_reply(data.data(), data.size(), &uri));
pkt.questions_.clear();
pkt.add_question(new DNSQuestion("64.48.32.16.in-addr.arpa", T_PTR));
// Wrong sort of answer.
pkt.answers_.clear();
pkt.add_answer(new DNSMxRR("example.com", 100, 100, "mx1.example.com"));
data = pkt.data();
EXPECT_EQ(ARES_SUCCESS, ares_parse_uri_reply(data.data(), data.size(), &uri));
EXPECT_EQ(nullptr, uri);
pkt.answers_.clear();
pkt.add_answer(new DNSUriRR("example.abc.def.com", 180, 0, 10, "example.abc.def.com"));
// No answer.
pkt.answers_.clear();
data = pkt.data();
EXPECT_EQ(ARES_ENODATA, ares_parse_uri_reply(data.data(), data.size(), &uri));
pkt.add_answer(new DNSUriRR("example.abc.def.com", 180, 0, 10, "example.abc.def.com"));
// Truncated packets.
data = pkt.data();
for (size_t len = 1; len < data.size(); len++) {
int rc = ares_parse_uri_reply(data.data(), len, &uri);
EXPECT_TRUE(rc == ARES_EBADRESP || rc == ARES_EBADNAME);
}
}
TEST_F(LibraryTest, ParseUriReplyAllocFail) {
DNSPacket pkt;
pkt.set_qid(0x1234).set_response().set_aa()
.add_question(new DNSQuestion("example.abc.def.com", T_URI))
.add_answer(new DNSCnameRR("example.com", 300, "c.example.com"))
.add_answer(new DNSUriRR("example.abc.def.com", 180, 0, 10, "example.abc.def.com"));
std::vector<byte> data = pkt.data();
struct ares_uri_reply* uri = nullptr;
for (int ii = 1; ii <= 5; ii++) {
ClearFails();
SetAllocFail(ii);
EXPECT_EQ(ARES_ENOMEM, ares_parse_uri_reply(data.data(), data.size(), &uri)) << ii;
}
}
} // namespace test
} // namespace ares

@ -137,7 +137,7 @@ std::string RRTypeToString(int rrtype) {
case T_MAILB: return "MAILB";
case T_MAILA: return "MAILA";
case T_ANY: return "ANY";
case T_ZXFR: return "ZXFR";
case T_URI: return "URI";
case T_MAX: return "MAX";
default: return "UNKNOWN";
}
@ -371,6 +371,19 @@ std::string RRToString(const std::vector<byte>& packet,
}
break;
}
case T_URI: {
if (rdatalen > 4) {
const byte* p = *data;
unsigned long prio = DNS__16BIT(p);
unsigned long weight = DNS__16BIT(p + 2);
p += 4;
std::string uri(p, p + (rdatalen - 4));
ss << prio << " " << weight << " '" << uri << "'";
} else {
ss << "(RR too short)";
}
break;
}
case T_SOA: {
const byte* p = *data;
int rc = ares_expand_name(p, packet.data(), packet.size(), &name, &enclen);
@ -533,6 +546,16 @@ std::vector<byte> DNSSrvRR::data() const {
return data;
}
std::vector<byte> DNSUriRR::data() const {
std::vector<byte> data = DNSRR::data();
int len = 4 + target_.size();
PushInt16(&data, len);
PushInt16(&data, prio_);
PushInt16(&data, weight_);
data.insert(data.end(), target_.begin(), target_.end());
return data;
}
std::vector<byte> DNSAddressRR::data() const {
std::vector<byte> data = DNSRR::data();
int len = addr_.size();

@ -138,6 +138,16 @@ struct DNSSrvRR : public DNSRR {
std::string target_;
};
struct DNSUriRR : public DNSRR {
DNSUriRR(const std::string& name, int ttl,
int prio, int weight, const std::string& target)
: DNSRR(name, T_URI, ttl), prio_(prio), weight_(weight), target_(target) {}
virtual std::vector<byte> data() const;
int prio_;
int weight_;
std::string target_;
};
struct DNSSoaRR : public DNSRR {
DNSSoaRR(const std::string& name, int ttl,
const std::string& nsname, const std::string& rname,

Loading…
Cancel
Save