Made ares_gethostbyaddr support IPv6 by specifying AF_INET6 as the family
[platform/upstream/c-ares.git] / ares_gethostbyname.c
1 /* Copyright 1998 by the Massachusetts Institute of Technology.
2  *
3  * Permission to use, copy, modify, and distribute this
4  * software and its documentation for any purpose and without
5  * fee is hereby granted, provided that the above copyright
6  * notice appear in all copies and that both that copyright
7  * notice and this permission notice appear in supporting
8  * documentation, and that the name of M.I.T. not be used in
9  * advertising or publicity pertaining to distribution of the
10  * software without specific, written prior permission.
11  * M.I.T. makes no representations about the suitability of
12  * this software for any purpose.  It is provided "as is"
13  * without express or implied warranty.
14  */
15
16 #include "setup.h"
17 #include <sys/types.h>
18
19 #if defined(WIN32) && !defined(WATT32)
20 #include "nameser.h"
21 #else
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
25 #include <netdb.h>
26 #include <arpa/nameser.h>
27 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
28 #include <arpa/nameser_compat.h>
29 #endif
30 #endif
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <ctype.h>
36
37 #include "ares.h"
38 #include "ares_private.h"
39
40 #ifdef WATT32
41 #undef WIN32
42 #endif
43
44 struct host_query {
45   /* Arguments passed to ares_gethostbyname() */
46   ares_channel channel;
47   char *name;
48   ares_host_callback callback;
49   void *arg;
50
51   const char *remaining_lookups;
52 };
53
54 static void next_lookup(struct host_query *hquery);
55 static void host_callback(void *arg, int status, unsigned char *abuf,
56                           int alen);
57 static void end_hquery(struct host_query *hquery, int status,
58                        struct hostent *host);
59 static int fake_hostent(const char *name, ares_host_callback callback,
60                         void *arg);
61 static int file_lookup(const char *name, struct hostent **host);
62 static void sort_addresses(struct hostent *host, struct apattern *sortlist,
63                            int nsort);
64 static int get_address_index(struct in_addr *addr, struct apattern *sortlist,
65                              int nsort);
66
67 void ares_gethostbyname(ares_channel channel, const char *name, int family,
68                         ares_host_callback callback, void *arg)
69 {
70   struct host_query *hquery;
71
72   /* Right now we only know how to look up Internet addresses. */
73   if (family != AF_INET)
74     {
75       callback(arg, ARES_ENOTIMP, NULL);
76       return;
77     }
78
79   if (fake_hostent(name, callback, arg))
80     return;
81
82   /* Allocate and fill in the host query structure. */
83   hquery = malloc(sizeof(struct host_query));
84   if (!hquery)
85     {
86       callback(arg, ARES_ENOMEM, NULL);
87       return;
88     }
89   hquery->channel = channel;
90   hquery->name = strdup(name);
91   if (!hquery->name)
92     {
93       free(hquery);
94       callback(arg, ARES_ENOMEM, NULL);
95       return;
96     }
97   hquery->callback = callback;
98   hquery->arg = arg;
99   hquery->remaining_lookups = channel->lookups;
100
101   /* Start performing lookups according to channel->lookups. */
102   next_lookup(hquery);
103 }
104
105 static void next_lookup(struct host_query *hquery)
106 {
107   int status;
108   const char *p;
109   struct hostent *host;
110
111   for (p = hquery->remaining_lookups; *p; p++)
112     {
113       switch (*p)
114         {
115         case 'b':
116           /* DNS lookup */
117           hquery->remaining_lookups = p + 1;
118           ares_search(hquery->channel, hquery->name, C_IN, T_A, host_callback,
119                       hquery);
120           return;
121
122         case 'f':
123           /* Host file lookup */
124           status = file_lookup(hquery->name, &host);
125           if (status != ARES_ENOTFOUND)
126             {
127               end_hquery(hquery, status, host);
128               return;
129             }
130           break;
131         }
132     }
133   end_hquery(hquery, ARES_ENOTFOUND, NULL);
134 }
135
136 static void host_callback(void *arg, int status, unsigned char *abuf, int alen)
137 {
138   struct host_query *hquery = (struct host_query *) arg;
139   ares_channel channel = hquery->channel;
140   struct hostent *host;
141
142   if (status == ARES_SUCCESS)
143     {
144       status = ares_parse_a_reply(abuf, alen, &host);
145       if (host && channel->nsort)
146         sort_addresses(host, channel->sortlist, channel->nsort);
147       end_hquery(hquery, status, host);
148     }
149   else if (status == ARES_EDESTRUCTION)
150     end_hquery(hquery, status, NULL);
151   else
152     next_lookup(hquery);
153 }
154
155 static void end_hquery(struct host_query *hquery, int status,
156                        struct hostent *host)
157 {
158   hquery->callback(hquery->arg, status, host);
159   if (host)
160     ares_free_hostent(host);
161   free(hquery->name);
162   free(hquery);
163 }
164
165 /* If the name looks like an IP address, fake up a host entry, end the
166  * query immediately, and return true.  Otherwise return false.
167  */
168 static int fake_hostent(const char *name, ares_host_callback callback,
169                         void *arg)
170 {
171   struct in_addr addr;
172   struct hostent hostent;
173   const char *p;
174   char *aliases[1] = { NULL };
175   char *addrs[2];
176
177   /* It only looks like an IP address if it's all numbers and dots. */
178   for (p = name; *p; p++)
179     {
180       if (!isdigit((unsigned char)*p) && *p != '.')
181         return 0;
182     }
183
184   /* It also only looks like an IP address if it's non-zero-length and
185    * doesn't end with a dot.
186    */
187   if (p == name || *(p - 1) == '.')
188     return 0;
189
190   /* It looks like an IP address.  Figure out what IP address it is. */
191   addr.s_addr = inet_addr(name);
192   if (addr.s_addr == INADDR_NONE)
193     {
194       callback(arg, ARES_EBADNAME, NULL);
195       return 1;
196     }
197
198   /* Duplicate the name, to avoid a constness violation. */
199   hostent.h_name = strdup(name);
200   if (!hostent.h_name)
201     {
202       callback(arg, ARES_ENOMEM, NULL);
203       return 1;
204     }
205
206   /* Fill in the rest of the host structure and terminate the query. */
207   addrs[0] = (char *) &addr;
208   addrs[1] = NULL;
209   hostent.h_aliases = aliases;
210   hostent.h_addrtype = AF_INET;
211   hostent.h_length = sizeof(struct in_addr);
212   hostent.h_addr_list = addrs;
213   callback(arg, ARES_SUCCESS, &hostent);
214
215   free(hostent.h_name);
216   return 1;
217 }
218
219 static int file_lookup(const char *name, struct hostent **host)
220 {
221   FILE *fp;
222   char **alias;
223   int status;
224
225 #ifdef WIN32
226   char PATH_HOSTS[MAX_PATH];
227   if (IS_NT()) {
228     char tmp[MAX_PATH];
229     HKEY hkeyHosts;
230
231     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, &hkeyHosts)
232         == ERROR_SUCCESS)
233     {
234       DWORD dwLength = MAX_PATH;
235       RegQueryValueEx(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp,
236                       &dwLength);
237       ExpandEnvironmentStrings(tmp, PATH_HOSTS, MAX_PATH);
238       RegCloseKey(hkeyHosts);
239     }
240   }
241   else
242     GetWindowsDirectory(PATH_HOSTS, MAX_PATH);
243
244   strcat(PATH_HOSTS, WIN_PATH_HOSTS);
245
246 #elif defined(WATT32)
247   extern const char *_w32_GetHostsFile (void);
248   const char *PATH_HOSTS = _w32_GetHostsFile();
249
250   if (!PATH_HOSTS)
251     return ARES_ENOTFOUND;
252 #endif
253
254   fp = fopen(PATH_HOSTS, "r");
255   if (!fp)
256     return ARES_ENOTFOUND;
257
258   while ((status = ares__get_hostent(fp, AF_INET, host)) == ARES_SUCCESS)
259     {
260       if (strcasecmp((*host)->h_name, name) == 0)
261         break;
262       for (alias = (*host)->h_aliases; *alias; alias++)
263         {
264           if (strcasecmp(*alias, name) == 0)
265             break;
266         }
267       if (*alias)
268         break;
269       ares_free_hostent(*host);
270     }
271   fclose(fp);
272   if (status == ARES_EOF)
273     status = ARES_ENOTFOUND;
274   if (status != ARES_SUCCESS)
275     *host = NULL;
276   return status;
277 }
278
279 static void sort_addresses(struct hostent *host, struct apattern *sortlist,
280                            int nsort)
281 {
282   struct in_addr a1, a2;
283   int i1, i2, ind1, ind2;
284
285   /* This is a simple insertion sort, not optimized at all.  i1 walks
286    * through the address list, with the loop invariant that everything
287    * to the left of i1 is sorted.  In the loop body, the value at i1 is moved
288    * back through the list (via i2) until it is in sorted order.
289    */
290   for (i1 = 0; host->h_addr_list[i1]; i1++)
291     {
292       memcpy(&a1, host->h_addr_list[i1], sizeof(struct in_addr));
293       ind1 = get_address_index(&a1, sortlist, nsort);
294       for (i2 = i1 - 1; i2 >= 0; i2--)
295         {
296           memcpy(&a2, host->h_addr_list[i2], sizeof(struct in_addr));
297           ind2 = get_address_index(&a2, sortlist, nsort);
298           if (ind2 <= ind1)
299             break;
300           memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct in_addr));
301         }
302       memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct in_addr));
303     }
304 }
305
306 /* Find the first entry in sortlist which matches addr.  Return nsort
307  * if none of them match.
308  */
309 static int get_address_index(struct in_addr *addr, struct apattern *sortlist,
310                              int nsort)
311 {
312   int i;
313
314   for (i = 0; i < nsort; i++)
315     {
316       if ((addr->s_addr & sortlist[i].mask.s_addr) == sortlist[i].addr.s_addr)
317         break;
318     }
319   return i;
320 }