/* * 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); }