adig: RFC4034 resource record type detection
[platform/upstream/c-ares.git] / ares_parse_ptr_reply.c
1
2 /* Copyright 1998 by the Massachusetts Institute of Technology.
3  *
4  * Permission to use, copy, modify, and distribute this
5  * software and its documentation for any purpose and without
6  * fee is hereby granted, provided that the above copyright
7  * notice appear in all copies and that both that copyright
8  * notice and this permission notice appear in supporting
9  * documentation, and that the name of M.I.T. not be used in
10  * advertising or publicity pertaining to distribution of the
11  * software without specific, written prior permission.
12  * M.I.T. makes no representations about the suitability of
13  * this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  */
16
17 #include "ares_setup.h"
18
19 #ifdef HAVE_SYS_SOCKET_H
20 #  include <sys/socket.h>
21 #endif
22 #ifdef HAVE_NETINET_IN_H
23 #  include <netinet/in.h>
24 #endif
25 #ifdef HAVE_NETDB_H
26 #  include <netdb.h>
27 #endif
28 #ifdef HAVE_ARPA_NAMESER_H
29 #  include <arpa/nameser.h>
30 #else
31 #  include "nameser.h"
32 #endif
33 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
34 #  include <arpa/nameser_compat.h>
35 #endif
36
37 #ifdef HAVE_STRINGS_H
38 #  include <strings.h>
39 #endif
40
41 #include <stdlib.h>
42 #include <string.h>
43 #include "ares.h"
44 #include "ares_dns.h"
45 #include "ares_private.h"
46
47 int ares_parse_ptr_reply(const unsigned char *abuf, int alen, const void *addr,
48                          int addrlen, int family, struct hostent **host)
49 {
50   unsigned int qdcount, ancount;
51   int status, i, rr_type, rr_class, rr_len;
52   long len;
53   const unsigned char *aptr;
54   char *ptrname, *hostname, *rr_name, *rr_data;
55   struct hostent *hostent;
56   int aliascnt = 0;
57   int alias_alloc = 8;
58   char ** aliases;
59
60   /* Set *host to NULL for all failure cases. */
61   *host = NULL;
62
63   /* Give up if abuf doesn't have room for a header. */
64   if (alen < HFIXEDSZ)
65     return ARES_EBADRESP;
66
67   /* Fetch the question and answer count from the header. */
68   qdcount = DNS_HEADER_QDCOUNT(abuf);
69   ancount = DNS_HEADER_ANCOUNT(abuf);
70   if (qdcount != 1)
71     return ARES_EBADRESP;
72
73   /* Expand the name from the question, and skip past the question. */
74   aptr = abuf + HFIXEDSZ;
75   status = ares__expand_name_for_response(aptr, abuf, alen, &ptrname, &len);
76   if (status != ARES_SUCCESS)
77     return status;
78   if (aptr + len + QFIXEDSZ > abuf + alen)
79     {
80       free(ptrname);
81       return ARES_EBADRESP;
82     }
83   aptr += len + QFIXEDSZ;
84
85   /* Examine each answer resource record (RR) in turn. */
86   hostname = NULL;
87   aliases = malloc(alias_alloc * sizeof(char *));
88   if (!aliases)
89     {
90       free(ptrname);
91       return ARES_ENOMEM;
92     }
93   for (i = 0; i < (int)ancount; i++)
94     {
95       /* Decode the RR up to the data field. */
96       status = ares__expand_name_for_response(aptr, abuf, alen, &rr_name, &len);
97       if (status != ARES_SUCCESS)
98         break;
99       aptr += len;
100       if (aptr + RRFIXEDSZ > abuf + alen)
101         {
102           status = ARES_EBADRESP;
103           break;
104         }
105       rr_type = DNS_RR_TYPE(aptr);
106       rr_class = DNS_RR_CLASS(aptr);
107       rr_len = DNS_RR_LEN(aptr);
108       aptr += RRFIXEDSZ;
109
110       if (rr_class == C_IN && rr_type == T_PTR
111           && strcasecmp(rr_name, ptrname) == 0)
112         {
113           /* Decode the RR data and set hostname to it. */
114           status = ares__expand_name_for_response(aptr, abuf, alen, &rr_data,
115                                                   &len);
116           if (status != ARES_SUCCESS)
117             break;
118           if (hostname)
119             free(hostname);
120           hostname = rr_data;
121           aliases[aliascnt] = malloc((strlen(rr_data)+1) * sizeof(char *));
122           if (!aliases[aliascnt])
123             {
124               status = ARES_ENOMEM;
125               break;
126             }
127           strncpy(aliases[aliascnt], rr_data, strlen(rr_data)+1);
128           aliascnt++;
129           if (aliascnt >= alias_alloc) {
130             char **ptr;
131             alias_alloc *= 2;
132             ptr = realloc(aliases, alias_alloc * sizeof(char *));
133             if(!ptr) {
134               status = ARES_ENOMEM;
135               break;
136             }
137             aliases = ptr;
138           }
139         }
140
141       if (rr_class == C_IN && rr_type == T_CNAME)
142         {
143           /* Decode the RR data and replace ptrname with it. */
144           status = ares__expand_name_for_response(aptr, abuf, alen, &rr_data,
145                                                   &len);
146           if (status != ARES_SUCCESS)
147             break;
148           free(ptrname);
149           ptrname = rr_data;
150         }
151
152       free(rr_name);
153       aptr += rr_len;
154       if (aptr > abuf + alen)
155         {
156           status = ARES_EBADRESP;
157           break;
158         }
159     }
160
161   if (status == ARES_SUCCESS && !hostname)
162     status = ARES_ENODATA;
163   if (status == ARES_SUCCESS)
164     {
165       /* We got our answer.  Allocate memory to build the host entry. */
166       hostent = malloc(sizeof(struct hostent));
167       if (hostent)
168         {
169           hostent->h_addr_list = malloc(2 * sizeof(char *));
170           if (hostent->h_addr_list)
171             {
172               hostent->h_addr_list[0] = malloc(addrlen);
173               if (hostent->h_addr_list[0])
174                 {
175                   hostent->h_aliases = malloc((aliascnt+1) * sizeof (char *));
176                   if (hostent->h_aliases)
177                     {
178                       /* Fill in the hostent and return successfully. */
179                       hostent->h_name = hostname;
180                       for (i=0 ; i<aliascnt ; i++)
181                         hostent->h_aliases[i] = aliases[i];
182                       hostent->h_aliases[aliascnt] = NULL;
183                       hostent->h_addrtype = family;
184                       hostent->h_length = addrlen;
185                       memcpy(hostent->h_addr_list[0], addr, addrlen);
186                       hostent->h_addr_list[1] = NULL;
187                       *host = hostent;
188                       free(aliases);
189                       free(ptrname);
190                       return ARES_SUCCESS;
191                     }
192                   free(hostent->h_addr_list[0]);
193                 }
194               free(hostent->h_addr_list);
195             }
196           free(hostent);
197         }
198       status = ARES_ENOMEM;
199     }
200   for (i=0 ; i<aliascnt ; i++)
201     if (aliases[i]) 
202       free(aliases[i]);
203   free(aliases);
204   if (hostname)
205     free(hostname);
206   free(ptrname);
207   return status;
208 }