File: cdns.c

package info (click to toggle)
ircii-pana 1%3A1.1-1
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 14,048 kB
  • ctags: 14,567
  • sloc: ansic: 130,654; sql: 6,041; makefile: 4,313; cpp: 1,270; tcl: 1,230; sh: 638; java: 151
file content (493 lines) | stat: -rw-r--r-- 13,345 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
/************
 *  cdns.c  *
 ************
 *
 * A threaded DNS lookup implementation.
 *
 * This was written exclusively to get around the fact that
 * gethostbyaddr()/gethostbyname() will BLOCK until the resolver either
 * gets an answer, or times out. So we create a thread, and allow
 * the calls to block as long as they want, as the main app's thread
 * will keep on chugging along regardless.
 *
 * BTW, even tho the file is .cc, it really is only C code, I just like
 * C++'s strict type checking.
 *
 * Written by Scott H Kilau
 *
 * Copyright(c) 1999
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT
 * $Id: cdns.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $
 */

#include <sys/types.h>
#include <netinet/in.h>

#include "cdns.h"
#include "irc.h"	/* To pick up our next #define checks */
static char cvsrevision[] = "$Id: cdns.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
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)

/* Our static functions */
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 *);

/* Our static globals */
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;

/*
 * start_dns : This should be called by the main app thread,
 * whenever it wants to start up the dns stuff
 */
void start_dns(void)
{
	int fd_array[2];
	/* Init our queues. */
	Q_OPEN(&PendingQueueHead, &PendingQueueTail);
	Q_OPEN(&FinishedQueueHead, &FinishedQueueTail);
	/* Create our mutexes */
	init_dns_mutexes();
	/* init our pending queue condition, and set to default attributes (NULL) */
	pthread_cond_init(&pending_queue_cond, NULL);
	/* lock up the quit mutex */
	pthread_mutex_lock(&quit_mutex);
	/* create our pipe [0] = main to read from, [1] = cdns to write to */
	pipe(fd_array);
	cdns_read = fd_array[0];
	cdns_write = fd_array[1];
	new_open(cdns_read);
	new_open(cdns_write);
	/* create our dns thread */
	pthread_create(&dns_thread, NULL, start_dns_thread, NULL);
}

/* 
 * stop_dns : This should be called by the main app thread,
 * whenever it wants to stop the dns stuff.
 */
void stop_dns(void)
{
	void *ptr;
	if (!dns_thread)
		return;
	/* Unlock the quit mutex */
	pthread_mutex_unlock(&quit_mutex);
	/* lock up pending queue mutex */
	pthread_mutex_lock(&pending_queue_mutex);
	/* signal thread to wake up */
	pthread_cond_signal(&pending_queue_cond);
	/* Give lock back, so dns thread can react to signal */
	pthread_mutex_unlock(&pending_queue_mutex);
	/* Wait until the thread kills itself. */
	pthread_join(dns_thread, &ptr);
	cleanup_dns();
}

/* 
 * kill_dns : This should be called by the main app thread,
 * when it wants the dns thread to go away forever. This function
 * kills the thread uncleanly, and probably should only be used
 * when the main app is about to exit()
 */
void kill_dns(void)
{
	sigset_t set, oldset;

	/* Fill set with all known signals */
	sigfillset(&set);
	/* Remove the few signals that POSIX says we should */
	sigdelset(&set, SIGFPE);
	sigdelset(&set, SIGILL);
	sigdelset(&set, SIGSEGV);
	/* Tell our thread (main) to block against the above signals */
	sigprocmask(SIG_BLOCK, &set, &oldset);
	/* lock up pending queue mutex */
	pthread_mutex_lock(&pending_queue_mutex);
	/* signal thread to wake up */
	pthread_cond_signal(&pending_queue_cond);
	/* Kill the dns thread, using the SIGQUIT signal */
	pthread_kill(dns_thread, SIGQUIT);
	/* Put back the previous blocking signals */
	sigprocmask(SIG_BLOCK, &oldset, &set);
	/* cleanup anything dealing with the dns stuff */
	cleanup_dns();
}

