Home | History | Annotate | Download | only in c-ares
      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