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