Makefile.am: increment -version-info for 1.10.0 release
[platform/upstream/c-ares.git] / ares_gethostbyname.c
1
2 /* Copyright 1998, 2011, 2013 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_NETINET_IN_H
20 #  include <netinet/in.h>
21 #endif
22 #ifdef HAVE_NETDB_H
23 #  include <netdb.h>
24 #endif
25 #ifdef HAVE_ARPA_INET_H
26 #  include <arpa/inet.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 "ares.h"
42 #include "ares_inet_net_pton.h"
43 #include "bitncmp.h"
44 #include "ares_platform.h"
45 #include "ares_nowarn.h"
46 #include "ares_private.h"
47
48 #ifdef WATT32
49 #undef WIN32
50 #endif
51
52 struct host_query {
53   /* Arguments passed to ares_gethostbyname() */
54   ares_channel channel;
55   char *name;
56   ares_host_callback callback;
57   void *arg;
58   int sent_family; /* this family is what was is being used */
59   int want_family; /* this family is what is asked for in the API */
60   const char *remaining_lookups;
61   int timeouts;
62 };
63
64 static void next_lookup(struct host_query *hquery, int status_code);
65 static void host_callback(void *arg, int status, int timeouts,
66                           unsigned char *abuf, int alen);
67 static void end_hquery(struct host_query *hquery, int status,
68                        struct hostent *host);
69 static int fake_hostent(const char *name, int family,
70                         ares_host_callback callback, void *arg);
71 static int file_lookup(const char *name, int family, struct hostent **host);
72 static void sort_addresses(struct hostent *host,
73                            const struct apattern *sortlist, int nsort);
74 static void sort6_addresses(struct hostent *host,
75                             const struct apattern *sortlist, int nsort);
76 static int get_address_index(const struct in_addr *addr,
77                              const struct apattern *sortlist, int nsort);
78 static int get6_address_index(const struct ares_in6_addr *addr,
79                               const struct apattern *sortlist, int nsort);
80
81 void ares_gethostbyname(ares_channel channel, const char *name, int family,
82                         ares_host_callback callback, void *arg)
83 {
84   struct host_query *hquery;
85
86   /* Right now we only know how to look up Internet addresses - and unspec
87      means try both basically. */
88   switch (family) {
89   case AF_INET:
90   case AF_INET6:
91   case AF_UNSPEC:
92     break;
93   default:
94     callback(arg, ARES_ENOTIMP, 0, NULL);
95     return;
96   }
97
98   if (fake_hostent(name, family, callback, arg))
99     return;
100
101   /* Allocate and fill in the host query structure. */
102   hquery = malloc(sizeof(struct host_query));
103   if (!hquery)
104     {
105       callback(arg, ARES_ENOMEM, 0, NULL);
106       return;
107     }
108   hquery->channel = channel;
109   hquery->name = strdup(name);
110   hquery->want_family = family;
111   hquery->sent_family = -1; /* nothing is sent yet */
112   if (!hquery->name) {
113     free(hquery);
114     callback(arg, ARES_ENOMEM, 0, NULL);
115     return;
116   }
117   hquery->callback = callback;
118   hquery->arg = arg;
119   hquery->remaining_lookups = channel->lookups;
120   hquery->timeouts = 0;
121
122   /* Start performing lookups according to channel->lookups. */
123   next_lookup(hquery, ARES_ECONNREFUSED /* initial error code */);
124 }
125
126 static void next_lookup(struct host_query *hquery, int status_code)
127 {
128   const char *p;
129   struct hostent *host;
130   int status = status_code;
131
132   for (p = hquery->remaining_lookups; *p; p++)
133     {
134       switch (*p)
135         {
136         case 'b':
137           /* DNS lookup */
138           hquery->remaining_lookups = p + 1;
139           if ((hquery->want_family == AF_INET6) ||
140               (hquery->want_family == AF_UNSPEC)) {
141             /* if inet6 or unspec, start out with AAAA */
142             hquery->sent_family = AF_INET6;
143             ares_search(hquery->channel, hquery->name, C_IN, T_AAAA,
144                         host_callback, hquery);
145           }
146           else {
147             hquery->sent_family = AF_INET;
148             ares_search(hquery->channel, hquery->name, C_IN, T_A,
149                         host_callback, hquery);
150           }
151           return;
152
153         case 'f':
154           /* Host file lookup */
155           status = file_lookup(hquery->name, hquery->want_family, &host);
156
157           /* this status check below previously checked for !ARES_ENOTFOUND,
158              but we should not assume that this single error code is the one
159              that can occur, as that is in fact no longer the case */
160           if (status == ARES_SUCCESS)
161             {
162               end_hquery(hquery, status, host);
163               return;
164             }
165           status = status_code;   /* Use original status code */
166           break;
167         }
168     }
169   end_hquery(hquery, status, NULL);
170 }
171
172 static void host_callback(void *arg, int status, int timeouts,
173                           unsigned char *abuf, int alen)
174 {
175   struct host_query *hquery = (struct host_query *) arg;
176   ares_channel channel = hquery->channel;
177   struct hostent *host = NULL;
178
179   hquery->timeouts += timeouts;
180   if (status == ARES_SUCCESS)
181     {
182       if (hquery->sent_family == AF_INET)
183         {
184           status = ares_parse_a_reply(abuf, alen, &host, NULL, NULL);
185           if (host && channel->nsort)
186             sort_addresses(host, channel->sortlist, channel->nsort);
187         }
188       else if (hquery->sent_family == AF_INET6)
189         {
190           status = ares_parse_aaaa_reply(abuf, alen, &host, NULL, NULL);
191           if ((status == ARES_ENODATA || status == ARES_EBADRESP) &&
192                hquery->want_family == AF_UNSPEC) {
193             /* The query returned something but either there were no AAAA
194                records (e.g. just CNAME) or the response was malformed.  Try
195                looking up A instead. */
196             hquery->sent_family = AF_INET;
197             ares_search(hquery->channel, hquery->name, C_IN, T_A,
198                         host_callback, hquery);
199             return;
200           }
201           if (host && channel->nsort)
202             sort6_addresses(host, channel->sortlist, channel->nsort);
203         }
204       end_hquery(hquery, status, host);
205     }
206   else if ((status == ARES_ENODATA || status == ARES_EBADRESP ||
207             status == ARES_ETIMEOUT) && (hquery->sent_family == AF_INET6 &&
208             hquery->want_family == AF_UNSPEC))
209     {
210       /* The AAAA query yielded no useful result.  Now look up an A instead. */
211       hquery->sent_family = AF_INET;
212       ares_search(hquery->channel, hquery->name, C_IN, T_A, host_callback,
213                   hquery);
214     }
215   else if (status == ARES_EDESTRUCTION)
216     end_hquery(hquery, status, NULL);
217   else
218     next_lookup(hquery, status);
219 }
220
221 static void end_hquery(struct host_query *hquery, int status,
222                        struct hostent *host)
223 {
224   hquery->callback(hquery->arg, status, hquery->timeouts, host);
225   if (host)
226     ares_free_hostent(host);
227   free(hquery->name);
228   free(hquery);
229 }
230
231 /* If the name looks like an IP address, fake up a host entry, end the
232  * query immediately, and return true.  Otherwise return false.
233  */
234 static int fake_hostent(const char *name, int family,
235                         ares_host_callback callback, void *arg)
236 {
237   struct hostent hostent;
238   char *aliases[1] = { NULL };
239   char *addrs[2];
240   int result = 0;
241   struct in_addr in;
242   struct ares_in6_addr in6;
243
244   if (family == AF_INET || family == AF_INET6)
245     {
246       /* It only looks like an IP address if it's all numbers and dots. */
247       int numdots = 0, valid = 1;
248       const char *p;
249       for (p = name; *p; p++)
250         {
251           if (!ISDIGIT(*p) && *p != '.') {
252             valid = 0;
253             break;
254           } else if (*p == '.') {
255             numdots++;
256           }
257         }
258
259       /* if we don't have 3 dots, it is illegal
260        * (although inet_addr doesn't think so).
261        */
262       if (numdots != 3 || !valid)
263         result = 0;
264       else
265         result = ((in.s_addr = inet_addr(name)) == INADDR_NONE ? 0 : 1);
266
267       if (result)
268         family = AF_INET;
269     }
270   if (family == AF_INET6)
271     result = (ares_inet_pton(AF_INET6, name, &in6) < 1 ? 0 : 1);
272
273   if (!result)
274     return 0;
275
276   if (family == AF_INET)
277     {
278       hostent.h_length = (int)sizeof(struct in_addr);
279       addrs[0] = (char *)&in;
280     }
281   else if (family == AF_INET6)
282     {
283       hostent.h_length = (int)sizeof(struct ares_in6_addr);
284       addrs[0] = (char *)&in6;
285     }
286   /* Duplicate the name, to avoid a constness violation. */
287   hostent.h_name = strdup(name);
288   if (!hostent.h_name)
289     {
290       callback(arg, ARES_ENOMEM, 0, NULL);
291       return 1;
292     }
293
294   /* Fill in the rest of the host structure and terminate the query. */
295   addrs[1] = NULL;
296   hostent.h_aliases = aliases;
297   hostent.h_addrtype = aresx_sitoss(family);
298   hostent.h_addr_list = addrs;
299   callback(arg, ARES_SUCCESS, 0, &hostent);
300
301   free((char *)(hostent.h_name));
302   return 1;
303 }
304
305 /* This is an API method */
306 int ares_gethostbyname_file(ares_channel channel, const char *name,
307                             int family, struct hostent **host)
308 {
309   int result;
310
311   /* We only take the channel to ensure that ares_init() been called. */
312   if(channel == NULL)
313     {
314       /* Anything will do, really.  This seems fine, and is consistent with
315          other error cases. */
316       *host = NULL;
317       return ARES_ENOTFOUND;
318     }
319
320   /* Just chain to the internal implementation we use here; it's exactly
321    * what we want.
322    */
323   result = file_lookup(name, family, host);
324   if(result != ARES_SUCCESS)
325     {
326       /* We guarantee a NULL hostent on failure. */
327       *host = NULL;
328     }
329   return result;
330 }
331
332 static int file_lookup(const char *name, int family, struct hostent **host)
333 {
334   FILE *fp;
335   char **alias;
336   int status;
337   int error;
338
339 #ifdef WIN32
340   char PATH_HOSTS[MAX_PATH];
341   win_platform platform;
342
343   PATH_HOSTS[0] = '\0';
344
345   platform = ares__getplatform();
346
347   if (platform == WIN_NT) {
348     char tmp[MAX_PATH];
349     HKEY hkeyHosts;
350
351     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ,
352                      &hkeyHosts) == ERROR_SUCCESS)
353     {
354       DWORD dwLength = MAX_PATH;
355       RegQueryValueEx(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp,
356                       &dwLength);
357       ExpandEnvironmentStrings(tmp, PATH_HOSTS, MAX_PATH);
358       RegCloseKey(hkeyHosts);
359     }
360   }
361   else if (platform == WIN_9X)
362     GetWindowsDirectory(PATH_HOSTS, MAX_PATH);
363   else
364     return ARES_ENOTFOUND;
365
366   strcat(PATH_HOSTS, WIN_PATH_HOSTS);
367
368 #elif defined(WATT32)
369   extern const char *_w32_GetHostsFile (void);
370   const char *PATH_HOSTS = _w32_GetHostsFile();
371
372   if (!PATH_HOSTS)
373     return ARES_ENOTFOUND;
374 #endif
375
376   fp = fopen(PATH_HOSTS, "r");
377   if (!fp)
378     {
379       error = ERRNO;
380       switch(error)
381         {
382         case ENOENT:
383         case ESRCH:
384           return ARES_ENOTFOUND;
385         default:
386           DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n",
387                          error, strerror(error)));
388           DEBUGF(fprintf(stderr, "Error opening file: %s\n",
389                          PATH_HOSTS));
390           *host = NULL;
391           return ARES_EFILE;
392         }
393     }
394   while ((status = ares__get_hostent(fp, family, host)) == ARES_SUCCESS)
395     {
396       if (strcasecmp((*host)->h_name, name) == 0)
397         break;
398       for (alias = (*host)->h_aliases; *alias; alias++)
399         {
400           if (strcasecmp(*alias, name) == 0)
401             break;
402         }
403       if (*alias)
404         break;
405       ares_free_hostent(*host);
406     }
407   fclose(fp);
408   if (status == ARES_EOF)
409     status = ARES_ENOTFOUND;
410   if (status != ARES_SUCCESS)
411     *host = NULL;
412   return status;
413 }
414
415 static void sort_addresses(struct hostent *host,
416                            const struct apattern *sortlist, int nsort)
417 {
418   struct in_addr a1, a2;
419   int i1, i2, ind1, ind2;
420
421   /* This is a simple insertion sort, not optimized at all.  i1 walks
422    * through the address list, with the loop invariant that everything
423    * to the left of i1 is sorted.  In the loop body, the value at i1 is moved
424    * back through the list (via i2) until it is in sorted order.
425    */
426   for (i1 = 0; host->h_addr_list[i1]; i1++)
427     {
428       memcpy(&a1, host->h_addr_list[i1], sizeof(struct in_addr));
429       ind1 = get_address_index(&a1, sortlist, nsort);
430       for (i2 = i1 - 1; i2 >= 0; i2--)
431         {
432           memcpy(&a2, host->h_addr_list[i2], sizeof(struct in_addr));
433           ind2 = get_address_index(&a2, sortlist, nsort);
434           if (ind2 <= ind1)
435             break;
436           memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct in_addr));
437         }
438       memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct in_addr));
439     }
440 }
441
442 /* Find the first entry in sortlist which matches addr.  Return nsort
443  * if none of them match.
444  */
445 static int get_address_index(const struct in_addr *addr,
446                              const struct apattern *sortlist,
447                              int nsort)
448 {
449   int i;
450
451   for (i = 0; i < nsort; i++)
452     {
453       if (sortlist[i].family != AF_INET)
454         continue;
455       if (sortlist[i].type == PATTERN_MASK)
456         {
457           if ((addr->s_addr & sortlist[i].mask.addr4.s_addr)
458               == sortlist[i].addrV4.s_addr)
459             break;
460         }
461       else
462         {
463           if (!ares__bitncmp(&addr->s_addr, &sortlist[i].addrV4.s_addr,
464                              sortlist[i].mask.bits))
465             break;
466         }
467     }
468   return i;
469 }
470
471 static void sort6_addresses(struct hostent *host,
472                             const struct apattern *sortlist, int nsort)
473 {
474   struct ares_in6_addr a1, a2;
475   int i1, i2, ind1, ind2;
476
477   /* This is a simple insertion sort, not optimized at all.  i1 walks
478    * through the address list, with the loop invariant that everything
479    * to the left of i1 is sorted.  In the loop body, the value at i1 is moved
480    * back through the list (via i2) until it is in sorted order.
481    */
482   for (i1 = 0; host->h_addr_list[i1]; i1++)
483     {
484       memcpy(&a1, host->h_addr_list[i1], sizeof(struct ares_in6_addr));
485       ind1 = get6_address_index(&a1, sortlist, nsort);
486       for (i2 = i1 - 1; i2 >= 0; i2--)
487         {
488           memcpy(&a2, host->h_addr_list[i2], sizeof(struct ares_in6_addr));
489           ind2 = get6_address_index(&a2, sortlist, nsort);
490           if (ind2 <= ind1)
491             break;
492           memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct ares_in6_addr));
493         }
494       memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct ares_in6_addr));
495     }
496 }
497
498 /* Find the first entry in sortlist which matches addr.  Return nsort
499  * if none of them match.
500  */
501 static int get6_address_index(const struct ares_in6_addr *addr,
502                               const struct apattern *sortlist,
503                               int nsort)
504 {
505   int i;
506
507   for (i = 0; i < nsort; i++)
508     {
509       if (sortlist[i].family != AF_INET6)
510         continue;
511       if (!ares__bitncmp(addr, &sortlist[i].addrV6, sortlist[i].mask.bits))
512         break;
513     }
514   return i;
515 }