/*
 * add_to_dns_queue : This should be called by the main app thread,
 * whenever it wants us to resolve something... host <---> ip.
 */
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) {
		/* Look for user@host */
		split = index(userhost, '@');
		if (split)
			split++;
		else
			split = userhost;
		if (split && *split) {
			/* Build dns entry */
			tmp = build_dns_entry(split, callback, cmd, data);
			/* Wait until we can get mutex lock for queue */
			pthread_mutex_lock(&pending_queue_mutex);
			/* Enqueue the entry, checking urgency */
			if (urgency == DNS_URGENT)
				dns_enqueue_urgent(&PendingQueueHead,
					&PendingQueueTail, tmp);
			else
				dns_enqueue(&PendingQueueHead,
					&PendingQueueTail, tmp);
			/* signal dns thread, its got work to do */
			pthread_cond_signal(&pending_queue_cond);
			/* Give lock back, so dns thread can react to signal */
			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__);
}

/* set_dns_output_fd : This makes check_dns_queue much better.
 * This will add our piped fd into the main's select() loop, so
 * we will know right away when the output queue has data waiting.
 */
void set_dns_output_fd(fd_set *rd)
{
	if (cdns_read >= 0)
		FD_SET(cdns_read, rd);
}

/* dns_check : See above. */
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();
	}
}

/*
 * check_dns_queue : This should be called by the main app thread, at
 * periodic intervals, ie, whenever its convenient.
 */
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;
	}
}

/* Give back memory that we allocated for this entry */
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);
}

/* init our dns mutexes */
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)
{
	/* Empty */
}

static void kill_dns_thread2(int notused)
{
	int ecode = 0;
	pthread_exit(&ecode);
}

static void dns_thread_signal_setup(void)
{
	sigset_t set;

	/* Create SIGQUIT signal handler for this thread */
#if 0
	signal(SIGQUIT, kill_dns_thread);
#endif
	/* Use ircii's portable implementation of signal() instead */
	my_signal(SIGQUIT, (sigfunc *) kill_dns_thread, 0);
	/* Fill set with every signal */
	sigfillset(&set);
	/* Remove the SIGQUIT signal from the set */
	sigdelset(&set, SIGQUIT);
	/* Apply the mask on this thread */
	pthread_sigmask(SIG_BLOCK, &set, NULL);
}	

/* Our main function of the dns thread */
static void *start_dns_thread(void *args)
{
	DNS_QUEUE *dns = NULL;
	/* Set up the thread's signal handlers and mask */
	dns_thread_signal_setup();
	/* loop */
	while(1) {
		/* Try the quit mutex, if we get it, that means
		 * main() wants us to die.
		 */
		if (pthread_mutex_trylock(&quit_mutex) == 0) {
			kill_dns_thread2(0);
		}
		/* Lock the queue */
		pthread_mutex_lock(&pending_queue_mutex);
		dns = dns_dequeue(&PendingQueueHead, &PendingQueueTail);
		if (!dns) {
			/*
			 * Give back the cpu, and go to sleep until cond gets signalled.
			 * Note: the mutex MUST be locked, before going into the wait!
			 */
			pthread_cond_wait(&pending_queue_cond, &pending_queue_mutex);
			/* We have been woken up. Thus, 2 conditions are present.
			 * 1) We have been signalled that data is waiting.
			 * 2) The mutex is locked.
			 */
			pthread_mutex_unlock(&pending_queue_mutex);
		}
		else {
			char c = ' ';
			pthread_mutex_unlock(&pending_queue_mutex);
			do_dns_lookup(dns);
			/* block until we get the lock */
			pthread_mutex_lock(&finished_queue_mutex);
			dns_enqueue(&FinishedQueueHead, &FinishedQueueTail, dns);
			pthread_mutex_unlock(&finished_queue_mutex);
			/* Write to our pipe, this will wake up mains select loop */
			write(cdns_write, &c, 1);
		}
	}
}

/* Makes a malloced duplicate of the hostent returned. */
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;
}

/* Free's the replica hostent. */
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);
}

/* Does the actual DNS lookup.... host <---> ip  */
static void do_dns_lookup(DNS_QUEUE *dns)
{
	struct hostent *temp;
	struct in_addr temp1;
	int ip = 0;
 
	/* If nothing, give back nothing */
	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);
	}
}

/* dequeue an entry from the passed in queue */
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;
}

/* enqueue a request onto the passed in queue */
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;
}

/* enqueue a request onto the passed in queue, putting it at the front
 * of the queue. This means it will be the next requested dequeue.
 */
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;
}

/* build a dns entry struct */
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;
}

/* cleanup_dns : cleanup anything regarding the dns thread */
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;
}

/* destroy_dns_queue : Walks the queue, blowing away each node */
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 /* THREAD  && NSLOOKUP */