ares_parse_txt_reply.c revision 968bf19396ad404e89420f5d67900fce13f4186c
1014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
2014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch/* Copyright 1998 by the Massachusetts Institute of Technology.
3014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * Copyright (C) 2009 by Jakub Hrozek <jhrozek@redhat.com>
4014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch *
5014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * Permission to use, copy, modify, and distribute this
6014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * software and its documentation for any purpose and without
7014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * fee is hereby granted, provided that the above copyright
8014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * notice appear in all copies and that both that copyright
9014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * notice and this permission notice appear in supporting
10 * documentation, and that the name of M.I.T. not be used in
11 * advertising or publicity pertaining to distribution of the
12 * software without specific, written prior permission.
13 * M.I.T. makes no representations about the suitability of
14 * this software for any purpose.  It is provided "as is"
15 * without express or implied warranty.
16 */
17
18#include "ares_setup.h"
19
20#ifdef HAVE_SYS_SOCKET_H
21#  include <sys/socket.h>
22#endif
23#ifdef HAVE_NETINET_IN_H
24#  include <netinet/in.h>
25#endif
26#ifdef HAVE_NETDB_H
27#  include <netdb.h>
28#endif
29#ifdef HAVE_ARPA_INET_H
30#  include <arpa/inet.h>
31#endif
32#ifdef HAVE_ARPA_NAMESER_H
33#  include <arpa/nameser.h>
34#else
35#  include "nameser.h"
36#endif
37#ifdef HAVE_ARPA_NAMESER_COMPAT_H
38#  include <arpa/nameser_compat.h>
39#endif
40
41#ifdef HAVE_STRINGS_H
42#  include <strings.h>
43#endif
44
45#include <stdlib.h>
46#include <string.h>
47
48#include "ares.h"
49#include "ares_dns.h"
50#include "ares_data.h"
51#include "ares_private.h"
52
53int
54ares_parse_txt_reply (const unsigned char *abuf, int alen,
55                      struct ares_txt_reply **txt_out)
56{
57  size_t substr_len, str_len;
58  unsigned int qdcount, ancount, i;
59  const unsigned char *aptr;
60  const unsigned char *strptr;
61  int status, rr_type, rr_class, rr_len;
62  long len;
63  char *hostname = NULL, *rr_name = NULL;
64  struct ares_txt_reply *txt_head = NULL;
65  struct ares_txt_reply *txt_last = NULL;
66  struct ares_txt_reply *txt_curr;
67
68  /* Set *txt_out to NULL for all failure cases. */
69  *txt_out = NULL;
70
71  /* Give up if abuf doesn't have room for a header. */
72  if (alen < HFIXEDSZ)
73    return ARES_EBADRESP;
74
75  /* Fetch the question and answer count from the header. */
76  qdcount = DNS_HEADER_QDCOUNT (abuf);
77  ancount = DNS_HEADER_ANCOUNT (abuf);
78  if (qdcount != 1)
79    return ARES_EBADRESP;
80  if (ancount == 0)
81    return ARES_ENODATA;
82
83  /* Expand the name from the question, and skip past the question. */
84  aptr = abuf + HFIXEDSZ;
85  status = ares_expand_name (aptr, abuf, alen, &hostname, &len);
86  if (status != ARES_SUCCESS)
87    return status;
88
89  if (aptr + len + QFIXEDSZ > abuf + alen)
90    {
91      free (hostname);
92      return ARES_EBADRESP;
93    }
94  aptr += len + QFIXEDSZ;
95
96  /* Examine each answer resource record (RR) in turn. */
97  for (i = 0; i < ancount; i++)
98    {
99      /* Decode the RR up to the data field. */
100      status = ares_expand_name (aptr, abuf, alen, &rr_name, &len);
101      if (status != ARES_SUCCESS)
102        {
103          break;
104        }
105      aptr += len;
106      if (aptr + RRFIXEDSZ > abuf + alen)
107        {
108          status = ARES_EBADRESP;
109          break;
110        }
111      rr_type = DNS_RR_TYPE (aptr);
112      rr_class = DNS_RR_CLASS (aptr);
113      rr_len = DNS_RR_LEN (aptr);
114      aptr += RRFIXEDSZ;
115
116      /* Check if we are really looking at a TXT record */
117      if (rr_class == C_IN && rr_type == T_TXT)
118        {
119          /* Allocate storage for this TXT answer appending it to the list */
120          txt_curr = ares_malloc_data(ARES_DATATYPE_TXT_REPLY);
121          if (!txt_curr)
122            {
123              status = ARES_ENOMEM;
124              break;
125            }
126          if (txt_last)
127            {
128              txt_last->next = txt_curr;
129            }
130          else
131            {
132              txt_head = txt_curr;
133            }
134          txt_last = txt_curr;
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_curr->length += substr_len;
150              strptr += substr_len + 1;
151            }
152
153          /* Including null byte */
154          txt_curr->txt = malloc (txt_curr->length + 1);
155          if (txt_curr->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_curr->txt + str_len, strptr, substr_len);
169              str_len += substr_len;
170              strptr += substr_len;
171            }
172          /* Make sure we NULL-terminate */
173          *((char *) txt_curr->txt + txt_curr->length) = '\0';
174        }
175
176      /* Don't lose memory in the next iteration */
177      free (rr_name);
178      rr_name = NULL;
179
180      /* Move on to the next record */
181      aptr += rr_len;
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      if (txt_head)
193        ares_free_data (txt_head);
194      return status;
195    }
196
197  /* everything looks fine, return the data */
198  *txt_out = txt_head;
199
200  return ARES_SUCCESS;
201}
202