/* * dnschild.c * * Copyright (c) 2004,2005 Martin Murray <mmurray@monkey.org> * All rights reserved. * */ #include "copyright.h" #include "config.h" #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/file.h> #include <sys/ioctl.h> #include <sys/wait.h> #include <signal.h> #include <errno.h> #include "externs.h" #include "debug.h" static struct dns_query_state_t { DESC *desc; struct event ev; struct dns_query_state_t *next; int pid; int fd; } *running = NULL; static int running_queries = 0; static int dnschild_state = 0; static struct timeval query_timeout = { 60, 0 }; static void dnschild_finish(int fd, short event, void *arg); int dnschild_init() { dprintk("dnschild initialized."); dnschild_state = 1; return 1; } void *dnschild_request(DESC * d) { int fds[2]; struct dns_query_state_t *dqst; char address[1024]; char outbuffer[255]; int length; int result; if(!dnschild_state) { dprintk("bailing query, dnschild is shut down."); return 0; } dqst = malloc(sizeof(struct dns_query_state_t)); memset(dqst, 0, sizeof(struct dns_query_state_t)); dqst->desc = d; if(pipe(fds) < 0) { log_perror("DNS", "ERR", NULL, "pipe"); } event_set(&dqst->ev, fds[0], EV_TIMEOUT | EV_READ, dnschild_finish, dqst); event_add(&dqst->ev, &query_timeout); dqst->pid = fork(); if(dqst->pid == 0) { close(fds[0]); /* begin child section */ unbind_signals(); memset(address, 0, 1023); if((result=getnameinfo((struct sockaddr *) &d->saddr, d->saddr_len, address, 1023, NULL, 0, NI_NAMEREQD))) { length = snprintf(outbuffer, 255, "0%s/%s", gai_strerror(result), strerror(errno)); outbuffer[length++] = '\0'; write(fds[1], outbuffer, length); } else { length = snprintf(outbuffer, 255, "1%s", address); outbuffer[length++] = '\0'; write(fds[1], outbuffer, length); } close(fds[1]); exit(0); /* end child section */ } dqst->fd = fds[0]; close(fds[1]); dqst->next = running; running = dqst; running_queries++; return (void *) dqst; } void dnschild_destruct() { struct dns_query_state_t *dqst; dprintk("dnschild expiring queries and shutting down."); dnschild_state = 0; while (running) { dqst = running; dprintk("dnschild query for %s aborting early.", dqst->desc->addr); if(event_pending(&dqst->ev, EV_READ, NULL)) event_del(&dqst->ev); close(dqst->fd); dqst->desc->outstanding_dnschild_query = NULL; running = running->next; free(dqst); } } void dnschild_kill(void *arg) { struct dns_query_state_t *dqst = (struct dns_query_state_t *) arg, *iter; dprintk("dnschild query for %s aborting early.", dqst->desc->addr); iter = running; if(running == dqst) { running = dqst->next; } else { while (iter) { if(iter->next == dqst) { iter->next = dqst->next; break; } iter = iter->next; } } if(event_pending(&dqst->ev, EV_READ, NULL)) event_del(&dqst->ev); dqst->desc->outstanding_dnschild_query = NULL; close(dqst->fd); free(dqst); } static void dnschild_finish(int fd, short event, void *arg) { char buffer[2048]; struct dns_query_state_t *dqst = (struct dns_query_state_t *) arg, *iter; iter = running; if(running == dqst) { running = dqst->next; } else { while (iter) { if(iter->next == dqst) { iter->next = dqst->next; break; } iter = iter->next; } } dqst->desc->outstanding_dnschild_query = NULL; if(event & EV_TIMEOUT || !dnschild_state) { kill(dqst->pid, SIGTERM); log_perror("DNS", "ERR", NULL, "dnschild failed to finish."); close(fd); free(dqst); return; } read(fd, buffer, 2048); if(buffer[0] == '0') { dprintk("dnschild failed with error: %s", buffer + 1); close(fd); free(dqst); return; } strncpy(dqst->desc->addr, buffer + 1, sizeof(dqst->desc->addr) - 1); dprintk("dnschild resolved %s correctly.", buffer + 1); close(fd); free(dqst); return; }