Newer
Older
/* Copyright 1998 by the Massachusetts Institute of Technology.
*
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting
* documentation, and that the name of M.I.T. not be used in
* advertising or publicity pertaining to distribution of the
* software without specific, written prior permission.
* M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is"
* without express or implied warranty.
*/
#include "setup.h"
#if defined(WIN32) && !defined(WATT32)
#include "nameser.h"
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
Daniel Stenberg
committed
#ifdef HAVE_ARPA_NAMESER_H
#include <arpa/nameser.h>
#endif
Daniel Stenberg
committed
#ifdef HAVE_ARPA_NAMESER_COMPAT_H
#include <arpa/nameser_compat.h>
#endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "ares.h"
#include "ares_private.h"
#include "inet_net_pton.h"
#ifdef WATT32
#undef WIN32
#endif
struct host_query {
/* Arguments passed to ares_gethostbyname() */
ares_channel channel;
char *name;
ares_host_callback callback;
void *arg;
const char *remaining_lookups;
Steinar H. Gunderson
committed
int timeouts;
};
static void next_lookup(struct host_query *hquery);
Steinar H. Gunderson
committed
static void host_callback(void *arg, int status, int timeouts,
unsigned char *abuf, int alen);
static void end_hquery(struct host_query *hquery, int status,
static int fake_hostent(const char *name, int family, ares_host_callback callback,
static int file_lookup(const char *name, int family, struct hostent **host);
static void sort_addresses(struct hostent *host, struct apattern *sortlist,
static void sort6_addresses(struct hostent *host, struct apattern *sortlist,
int nsort);
static int get_address_index(struct in_addr *addr, struct apattern *sortlist,
static int get6_address_index(struct in6_addr *addr, struct apattern *sortlist,
int nsort);
void ares_gethostbyname(ares_channel channel, const char *name, int family,
{
struct host_query *hquery;
/* Right now we only know how to look up Internet addresses. */
if (family != AF_INET && family != AF_INET6)
Steinar H. Gunderson
committed
callback(arg, ARES_ENOTIMP, 0, NULL);
if (fake_hostent(name, family, callback, arg))
return;
/* Allocate and fill in the host query structure. */
hquery = malloc(sizeof(struct host_query));
if (!hquery)
{
Steinar H. Gunderson
committed
callback(arg, ARES_ENOMEM, 0, NULL);
return;
}
hquery->channel = channel;
hquery->name = strdup(name);
hquery->family = family;
if (!hquery->name)
{
free(hquery);
Steinar H. Gunderson
committed
callback(arg, ARES_ENOMEM, 0, NULL);
return;
}
hquery->callback = callback;
hquery->arg = arg;
hquery->remaining_lookups = channel->lookups;
Steinar H. Gunderson
committed
hquery->timeouts = 0;
/* Start performing lookups according to channel->lookups. */
next_lookup(hquery);
}
static void next_lookup(struct host_query *hquery)
{
int status;
const char *p;
Daniel Stenberg
committed
struct hostent *host;
for (p = hquery->remaining_lookups; *p; p++)
{
case 'b':
/* DNS lookup */
hquery->remaining_lookups = p + 1;
if (hquery->family == AF_INET6)
ares_search(hquery->channel, hquery->name, C_IN, T_AAAA, host_callback,
hquery);
else
ares_search(hquery->channel, hquery->name, C_IN, T_A, host_callback,
hquery);
status = file_lookup(hquery->name, hquery->family, &host);
if (status != ARES_ENOTFOUND)
{
end_hquery(hquery, status, host);
return;
}
break;
}
Steinar H. Gunderson
committed
static void host_callback(void *arg, int status, int timeouts,
unsigned char *abuf, int alen)
{
struct host_query *hquery = (struct host_query *) arg;
ares_channel channel = hquery->channel;
Daniel Stenberg
committed
struct hostent *host;
Steinar H. Gunderson
committed
hquery->timeouts += timeouts;
if (status == ARES_SUCCESS)
{
if (hquery->family == AF_INET)
{
status = ares_parse_a_reply(abuf, alen, &host, NULL, NULL);
if (host && channel->nsort)
sort_addresses(host, channel->sortlist, channel->nsort);
}
else if (hquery->family == AF_INET6)
{
status = ares_parse_aaaa_reply(abuf, alen, &host, NULL, NULL);
if (host && channel->nsort)
sort6_addresses(host, channel->sortlist, channel->nsort);
end_hquery(hquery, status, host);
}
else if (status == ARES_ENODATA && hquery->family == AF_INET6)
{
hquery->family = AF_INET;
ares_search(hquery->channel, hquery->name, C_IN, T_A, host_callback,
hquery);
else if (status == ARES_EDESTRUCTION)
end_hquery(hquery, status, NULL);
else
next_lookup(hquery);
}
static void end_hquery(struct host_query *hquery, int status,
Steinar H. Gunderson
committed
hquery->callback(hquery->arg, status, hquery->timeouts, host);
if (host)
ares_free_hostent(host);
free(hquery->name);
free(hquery);
}
/* If the name looks like an IP address, fake up a host entry, end the
* query immediately, and return true. Otherwise return false.
*/
static int fake_hostent(const char *name, int family, ares_host_callback callback,
{
struct hostent hostent;
char *aliases[1] = { NULL };
char *addrs[2];
int result = 0;
struct in_addr in;
struct in6_addr in6;
if (family == AF_INET)
result = ((in.s_addr = inet_addr(name)) == INADDR_NONE ? 0 : 1);
else if (family == AF_INET6)
result = (ares_inet_pton(AF_INET6, name, &in6) < 1 ? 0 : 1);
if (family == AF_INET)
hostent.h_length = sizeof(struct in_addr);
addrs[0] = (char *)∈
}
else if (family == AF_INET6)
{
hostent.h_length = sizeof(struct in6_addr);
addrs[0] = (char *)&in6;
}
/* Duplicate the name, to avoid a constness violation. */
hostent.h_name = strdup(name);
if (!hostent.h_name)
{
Steinar H. Gunderson
committed
callback(arg, ARES_ENOMEM, 0, NULL);
return 1;
}
/* Fill in the rest of the host structure and terminate the query. */
addrs[1] = NULL;
hostent.h_aliases = aliases;
hostent.h_addrtype = family;
hostent.h_addr_list = addrs;
Steinar H. Gunderson
committed
callback(arg, ARES_SUCCESS, 0, &hostent);
Yang Tse
committed
free((char *)(hostent.h_name));
static int file_lookup(const char *name, int family, struct hostent **host)
{
FILE *fp;
char **alias;
int status;
#ifdef WIN32
char PATH_HOSTS[MAX_PATH];
char tmp[MAX_PATH];
HKEY hkeyHosts;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, &hkeyHosts)
== ERROR_SUCCESS)
{
DWORD dwLength = MAX_PATH;
RegQueryValueEx(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp,
&dwLength);
ExpandEnvironmentStrings(tmp, PATH_HOSTS, MAX_PATH);
RegCloseKey(hkeyHosts);
}
GetWindowsDirectory(PATH_HOSTS, MAX_PATH);
strcat(PATH_HOSTS, WIN_PATH_HOSTS);
#elif defined(WATT32)
extern const char *_w32_GetHostsFile (void);
const char *PATH_HOSTS = _w32_GetHostsFile();
if (!PATH_HOSTS)
return ARES_ENOTFOUND;
#endif
fp = fopen(PATH_HOSTS, "r");
if (!fp)
Daniel Stenberg
committed
{
case ENOENT:
case ESRCH:
return ARES_ENOTFOUND;
default:
DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n",
error, strerror(error)));
DEBUGF(fprintf(stderr, "Error opening file: %s\n",
PATH_HOSTS));
*host = NULL;
return ARES_EFILE;
}
while ((status = ares__get_hostent(fp, family, host)) == ARES_SUCCESS)
{
if (strcasecmp((*host)->h_name, name) == 0)
Daniel Stenberg
committed
break;
for (alias = (*host)->h_aliases; *alias; alias++)
Daniel Stenberg
committed
if (*alias)
break;
ares_free_hostent(*host);
}
fclose(fp);
if (status == ARES_EOF)
Daniel Stenberg
committed
status = ARES_ENOTFOUND;
if (status != ARES_SUCCESS)
*host = NULL;
return status;
}
static void sort_addresses(struct hostent *host, struct apattern *sortlist,
{
struct in_addr a1, a2;
int i1, i2, ind1, ind2;
/* This is a simple insertion sort, not optimized at all. i1 walks
* through the address list, with the loop invariant that everything
* to the left of i1 is sorted. In the loop body, the value at i1 is moved
* back through the list (via i2) until it is in sorted order.
*/
for (i1 = 0; host->h_addr_list[i1]; i1++)
{
memcpy(&a1, host->h_addr_list[i1], sizeof(struct in_addr));
ind1 = get_address_index(&a1, sortlist, nsort);
for (i2 = i1 - 1; i2 >= 0; i2--)
{
memcpy(&a2, host->h_addr_list[i2], sizeof(struct in_addr));
ind2 = get_address_index(&a2, sortlist, nsort);
if (ind2 <= ind1)
break;
memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct in_addr));
}
memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct in_addr));
}
}
/* Find the first entry in sortlist which matches addr. Return nsort
* if none of them match.
*/
static int get_address_index(struct in_addr *addr, struct apattern *sortlist,
{
int i;
for (i = 0; i < nsort; i++)
{
if (sortlist[i].family != AF_INET)
continue;
if (sortlist[i].type == PATTERN_MASK)
{
== sortlist[i].addr.addr4.s_addr)
break;
}
if (!ares_bitncmp(&addr->s_addr, &sortlist[i].addr.addr4.s_addr,
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
sortlist[i].mask.bits))
break;
}
}
return i;
}
static void sort6_addresses(struct hostent *host, struct apattern *sortlist,
int nsort)
{
struct in6_addr a1, a2;
int i1, i2, ind1, ind2;
/* This is a simple insertion sort, not optimized at all. i1 walks
* through the address list, with the loop invariant that everything
* to the left of i1 is sorted. In the loop body, the value at i1 is moved
* back through the list (via i2) until it is in sorted order.
*/
for (i1 = 0; host->h_addr_list[i1]; i1++)
{
memcpy(&a1, host->h_addr_list[i1], sizeof(struct in6_addr));
ind1 = get6_address_index(&a1, sortlist, nsort);
for (i2 = i1 - 1; i2 >= 0; i2--)
{
memcpy(&a2, host->h_addr_list[i2], sizeof(struct in6_addr));
ind2 = get6_address_index(&a2, sortlist, nsort);
if (ind2 <= ind1)
break;
memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct in6_addr));
}
memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct in6_addr));
}
}
/* Find the first entry in sortlist which matches addr. Return nsort
* if none of them match.
*/
static int get6_address_index(struct in6_addr *addr, struct apattern *sortlist,
int nsort)
{
int i;
for (i = 0; i < nsort; i++)
{
if (sortlist[i].family != AF_INET6)
continue;
if (!ares_bitncmp(&addr->s6_addr, &sortlist[i].addr.addr6.s6_addr, sortlist[i].mask.bits))