• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1diff --git a/src/lib/ares_process.c b/src/lib/ares_process.c
2index 605e5f8..f07d7b3 100644
3--- a/src/lib/ares_process.c
4+++ b/src/lib/ares_process.c
5@@ -579,6 +579,12 @@ static void process_answer(ares_channel channel, unsigned char *abuf,
6   struct query *query;
7   struct list_node* list_head;
8   struct list_node* list_node;
9+  int rr_type, rr_class, rr_len, status;
10+  const unsigned char *aptr;
11+  unsigned int qdcount, ancount, i;
12+  char *hostname = NULL, *rr_name = NULL;
13+  long len;
14+  int acount = 0;
15
16   /* If there's no room in the answer for a header, we can't do much
17    * with it. */
18@@ -590,6 +596,14 @@ static void process_answer(ares_channel channel, unsigned char *abuf,
19   tc = DNS_HEADER_TC(abuf);
20   rcode = DNS_HEADER_RCODE(abuf);
21
22+  /* Fetch the question and answer count from the header. */
23+  qdcount = DNS_HEADER_QDCOUNT(abuf);
24+  ancount = DNS_HEADER_ANCOUNT(abuf);
25+  if (qdcount != 1)
26+    return;
27+  if (ancount == 0)
28+    return;
29+
30   /* Find the query corresponding to this packet. The queries are
31    * hashed/bucketed by query id, so this lookup should be quick.  Note that
32    * both the query id and the questions must be the same; when the query id
33@@ -654,12 +668,78 @@ static void process_answer(ares_channel channel, unsigned char *abuf,
34   if (alen > packetsz && !tcp)
35       alen = packetsz;
36
37+  /* Parse the RR name. */
38+  aptr = abuf + HFIXEDSZ;
39+  status = ares_expand_name(aptr, abuf, alen, &hostname, &len);
40+  if (status != ARES_SUCCESS)
41+    return;
42+
43+  /* Make sure there is enough data after the RR name for the fixed
44+   * part of the RR.
45+   */
46+  if (aptr + RRFIXEDSZ > abuf + alen)
47+  {
48+    ares_free_string(hostname);
49+    return;
50+  }
51+  aptr += len + QFIXEDSZ;
52+
53+  /* Examine each answer resource record (RR) in turn. */
54+  for (i = 0; i < ancount; i++)
55+  {
56+    /* Decode the RR up to the data field. */
57+    status = ares_expand_name(aptr, abuf, alen, &rr_name, &len);
58+    if (status != ARES_SUCCESS)
59+      {
60+        break;
61+      }
62+    aptr += len;
63+    if (aptr + RRFIXEDSZ > abuf + alen)
64+      {
65+        status = ARES_EBADRESP;
66+        break;
67+      }
68+    rr_type = DNS_RR_TYPE(aptr);
69+    rr_class = DNS_RR_CLASS(aptr);
70+    rr_len = DNS_RR_LEN(aptr);
71+    aptr += RRFIXEDSZ;
72+    if (aptr + rr_len > abuf + alen)
73+      {
74+        status = ARES_EBADRESP;
75+        break;
76+      }
77+
78+    /* Check if we are really looking at a CAA record */
79+    if ((rr_class == C_IN || rr_class == C_CHAOS) && (rr_type == T_A || rr_type == T_AAAA))
80+      {
81+        acount++;
82+      }
83+
84+    /* Propagate any failures */
85+    if (status != ARES_SUCCESS)
86+      {
87+        break;
88+      }
89+
90+    /* Don't lose memory in the next iteration */
91+    ares_free(rr_name);
92+    rr_name = NULL;
93+
94+    /* Move on to the next record */
95+    aptr += rr_len;
96+  }
97+
98+  if (hostname)
99+    ares_free(hostname);
100+  if (rr_name)
101+    ares_free(rr_name);
102+
103   /* If we aren't passing through all error packets, discard packets
104    * with SERVFAIL, NOTIMP, or REFUSED response codes.
105    */
106   if (!(channel->flags & ARES_FLAG_NOCHECKRESP))
107     {
108-      if (rcode == SERVFAIL || rcode == NOTIMP || rcode == REFUSED)
109+      if (rcode == SERVFAIL || rcode == NOTIMP || rcode == REFUSED || acount == 0)
110         {
111           skip_server(channel, query, whichserver);
112           if (query->server == whichserver)
113