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