1 /* Domain Name System Resolver Department */
11 #include <netdb.h> /* OS/2 needs this after sys/types.h */
13 #ifdef HAVE_SYS_SOCKET_H
14 #include <sys/socket.h> /* OS/2 needs this after sys/types.h */
17 #include <fcntl.h> /* OS/2 needs this after sys/types.h */
23 /* Go and say 'thanks' to BSD. */
24 #ifdef HAVE_NETINET_IN_H
25 #include <netinet/in.h>
27 #ifdef HAVE_ARPA_INET_H
28 #include <arpa/inet.h>
33 #include "config/options.h"
34 #include "main/select.h"
35 #include "network/dns.h"
36 #include "osdep/osdep.h"
37 #include "protocol/uri.h"
38 #include "util/error.h"
39 #include "util/memory.h"
40 #include "util/time.h"
44 LIST_HEAD(struct dnsentry
);
46 struct sockaddr_storage
*addr
; /* Pointer to array of addresses. */
47 int addrno
; /* Adress array length. */
48 timeval_T creation_time
; /* Creation time; let us do timeouts. */
49 unsigned char name
[1]; /* Associated host; XXX: Must be last. */
53 #ifdef THREAD_SAFE_LOOKUP
54 struct dnsquery
*next_in_queue
; /* Got queued? */
56 dns_callback_T done
; /* Used for reporting back DNS result. */
57 void *data
; /* Private callback data. */
59 /* The @done callback is called with these members. Thus, when
60 * free()ing, *always* set pointer to NULL ! */
61 struct sockaddr_storage
*addr
; /* Reference to array of addresses. */
62 int addrno
; /* Reference to array len. */
64 /* As with the two members above, when stopping a DNS query *always* set
65 * this pointer to NULL. */
66 struct dnsquery
**queryref
; /* Reference to callers DNS member. */
68 #ifndef NO_ASYNC_LOOKUP
69 int h
; /* One end of the async thread pipe. */
71 unsigned char name
[1]; /* Associated host; XXX: Must be last. */
75 #ifdef THREAD_SAFE_LOOKUP
76 static struct dnsquery
*dns_queue
= NULL
;
79 static INIT_LIST_OF(struct dnsentry
, dns_cache
);
81 static void done_dns_lookup(struct dnsquery
*query
, enum dns_result res
);
84 /* DNS cache management: */
86 static struct dnsentry
*
87 find_in_dns_cache(unsigned char *name
)
89 struct dnsentry
*dnsentry
;
91 foreach (dnsentry
, dns_cache
)
92 if (!strcasecmp(dnsentry
->name
, name
)) {
93 move_to_top_of_list(dns_cache
, dnsentry
);
101 add_to_dns_cache(unsigned char *name
, struct sockaddr_storage
*addr
, int addrno
)
103 int namelen
= strlen(name
);
104 struct dnsentry
*dnsentry
;
109 dnsentry
= mem_calloc(1, sizeof(*dnsentry
) + namelen
);
110 if (!dnsentry
) return;
112 size
= addrno
* sizeof(*dnsentry
->addr
);
113 dnsentry
->addr
= mem_alloc(size
);
114 if (!dnsentry
->addr
) {
119 /* calloc() sets NUL char for us. */
120 memcpy(dnsentry
->name
, name
, namelen
);
121 memcpy(dnsentry
->addr
, addr
, size
);;
123 dnsentry
->addrno
= addrno
;
125 timeval_now(&dnsentry
->creation_time
);
126 add_to_list(dns_cache
, dnsentry
);
130 del_dns_cache_entry(struct dnsentry
*dnsentry
)
132 del_from_list(dnsentry
);
133 mem_free_if(dnsentry
->addr
);
138 /* Synchronous DNS lookup management: */
141 do_real_lookup(unsigned char *name
, struct sockaddr_storage
**addrs
, int *addrno
,
145 struct addrinfo hint
, *ai
, *ai_cur
;
147 struct hostent
*hostent
= NULL
;
151 if (!name
|| !addrs
|| !addrno
)
155 /* I had a strong preference for the following, but the glibc is really
156 * obsolete so I had to rather use much more complicated getaddrinfo().
157 * But we duplicate the code terribly here :|. */
158 /* hostent = getipnodebyname(name, AF_INET6, AI_ALL | AI_ADDRCONFIG, NULL); */
159 memset(&hint
, 0, sizeof(hint
));
160 hint
.ai_family
= AF_UNSPEC
;
161 hint
.ai_socktype
= SOCK_STREAM
;
162 if (getaddrinfo(name
, NULL
, &hint
, &ai
) != 0) return DNS_ERROR
;
165 /* Seems there are problems on Mac, so we first need to try
166 * gethostbyaddr(), but there are problems with gethostbyaddr on Cygwin,
167 * so we do not use gethostbyaddr there. */
168 #if defined(HAVE_GETHOSTBYADDR) && !defined(HAVE_SYS_CYGWIN_H)
172 if (is_ip_address(name
, strlen(name
)) && inet_aton(name
, &inp
))
173 hostent
= gethostbyaddr(&inp
, sizeof(inp
), AF_INET
);
178 hostent
= gethostbyname(name
);
179 if (!hostent
) return DNS_ERROR
;
184 for (i
= 0, ai_cur
= ai
; ai_cur
; i
++, ai_cur
= ai_cur
->ai_next
);
186 for (i
= 0; hostent
->h_addr_list
[i
] != NULL
; i
++);
189 /* We cannot use mem_*() in thread ("It will chew memory on OS/2 and
190 * BeOS because there are no locks around the memory debugging code."
191 * -- Mikulas). So we don't if in_thread != 0. */
192 *addrs
= in_thread
? calloc(i
, sizeof(**addrs
))
193 : mem_calloc(i
, sizeof(**addrs
));
194 if (!*addrs
) return DNS_ERROR
;
198 for (i
= 0, ai_cur
= ai
; ai_cur
; i
++, ai_cur
= ai_cur
->ai_next
) {
199 /* Don't use struct sockaddr_in6 here: because we
200 * called getaddrinfo with AF_UNSPEC, the address
201 * might not be for IP at all. */
202 struct sockaddr_storage
*addr
= &(*addrs
)[i
];
204 /* RFC 3493 says struct sockaddr_storage is supposed
205 * to be "Large enough to accommodate all supported
206 * protocol-specific address structures." So if
207 * getaddrinfo supports an address that does not fit
208 * in struct sockaddr_storage, then it is a bug in the
209 * library. In this case, fail the whole lookup, to
210 * make the bug more likely to be noticed. */
211 assert(ai_cur
->ai_addrlen
<= sizeof(*addr
));
223 memcpy(addr
, ai_cur
->ai_addr
, ai_cur
->ai_addrlen
);
229 for (i
= 0; hostent
->h_addr_list
[i
] != NULL
; i
++) {
230 struct sockaddr_in
*addr
= (struct sockaddr_in
*) &(*addrs
)[i
];
232 addr
->sin_family
= hostent
->h_addrtype
;
233 memcpy(&addr
->sin_addr
.s_addr
, hostent
->h_addr_list
[i
], hostent
->h_length
);
241 /* Asynchronous DNS lookup management: */
243 #ifndef NO_ASYNC_LOOKUP
244 static enum dns_result
245 write_dns_data(int h
, void *data
, size_t datalen
)
250 int w
= safe_write(h
, data
+ done
, datalen
- done
);
252 if (w
< 0) return DNS_ERROR
;
254 } while (done
< datalen
);
256 assert(done
== datalen
);
262 async_dns_writer(void *data
, int h
)
264 unsigned char *name
= (unsigned char *) data
;
265 struct sockaddr_storage
*addrs
;
268 if (do_real_lookup(name
, &addrs
, &addrno
, 1) == DNS_ERROR
)
271 /* We will do blocking I/O here, however it's only local communication
272 * and it's supposed to be just a flash talk, so it shouldn't matter.
273 * And it would be incredibly more complicated and messy (and mainly
274 * useless) to do this in non-blocking way. */
275 if (set_blocking_fd(h
) < 0) return;
277 if (write_dns_data(h
, &addrno
, sizeof(addrno
)) == DNS_ERROR
)
280 for (i
= 0; i
< addrno
; i
++) {
281 struct sockaddr_storage
*addr
= &addrs
[i
];
283 if (write_dns_data(h
, addr
, sizeof(*addr
)) == DNS_ERROR
)
287 /* We're in thread, thus we must do plain free(). */
291 static enum dns_result
292 read_dns_data(int h
, void *data
, size_t datalen
)
297 ssize_t r
= safe_read(h
, data
+ done
, datalen
- done
);
299 if (r
<= 0) return DNS_ERROR
;
301 } while (done
< datalen
);
303 assert(done
== datalen
);
309 async_dns_reader(struct dnsquery
*query
)
311 enum dns_result result
= DNS_ERROR
;
314 /* We will do blocking I/O here, however it's only local communication
315 * and it's supposed to be just a flash talk, so it shouldn't matter.
316 * And it would be incredibly more complicated and messy (and mainly
317 * useless) to do this in non-blocking way. */
318 if (set_blocking_fd(query
->h
) < 0) goto done
;
320 if (read_dns_data(query
->h
, &query
->addrno
, sizeof(query
->addrno
)) == DNS_ERROR
)
323 query
->addr
= mem_calloc(query
->addrno
, sizeof(*query
->addr
));
324 if (!query
->addr
) goto done
;
326 for (i
= 0; i
< query
->addrno
; i
++) {
327 struct sockaddr_storage
*addr
= &query
->addr
[i
];
329 if (read_dns_data(query
->h
, addr
, sizeof(*addr
)) == DNS_ERROR
)
333 result
= DNS_SUCCESS
;
336 if (result
== DNS_ERROR
)
337 mem_free_set(&query
->addr
, NULL
);
339 done_dns_lookup(query
, result
);
343 async_dns_error(struct dnsquery
*query
)
345 done_dns_lookup(query
, DNS_ERROR
);
349 init_async_dns_lookup(struct dnsquery
*dnsquery
, int force_async
)
351 if (!force_async
&& !get_opt_bool("connection.async_dns")) {
356 dnsquery
->h
= start_thread(async_dns_writer
, dnsquery
->name
,
357 strlen(dnsquery
->name
) + 1);
358 if (dnsquery
->h
== -1)
361 set_handlers(dnsquery
->h
, (select_handler_T
) async_dns_reader
, NULL
,
362 (select_handler_T
) async_dns_error
, dnsquery
);
368 done_async_dns_lookup(struct dnsquery
*dnsquery
)
370 if (dnsquery
->h
== -1) return;
372 clear_handlers(dnsquery
->h
);
377 #define init_async_dns_lookup(dnsquery, force) (0)
378 #define done_async_dns_lookup(dnsquery) /* Nada. */
379 #endif /* NO_ASYNC_LOOKUP */
382 static enum dns_result
383 do_lookup(struct dnsquery
*query
, int force_async
)
385 enum dns_result result
;
387 /* DBG("starting lookup for %s", query->name); */
390 if (init_async_dns_lookup(query
, force_async
))
394 result
= do_real_lookup(query
->name
, &query
->addr
, &query
->addrno
, 0);
395 done_dns_lookup(query
, result
);
400 static enum dns_result
401 do_queued_lookup(struct dnsquery
*query
)
403 #ifdef THREAD_SAFE_LOOKUP
404 query
->next_in_queue
= NULL
;
407 /* DBG("queuing lookup for %s", q->name); */
408 assertm(!dns_queue
->next_in_queue
, "DNS queue corrupted");
409 dns_queue
->next_in_queue
= query
;
416 /* DBG("direct lookup"); */
417 return do_lookup(query
, 0);
422 done_dns_lookup(struct dnsquery
*query
, enum dns_result result
)
424 struct dnsentry
*dnsentry
;
426 /* DBG("end lookup %s (%d)", query->name, res); */
428 /* do_lookup() might start a new async thread */
429 done_async_dns_lookup(query
);
431 #ifdef THREAD_SAFE_LOOKUP
432 if (query
->next_in_queue
) {
433 /* DBG("processing next in queue: %s", query->next_in_queue->name); */
434 do_lookup(query
->next_in_queue
, 1);
440 /* Make sure the query is unregister _before_ calling any callbacks. */
441 *query
->queryref
= NULL
;
443 /* If the callback was cleared skip to the freeing part. */
447 dnsentry
= find_in_dns_cache(query
->name
);
449 /* If the query failed, use the existing DNS cache entry even if
451 if (result
== DNS_ERROR
) {
452 query
->done(query
->data
, dnsentry
->addr
, dnsentry
->addrno
);
456 del_dns_cache_entry(dnsentry
);
459 if (result
== DNS_SUCCESS
)
460 add_to_dns_cache(query
->name
, query
->addr
, query
->addrno
);
462 query
->done(query
->data
, query
->addr
, query
->addrno
);
465 mem_free_set(&query
->addr
, NULL
);
469 static enum dns_result
470 init_dns_lookup(unsigned char *name
, void **queryref
,
471 dns_callback_T done
, void *data
)
473 struct dnsquery
*query
;
474 int namelen
= strlen(name
);
476 query
= mem_calloc(1, sizeof(*query
) + namelen
);
485 /* calloc() sets NUL char for us. */
486 memcpy(query
->name
, name
, namelen
);
488 query
->queryref
= (struct dnsquery
**) queryref
;
489 *(query
->queryref
) = query
;
491 return do_queued_lookup(query
);
496 find_host(unsigned char *name
, void **queryref
,
497 dns_callback_T done
, void *data
, int no_cache
)
499 struct dnsentry
*dnsentry
;
505 return init_dns_lookup(name
, queryref
, done
, data
);
507 /* Check if the DNS name is in the cache. If the cache entry is too old
508 * do a new lookup. However, old cache entries will be used as a
509 * fallback if the new lookup fails. */
510 dnsentry
= find_in_dns_cache(name
);
512 timeval_T age
, now
, max_age
;
514 assert(dnsentry
&& dnsentry
->addrno
> 0);
516 timeval_from_seconds(&max_age
, DNS_CACHE_TIMEOUT
);
518 timeval_sub(&age
, &dnsentry
->creation_time
, &now
);
520 if (timeval_cmp(&age
, &max_age
) <= 0) {
521 done(data
, dnsentry
->addr
, dnsentry
->addrno
);
526 return init_dns_lookup(name
, queryref
, done
, data
);
530 kill_dns_request(void **queryref
)
532 struct dnsquery
*query
= *queryref
;
537 done_dns_lookup(query
, DNS_ERROR
);
541 shrink_dns_cache(int whole
)
543 struct dnsentry
*dnsentry
, *next
;
546 foreachsafe (dnsentry
, next
, dns_cache
)
547 del_dns_cache_entry(dnsentry
);
550 timeval_T now
, max_age
;
552 timeval_from_seconds(&max_age
, DNS_CACHE_TIMEOUT
);
555 foreachsafe (dnsentry
, next
, dns_cache
) {
558 timeval_sub(&age
, &dnsentry
->creation_time
, &now
);
560 if (timeval_cmp(&age
, &max_age
) > 0)
561 del_dns_cache_entry(dnsentry
);