Make the query callbacks return the number of timeouts that happened during the execu...
[platform/upstream/c-ares.git] / ares_gethostbyname.c
1 /* $Id$ */
2
3 /* Copyright 1998 by the Massachusetts Institute of Technology.
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 "setup.h"
19
20 #if defined(WIN32) && !defined(WATT32)
21 #include "nameser.h"
22 #else
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 #include <netdb.h>
27 #ifdef HAVE_ARPA_NAMESER_H
28 #include <arpa/nameser.h>
29 #endif
30 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
31 #include <arpa/nameser_compat.h>
32 #endif
33 #endif
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <ctype.h>
39
40 #include "ares.h"
41 #include "ares_private.h"
42 #include "inet_net_pton.h"
43 #include "bitncmp.h"
44
45 #ifdef WATT32
46 #undef WIN32
47 #endif
48
49 struct host_query {
50   /* Arguments passed to ares_gethostbyname() */
51   ares_channel channel;
52   char *name;
53   ares_host_callback callback;
54   void *arg;
55   int family;
56   const char *remaining_lookups;
57   int timeouts;
58 };
59
60 static void next_lookup(struct host_query *hquery);
61 static void host_callback(void *arg, int status, int timeouts,
62                           unsigned char *abuf, int alen);
63 static void end_hquery(struct host_query *hquery, int status,
64                        struct hostent *host);
65 static int fake_hostent(const char *name, int family, ares_host_callback callback,
66                         void *arg);
67 static int file_lookup(const char *name, int family, struct hostent **host);
68 static void sort_addresses(struct hostent *host, struct apattern *sortlist,
69                            int nsort);
70 static void sort6_addresses(struct hostent *host, struct apattern *sortlist,
71                            int nsort);
72 static int get_address_index(struct in_addr *addr, struct apattern *sortlist,
73                              int nsort);
74 static int get6_address_index(struct in6_addr *addr, struct apattern *sortlist,
75                              int nsort);
76
77 void ares_gethostbyname(ares_channel channel, const char *name, int family,
78                         ares_host_callback callback, void *arg)
79 {
80   struct host_query *hquery;
81
82   /* Right now we only know how to look up Internet addresses. */
83   if (family != AF_INET && family != AF_INET6)
84     {
85       callback(arg, ARES_ENOTIMP, 0, NULL);
86       return;
87     }
88
89   if (fake_hostent(name, family, callback, arg))
90     return;
91
92   /* Allocate and fill in the host query structure. */
93   hquery = malloc(sizeof(struct host_query));
94   if (!hquery)
95     {
96       callback(arg, ARES_ENOMEM, 0, NULL);
97       return;
98     }
99   hquery->channel = channel;
100   hquery->name = strdup(name);
101   hquery->family = family;
102   if (!hquery->name)
103     {
104       free(hquery);
105       callback(arg, ARES_ENOMEM, 0, NULL);
106       return;
107     }
108   hquery->callback = callback;
109   hquery->arg = arg;
110   hquery->remaining_lookups = channel->lookups;
111   hquery->timeouts = 0;
112
113   /* Start performing lookups according to channel->lookups. */
114   next_lookup(hquery);
115 }
116
117 static void next_lookup(struct host_query *hquery)
118 {
119   int status;
120   const char *p;
121   struct hostent *host;
122
123   for (p = hquery->remaining_lookups; *p; p++)
124     {
125       switch (*p)
126         {
127         case 'b':
128           /* DNS lookup */
129           hquery->remaining_lookups = p + 1;
130           if (hquery->family == AF_INET6)
131             ares_search(hquery->channel, hquery->name, C_IN, T_AAAA, host_callback,
132                         hquery);
133           else
134             ares_search(hquery->channel, hquery->name, C_IN, T_A, host_callback,
135                         hquery);
136           return;
137
138         case 'f':
139           /* Host file lookup */
140           status = file_lookup(hquery->name, hquery->family, &host);
141           if (status != ARES_ENOTFOUND)
142             {
143               end_hquery(hquery, status, host);
144               return;
145             }
146           break;
147         }
148     }
149 }
150
151 static void host_callback(void *arg, int status, int timeouts,
152                           unsigned char *abuf, int alen)
153 {
154   struct host_query *hquery = (struct host_query *) arg;
155   ares_channel channel = hquery->channel;
156   struct hostent *host;
157
158   hquery->timeouts += timeouts;
159   if (status == ARES_SUCCESS)
160     {
161       if (hquery->family == AF_INET)
162         {
163           status = ares_parse_a_reply(abuf, alen, &host);
164           if (host && channel->nsort)
165             sort_addresses(host, channel->sortlist, channel->nsort);
166         }
167       else if (hquery->family == AF_INET6)
168         {
169           status = ares_parse_aaaa_reply(abuf, alen, &host);
170           if (host && channel->nsort)
171             sort6_addresses(host, channel->sortlist, channel->nsort);
172         }
173       end_hquery(hquery, status, host);
174     }
175   else if (status == ARES_ENODATA && hquery->family == AF_INET6)
176     {
177       /* There was no AAAA. Now lookup an A */
178       hquery->family = AF_INET;
179       ares_search(hquery->channel, hquery->name, C_IN, T_A, host_callback,
180                   hquery);
181     }
182   else if (status == ARES_EDESTRUCTION)
183     end_hquery(hquery, status, NULL);
184   else
185     next_lookup(hquery);
186 }
187
188 static void end_hquery(struct host_query *hquery, int status,
189                        struct hostent *host)
190 {
191   hquery->callback(hquery->arg, status, hquery->timeouts, host);
192   if (host)
193     ares_free_hostent(host);
194   free(hquery->name);
195   free(hquery);
196 }
197
198 /* If the name looks like an IP address, fake up a host entry, end the
199  * query immediately, and return true.  Otherwise return false.
200  */
201 static int fake_hostent(const char *name, int family, ares_host_callback callback,
202                         void *arg)
203 {
204   struct hostent hostent;
205   char *aliases[1] = { NULL };
206   char *addrs[2];
207   int result = 0;
208   struct in_addr in;
209   struct in6_addr in6;
210
211   if (family == AF_INET)
212     result = ((in.s_addr = inet_addr(name)) == INADDR_NONE ? 0 : 1);
213   else if (family == AF_INET6)
214     result = (ares_inet_pton(AF_INET6, name, &in6) < 1 ? 0 : 1);
215
216   if (!result)
217     return 0;
218
219   if (family == AF_INET)
220     {
221       hostent.h_length = sizeof(struct in_addr);
222       addrs[0] = (char *)&in;
223     }
224   else if (family == AF_INET6)
225     {
226       hostent.h_length = sizeof(struct in6_addr);
227       addrs[0] = (char *)&in6;
228     }
229   /* Duplicate the name, to avoid a constness violation. */
230   hostent.h_name = strdup(name);
231   if (!hostent.h_name)
232     {
233       callback(arg, ARES_ENOMEM, 0, NULL);
234       return 1;
235     }
236
237   /* Fill in the rest of the host structure and terminate the query. */
238   addrs[1] = NULL;
239   hostent.h_aliases = aliases;
240   hostent.h_addrtype = family;
241   hostent.h_addr_list = addrs;
242   callback(arg, ARES_SUCCESS, 0, &hostent);
243
244   free((char *)(hostent.h_name));
245   return 1;
246 }
247
248 static int file_lookup(const char *name, int family, struct hostent **host)
249 {
250   FILE *fp;
251   char **alias;
252   int status;
253   int error;
254
255 #ifdef WIN32
256   char PATH_HOSTS[MAX_PATH];
257   if (IS_NT()) {
258     char tmp[MAX_PATH];
259     HKEY hkeyHosts;
260
261     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, &hkeyHosts)
262         == ERROR_SUCCESS)
263     {
264       DWORD dwLength = MAX_PATH;
265       RegQueryValueEx(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp,
266                       &dwLength);
267       ExpandEnvironmentStrings(tmp, PATH_HOSTS, MAX_PATH);
268       RegCloseKey(hkeyHosts);
269     }
270   }
271   else
272     GetWindowsDirectory(PATH_HOSTS, MAX_PATH);
273
274   strcat(PATH_HOSTS, WIN_PATH_HOSTS);
275
276 #elif defined(WATT32)
277   extern const char *_w32_GetHostsFile (void);
278   const char *PATH_HOSTS = _w32_GetHostsFile();
279
280   if (!PATH_HOSTS)
281     return ARES_ENOTFOUND;
282 #endif
283
284   fp = fopen(PATH_HOSTS, "r");
285   if (!fp)
286     {
287       error = ERRNO;
288       switch(error)
289         {
290         case ENOENT:
291         case ESRCH:
292           return ARES_ENOTFOUND;
293         default:
294           DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n",
295                          error, strerror(error)));
296           DEBUGF(fprintf(stderr, "Error opening file: %s\n",
297                          PATH_HOSTS));
298           *host = NULL;
299           return ARES_EFILE;
300         }
301     }
302   while ((status = ares__get_hostent(fp, family, host)) == ARES_SUCCESS)
303     {
304       if (strcasecmp((*host)->h_name, name) == 0)
305         break;
306       for (alias = (*host)->h_aliases; *alias; alias++)
307         {
308           if (strcasecmp(*alias, name) == 0)
309             break;
310         }
311       if (*alias)
312         break;
313       ares_free_hostent(*host);
314     }
315   fclose(fp);
316   if (status == ARES_EOF)
317     status = ARES_ENOTFOUND;
318   if (status != ARES_SUCCESS)
319     *host = NULL;
320   return status;
321 }
322
323 static void sort_addresses(struct hostent *host, struct apattern *sortlist,
324                            int nsort)
325 {
326   struct in_addr a1, a2;
327   int i1, i2, ind1, ind2;
328
329   /* This is a simple insertion sort, not optimized at all.  i1 walks
330    * through the address list, with the loop invariant that everything
331    * to the left of i1 is sorted.  In the loop body, the value at i1 is moved
332    * back through the list (via i2) until it is in sorted order.
333    */
334   for (i1 = 0; host->h_addr_list[i1]; i1++)
335     {
336       memcpy(&a1, host->h_addr_list[i1], sizeof(struct in_addr));
337       ind1 = get_address_index(&a1, sortlist, nsort);
338       for (i2 = i1 - 1; i2 >= 0; i2--)
339         {
340           memcpy(&a2, host->h_addr_list[i2], sizeof(struct in_addr));
341           ind2 = get_address_index(&a2, sortlist, nsort);
342           if (ind2 <= ind1)
343             break;
344           memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct in_addr));
345         }
346       memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct in_addr));
347     }
348 }
349
350 /* Find the first entry in sortlist which matches addr.  Return nsort
351  * if none of them match.
352  */
353 static int get_address_index(struct in_addr *addr, struct apattern *sortlist,
354                              int nsort)
355 {
356   int i;
357
358   for (i = 0; i < nsort; i++)
359     {
360       if (sortlist[i].family != AF_INET)
361         continue;
362       if (sortlist[i].type == PATTERN_MASK)
363         {
364           if ((addr->s_addr & sortlist[i].mask.addr.addr4.s_addr)
365               == sortlist[i].addr.addr4.s_addr)
366             break;
367         }
368       else
369         {
370           if (!ares_bitncmp(&addr->s_addr, &sortlist[i].addr.addr4.s_addr,
371                             sortlist[i].mask.bits))
372             break;
373         }
374     }
375   return i;
376 }
377
378 static void sort6_addresses(struct hostent *host, struct apattern *sortlist,
379                            int nsort)
380 {
381   struct in6_addr a1, a2;
382   int i1, i2, ind1, ind2;
383
384   /* This is a simple insertion sort, not optimized at all.  i1 walks
385    * through the address list, with the loop invariant that everything
386    * to the left of i1 is sorted.  In the loop body, the value at i1 is moved
387    * back through the list (via i2) until it is in sorted order.
388    */
389   for (i1 = 0; host->h_addr_list[i1]; i1++)
390     {
391       memcpy(&a1, host->h_addr_list[i1], sizeof(struct in6_addr));
392       ind1 = get6_address_index(&a1, sortlist, nsort);
393       for (i2 = i1 - 1; i2 >= 0; i2--)
394         {
395           memcpy(&a2, host->h_addr_list[i2], sizeof(struct in6_addr));
396           ind2 = get6_address_index(&a2, sortlist, nsort);
397           if (ind2 <= ind1)
398             break;
399           memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct in6_addr));
400         }
401       memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct in6_addr));
402     }
403 }
404
405 /* Find the first entry in sortlist which matches addr.  Return nsort
406  * if none of them match.
407  */
408 static int get6_address_index(struct in6_addr *addr, struct apattern *sortlist,
409                              int nsort)
410 {
411   int i;
412
413   for (i = 0; i < nsort; i++)
414     {
415       if (sortlist[i].family != AF_INET6)
416         continue;
417         if (!ares_bitncmp(&addr->s6_addr, &sortlist[i].addr.addr6.s6_addr, sortlist[i].mask.bits))
418           break;
419     }
420   return i;
421 }