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