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 #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 /* AIX portability check */
     49 #ifndef T_SRV
     50 #  define T_SRV 33 /* server selection */
     51 #endif
     52 
     53 int
     54 ares_parse_srv_reply (const unsigned char *abuf, int alen,
     55                       struct ares_srv_reply **srv_out)
     56 {
     57   unsigned int qdcount, ancount, i;
     58   const unsigned char *aptr, *vptr;
     59   int status, rr_type, rr_class, rr_len;
     60   long len;
     61   char *hostname = NULL, *rr_name = NULL;
     62   struct ares_srv_reply *srv_head = NULL;
     63   struct ares_srv_reply *srv_last = NULL;
     64   struct ares_srv_reply *srv_curr;
     65 
     66   /* Set *srv_out to NULL for all failure cases. */
     67   *srv_out = NULL;
     68 
     69   /* Give up if abuf doesn't have room for a header. */
     70   if (alen < HFIXEDSZ)
     71     return ARES_EBADRESP;
     72 
     73   /* Fetch the question and answer count from the header. */
     74   qdcount = DNS_HEADER_QDCOUNT (abuf);
     75   ancount = DNS_HEADER_ANCOUNT (abuf);
     76   if (qdcount != 1)
     77     return ARES_EBADRESP;
     78   if (ancount == 0)
     79     return ARES_ENODATA;
     80 
     81   /* Expand the name from the question, and skip past the question. */
     82   aptr = abuf + HFIXEDSZ;
     83   status = ares_expand_name (aptr, abuf, alen, &hostname, &len);
     84   if (status != ARES_SUCCESS)
     85     return status;
     86 
     87   if (aptr + len + QFIXEDSZ > abuf + alen)
     88     {
     89       free (hostname);
     90       return ARES_EBADRESP;
     91     }
     92   aptr += len + QFIXEDSZ;
     93 
     94   /* Examine each answer resource record (RR) in turn. */
     95   for (i = 0; i < ancount; i++)
     96     {
     97       /* Decode the RR up to the data field. */
     98       status = ares_expand_name (aptr, abuf, alen, &rr_name, &len);
     99       if (status != ARES_SUCCESS)
    100         {
    101           break;
    102         }
    103       aptr += len;
    104       if (aptr + RRFIXEDSZ > abuf + alen)
    105         {
    106           status = ARES_EBADRESP;
    107           break;
    108         }
    109       rr_type = DNS_RR_TYPE (aptr);
    110       rr_class = DNS_RR_CLASS (aptr);
    111       rr_len = DNS_RR_LEN (aptr);
    112       aptr += RRFIXEDSZ;
    113 
    114       /* Check if we are really looking at a SRV record */
    115       if (rr_class == C_IN && rr_type == T_SRV)
    116         {
    117           /* parse the SRV record itself */
    118           if (rr_len < 6)
    119             {
    120               status = ARES_EBADRESP;
    121               break;
    122             }
    123 
    124           /* Allocate storage for this SRV answer appending it to the list */
    125           srv_curr = ares_malloc_data(ARES_DATATYPE_SRV_REPLY);
    126           if (!srv_curr)
    127             {
    128               status = ARES_ENOMEM;
    129               break;
    130             }
    131           if (srv_last)
    132             {
    133               srv_last->next = srv_curr;
    134             }
    135           else
    136             {
    137               srv_head = srv_curr;
    138             }
    139           srv_last = srv_curr;
    140 
    141           vptr = aptr;
    142           srv_curr->priority = DNS__16BIT(vptr);
    143           vptr += sizeof(unsigned short);
    144           srv_curr->weight = DNS__16BIT(vptr);
    145           vptr += sizeof(unsigned short);
    146           srv_curr->port = DNS__16BIT(vptr);
    147           vptr += sizeof(unsigned short);
    148 
    149           status = ares_expand_name (vptr, abuf, alen, &srv_curr->host, &len);
    150           if (status != ARES_SUCCESS)
    151             break;
    152         }
    153 
    154       /* Don't lose memory in the next iteration */
    155       free (rr_name);
    156       rr_name = NULL;
    157 
    158       /* Move on to the next record */
    159       aptr += rr_len;
    160     }
    161 
    162   if (hostname)
    163     free (hostname);
    164   if (rr_name)
    165     free (rr_name);
    166 
    167   /* clean up on error */
    168   if (status != ARES_SUCCESS)
    169     {
    170       if (srv_head)
    171         ares_free_data (srv_head);
    172       return status;
    173     }
    174 
    175   /* everything looks fine, return the data */
    176   *srv_out = srv_head;
    177 
    178   return ARES_SUCCESS;
    179 }
    180