Renamed c-ares setup.h to ares_setup.h
[platform/upstream/c-ares.git] / ares_parse_txt_reply.c
1 /* $Id$ */
2
3 /* Copyright 1998 by the Massachusetts Institute of Technology.
4  * Copyright (C) 2009 Jakub Hrozek <jhrozek@redhat.com>
5  * Copyright (C) 2009 Yang Tse <yangsita@gmail.com>
6  *
7  * Permission to use, copy, modify, and distribute this
8  * software and its documentation for any purpose and without
9  * fee is hereby granted, provided that the above copyright
10  * notice appear in all copies and that both that copyright
11  * notice and this permission notice appear in supporting
12  * documentation, and that the name of M.I.T. not be used in
13  * advertising or publicity pertaining to distribution of the
14  * software without specific, written prior permission.
15  * M.I.T. makes no representations about the suitability of
16  * this software for any purpose.  It is provided "as is"
17  * without express or implied warranty.
18  */
19
20 #include "ares_setup.h"
21
22 #ifdef HAVE_SYS_SOCKET_H
23 #  include <sys/socket.h>
24 #endif
25 #ifdef HAVE_NETINET_IN_H
26 #  include <netinet/in.h>
27 #endif
28 #ifdef HAVE_NETDB_H
29 #  include <netdb.h>
30 #endif
31 #ifdef HAVE_ARPA_INET_H
32 #  include <arpa/inet.h>
33 #endif
34 #ifdef HAVE_ARPA_NAMESER_H
35 #  include <arpa/nameser.h>
36 #else
37 #  include "nameser.h"
38 #endif
39 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
40 #  include <arpa/nameser_compat.h>
41 #endif
42
43 #ifdef HAVE_STRINGS_H
44 #  include <strings.h>
45 #endif
46
47 #include <stdlib.h>
48 #include <string.h>
49
50 #include "ares.h"
51 #include "ares_dns.h"
52 #include "ares_private.h"
53
54 int
55 ares_parse_txt_reply (const unsigned char *abuf, int alen,
56                       struct ares_txt_reply **txt_out, int *ntxtreply)
57 {
58   size_t substr_len, str_len;
59   unsigned int qdcount, ancount, i;
60   const unsigned char *aptr;
61   const unsigned char *strptr;
62   int status, rr_type, rr_class, rr_len;
63   long len;
64   char *hostname = NULL, *rr_name = NULL;
65   struct ares_txt_reply *txt = NULL;
66
67   /* Set *txt_out to NULL for all failure cases. */
68   *txt_out = NULL;
69
70   /* Same with *ntxtreply. */
71   *ntxtreply = 0;
72
73   /* Give up if abuf doesn't have room for a header. */
74   if (alen < HFIXEDSZ)
75     return ARES_EBADRESP;
76
77   /* Fetch the question and answer count from the header. */
78   qdcount = DNS_HEADER_QDCOUNT (abuf);
79   ancount = DNS_HEADER_ANCOUNT (abuf);
80   if (qdcount != 1)
81     return ARES_EBADRESP;
82   if (ancount == 0)
83     return ARES_ENODATA;
84
85   /* Expand the name from the question, and skip past the question. */
86   aptr = abuf + HFIXEDSZ;
87   status = ares_expand_name (aptr, abuf, alen, &hostname, &len);
88   if (status != ARES_SUCCESS)
89     return status;
90
91   if (aptr + len + QFIXEDSZ > abuf + alen)
92     {
93       free (hostname);
94       return ARES_EBADRESP;
95     }
96   aptr += len + QFIXEDSZ;
97
98   /* Allocate ares_txt_reply array; ancount gives an upper bound */
99   txt = malloc ((ancount) * sizeof (struct ares_txt_reply));
100   if (!txt)
101     {
102       free (hostname);
103       return ARES_ENOMEM;
104     }
105
106   /* Initialize ares_txt_reply array */
107   for (i = 0; i < ancount; i++)
108     {
109       txt[i].txt = NULL;
110       txt[i].length = 0;
111     }
112
113   /* Examine each answer resource record (RR) in turn. */
114   for (i = 0; i < ancount; i++)
115     {
116       /* Decode the RR up to the data field. */
117       status = ares_expand_name (aptr, abuf, alen, &rr_name, &len);
118       if (status != ARES_SUCCESS)
119         {
120           break;
121         }
122       aptr += len;
123       if (aptr + RRFIXEDSZ > abuf + alen)
124         {
125           status = ARES_EBADRESP;
126           break;
127         }
128       rr_type = DNS_RR_TYPE (aptr);
129       rr_class = DNS_RR_CLASS (aptr);
130       rr_len = DNS_RR_LEN (aptr);
131       aptr += RRFIXEDSZ;
132
133       /* Check if we are really looking at a TXT record */
134       if (rr_class == C_IN && rr_type == T_TXT)
135         {
136           /*
137            * There may be multiple substrings in a single TXT record. Each
138            * substring may be up to 255 characters in length, with a
139            * "length byte" indicating the size of the substring payload.
140            * RDATA contains both the length-bytes and payloads of all
141            * substrings contained therein.
142            */
143
144           /* Compute total length to allow a single memory allocation */
145           strptr = aptr;
146           while (strptr < (aptr + rr_len))
147             {
148               substr_len = (unsigned char)*strptr;
149               txt[i].length += substr_len;
150               strptr += substr_len + 1;
151             }
152
153           /* Including null byte */
154           txt[i].txt = malloc (txt[i].length + 1);
155           if (txt[i].txt == NULL)
156             {
157               status = ARES_ENOMEM;
158               break;
159             }
160
161           /* Step through the list of substrings, concatenating them */
162           str_len = 0;
163           strptr = aptr;
164           while (strptr < (aptr + rr_len))
165             {
166               substr_len = (unsigned char)*strptr;
167               strptr++;
168               memcpy ((char *) txt[i].txt + str_len, strptr, substr_len);
169               str_len += substr_len;
170               strptr += substr_len;
171             }
172           /* Make sure we NULL-terminate */
173           txt[i].txt[txt[i].length] = '\0';
174
175           /* Move on to the next record */
176           aptr += rr_len;
177         }
178
179       /* Don't lose memory in the next iteration */
180       free (rr_name);
181       rr_name = NULL;
182     }
183
184   if (hostname)
185     free (hostname);
186   if (rr_name)
187     free (rr_name);
188
189   /* clean up on error */
190   if (status != ARES_SUCCESS)
191     {
192     for (i = 0; i < ancount; i++)
193       {
194         if (txt[i].txt)
195           free (txt[i].txt);
196       }
197       return status;
198     }
199
200   /* everything looks fine, return the data */
201   *txt_out = txt;
202   *ntxtreply = ancount;
203
204   return ARES_SUCCESS;
205 }