Home | History | Annotate | Download | only in c-ares
      1 
      2 /* Copyright 1998 by the Massachusetts Institute of Technology.
      3  * Copyright (C) 2009 by Jakub Hrozek <jhrozek (at) redhat.com>
      4  *
      5  * Permission to use, copy, modify, and distribute this
      6  * software and its documentation for any purpose and without
      7  * fee is hereby granted, provided that the above copyright
      8  * notice appear in all copies and that both that copyright
      9  * 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 
     53 int
     54 ares_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