Dillo v3.2.0-88-g47ab7c70
Loading...
Searching...
No Matches
dns.c
Go to the documentation of this file.
1/*
2 * File: dns.c
3 *
4 * Copyright (C) 1999-2007 Jorge Arellano Cid <jcid@dillo.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 */
11
12/* @file
13 * Non blocking pthread-handled Dns scheme
14 */
15
16#include "config.h"
17
18/*
19 * Uncomment the following line for debugging or gprof profiling.
20 */
21/* #undef D_DNS_THREADED */
22
23#ifdef D_DNS_THREADED
24# include <pthread.h>
25#endif
26
27
28#include <assert.h>
29#include <netdb.h>
30#include <sys/types.h>
31#include <sys/socket.h>
32#include <arpa/inet.h>
33#include <netinet/in.h>
34#include <fcntl.h>
35#include <errno.h>
36#include <unistd.h>
37#include <stdlib.h>
38#include <stdio.h>
39#include <string.h>
40
41#include "msg.h"
42#include "dns.h"
43#include "list.h"
44#include "IO/iowatch.hh"
45
46
47/* Maximum dns resolving threads */
48#ifdef D_DNS_THREADED
49# define D_DNS_MAX_SERVERS 4
50#else
51# define D_DNS_MAX_SERVERS 1
52#endif
53
59
60typedef struct {
61 int channel;
62 DnsServerState_t state;
63 Dlist *addr_list;
64 char *hostname;
65 int status;
66#ifdef D_DNS_THREADED
67 pthread_t th1;
68#endif
69} DnsServer;
70
71typedef struct {
72 char *hostname;
73 Dlist *addr_list;
74} GDnsCache;
75
76typedef struct {
77 int channel;
78 char *hostname;
79 DnsCallback_t cb_func;
80 void *cb_data;
81} GDnsQueue;
82
83
84/*
85 * Forward declarations
86 */
87static void Dns_timeout_client(int fd, void *data);
88
89/*
90 * Local Data
91 */
92static DnsServer dns_server[D_DNS_MAX_SERVERS];
93static int num_servers;
94static GDnsCache *dns_cache;
96static GDnsQueue *dns_queue;
98static int dns_notify_pipe[2];
99
100
101/* ----------------------------------------------------------------------
102 * Dns queue functions
103 */
104static void Dns_queue_add(int channel, const char *hostname,
105 DnsCallback_t cb_func, void *cb_data)
106{
108 dns_queue[dns_queue_size].channel = channel;
109 dns_queue[dns_queue_size].hostname = dStrdup(hostname);
110 dns_queue[dns_queue_size].cb_func = cb_func;
111 dns_queue[dns_queue_size].cb_data = cb_data;
113}
114
119static int Dns_queue_find(const char *hostname)
120{
121 int i;
122
123 for (i = 0; i < dns_queue_size; i++)
124 if (!dStrAsciiCasecmp(hostname, dns_queue[i].hostname))
125 return i;
126
127 return -1;
128}
129
133static void Dns_queue_remove(int index)
134{
135 int i;
136
137 _MSG("Dns_queue_remove: deleting client [%d] [queue_size=%d]\n",
138 index, dns_queue_size);
139
140 if (index < dns_queue_size) {
141 dFree(dns_queue[index].hostname);
142 --dns_queue_size; /* you'll find out why ;-) */
143 for (i = index; i < dns_queue_size; i++)
144 dns_queue[i] = dns_queue[i + 1];
145 }
146}
147
148/*
149 * Debug function
150 *
151void Dns_queue_print()
152{
153 int i;
154
155 MSG("Queue: [");
156 for (i = 0; i < dns_queue_size; i++)
157 MSG("%d:%s ", dns_queue[i].channel, dns_queue[i].hostname);
158 MSG("]\n");
159}
160 */
161
165static void Dns_cache_add(char *hostname, Dlist *addr_list)
166{
168 dns_cache[dns_cache_size].hostname = dStrdup(hostname);
169 dns_cache[dns_cache_size].addr_list = addr_list;
171 _MSG("Cache objects: %d\n", dns_cache_size);
172}
173
174
178void a_Dns_init(void)
179{
180 int res, i;
181
182#ifdef D_DNS_THREADED
183 MSG("dillo_dns_init: Here we go! (threaded)\n");
184#else
185 MSG("dillo_dns_init: Here we go! (not threaded)\n");
186#endif
187
188 dns_queue_size = 0;
190 dns_queue = dNew(GDnsQueue, dns_queue_size_max);
191
192 dns_cache_size = 0;
194 dns_cache = dNew(GDnsCache, dns_cache_size_max);
195
197
198 res = pipe(dns_notify_pipe);
199 assert(res == 0);
200 fcntl(dns_notify_pipe[0], F_SETFL, O_NONBLOCK);
202
203 /* Initialize servers data */
204 for (i = 0; i < num_servers; ++i) {
205 dns_server[i].channel = i;
206 dns_server[i].state = DNS_SERVER_IDLE;
207 dns_server[i].addr_list = NULL;
208 dns_server[i].hostname = NULL;
209 dns_server[i].status = 0;
210#ifdef D_DNS_THREADED
211 dns_server[i].th1 = (pthread_t) -1;
212#endif
213 }
214}
215
220static void Dns_note_hosts(Dlist *list, struct addrinfo *res0)
221{
222 struct addrinfo *res;
223 DilloHost *dh;
224
225 for (res = res0; res; res = res->ai_next) {
226
227 if (res->ai_family == AF_INET) {
228 struct sockaddr_in *in_addr;
229
230 if (res->ai_addrlen < sizeof(struct sockaddr_in)) {
231 continue;
232 }
233
234 dh = dNew0(DilloHost, 1);
235 dh->af = AF_INET;
236
237 in_addr = (struct sockaddr_in*) res->ai_addr;
238 dh->alen = sizeof (struct in_addr);
239 memcpy(&dh->data[0], &in_addr->sin_addr.s_addr, dh->alen);
240
241 dList_append(list, dh);
242#ifdef ENABLE_IPV6
243 } else if (res->ai_family == AF_INET6) {
244 struct sockaddr_in6 *in6_addr;
245
246 if (res->ai_addrlen < sizeof(struct sockaddr_in6)) {
247 continue;
248 }
249
250 dh = dNew0(DilloHost, 1);
251 dh->af = AF_INET6;
252
253 in6_addr = (struct sockaddr_in6*) res->ai_addr;
254 dh->alen = sizeof (struct in6_addr);
255 memcpy(&dh->data[0], &in6_addr->sin6_addr.s6_addr, dh->alen);
256
257 dList_append(list, dh);
258#endif
259 }
260 }
261}
262
266static void *Dns_server(void *data)
267{
268 int channel = VOIDP2INT(data);
269 struct addrinfo hints, *res0;
270 int error;
271 Dlist *hosts;
272 size_t length, i;
273 char addr_string[40];
274
275 memset(&hints, 0, sizeof(hints));
276#ifdef ENABLE_IPV6
277 hints.ai_family = AF_UNSPEC;
278#else
279 hints.ai_family = AF_INET;
280#endif
281 hints.ai_socktype = SOCK_STREAM;
282
283 hosts = dList_new(2);
284
285 _MSG("Dns_server: starting...\n ch: %d host: %s\n",
286 channel, dns_server[channel].hostname);
287
288 error = getaddrinfo(dns_server[channel].hostname, NULL, &hints, &res0);
289
290 if (error != 0) {
291 dns_server[channel].status = error;
292 MSG("DNS error: %s\n", gai_strerror(error));
293 } else {
294 Dns_note_hosts(hosts, res0);
295 dns_server[channel].status = 0;
296 freeaddrinfo(res0);
297 }
298
299 if (dList_length(hosts) > 0) {
300 dns_server[channel].status = 0;
301 } else {
302 dList_free(hosts);
303 hosts = NULL;
304 }
305
306 /* tell our findings */
307 MSG("Dns_server [%d]: %s is", channel,
308 dns_server[channel].hostname);
309 if ((length = dList_length(hosts))) {
310 for (i = 0; i < length; i++) {
312 addr_string, sizeof(addr_string));
313 MSG(" %s", addr_string);
314 }
315 MSG("\n");
316 } else {
317 MSG(" (nil)\n");
318 }
319 dns_server[channel].addr_list = hosts;
320 dns_server[channel].state = DNS_SERVER_RESOLVED;
321
322 write(dns_notify_pipe[1], ".", 1);
323
324 return NULL; /* (avoids a compiler warning) */
325}
326
327
331static void Dns_server_req(int channel, const char *hostname)
332{
333#ifdef D_DNS_THREADED
334 static pthread_attr_t thrATTR;
335 static int thrATTRInitialized = 0;
336#endif
337
338 dns_server[channel].state = DNS_SERVER_PROCESSING;
339
340 dFree(dns_server[channel].hostname);
341 dns_server[channel].hostname = dStrdup(hostname);
342
343#ifdef D_DNS_THREADED
344 /* set the thread attribute to the detached state */
345 if (!thrATTRInitialized) {
346 pthread_attr_init(&thrATTR);
347 pthread_attr_setdetachstate(&thrATTR, PTHREAD_CREATE_DETACHED);
348 thrATTRInitialized = 1;
349 }
350 /* Spawn thread */
351 pthread_create(&dns_server[channel].th1, &thrATTR, Dns_server,
352 INT2VOIDP(dns_server[channel].channel));
353#else
354 Dns_server(0);
355#endif
356}
357
362void a_Dns_resolve(const char *hostname, DnsCallback_t cb_func, void *cb_data)
363{
364 int i, channel;
365
366 if (!hostname)
367 return;
368
369 /* check for cache hit. */
370 for (i = 0; i < dns_cache_size; i++)
371 if (!dStrAsciiCasecmp(hostname, dns_cache[i].hostname))
372 break;
373
374 if (i < dns_cache_size) {
375 /* already resolved, call the Callback immediately. */
376 cb_func(0, dns_cache[i].addr_list, cb_data);
377
378 } else if ((i = Dns_queue_find(hostname)) != -1) {
379 /* hit in queue, but answer hasn't come back yet. */
380 Dns_queue_add(dns_queue[i].channel, hostname, cb_func, cb_data);
381
382 } else {
383 /* Never requested before -- we must resolve it! */
384
385 /* Find a channel we can send the request to */
386 for (channel = 0; channel < num_servers; channel++)
387 if (dns_server[channel].state == DNS_SERVER_IDLE)
388 break;
389 if (channel < num_servers) {
390 /* Found a free channel! */
391 Dns_queue_add(channel, hostname, cb_func, cb_data);
392 Dns_server_req(channel, hostname);
393 } else {
394 /* We'll have to wait for a thread to finish... */
395 Dns_queue_add(-2, hostname, cb_func, cb_data);
396 }
397 }
398}
399
403static void Dns_serve_channel(int channel)
404{
405 int i;
406 DnsServer *srv = &dns_server[channel];
407
408 for (i = 0; i < dns_queue_size; i++) {
409 if (dns_queue[i].channel == channel) {
410 dns_queue[i].cb_func(srv->status, srv->addr_list,
411 dns_queue[i].cb_data);
413 --i;
414 }
415 }
416}
417
421static void Dns_assign_channels(void)
422{
423 int ch, i, j;
424
425 for (ch = 0; ch < num_servers; ++ch) {
426 if (dns_server[ch].state == DNS_SERVER_IDLE) {
427 /* Find the next query in the queue (we're a FIFO) */
428 for (i = 0; i < dns_queue_size; i++)
429 if (dns_queue[i].channel == -2)
430 break;
431
432 if (i < dns_queue_size) {
433 /* assign this channel to every queued request
434 * with the same hostname*/
435 for (j = i; j < dns_queue_size; j++)
436 if (dns_queue[j].channel == -2 &&
437 !dStrAsciiCasecmp(dns_queue[j].hostname,
438 dns_queue[i].hostname)) {
439 dns_queue[j].channel = ch;
440 }
441 Dns_server_req(ch, dns_queue[i].hostname);
442 } else
443 return;
444 }
445 }
446}
447
452static void Dns_timeout_client(int fd, void *data)
453{
454 int i;
455 char buf[16];
456
457 while (read(dns_notify_pipe[0], buf, sizeof(buf)) > 0);
458
459 for (i = 0; i < num_servers; ++i) {
460 DnsServer *srv = &dns_server[i];
461
462 if (srv->state == DNS_SERVER_RESOLVED) {
463 if (srv->addr_list != NULL) {
464 /* DNS succeeded, let's cache it */
465 Dns_cache_add(srv->hostname, srv->addr_list);
466 }
468 srv->state = DNS_SERVER_IDLE;
469 }
470 }
472}
473
474
482{
483 int i, j;
484
485 for ( i = 0; i < dns_cache_size; ++i ){
486 dFree(dns_cache[i].hostname);
487 for ( j = 0; j < dList_length(dns_cache[i].addr_list); ++j)
488 dFree(dList_nth_data(dns_cache[i].addr_list, j));
489 dList_free(dns_cache[i].addr_list);
490 }
495}
496
503void a_Dns_dillohost_to_string(DilloHost *host, char *dst, size_t size)
504{
505 if (!inet_ntop(host->af, host->data, dst, size)) {
506 switch (errno) {
507 case EAFNOSUPPORT:
508 snprintf(dst, size, "Unknown address family");
509 break;
510 case ENOSPC:
511 snprintf(dst, size, "Buffer too small");
512 break;
513 }
514 }
515}
#define _MSG(...)
Definition bookmarks.c:45
#define MSG(...)
Definition bookmarks.c:46
void dFree(void *mem)
Definition dlib.c:68
int dStrAsciiCasecmp(const char *s1, const char *s2)
Definition dlib.c:203
char * dStrdup(const char *s)
Definition dlib.c:77
Dlist * dList_new(int size)
Create a new empty list.
Definition dlib.c:576
int dList_length(Dlist *lp)
For completing the ADT.
Definition dlib.c:641
void * dList_nth_data(Dlist *lp, int n0)
Return the nth data item, NULL when not found or 'n0' is out of range.
Definition dlib.c:690
int dClose(int fd)
Close a FD handling EINTR.
Definition dlib.c:979
void dList_append(Dlist *lp, void *data)
Append a data item to the list.
Definition dlib.c:625
void dList_free(Dlist *lp)
Free a list (not its elements)
Definition dlib.c:592
#define dNew0(type, count)
Definition dlib.h:63
#define VOIDP2INT(p)
Definition dlib.h:55
#define INT2VOIDP(i)
Definition dlib.h:56
#define dNew(type, count)
Definition dlib.h:61
static int dns_cache_size_max
Definition dns.c:95
static int dns_queue_size
Definition dns.c:97
void a_Dns_dillohost_to_string(DilloHost *host, char *dst, size_t size)
Writes a string representation of the given DilloHost into dst.
Definition dns.c:503
static DnsServer dns_server[D_DNS_MAX_SERVERS]
Definition dns.c:92
static void Dns_serve_channel(int channel)
Give answer to all queued callbacks on this channel.
Definition dns.c:403
static void * Dns_server(void *data)
Server function (runs on its own thread)
Definition dns.c:266
void a_Dns_init(void)
Initializer function.
Definition dns.c:178
static void Dns_queue_add(int channel, const char *hostname, DnsCallback_t cb_func, void *cb_data)
Definition dns.c:104
static int dns_queue_size_max
Definition dns.c:97
void a_Dns_freeall(void)
Dns memory-deallocation.
Definition dns.c:481
static void Dns_queue_remove(int index)
Given an index, remove an entry from the dns_queue.
Definition dns.c:133
static void Dns_note_hosts(Dlist *list, struct addrinfo *res0)
Allocate a host structure and add it to the list.
Definition dns.c:220
static void Dns_server_req(int channel, const char *hostname)
Request function (spawn a server and let it handle the request)
Definition dns.c:331
static GDnsCache * dns_cache
Definition dns.c:94
static void Dns_assign_channels(void)
Assign free channels to waiting clients (-2)
Definition dns.c:421
static int Dns_queue_find(const char *hostname)
Find hostname index in dns_queue (if found, returns queue index; -1 if not)
Definition dns.c:119
#define D_DNS_MAX_SERVERS
Definition dns.c:51
static int num_servers
Definition dns.c:93
static int dns_cache_size
Definition dns.c:95
static int dns_notify_pipe[2]
Definition dns.c:98
void a_Dns_resolve(const char *hostname, DnsCallback_t cb_func, void *cb_data)
Return the IP for the given hostname using a callback.
Definition dns.c:362
static void Dns_cache_add(char *hostname, Dlist *addr_list)
Add an IP/hostname pair to Dns-cache.
Definition dns.c:165
DnsServerState_t
Definition dns.c:54
@ DNS_SERVER_PROCESSING
Definition dns.c:56
@ DNS_SERVER_IDLE
Definition dns.c:55
@ DNS_SERVER_RESOLVED
Definition dns.c:57
static GDnsQueue * dns_queue
Definition dns.c:96
static void Dns_timeout_client(int fd, void *data)
This function is called on the main thread and reads the DNS results.
Definition dns.c:452
void(* DnsCallback_t)(int status, Dlist *addr_list, void *data)
Definition dns.h:12
#define a_List_add(list, num_items, alloc_step)
Definition cookies.c:68
static void error(char *msg)
Definition dpidc.c:39
void a_IOwatch_add_fd(int fd, int when, Fl_FD_Handler Callback, void *usr_data=0)
Hook a Callback for a certain activities in a FD.
Definition iowatch.cc:22
void a_IOwatch_remove_fd(int fd, int when)
Remove a Callback for a given FD (or just remove some events)
Definition iowatch.cc:32
#define DIO_READ
Definition iowatch.hh:7
Fast list methods.
char data[DILLO_ADDR_MAX]
Definition dns.h:27
int alen
Definition dns.h:26
int af
Definition dns.h:25
Definition dlib.h:144