1 /* Copyright 1998, 2011, 2013 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 "ares_setup.h"
17
18 #ifdef HAVE_NETINET_IN_H
19 # include <netinet/in.h>
20 #endif
21 #ifdef HAVE_NETDB_H
22 # include <netdb.h>
23 #endif
24 #ifdef HAVE_ARPA_INET_H
25 # include <arpa/inet.h>
26 #endif
27
28 #include "ares_nameser.h"
29
30 #ifdef HAVE_STRINGS_H
31 #include <strings.h>
32 #endif
33
34 #include "ares.h"
35 #include "ares_inet_net_pton.h"
36 #include "bitncmp.h"
37 #include "ares_platform.h"
38 #include "ares_nowarn.h"
39 #include "ares_private.h"
40
41 static void sort_addresses(struct hostent *host,
42 const struct apattern *sortlist, int nsort);
43 static void sort6_addresses(struct hostent *host,
44 const struct apattern *sortlist, int nsort);
45 static int get_address_index(const struct in_addr *addr,
46 const struct apattern *sortlist, int nsort);
47 static int get6_address_index(const struct ares_in6_addr *addr,
48 const struct apattern *sortlist, int nsort);
49
50 struct host_query {
51 ares_host_callback callback;
52 void *arg;
53 ares_channel channel;
54 };
55
ares_gethostbyname_callback(void * arg,int status,int timeouts,struct ares_addrinfo * result)56 static void ares_gethostbyname_callback(void *arg, int status, int timeouts,
57 struct ares_addrinfo *result)
58 {
59 struct hostent *hostent = NULL;
60 struct host_query *ghbn_arg = arg;
61
62 if (status == ARES_SUCCESS)
63 {
64 status = ares__addrinfo2hostent(result, AF_UNSPEC, &hostent);
65 }
66
67 /* addrinfo2hostent will only return ENODATA if there are no addresses _and_
68 * no cname/aliases. However, gethostbyname will return ENODATA even if there
69 * is cname/alias data */
70 if (status == ARES_SUCCESS && hostent &&
71 (!hostent->h_addr_list || !hostent->h_addr_list[0]))
72 {
73 status = ARES_ENODATA;
74 }
75
76 if (status == ARES_SUCCESS && ghbn_arg->channel->nsort && hostent)
77 {
78 if (hostent->h_addrtype == AF_INET6)
79 sort6_addresses(hostent, ghbn_arg->channel->sortlist,
80 ghbn_arg->channel->nsort);
81 if (hostent->h_addrtype == AF_INET)
82 sort_addresses(hostent, ghbn_arg->channel->sortlist,
83 ghbn_arg->channel->nsort);
84 }
85
86 ghbn_arg->callback(ghbn_arg->arg, status, timeouts, hostent);
87
88 ares_freeaddrinfo(result);
89 ares_free(ghbn_arg);
90 ares_free_hostent(hostent);
91 }
92
ares_gethostbyname(ares_channel channel,const char * name,int family,ares_host_callback callback,void * arg)93 void ares_gethostbyname(ares_channel channel, const char *name, int family,
94 ares_host_callback callback, void *arg)
95 {
96 const struct ares_addrinfo_hints hints = { ARES_AI_CANONNAME, family, 0, 0 };
97 struct host_query *ghbn_arg;
98
99 if (!callback)
100 return;
101
102 ghbn_arg = ares_malloc(sizeof(*ghbn_arg));
103 if (!ghbn_arg)
104 {
105 callback(arg, ARES_ENOMEM, 0, NULL);
106 return;
107 }
108
109 ghbn_arg->callback=callback;
110 ghbn_arg->arg=arg;
111 ghbn_arg->channel=channel;
112
113 ares_getaddrinfo(channel, name, NULL, &hints, ares_gethostbyname_callback,
114 ghbn_arg);
115 }
116
117
sort_addresses(struct hostent * host,const struct apattern * sortlist,int nsort)118 static void sort_addresses(struct hostent *host,
119 const struct apattern *sortlist, int nsort)
120 {
121 struct in_addr a1, a2;
122 int i1, i2, ind1, ind2;
123
124 /* This is a simple insertion sort, not optimized at all. i1 walks
125 * through the address list, with the loop invariant that everything
126 * to the left of i1 is sorted. In the loop body, the value at i1 is moved
127 * back through the list (via i2) until it is in sorted order.
128 */
129 for (i1 = 0; host->h_addr_list[i1]; i1++)
130 {
131 memcpy(&a1, host->h_addr_list[i1], sizeof(struct in_addr));
132 ind1 = get_address_index(&a1, sortlist, nsort);
133 for (i2 = i1 - 1; i2 >= 0; i2--)
134 {
135 memcpy(&a2, host->h_addr_list[i2], sizeof(struct in_addr));
136 ind2 = get_address_index(&a2, sortlist, nsort);
137 if (ind2 <= ind1)
138 break;
139 memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct in_addr));
140 }
141 memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct in_addr));
142 }
143 }
144
145 /* Find the first entry in sortlist which matches addr. Return nsort
146 * if none of them match.
147 */
get_address_index(const struct in_addr * addr,const struct apattern * sortlist,int nsort)148 static int get_address_index(const struct in_addr *addr,
149 const struct apattern *sortlist,
150 int nsort)
151 {
152 int i;
153
154 for (i = 0; i < nsort; i++)
155 {
156 if (sortlist[i].family != AF_INET)
157 continue;
158 if (sortlist[i].type == PATTERN_MASK)
159 {
160 if ((addr->s_addr & sortlist[i].mask.addr4.s_addr)
161 == sortlist[i].addrV4.s_addr)
162 break;
163 }
164 else
165 {
166 if (!ares__bitncmp(&addr->s_addr, &sortlist[i].addrV4.s_addr,
167 sortlist[i].mask.bits))
168 break;
169 }
170 }
171 return i;
172 }
173
sort6_addresses(struct hostent * host,const struct apattern * sortlist,int nsort)174 static void sort6_addresses(struct hostent *host,
175 const struct apattern *sortlist, int nsort)
176 {
177 struct ares_in6_addr a1, a2;
178 int i1, i2, ind1, ind2;
179
180 /* This is a simple insertion sort, not optimized at all. i1 walks
181 * through the address list, with the loop invariant that everything
182 * to the left of i1 is sorted. In the loop body, the value at i1 is moved
183 * back through the list (via i2) until it is in sorted order.
184 */
185 for (i1 = 0; host->h_addr_list[i1]; i1++)
186 {
187 memcpy(&a1, host->h_addr_list[i1], sizeof(struct ares_in6_addr));
188 ind1 = get6_address_index(&a1, sortlist, nsort);
189 for (i2 = i1 - 1; i2 >= 0; i2--)
190 {
191 memcpy(&a2, host->h_addr_list[i2], sizeof(struct ares_in6_addr));
192 ind2 = get6_address_index(&a2, sortlist, nsort);
193 if (ind2 <= ind1)
194 break;
195 memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct ares_in6_addr));
196 }
197 memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct ares_in6_addr));
198 }
199 }
200
201 /* Find the first entry in sortlist which matches addr. Return nsort
202 * if none of them match.
203 */
get6_address_index(const struct ares_in6_addr * addr,const struct apattern * sortlist,int nsort)204 static int get6_address_index(const struct ares_in6_addr *addr,
205 const struct apattern *sortlist,
206 int nsort)
207 {
208 int i;
209
210 for (i = 0; i < nsort; i++)
211 {
212 if (sortlist[i].family != AF_INET6)
213 continue;
214 if (!ares__bitncmp(addr, &sortlist[i].addrV6, sortlist[i].mask.bits))
215 break;
216 }
217 return i;
218 }
219
220
221
222 static int file_lookup(const char *name, int family, struct hostent **host);
223
224 /* I really have no idea why this is exposed as a public function, but since
225 * it is, we can't kill this legacy function. */
ares_gethostbyname_file(ares_channel channel,const char * name,int family,struct hostent ** host)226 int ares_gethostbyname_file(ares_channel channel, const char *name,
227 int family, struct hostent **host)
228 {
229 int result;
230
231 /* We only take the channel to ensure that ares_init() been called. */
232 if(channel == NULL)
233 {
234 /* Anything will do, really. This seems fine, and is consistent with
235 other error cases. */
236 *host = NULL;
237 return ARES_ENOTFOUND;
238 }
239
240 /* Just chain to the internal implementation we use here; it's exactly
241 * what we want.
242 */
243 result = file_lookup(name, family, host);
244 if(result != ARES_SUCCESS)
245 {
246 /* We guarantee a NULL hostent on failure. */
247 *host = NULL;
248 }
249 return result;
250 }
251
file_lookup(const char * name,int family,struct hostent ** host)252 static int file_lookup(const char *name, int family, struct hostent **host)
253 {
254 FILE *fp;
255 char **alias;
256 int status;
257 int error;
258
259 #ifdef WIN32
260 char PATH_HOSTS[MAX_PATH];
261 win_platform platform;
262
263 PATH_HOSTS[0] = '\0';
264
265 platform = ares__getplatform();
266
267 if (platform == WIN_NT) {
268 char tmp[MAX_PATH];
269 HKEY hkeyHosts;
270
271 if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ,
272 &hkeyHosts) == ERROR_SUCCESS)
273 {
274 DWORD dwLength = MAX_PATH;
275 RegQueryValueExA(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp,
276 &dwLength);
277 ExpandEnvironmentStringsA(tmp, PATH_HOSTS, MAX_PATH);
278 RegCloseKey(hkeyHosts);
279 }
280 }
281 else if (platform == WIN_9X)
282 GetWindowsDirectoryA(PATH_HOSTS, MAX_PATH);
283 else
284 return ARES_ENOTFOUND;
285
286 strcat(PATH_HOSTS, WIN_PATH_HOSTS);
287
288 #elif defined(WATT32)
289 const char *PATH_HOSTS = _w32_GetHostsFile();
290
291 if (!PATH_HOSTS)
292 return ARES_ENOTFOUND;
293 #endif
294
295 /* Per RFC 7686, reject queries for ".onion" domain names with NXDOMAIN. */
296 if (ares__is_onion_domain(name))
297 return ARES_ENOTFOUND;
298
299
300 fp = fopen(PATH_HOSTS, "r");
301 if (!fp)
302 {
303 error = ERRNO;
304 switch(error)
305 {
306 case ENOENT:
307 case ESRCH:
308 return ARES_ENOTFOUND;
309 default:
310 DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n",
311 error, strerror(error)));
312 DEBUGF(fprintf(stderr, "Error opening file: %s\n",
313 PATH_HOSTS));
314 *host = NULL;
315 return ARES_EFILE;
316 }
317 }
318 while ((status = ares__get_hostent(fp, family, host)) == ARES_SUCCESS)
319 {
320 if (strcasecmp((*host)->h_name, name) == 0)
321 break;
322 for (alias = (*host)->h_aliases; *alias; alias++)
323 {
324 if (strcasecmp(*alias, name) == 0)
325 break;
326 }
327 if (*alias)
328 break;
329 ares_free_hostent(*host);
330 }
331 fclose(fp);
332 if (status == ARES_EOF)
333 status = ARES_ENOTFOUND;
334 if (status != ARES_SUCCESS)
335 *host = NULL;
336 return status;
337 }
338
339