#include <sys/types.h>
#include <netinet/in.h>
#include "cdns.h"
#include "irc.h"
static char cvsrevision[] = "$Id: cdns.c 3 2008-02-25 09:49:14Z keaston $";
CVS_REVISION(cdns_c)
#include "commands.h"
#include "struct.h"
#include "newio.h"
#define MAIN_SOURCE
#include "modval.h"
#if defined(THREAD) && defined(WANT_NSLOOKUP)
static void init_dns_mutexes(void);
static void *start_dns_thread(void *);
static void do_dns_lookup(DNS_QUEUE *);
static void cleanup_dns(void);
static void destroy_dns_queue(DNS_QUEUE **, DNS_QUEUE **);
static void free_dns_entry(DNS_QUEUE *);
static void kill_dns_thread(int);
static DNS_QUEUE *build_dns_entry(char *, void (*)(DNS_QUEUE *), char *, void *);
static DNS_QUEUE *dns_dequeue(DNS_QUEUE **, DNS_QUEUE **);
static DNS_QUEUE *dns_enqueue(DNS_QUEUE **, DNS_QUEUE **, DNS_QUEUE *);
static DNS_QUEUE *dns_enqueue_urgent(DNS_QUEUE **, DNS_QUEUE **, DNS_QUEUE *);
static pthread_t dns_thread = {0};
static pthread_mutex_t pending_queue_mutex = {0};
static pthread_mutex_t finished_queue_mutex = {0};
static pthread_mutex_t quit_mutex = {0};
static pthread_cond_t pending_queue_cond;
static DNS_QUEUE *PendingQueueHead = NULL, *PendingQueueTail = NULL;
static DNS_QUEUE *FinishedQueueHead = NULL, *FinishedQueueTail = NULL;
static int cdns_write = -1;
static int cdns_read = -1;
void start_dns(void)
{
int fd_array[2];
Q_OPEN(&PendingQueueHead, &PendingQueueTail);
Q_OPEN(&FinishedQueueHead, &FinishedQueueTail);
init_dns_mutexes();
pthread_cond_init(&pending_queue_cond, NULL);
pthread_mutex_lock(&quit_mutex);
pipe(fd_array);
cdns_read = fd_array[0];
cdns_write = fd_array[1];
new_open(cdns_read);
new_open(cdns_write);
pthread_create(&dns_thread, NULL, start_dns_thread, NULL);
}
void stop_dns(void)
{
void *ptr;
if (!dns_thread)
return;
pthread_mutex_unlock(&quit_mutex);
pthread_mutex_lock(&pending_queue_mutex);
pthread_cond_signal(&pending_queue_cond);
pthread_mutex_unlock(&pending_queue_mutex);
pthread_join(dns_thread, &ptr);
cleanup_dns();
}
void kill_dns(void)
{
sigset_t set, oldset;
sigfillset(&set);
sigdelset(&set, SIGFPE);
sigdelset(&set, SIGILL);
sigdelset(&set, SIGSEGV);
sigprocmask(SIG_BLOCK, &set, &oldset);
pthread_mutex_lock(&pending_queue_mutex);
pthread_cond_signal(&pending_queue_cond);
pthread_kill(dns_thread, SIGQUIT);
sigprocmask(SIG_BLOCK, &oldset, &set);
cleanup_dns();
}
void
add_to_dns_queue(char *userhost, void (*callback)(DNS_QUEUE *), char *cmd, void *data, int urgency)
{
char *split = (char *) 0;
DNS_QUEUE *tmp;
if (userhost && *userhost) {
split = index(userhost, '@');
if (split)
split++;
else
split = userhost;
if (split && *split) {
tmp = build_dns_entry(split, callback, cmd, data);
pthread_mutex_lock(&pending_queue_mutex);
if (urgency == DNS_URGENT)
dns_enqueue_urgent(&PendingQueueHead,
&PendingQueueTail, tmp);
else
dns_enqueue(&PendingQueueHead,
&PendingQueueTail, tmp);
pthread_cond_signal(&pending_queue_cond);
pthread_mutex_unlock(&pending_queue_mutex);
}
else
fprintf(stderr, "%s:%d error: No host!", __FILE__, __LINE__);
}
else
fprintf(stderr, "%s:%d error: NULL!", __FILE__, __LINE__);
}
void set_dns_output_fd(fd_set *rd)
{
if (cdns_read >= 0)
FD_SET(cdns_read, rd);
}
void dns_check(fd_set *rd)
{
char blah[2];
if (cdns_read >= 0 && FD_ISSET(cdns_read, rd)) {
read(cdns_read, &blah, 1);
check_dns_queue();
}
}
void check_dns_queue()
{
DNS_QUEUE *dns = (DNS_QUEUE *) 0;
while (pthread_mutex_trylock(&finished_queue_mutex) == 0) {
dns = dns_dequeue(&FinishedQueueHead, &FinishedQueueTail);
pthread_mutex_unlock(&finished_queue_mutex);
if (dns) {
#ifdef MY_DEBUG
fprintf(stderr, "DNS IN: %s: %s <-> %s",
dns->in ? dns->in : "<NULL>",
dns->in ? dns->in : "<NULL>",
dns->out ? dns->out : "<NULL>");
#endif
if (dns->alias)
{
char buffer[BIG_BUFFER_SIZE+1];
snprintf(buffer, BIG_BUFFER_SIZE, "%s %s ", dns->in ? dns->in : "<NULL>", dns->out ? dns->out : "<NULL>");
parse_line("NSLOOKUP", dns->alias, buffer, 0, 0, 1);
}
else if (dns->callback)
dns->callback(dns);
else
fprintf(stderr, "%s:%d error: No callback!",
__FILE__, __LINE__);
free_dns_entry(dns);
}
else
return;
}
}
static void free_dns_entry(DNS_QUEUE *tmp)
{
if (tmp->in)
free(tmp->in);
if (tmp->out)
free(tmp->out);
if (tmp->alias)
free(tmp->alias);
if (tmp->hostentr)
freemyhostent(tmp->hostentr);
free(tmp);
}
static void init_dns_mutexes(void)
{
pthread_mutex_init(&pending_queue_mutex, NULL);
pthread_mutex_init(&finished_queue_mutex, NULL);
pthread_mutex_init(&quit_mutex, NULL);
}
static void kill_dns_thread(int notused)
{
}
static void kill_dns_thread2(int notused)
{
int ecode = 0;
pthread_exit(&ecode);
}
static void dns_thread_signal_setup(void)
{
sigset_t set;
#if 0
signal(SIGQUIT, kill_dns_thread);
#endif
my_signal(SIGQUIT, (sigfunc *) kill_dns_thread, 0);
sigfillset(&set);
sigdelset(&set, SIGQUIT);
pthread_sigmask(SIG_BLOCK, &set, NULL);
}
static void *start_dns_thread(void *args)
{
DNS_QUEUE *dns = NULL;
dns_thread_signal_setup();
while(1) {
if (pthread_mutex_trylock(&quit_mutex) == 0) {
kill_dns_thread2(0);
}
pthread_mutex_lock(&pending_queue_mutex);
dns = dns_dequeue(&PendingQueueHead, &PendingQueueTail);
if (!dns) {
pthread_cond_wait(&pending_queue_cond, &pending_queue_mutex);
pthread_mutex_unlock(&pending_queue_mutex);
}
else {
char c = ' ';
pthread_mutex_unlock(&pending_queue_mutex);
do_dns_lookup(dns);
pthread_mutex_lock(&finished_queue_mutex);
dns_enqueue(&FinishedQueueHead, &FinishedQueueTail, dns);
pthread_mutex_unlock(&finished_queue_mutex);
write(cdns_write, &c, 1);
}
}
}
my_hostent *duphostent(struct hostent *orig)
{
my_hostent *tmp = new_malloc(sizeof(my_hostent));
int z;
tmp->h_name = m_strdup(orig->h_name);
tmp->h_length = orig->h_length;
tmp->h_addrtype = orig->h_addrtype;
for(z=0;z<MAXALIASES && orig->h_aliases[z];z++)
tmp->h_aliases[z] = m_strdup(orig->h_aliases[z]);
for(z=0;z<MAXADDRS && orig->h_addr_list[z];z++)
memcpy(&tmp->h_addr_list[z], orig->h_addr_list[z], sizeof(*orig->h_addr_list));
return (my_hostent *)tmp;
}
void freemyhostent(my_hostent *freeme)
{
int z;
if(!freeme)
return;
new_free(&freeme->h_name);
for(z=0;z<MAXALIASES;z++)
if(freeme->h_aliases[z])
new_free(&freeme->h_aliases[z]);
new_free(&freeme);
}
static void do_dns_lookup(DNS_QUEUE *dns)
{
struct hostent *temp;
struct in_addr temp1;
int ip = 0;
if (!dns->in)
return;
if (isdigit(*(dns->in + strlen(dns->in) - 1))) {
ip = 1;
temp1.s_addr = inet_addr(dns->in);
temp = gethostbyaddr((char*) &temp1,
sizeof (struct in_addr), AF_INET);
}
else {
temp = gethostbyname(dns->in);
if (temp)
#if defined(_Windows)
memcpy(&temp1, temp->h_addr, temp->h_length);
#else
memcpy((caddr_t)&temp1, temp->h_addr, temp->h_length);
#endif
else
return;
}
if (!temp)
return;
if (ip) {
dns->ip = 1;
if (temp->h_name && *temp->h_name) {
dns->out = (char *) malloc(strlen(temp->h_name) + 1);
strcpy(dns->out, temp->h_name);
dns->hostentr = duphostent(temp);
}
}
else {
dns->ip = 0;
dns->out = (char *) malloc(strlen(inet_ntoa(temp1)) + 1);
strcpy(dns->out, inet_ntoa(temp1));
dns->hostentr = duphostent(temp);
}
}
DNS_QUEUE *
dns_dequeue(DNS_QUEUE **headp, DNS_QUEUE **tailp)
{
DNS_QUEUE *tmp = NULL;
if (*headp == NULL)
return NULL;
tmp = *headp;
*headp = Q_NEXT(tmp);
if (*headp == NULL)
*tailp = NULL;
Q_NEXT(tmp) = NULL;
return tmp;
}
DNS_QUEUE *
dns_enqueue(DNS_QUEUE **headp, DNS_QUEUE **tailp, DNS_QUEUE *tmp)
{
Q_NEXT(tmp) = NULL;
if (*headp == NULL)
*headp = *tailp = tmp;
else {
Q_NEXT(*tailp) = tmp;
*tailp = tmp;
}
return NULL;
}
DNS_QUEUE *
dns_enqueue_urgent(DNS_QUEUE **headp, DNS_QUEUE **tailp, DNS_QUEUE *tmp)
{
Q_NEXT(tmp) = *headp;
*headp = tmp;
if (*tailp == NULL)
*tailp = tmp;
return NULL;
}
static DNS_QUEUE *
build_dns_entry(char *text, void (*callback) (DNS_QUEUE *), char *cmd, void *data)
{
DNS_QUEUE *tmp = (DNS_QUEUE *) malloc(sizeof(DNS_QUEUE));
bzero(tmp, sizeof(DNS_QUEUE));
tmp->in = (char *) malloc(strlen(text) + 1);
strcpy(tmp->in, text);
tmp->callback = callback;
tmp->callinfo = data;
if (cmd)
tmp->alias = strdup(cmd);
return tmp;
}
static void cleanup_dns(void)
{
pthread_mutex_destroy(&pending_queue_mutex);
pthread_mutex_destroy(&finished_queue_mutex);
pthread_mutex_destroy(&quit_mutex);
pthread_cond_destroy(&pending_queue_cond);
destroy_dns_queue(&PendingQueueHead, &PendingQueueTail);
destroy_dns_queue(&FinishedQueueHead, &FinishedQueueTail);
close(cdns_read);
close(cdns_write);
cdns_read = cdns_write = -1;
}
static void destroy_dns_queue(DNS_QUEUE **QueueHead, DNS_QUEUE **QueueTail)
{
DNS_QUEUE *dns;
while((dns = dns_dequeue(QueueHead, QueueTail)) != NULL)
free_dns_entry(dns);
}
#endif