/*
* adns.c: Asynchronous DNS queries
*
* Copyright(c) 2000 - All Rights Reserved
*
* See the COPYRIGHT file.
*/
#ifndef lint
static char rcsid[] = "@(#)$Id: adns.c,v 1.9 2001/07/07 16:24:44 kalt Exp $";
#endif
#include "os.h"
#include "struct.h"
#include "server.h"
#include "window.h"
#include "utils.h"
#if !defined(__CYGWIN__)
extern int h_errno;
#endif
extern struct server_ *server;
struct query_ {
struct server_ *srv;
char *qdata;
#if defined(HAVE_GETADDRINFO)
struct addrinfo *rdata;
char *rname;
#else
char *rdata;
#endif
char *error;
struct query_ *nextq;
};
struct query_ *qlist = NULL;
struct query_ *rlist = NULL;
static pthread_t main_t;
static pthread_t dns_t;
static pthread_mutex_t dns_qlist;
static pthread_cond_t dns_qrdy;
static pthread_mutex_t dns_rlist;
/* dns_loop: start point, and self contained loop for the resolver thread */
static void *
dns_loop(arg)
void *arg;
{
sigset_t ss;
struct query_ *currentq;
/* block all signals: let the main thread handle them */
sigfillset(&ss);
if (pthread_sigmask(SIG_BLOCK, &ss, NULL))
abort();
while (1)
{
pthread_mutex_lock(&dns_qlist);
if (qlist == NULL)
{
/* No queries in the list, release the mutex, and wait.. */
pthread_cond_wait(&dns_qrdy, &dns_qlist);
}
/* pop the query off the list */
assert(qlist);
currentq = qlist;
qlist = qlist->nextq;
pthread_mutex_unlock(&dns_qlist);
/* work on the query */
#if defined(HAVE_GETADDRINFO)
/* use it if it's there, and automagically get IPv6 support :-) */
{
struct addrinfo *address, hints;
int rc;
char port[10];
if (currentq->srv)
sprintf(port, "%d", currentq->srv->port);
/* first, is it an IP or a host? */
bzero((char *)&hints, sizeof(hints));
hints.ai_flags = AI_NUMERICHOST;
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
/* the following call is DNS-less */
rc = getaddrinfo(currentq->qdata, currentq->srv ? port : NULL,
&hints, &address);
if (rc != 0)
{
/* look up what appears to be a hostname */
bzero((char *)&hints, sizeof(hints));
hints.ai_flags = AI_CANONNAME;
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
rc = getaddrinfo(currentq->qdata, currentq->srv ? port : NULL,
&hints, &address);
if (rc == 0)
currentq->rdata = address;
else
{
if (address)
freeaddrinfo(address);
currentq->error = malloc(strlen(currentq->qdata) +
strlen(gai_strerror(rc)) + 10);
sprintf(currentq->error, "\"%s\" %s.", currentq->qdata,
gai_strerror(rc));
}
}
else
{
/* it seems we have an IP address */
char host[NI_MAXHOST];
assert(currentq->srv == NULL);
rc = getnameinfo(address->ai_addr, address->ai_addrlen,
host, NI_MAXHOST, NULL, 0, NI_NAMEREQD);
freeaddrinfo(address);
if (rc == 0)
currentq->rname = strdup(host);
else
{
currentq->error = malloc(strlen(currentq->qdata) +
strlen("Host not found") + 10);
sprintf(currentq->error, "\"%s\" Host not found.",
currentq->qdata);
}
}
}
#else
{
struct hostent *h;
unsigned long ad;
ad = inet_addr(currentq->qdata);
if (ad != INADDR_NONE)
{
/* doesn't seem to be an IP address */
if (h = gethostbyaddr((char *) &ad, sizeof(ad), AF_INET))
currentq->rdata = strdup(h->h_name);
else
{
currentq->error = malloc(strlen(currentq->qdata) + 40);
sprintf(currentq->error, "\"%s\" %s. [%d]", currentq->qdata,
(h_errno == HOST_NOT_FOUND) ? "not found" :
(h_errno == TRY_AGAIN) ? "not found, try again" :
(h_errno == NO_DATA) ? "has no IP address" :
"unknown error", h_errno);
}
} else {
if (h = gethostbyname(currentq->qdata))
currentq->rdata = strdup((char *) inet_ntoa(*((struct in_addr *)h->h_addr_list[0])));
else
{
currentq->error = malloc(strlen(currentq->qdata) + 40);
sprintf(currentq->error, "\"%s\" %s. [%d]", currentq->qdata,
(h_errno == HOST_NOT_FOUND) ? "not found" :
(h_errno == TRY_AGAIN) ? "not found, try again" :
(h_errno == NO_DATA) ? "has no IP address" :
"unknown error", h_errno);
}
}
}
#endif
/* put currentq in the list of replies */
pthread_mutex_lock(&dns_rlist);
currentq->nextq = rlist;
rlist = currentq;
pthread_mutex_unlock(&dns_rlist);
pthread_kill(main_t, SIGUSR1);
}
}
/* dns_init: start the resolver thread. */
void
dns_init()
{
main_t = pthread_self();
if (pthread_create(&dns_t, NULL, dns_loop, NULL) < 0)
{
perror("Couldn't create resolver thread");
exit(1);
}
if (pthread_mutex_init(&dns_qlist, NULL) < 0)
{
perror("Couldn't initialize DNS mutex");
exit(1);
}
if (pthread_cond_init(&dns_qrdy, NULL) < 0)
{
perror("Couldn't initialize DNS condition variable");
exit(1);
}
if (pthread_mutex_init(&dns_rlist, NULL) < 0)
{
perror("Couldn't initialize DNS mutex");
exit(1);
}
}
/* dns_lookup: add lookup to the list of pending DNS lookups */
void
dns_lookup(what, srv)
char *what;
struct server_ *srv;
{
struct query_ *new, **tmp;
vsic_slog(LOG_DEBUG, "DNS: Adding \"%s\" to the list of queries [%d].",
what, srv ? 1 : 0);
new = (struct query_ *) malloc(sizeof(struct query_));
new->srv = srv;
new->qdata = strdup(what);
new->rdata = NULL;
#if defined(HAVE_GETADDRINFO)
new->rname = NULL;
#endif
new->error = NULL;
new->nextq = NULL;
/* lock the mutex before manipulating qlist */
pthread_mutex_lock(&dns_qlist);
/* add the new query to the end of the list */
tmp = &qlist;
while (*tmp)
tmp = &((*tmp)->nextq);
*tmp = new;
/* signal the resolver thread that there's work to be done */
pthread_cond_signal(&dns_qrdy);
/* release the mutex */
pthread_mutex_unlock(&dns_qlist);
}
/* dns_process: process results from queries */
void
dns_process()
{
struct query_ *reply, *tmp;
#if defined(GNU_PTH)
/*
** This might not be necessary since we use pth_select() rather than
** select() itself, but might as well be cautious here.
*/
if (qlist) pth_yield(NULL);
#endif
if (rlist == NULL) return;
pthread_mutex_lock(&dns_rlist);
tmp = rlist;
while (reply = tmp) {
tmp = tmp->nextq;
#if defined(HAVE_GETADDRINFO)
vsic_slog(LOG_DEBUG,
"DNS: query \"%s\", reply %X, rname \"%s\", error \"%s\"",
reply->qdata, reply->rdata, reply->rname ? reply->rname : "",
reply->error ? reply->error : "");
#else
vsic_slog(LOG_DEBUG, "DNS: query \"%s\", reply \"%s\", error \"%s\"",
reply->qdata, reply->rdata ? reply->rdata : "",
reply->error ? reply->error : "");
#endif
if (reply->error)
/* the lookup failed */
{
assert(reply->rdata == NULL);
if (reply->srv)
{
server = reply->srv;
if (option(reply->srv->sopt, S_CONNECTING))
select_active(NULL, 0);
}
vsic_slog(LOG_CLIENT, "--- %s: %s",
reply->srv ?
option(reply->srv->sopt, S_CONNECTING) ? "Connect failed":
"Server lookup failed" : "DNS",
reply->error);
if (reply->srv)
{
unset_option(reply->srv->sopt, S_DNS);
if (option(reply->srv->sopt, S_CONNECTING))
unset_option(reply->srv->sopt, S_CONNECTING);
}
free(reply->error);
}
else
{
/* the lookup was successful */
assert(reply->error == NULL);
if (reply->srv)
{
#if defined(HAVE_GETADDRINFO)
struct addrinfo *atmp;
assert(reply->srv->ip == NULL);
server = reply->srv;
if (option(reply->srv->sopt, S_CONNECTING))
select_active(NULL, 0);
atmp = reply->rdata;
while (atmp && atmp->ai_family != PF_INET &&
atmp->ai_family != PF_INET6)
atmp = atmp->ai_next;
if (atmp)
{
reply->srv->address = reply->rdata;
reply->srv->addrip = reply->rdata;
reply->srv->ip = aitoip(reply->rdata);
}
else
{
vsic_slog(LOG_CLIENT, "--- %s: No address found",
option(reply->srv->sopt, S_CONNECTING) ?
"Connect failed" : "Server lookup failed");
unset_option(reply->srv->sopt, S_DNS);
if (option(reply->srv->sopt, S_CONNECTING))
unset_option(reply->srv->sopt, S_CONNECTING);
freeaddrinfo(reply->rdata);
free(reply->qdata);
free(reply);
continue;
}
#else
reply->srv->ip = strdup(reply->rdata);
#endif
unset_option(reply->srv->sopt, S_DNS);
if (option(reply->srv->sopt, S_CONNECTING))
{
vsic_slog(LOG_CLIENT, "--- Connecting to %s %d [%s]",
reply->srv->sname, reply->srv->port,
reply->srv->ip);
sic_connect(reply->srv);
}
}
else
{
#if defined(HAVE_GETADDRINFO)
server = NULL;
select_active(NULL, 2);
if (reply->rdata)
{
struct addrinfo *atmp;
if (reply->rdata->ai_canonname &&
reply->rdata->ai_canonname[0] &&
strcasecmp(reply->qdata, reply->rdata->ai_canonname))
vsic_slog(LOG_CLIENT, "DNS: %s is nickname for %s",
reply->qdata, reply->rdata->ai_canonname);
atmp = reply->rdata;
while (atmp)
{
if (atmp->ai_family != PF_INET &&
atmp->ai_family != PF_INET6)
{
vsic_slog(LOG_DEBUG, "DNS: unexpected family: %d",
atmp->ai_family);
continue;
}
vsic_slog(LOG_CLIENT, "--- DNS: %s is %s",
reply->qdata, aitoip(atmp));
atmp = atmp->ai_next;
}
freeaddrinfo(reply->rdata);
}
else
{
vsic_slog(LOG_CLIENT, "--- DNS: %s is %s",
reply->qdata, reply->rname);
free(reply->rname);
}
#else
vsic_slog(LOG_CLIENT, "--- DNS: %s is %s",
reply->qdata, reply->rdata);
#endif
}
#if !defined(HAVE_GETADDRINFO)
free(reply->rdata);
#endif
}
free(reply->qdata);
free(reply);
}
rlist = NULL;
pthread_mutex_unlock(&dns_rlist);
}
syntax highlighted by Code2HTML, v. 0.9.1