/* $Id: hostlist.c,v 1.8 2004/02/13 14:59:57 ossi Exp $ *
*
* puf 0.9 Copyright (C) 2000-2003 by Oswald Buddenhagen <puf@ossi.cjb.net>
* based on puf 0.1.x (C) 1999,2000 by Anders Gavare <gavare@hotmail.com>
*
* You may modify and distribute this code under the terms of the GPL.
* There is NO WARRANTY of any kind. See COPYING for details.
*
* hostlist.c - manage the hostname cache
*
*/
#include "puf.h"
int always_primary_name;
host_t *hostlist; /* list of known hosts */
linear_queue(queue_dns_lookup, whost_t); /* waiting for start of lookup */
linear_na_queue(queue_dns_busy, dnsproc_t); /* helper processes performing lookup */
linear_na_queue(queue_dns_idle, dnsproc_t); /* helper process pool */
/* create new hostname structure */
static host_t *
new_host(char *name, int len, host_t **hptr)
{
host_t *h;
if ((h = mmalloc(sizeof(host_t) + len))) {
memcpy(h->name, name, len);
h->info = 0;
if (hptr) {
h->next = *hptr;
*hptr = h;
}
}
return h;
}
/* create a new hostname structure, if it's not the initiating hostname */
/* put the new or already existing structure into the host list */
static host_t *
p_new_host(whost_t *wh, hinfo_t *hi, int *hapi,
char *name, int len, host_t **hptr)
{
host_t *h;
if (*hapi && !memcmp(wh->host->name, name, len)) {
*hapi = 0;
h = wh->host;
h->next = *hptr;
*hptr = h;
} else if (!(h = new_host(name, len, hptr)))
return 0;
h->info = hi;
return h;
}
/* tolower() on whole string */
static int
lcase(char *d, const char *s)
{
int l;
for (l = 0; (d[l] = tolower((int)s[l])); l++);
return l + 1;
}
/* FIXME: maybe, we should use inet_aton (or inet_addr) before doing
the gethostbyname magic. this would save the forks for numerical
input. also, gethostbyname reportedly fails addresses like
http://2165339403/~f98anga/ on openbsd */
/* return a cached host entry for the given host */
host_t *
host_lookup_fast(char *name, int namlen)
{
host_t *h;
dbg(DNS, ("host_lookup_fast for '%.*s'\n", namlen, name));
/* known host? */
for (h = hostlist; h; h = h->next) {
if (!memcmp(name, h->name, namlen + 1))
return h;
}
return 0;
}
/* starts an asynchronous dns lookup for the given host */
/* the first name referred to determines the local directory
name for this host. */
host_t *
host_lookup_full(char *name, int namlen, url_t *u, proxy_t *prox)
{
host_t *h;
whost_t *wh;
wobj_t *wo;
dnsproc_t *pr;
dbg(DNS, ("host_lookup_full for '%.*s'\n", namlen, name));
/* prepare url for enqueuing */
if (!(wo = mmalloc(sizeof(*wo))))
return 0;
wo->url = u;
wo->proxy = prox;
/* pending lookup? */
for (wh = queue_dns_lookup; wh; wh = wh->next) {
if (!memcmp(name, wh->host->name, namlen + 1)) {
wo->next = wh->objq;
wh->objq = wo;
return wh->host;
}
}
for (pr = queue_dns_busy; pr; pr = pr->next) {
if (pr->whost && !memcmp(name, pr->whost->host->name, namlen + 1)) {
wo->next = pr->whost->objq;
pr->whost->objq = wo;
return pr->whost->host;
}
}
/* new host entry */
if (!(h = new_host(name, namlen + 1, 0))) {
free(wo);
return 0;
}
/* prepare lookup for enqueuing */
if (!(wh = mmalloc(sizeof(*wh)))) {
free(h);
free(wo);
return 0;
}
wo->next = 0;
wh->objq = wo;
wh->host = h;
lq_append(queue_dns_lookup, wh); /* XXX optimize idle case */
return h;
}
int
start_lookup(dnsproc_t *proc)
{
int l = strlen(proc->whost->host->name) + 1;
dbg(DNS, ("starting dns lookup, helper %d\n", proc->pid));
if (send(proc->fd, proc->whost->host->name, l, 0) != l) {
prx(ERR, "cannot talk to DNS helper!\n");
return 0;
}
return 1;
}
int
finish_lookup(dnsproc_t *proc)
{
whost_t *wh;
host_t *h, *sh;
hinfo_t *hi;
int i, na, cp, hapi = 1;
u_char buf[1024];
dbg(DNS, ("finishing dns lookup, helper %d\n", proc->pid));
if (recv(proc->fd, buf, sizeof(buf), 0) < (int)sizeof(int)) {
prx(ERR, "cannot talk to DNS helper!\n");
return 0;
}
wh = proc->whost;
na = ((int *)buf)[0];
if (!na) {
prx(ERR, "DNS lookup for '%s' failed!\n", wh->host->name);
goto badhost;
}
if (na < 0) {
prx(ERR, "DNS lookup for '%s' timed out!\n", wh->host->name);
goto badhost;
}
if (((int *)buf)[1] != sizeof(struct in_addr)) {
prx(ERR, "cannot handle address returned for '%s'!\n", wh->host->name);
goto badhost;
}
cp = 2 * sizeof(int) + na * sizeof(struct in_addr);
/* check if this is a new alias for a host already in the list */
for (h = hostlist; h; h = h->next)
if (!memcmp(h->name, buf + cp + 1, buf[cp])) {
hi = h->info;
dbg(DNS, ("found a new alias for '%s':\n", h->name));
goto havho;
}
dbg(DNS, ("creating new host '%s'\n", buf + cp + 1));
/* create new hostinfo structure ... */
if (!(hi = mmalloc(sizeof(hinfo_t) + na * sizeof(haddr_t))))
goto badhost;
/* create hostname structure for real name */
if (!(h = p_new_host(wh, hi, &hapi, buf + cp + 1, buf[cp], &hostlist))) {
free(hi);
goto badhost;
}
/* ... and initialize it */
hi->name = h->name;
hi->lname = always_primary_name ? h->name : wh->host->name;
hi->is_http11 = 1;
hi->cur_ip = 0;
hi->num_ips = na;
/* copy list of ip-addresses and initialize retry counters */
for (i = 0; i < na; i++) {
hi->ips[i].addr = ((struct in_addr *)(((int *)buf) + 2))[i];
hi->ips[i].retry_time = 0;
hi->ips[i].last_errt = 0;
hi->ips[i].attempt = 0;
}
havho:
/* save the alias names if we got any: */
for (;;) {
cp += buf[cp] + 1;
if (!buf[cp])
break;
/* check, if we already know this alias.
theoretically sh should be h->next, but it happens, that
the alias was reported as a primary name already (namely if
we used the alias first and it is listed in /etc/hosts */
for (sh = h; sh && sh->info == hi; sh = sh->next)
if (!memcmp(sh->name, buf + cp + 1, buf[cp]))
goto havho;
dbg(DNS, ("adding new alias '%s'\n", buf + cp + 1));
if (!p_new_host(wh, hi, &hapi, buf + cp + 1, buf[cp], &(h->next))) {
if (hapi) /* no host info -> real problem */
goto badhost;
else /* missed alias */
return 1;
}
}
if (hapi) {
/* was it a not fully qualified domain name? */
#ifdef CORRECT_DNS
int nl = strlen(wh->host->name);
for (sh = h; sh && sh->info == hi; sh = sh->next)
if (!memcmp(sh->name, wh->host->name, nl) && sh->name[nl] == '.')
goto neho;
prx(WRN, "your DNS resolver configuration seems to be messed up.\n");
neho:
#endif
dbg(DNS, ("adding not fully qualified hostname '%s'\n", wh->host->name));
wh->host->next = h->next;
h->next = wh->host;
wh->host->info = hi;
}
return 1;
badhost:
wh->host->next = hostlist;
hostlist = wh->host;
return 1;
}
#include <setjmp.h>
jmp_buf alrmjmp;
static void
sigalrm()
{
longjmp(alrmjmp, 1);
}
dnsproc_t *
fork_dnsproc()
{
dnsproc_t *proc;
struct hostent *he;
int hl, i, na, pid, fds[2];
u_int cp;
sigset_t ss, oss;
char buf[1024];
dbg(DNS, ("forking new dns helper\n"));
if (!(proc = mmalloc(sizeof(*proc))))
return 0;
if (socketpair(PF_UNIX, SOCK_STREAM, 0, fds) &&
(!free_fd() ||
(socketpair(PF_UNIX, SOCK_STREAM, 0, fds) &&
(!free_fd() ||
socketpair(PF_UNIX, SOCK_STREAM, 0, fds)))))
{
free(proc);
return 0;
}
sigfillset(&ss);
sigprocmask(SIG_SETMASK, &ss, &oss);
if ((pid = fork())) {
sigprocmask(SIG_SETMASK, &oss, 0);
close(fds[1]);
if (pid < 0) {
dbg(DNS, (" failed\n"));
close(fds[0]);
free(proc);
return 0;
} else {
dbg(DNS, (" pid = %d\n", pid));
proc->fd = fds[0];
proc->pid = pid;
return proc;
}
}
signal(SIGTERM, SIG_DFL);
signal(SIGINT, SIG_DFL);
sigprocmask(SIG_SETMASK, &oss, 0);
close(fds[0]);
for (;;) {
if (!setjmp(alrmjmp)) {
dbg(DNS, ("dns helper %d: awaiting request\n", getpid()));
if (read(fds[1], buf, sizeof(buf)) <= 0) {
prx(ERR, "DNS helper: cannot read control socket!\n");
exit(1);
}
dbg(DNS, ("dns helper %d: looking up '%s'\n", getpid(), buf));
signal(SIGALRM, (void (*)(int))sigalrm);
alarm(timeout_dns);
he = gethostbyname(buf);
alarm(0);
if (!he) {
dbg(DNS, ("dns helper %d: lookup failed\n", getpid()));
((int *)buf)[0] = 0;
if (write(fds[1], buf, sizeof(int)) != sizeof(int)) {
prx(ERR, "DNS helper: cannot write control socket!\n");
exit(1);
}
} else {
/* count ip-addresses for this name */
for (na = 0, cp = 2 * sizeof(int); he->h_addr_list[na];
na++, cp += he->h_length)
memcpy(buf + cp, he->h_addr_list[na], he->h_length);
dbg(DNS, ("dns helper %d: lookup successful, %d addresses\n", getpid(), na));
((int *)buf)[0] = na;
((int *)buf)[1] = he->h_length;
/* copy name and aliases */
hl = lcase(buf + cp + 1, he->h_name);
buf[cp] = hl;
cp += hl + 1;
for (i = 0; he->h_aliases[i]; i++) {
hl = lcase(buf + cp + 1, he->h_aliases[i]);
buf[cp] = hl;
cp += hl + 1;
}
buf[cp++] = 0;
if (write(fds[1], buf, cp) != (int)cp) {
prx(ERR, "DNS helper: cannot write control socket!\n");
exit(1);
}
}
} else {
dbg(DNS, ("dns helper %d: lookup timed out\n", getpid()));
((int *)buf)[0] = -1;
if (write(fds[1], buf, sizeof(int)) != sizeof(int)) {
prx(ERR, "DNS helper: cannot write control socket!\n");
exit(1);
}
}
}
}
void
reap_dnsproc(dnsproc_t *proc)
{
dbg(DNS, ("reaping dns helper %d\n", proc->pid));
kill(proc->pid, SIGTERM);
waitpid(proc->pid, 0, 0);
close(proc->fd);
free(proc);
}
syntax highlighted by Code2HTML, v. 0.9.1