Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
BitchX
GitHub Repository: BitchX/BitchX1.3
Path: blob/master/source/cdns.c
1069 views
1
/************
2
* cdns.c *
3
************
4
*
5
* A threaded DNS lookup implementation.
6
*
7
* This was written exclusively to get around the fact that
8
* gethostbyaddr()/gethostbyname() will BLOCK until the resolver either
9
* gets an answer, or times out. So we create a thread, and allow
10
* the calls to block as long as they want, as the main app's thread
11
* will keep on chugging along regardless.
12
*
13
* BTW, even tho the file is .cc, it really is only C code, I just like
14
* C++'s strict type checking.
15
*
16
* Written by Scott H Kilau
17
*
18
* Copyright(c) 1999
19
*
20
* See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT
21
* $Id: cdns.c 3 2008-02-25 09:49:14Z keaston $
22
*/
23
24
#include <sys/types.h>
25
#include <netinet/in.h>
26
27
#include "cdns.h"
28
#include "irc.h" /* To pick up our next #define checks */
29
static char cvsrevision[] = "$Id: cdns.c 3 2008-02-25 09:49:14Z keaston $";
30
CVS_REVISION(cdns_c)
31
#include "commands.h"
32
33
#include "struct.h"
34
#include "newio.h"
35
#define MAIN_SOURCE
36
#include "modval.h"
37
38
#if defined(THREAD) && defined(WANT_NSLOOKUP)
39
40
/* Our static functions */
41
static void init_dns_mutexes(void);
42
static void *start_dns_thread(void *);
43
static void do_dns_lookup(DNS_QUEUE *);
44
static void cleanup_dns(void);
45
static void destroy_dns_queue(DNS_QUEUE **, DNS_QUEUE **);
46
static void free_dns_entry(DNS_QUEUE *);
47
static void kill_dns_thread(int);
48
static DNS_QUEUE *build_dns_entry(char *, void (*)(DNS_QUEUE *), char *, void *);
49
static DNS_QUEUE *dns_dequeue(DNS_QUEUE **, DNS_QUEUE **);
50
static DNS_QUEUE *dns_enqueue(DNS_QUEUE **, DNS_QUEUE **, DNS_QUEUE *);
51
static DNS_QUEUE *dns_enqueue_urgent(DNS_QUEUE **, DNS_QUEUE **, DNS_QUEUE *);
52
53
/* Our static globals */
54
static pthread_t dns_thread = {0};
55
static pthread_mutex_t pending_queue_mutex = {0};
56
static pthread_mutex_t finished_queue_mutex = {0};
57
static pthread_mutex_t quit_mutex = {0};
58
static pthread_cond_t pending_queue_cond;
59
static DNS_QUEUE *PendingQueueHead = NULL, *PendingQueueTail = NULL;
60
static DNS_QUEUE *FinishedQueueHead = NULL, *FinishedQueueTail = NULL;
61
static int cdns_write = -1;
62
static int cdns_read = -1;
63
64
/*
65
* start_dns : This should be called by the main app thread,
66
* whenever it wants to start up the dns stuff
67
*/
68
void start_dns(void)
69
{
70
int fd_array[2];
71
/* Init our queues. */
72
Q_OPEN(&PendingQueueHead, &PendingQueueTail);
73
Q_OPEN(&FinishedQueueHead, &FinishedQueueTail);
74
/* Create our mutexes */
75
init_dns_mutexes();
76
/* init our pending queue condition, and set to default attributes (NULL) */
77
pthread_cond_init(&pending_queue_cond, NULL);
78
/* lock up the quit mutex */
79
pthread_mutex_lock(&quit_mutex);
80
/* create our pipe [0] = main to read from, [1] = cdns to write to */
81
pipe(fd_array);
82
cdns_read = fd_array[0];
83
cdns_write = fd_array[1];
84
new_open(cdns_read);
85
new_open(cdns_write);
86
/* create our dns thread */
87
pthread_create(&dns_thread, NULL, start_dns_thread, NULL);
88
}
89
90
/*
91
* stop_dns : This should be called by the main app thread,
92
* whenever it wants to stop the dns stuff.
93
*/
94
void stop_dns(void)
95
{
96
void *ptr;
97
if (!dns_thread)
98
return;
99
/* Unlock the quit mutex */
100
pthread_mutex_unlock(&quit_mutex);
101
/* lock up pending queue mutex */
102
pthread_mutex_lock(&pending_queue_mutex);
103
/* signal thread to wake up */
104
pthread_cond_signal(&pending_queue_cond);
105
/* Give lock back, so dns thread can react to signal */
106
pthread_mutex_unlock(&pending_queue_mutex);
107
/* Wait until the thread kills itself. */
108
pthread_join(dns_thread, &ptr);
109
cleanup_dns();
110
}
111
112
/*
113
* kill_dns : This should be called by the main app thread,
114
* when it wants the dns thread to go away forever. This function
115
* kills the thread uncleanly, and probably should only be used
116
* when the main app is about to exit()
117
*/
118
void kill_dns(void)
119
{
120
sigset_t set, oldset;
121
122
/* Fill set with all known signals */
123
sigfillset(&set);
124
/* Remove the few signals that POSIX says we should */
125
sigdelset(&set, SIGFPE);
126
sigdelset(&set, SIGILL);
127
sigdelset(&set, SIGSEGV);
128
/* Tell our thread (main) to block against the above signals */
129
sigprocmask(SIG_BLOCK, &set, &oldset);
130
/* lock up pending queue mutex */
131
pthread_mutex_lock(&pending_queue_mutex);
132
/* signal thread to wake up */
133
pthread_cond_signal(&pending_queue_cond);
134
/* Kill the dns thread, using the SIGQUIT signal */
135
pthread_kill(dns_thread, SIGQUIT);
136
/* Put back the previous blocking signals */
137
sigprocmask(SIG_BLOCK, &oldset, &set);
138
/* cleanup anything dealing with the dns stuff */
139
cleanup_dns();
140
}
141
142
/*
143
* add_to_dns_queue : This should be called by the main app thread,
144
* whenever it wants us to resolve something... host <---> ip.
145
*/
146
void
147
add_to_dns_queue(char *userhost, void (*callback)(DNS_QUEUE *), char *cmd, void *data, int urgency)
148
{
149
char *split = (char *) 0;
150
DNS_QUEUE *tmp;
151
152
if (userhost && *userhost) {
153
/* Look for user@host */
154
split = index(userhost, '@');
155
if (split)
156
split++;
157
else
158
split = userhost;
159
if (split && *split) {
160
/* Build dns entry */
161
tmp = build_dns_entry(split, callback, cmd, data);
162
/* Wait until we can get mutex lock for queue */
163
pthread_mutex_lock(&pending_queue_mutex);
164
/* Enqueue the entry, checking urgency */
165
if (urgency == DNS_URGENT)
166
dns_enqueue_urgent(&PendingQueueHead,
167
&PendingQueueTail, tmp);
168
else
169
dns_enqueue(&PendingQueueHead,
170
&PendingQueueTail, tmp);
171
/* signal dns thread, its got work to do */
172
pthread_cond_signal(&pending_queue_cond);
173
/* Give lock back, so dns thread can react to signal */
174
pthread_mutex_unlock(&pending_queue_mutex);
175
}
176
else
177
fprintf(stderr, "%s:%d error: No host!", __FILE__, __LINE__);
178
}
179
else
180
fprintf(stderr, "%s:%d error: NULL!", __FILE__, __LINE__);
181
}
182
183
/* set_dns_output_fd : This makes check_dns_queue much better.
184
* This will add our piped fd into the main's select() loop, so
185
* we will know right away when the output queue has data waiting.
186
*/
187
void set_dns_output_fd(fd_set *rd)
188
{
189
if (cdns_read >= 0)
190
FD_SET(cdns_read, rd);
191
}
192
193
/* dns_check : See above. */
194
void dns_check(fd_set *rd)
195
{
196
char blah[2];
197
if (cdns_read >= 0 && FD_ISSET(cdns_read, rd)) {
198
read(cdns_read, &blah, 1);
199
check_dns_queue();
200
}
201
}
202
203
/*
204
* check_dns_queue : This should be called by the main app thread, at
205
* periodic intervals, ie, whenever its convenient.
206
*/
207
void check_dns_queue()
208
{
209
DNS_QUEUE *dns = (DNS_QUEUE *) 0;
210
while (pthread_mutex_trylock(&finished_queue_mutex) == 0) {
211
dns = dns_dequeue(&FinishedQueueHead, &FinishedQueueTail);
212
pthread_mutex_unlock(&finished_queue_mutex);
213
if (dns) {
214
#ifdef MY_DEBUG
215
fprintf(stderr, "DNS IN: %s: %s <-> %s",
216
dns->in ? dns->in : "<NULL>",
217
dns->in ? dns->in : "<NULL>",
218
dns->out ? dns->out : "<NULL>");
219
#endif
220
if (dns->alias)
221
{
222
char buffer[BIG_BUFFER_SIZE+1];
223
snprintf(buffer, BIG_BUFFER_SIZE, "%s %s ", dns->in ? dns->in : "<NULL>", dns->out ? dns->out : "<NULL>");
224
parse_line("NSLOOKUP", dns->alias, buffer, 0, 0, 1);
225
}
226
else if (dns->callback)
227
dns->callback(dns);
228
else
229
fprintf(stderr, "%s:%d error: No callback!",
230
__FILE__, __LINE__);
231
free_dns_entry(dns);
232
}
233
else
234
return;
235
}
236
}
237
238
/* Give back memory that we allocated for this entry */
239
static void free_dns_entry(DNS_QUEUE *tmp)
240
{
241
if (tmp->in)
242
free(tmp->in);
243
if (tmp->out)
244
free(tmp->out);
245
if (tmp->alias)
246
free(tmp->alias);
247
if (tmp->hostentr)
248
freemyhostent(tmp->hostentr);
249
free(tmp);
250
}
251
252
/* init our dns mutexes */
253
static void init_dns_mutexes(void)
254
{
255
pthread_mutex_init(&pending_queue_mutex, NULL);
256
pthread_mutex_init(&finished_queue_mutex, NULL);
257
pthread_mutex_init(&quit_mutex, NULL);
258
}
259
260
static void kill_dns_thread(int notused)
261
{
262
/* Empty */
263
}
264
265
static void kill_dns_thread2(int notused)
266
{
267
int ecode = 0;
268
pthread_exit(&ecode);
269
}
270
271
static void dns_thread_signal_setup(void)
272
{
273
sigset_t set;
274
275
/* Create SIGQUIT signal handler for this thread */
276
#if 0
277
signal(SIGQUIT, kill_dns_thread);
278
#endif
279
/* Use ircii's portable implementation of signal() instead */
280
my_signal(SIGQUIT, (sigfunc *) kill_dns_thread, 0);
281
/* Fill set with every signal */
282
sigfillset(&set);
283
/* Remove the SIGQUIT signal from the set */
284
sigdelset(&set, SIGQUIT);
285
/* Apply the mask on this thread */
286
pthread_sigmask(SIG_BLOCK, &set, NULL);
287
}
288
289
/* Our main function of the dns thread */
290
static void *start_dns_thread(void *args)
291
{
292
DNS_QUEUE *dns = NULL;
293
/* Set up the thread's signal handlers and mask */
294
dns_thread_signal_setup();
295
/* loop */
296
while(1) {
297
/* Try the quit mutex, if we get it, that means
298
* main() wants us to die.
299
*/
300
if (pthread_mutex_trylock(&quit_mutex) == 0) {
301
kill_dns_thread2(0);
302
}
303
/* Lock the queue */
304
pthread_mutex_lock(&pending_queue_mutex);
305
dns = dns_dequeue(&PendingQueueHead, &PendingQueueTail);
306
if (!dns) {
307
/*
308
* Give back the cpu, and go to sleep until cond gets signalled.
309
* Note: the mutex MUST be locked, before going into the wait!
310
*/
311
pthread_cond_wait(&pending_queue_cond, &pending_queue_mutex);
312
/* We have been woken up. Thus, 2 conditions are present.
313
* 1) We have been signalled that data is waiting.
314
* 2) The mutex is locked.
315
*/
316
pthread_mutex_unlock(&pending_queue_mutex);
317
}
318
else {
319
char c = ' ';
320
pthread_mutex_unlock(&pending_queue_mutex);
321
do_dns_lookup(dns);
322
/* block until we get the lock */
323
pthread_mutex_lock(&finished_queue_mutex);
324
dns_enqueue(&FinishedQueueHead, &FinishedQueueTail, dns);
325
pthread_mutex_unlock(&finished_queue_mutex);
326
/* Write to our pipe, this will wake up mains select loop */
327
write(cdns_write, &c, 1);
328
}
329
}
330
}
331
332
/* Makes a malloced duplicate of the hostent returned. */
333
my_hostent *duphostent(struct hostent *orig)
334
{
335
my_hostent *tmp = new_malloc(sizeof(my_hostent));
336
int z;
337
338
tmp->h_name = m_strdup(orig->h_name);
339
tmp->h_length = orig->h_length;
340
tmp->h_addrtype = orig->h_addrtype;
341
342
for(z=0;z<MAXALIASES && orig->h_aliases[z];z++)
343
tmp->h_aliases[z] = m_strdup(orig->h_aliases[z]);
344
345
for(z=0;z<MAXADDRS && orig->h_addr_list[z];z++)
346
memcpy(&tmp->h_addr_list[z], orig->h_addr_list[z], sizeof(*orig->h_addr_list));
347
348
return (my_hostent *)tmp;
349
}
350
351
/* Free's the replica hostent. */
352
void freemyhostent(my_hostent *freeme)
353
{
354
int z;
355
356
if(!freeme)
357
return;
358
359
new_free(&freeme->h_name);
360
361
for(z=0;z<MAXALIASES;z++)
362
if(freeme->h_aliases[z])
363
new_free(&freeme->h_aliases[z]);
364
365
new_free(&freeme);
366
}
367
368
/* Does the actual DNS lookup.... host <---> ip */
369
static void do_dns_lookup(DNS_QUEUE *dns)
370
{
371
struct hostent *temp;
372
struct in_addr temp1;
373
int ip = 0;
374
375
/* If nothing, give back nothing */
376
if (!dns->in)
377
return;
378
if (isdigit(*(dns->in + strlen(dns->in) - 1))) {
379
ip = 1;
380
temp1.s_addr = inet_addr(dns->in);
381
temp = gethostbyaddr((char*) &temp1,
382
sizeof (struct in_addr), AF_INET);
383
}
384
else {
385
temp = gethostbyname(dns->in);
386
if (temp)
387
#if defined(_Windows)
388
memcpy(&temp1, temp->h_addr, temp->h_length);
389
#else
390
memcpy((caddr_t)&temp1, temp->h_addr, temp->h_length);
391
#endif
392
else
393
return;
394
}
395
if (!temp)
396
return;
397
if (ip) {
398
dns->ip = 1;
399
if (temp->h_name && *temp->h_name) {
400
dns->out = (char *) malloc(strlen(temp->h_name) + 1);
401
strcpy(dns->out, temp->h_name);
402
dns->hostentr = duphostent(temp);
403
}
404
}
405
else {
406
dns->ip = 0;
407
dns->out = (char *) malloc(strlen(inet_ntoa(temp1)) + 1);
408
strcpy(dns->out, inet_ntoa(temp1));
409
dns->hostentr = duphostent(temp);
410
}
411
}
412
413
/* dequeue an entry from the passed in queue */
414
DNS_QUEUE *
415
dns_dequeue(DNS_QUEUE **headp, DNS_QUEUE **tailp)
416
{
417
DNS_QUEUE *tmp = NULL;
418
419
if (*headp == NULL)
420
return NULL;
421
tmp = *headp;
422
*headp = Q_NEXT(tmp);
423
if (*headp == NULL)
424
*tailp = NULL;
425
Q_NEXT(tmp) = NULL;
426
return tmp;
427
}
428
429
/* enqueue a request onto the passed in queue */
430
DNS_QUEUE *
431
dns_enqueue(DNS_QUEUE **headp, DNS_QUEUE **tailp, DNS_QUEUE *tmp)
432
{
433
Q_NEXT(tmp) = NULL;
434
if (*headp == NULL)
435
*headp = *tailp = tmp;
436
else {
437
Q_NEXT(*tailp) = tmp;
438
*tailp = tmp;
439
}
440
return NULL;
441
}
442
443
/* enqueue a request onto the passed in queue, putting it at the front
444
* of the queue. This means it will be the next requested dequeue.
445
*/
446
DNS_QUEUE *
447
dns_enqueue_urgent(DNS_QUEUE **headp, DNS_QUEUE **tailp, DNS_QUEUE *tmp)
448
{
449
Q_NEXT(tmp) = *headp;
450
*headp = tmp;
451
if (*tailp == NULL)
452
*tailp = tmp;
453
return NULL;
454
}
455
456
/* build a dns entry struct */
457
static DNS_QUEUE *
458
build_dns_entry(char *text, void (*callback) (DNS_QUEUE *), char *cmd, void *data)
459
{
460
DNS_QUEUE *tmp = (DNS_QUEUE *) malloc(sizeof(DNS_QUEUE));
461
bzero(tmp, sizeof(DNS_QUEUE));
462
tmp->in = (char *) malloc(strlen(text) + 1);
463
strcpy(tmp->in, text);
464
tmp->callback = callback;
465
tmp->callinfo = data;
466
if (cmd)
467
tmp->alias = strdup(cmd);
468
return tmp;
469
}
470
471
/* cleanup_dns : cleanup anything regarding the dns thread */
472
static void cleanup_dns(void)
473
{
474
pthread_mutex_destroy(&pending_queue_mutex);
475
pthread_mutex_destroy(&finished_queue_mutex);
476
pthread_mutex_destroy(&quit_mutex);
477
pthread_cond_destroy(&pending_queue_cond);
478
destroy_dns_queue(&PendingQueueHead, &PendingQueueTail);
479
destroy_dns_queue(&FinishedQueueHead, &FinishedQueueTail);
480
close(cdns_read);
481
close(cdns_write);
482
cdns_read = cdns_write = -1;
483
}
484
485
/* destroy_dns_queue : Walks the queue, blowing away each node */
486
static void destroy_dns_queue(DNS_QUEUE **QueueHead, DNS_QUEUE **QueueTail)
487
{
488
DNS_QUEUE *dns;
489
490
while((dns = dns_dequeue(QueueHead, QueueTail)) != NULL)
491
free_dns_entry(dns);
492
}
493
#endif /* THREAD && NSLOOKUP */
494
495