1 2 /* Copyright 1998 by the Massachusetts Institute of Technology. 3 * Copyright (C) 2010 Jeremy Lal <kapouer (at) melix.org> 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 #include <stdlib.h> 42 #include <string.h> 43 #include "ares.h" 44 #include "ares_dns.h" 45 #include "ares_data.h" 46 #include "ares_private.h" 47 48 int 49 ares_parse_mx_reply (const unsigned char *abuf, int alen, 50 struct ares_mx_reply **mx_out) 51 { 52 unsigned int qdcount, ancount, i; 53 const unsigned char *aptr, *vptr; 54 int status, rr_type, rr_class, rr_len; 55 long len; 56 char *hostname = NULL, *rr_name = NULL; 57 struct ares_mx_reply *mx_head = NULL; 58 struct ares_mx_reply *mx_last = NULL; 59 struct ares_mx_reply *mx_curr; 60 61 /* Set *mx_out to NULL for all failure cases. */ 62 *mx_out = NULL; 63 64 /* Give up if abuf doesn't have room for a header. */ 65 if (alen < HFIXEDSZ) 66 return ARES_EBADRESP; 67 68 /* Fetch the question and answer count from the header. */ 69 qdcount = DNS_HEADER_QDCOUNT (abuf); 70 ancount = DNS_HEADER_ANCOUNT (abuf); 71 if (qdcount != 1) 72 return ARES_EBADRESP; 73 if (ancount == 0) 74 return ARES_ENODATA; 75 76 /* Expand the name from the question, and skip past the question. */ 77 aptr = abuf + HFIXEDSZ; 78 status = ares_expand_name (aptr, abuf, alen, &hostname, &len); 79 if (status != ARES_SUCCESS) 80 return status; 81 82 if (aptr + len + QFIXEDSZ > abuf + alen) 83 { 84 free (hostname); 85 return ARES_EBADRESP; 86 } 87 aptr += len + QFIXEDSZ; 88 89 /* Examine each answer resource record (RR) in turn. */ 90 for (i = 0; i < ancount; i++) 91 { 92 /* Decode the RR up to the data field. */ 93 status = ares_expand_name (aptr, abuf, alen, &rr_name, &len); 94 if (status != ARES_SUCCESS) 95 { 96 break; 97 } 98 aptr += len; 99 if (aptr + RRFIXEDSZ > abuf + alen) 100 { 101 status = ARES_EBADRESP; 102 break; 103 } 104 rr_type = DNS_RR_TYPE (aptr); 105 rr_class = DNS_RR_CLASS (aptr); 106 rr_len = DNS_RR_LEN (aptr); 107 aptr += RRFIXEDSZ; 108 109 /* Check if we are really looking at a MX record */ 110 if (rr_class == C_IN && rr_type == T_MX) 111 { 112 /* parse the MX record itself */ 113 if (rr_len < 2) 114 { 115 status = ARES_EBADRESP; 116 break; 117 } 118 119 /* Allocate storage for this MX answer appending it to the list */ 120 mx_curr = ares_malloc_data(ARES_DATATYPE_MX_REPLY); 121 if (!mx_curr) 122 { 123 status = ARES_ENOMEM; 124 break; 125 } 126 if (mx_last) 127 { 128 mx_last->next = mx_curr; 129 } 130 else 131 { 132 mx_head = mx_curr; 133 } 134 mx_last = mx_curr; 135 136 vptr = aptr; 137 mx_curr->priority = DNS__16BIT(vptr); 138 vptr += sizeof(unsigned short); 139 140 status = ares_expand_name (vptr, abuf, alen, &mx_curr->host, &len); 141 if (status != ARES_SUCCESS) 142 break; 143 } 144 145 /* Don't lose memory in the next iteration */ 146 free (rr_name); 147 rr_name = NULL; 148 149 /* Move on to the next record */ 150 aptr += rr_len; 151 } 152 153 if (hostname) 154 free (hostname); 155 if (rr_name) 156 free (rr_name); 157 158 /* clean up on error */ 159 if (status != ARES_SUCCESS) 160 { 161 if (mx_head) 162 ares_free_data (mx_head); 163 return status; 164 } 165 166 /* everything looks fine, return the data */ 167 *mx_out = mx_head; 168 169 return ARES_SUCCESS; 170 } 171