📄 dns.c
字号:
/* Domain Name System Resolver Department *//* $Id: dns.c,v 1.53.2.4 2005/05/01 21:15:32 jonas Exp $ */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <stdlib.h>#include <string.h>#include <sys/types.h>#ifdef HAVE_NETDB_H#include <netdb.h> /* OS/2 needs this after sys/types.h */#endif#ifdef HAVE_SYS_SOCKET_H#include <sys/socket.h> /* OS/2 needs this after sys/types.h */#endif#ifdef HAVE_FCNTL_H#include <fcntl.h> /* OS/2 needs this after sys/types.h */#endif#ifdef HAVE_UNISTD_H#include <unistd.h>#endif/* Go and say 'thanks' to BSD. */#ifdef HAVE_NETINET_IN_H#include <netinet/in.h>#endif#ifdef HAVE_ARPA_INET_H#include <arpa/inet.h>#endif#include "elinks.h"#include "config/options.h"#include "lowlevel/dns.h"#include "lowlevel/select.h"#include "osdep/osdep.h"#include "util/error.h"#include "util/memory.h"#include "util/ttime.h"struct dnsentry { LIST_HEAD(struct dnsentry); struct sockaddr_storage *addr; /* pointer to array of addresses */ ttime get_time; int addrno; /* array len / sizeof(sockaddr_storage) */ unsigned char name[1]; /* Must be last */};struct dnsquery {#ifdef THREAD_SAFE_LOOKUP struct dnsquery *next_in_queue;#endif void (*fn)(void *, int); void *data; void (*xfn)(struct dnsquery *, int); struct dnsquery **s; /* addr and addrno lifespan exceeds life of this structure, the caller * holds memory being pointed upon be these functions. Thus, when * free()ing, *always* set pointer to NULL ! */ struct sockaddr_storage **addr; /* addr of pointer to array of addresses */ int *addrno; /* array len / sizeof(sockaddr_storage) */ int h; unsigned char name[1]; /* Must be last */};#ifdef THREAD_SAFE_LOOKUPstatic struct dnsquery *dns_queue = NULL;#endifINIT_LIST_HEAD(dns_cache);intdo_real_lookup(unsigned char *name, struct sockaddr_storage **addrs, int *addrno, int in_thread){#ifdef CONFIG_IPV6 struct addrinfo hint, *ai, *ai_cur;#else struct hostent *hostent;#endif int i; if (!name || !addrs || !addrno) return -1;#ifdef CONFIG_IPV6 /* I had a strong preference for the following, but the glibc is really * obsolete so I had to rather use much more complicated getaddrinfo(). * But we duplicate the code terribly here :|. */ /* hostent = getipnodebyname(name, AF_INET6, AI_ALL | AI_ADDRCONFIG, NULL); */ memset(&hint, 0, sizeof(hint)); hint.ai_family = AF_UNSPEC; hint.ai_socktype = SOCK_STREAM; if (getaddrinfo(name, NULL, &hint, &ai) != 0) return -1;#else /* Seems there are problems on Mac, so we first need to try * gethostbyaddr(). */#ifdef HAVE_GETHOSTBYADDR hostent = gethostbyaddr(name, strlen(name), AF_INET); if (!hostent)#endif { hostent = gethostbyname(name); if (!hostent) return -1; }#endif#ifdef CONFIG_IPV6 for (i = 0, ai_cur = ai; ai_cur; i++, ai_cur = ai_cur->ai_next);#else for (i = 0; hostent->h_addr_list[i] != NULL; i++);#endif /* We cannot use mem_*() in thread ("It will chew memory on OS/2 and * BeOS because there are no locks around the memory debugging code." * -- Mikulas). So we don't if in_thread != 0. */ *addrs = in_thread ? calloc(i, sizeof(**addrs)) : mem_calloc(i, sizeof(**addrs)); if (!*addrs) return -1; *addrno = i;#ifdef CONFIG_IPV6 for (i = 0, ai_cur = ai; ai_cur; i++, ai_cur = ai_cur->ai_next) { struct sockaddr_in6 *addr = (struct sockaddr_in6 *) &(*addrs)[i]; memcpy(addr, ai_cur->ai_addr, ai_cur->ai_addrlen); } freeaddrinfo(ai);#else for (i = 0; hostent->h_addr_list[i] != NULL; i++) { struct sockaddr_in *addr = (struct sockaddr_in *) &(*addrs)[i]; addr->sin_family = hostent->h_addrtype; memcpy(&addr->sin_addr.s_addr, hostent->h_addr_list[i], hostent->h_length); }#endif return 0;}#ifndef NO_ASYNC_LOOKUPstatic voidlookup_fn(void *data, int h){ unsigned char *name = (unsigned char *) data; struct sockaddr_storage *addrs; int addrno, i, done, todo; if (do_real_lookup(name, &addrs, &addrno, 1) < 0) return; /* We will do blocking I/O here, however it's only local communication * and it's supposed to be just a flash talk, so it shouldn't matter. * And it would be incredibly more complicated and messy (and mainly * useless) to do this in non-blocking way. */ if (set_blocking_fd(h) < 0) return; todo = sizeof(addrno); done = 0; do { int w = safe_write(h, &addrno + done, todo - done); if (w < 0) return; done += w; } while (done < todo); assert(done == todo); for (i = 0; i < addrno; i++) { struct sockaddr_storage *addr = &addrs[i]; todo = sizeof(*addr); done = 0; do { int w = safe_write(h, addr + done, todo - done); if (w < 0) return; done += w; } while (done < todo); assert(done == todo); } /* We're in thread, thus we must do plain free(). */ free(addrs);}#endif#ifndef NO_ASYNC_LOOKUPstatic voidend_real_lookup(void *data){ struct dnsquery *query = (struct dnsquery *) data; int res = -1; int i, done, todo; if (!query->addr || !query->addrno) goto done; *query->addr = NULL; /* XXX: is this correct ?? --Zas */ /* We will do blocking I/O here, however it's only local communication * and it's supposed to be just a flash talk, so it shouldn't matter. * And it would be incredibly more complicated and messy (and mainly * useless) to do this in non-blocking way. */ if (set_blocking_fd(query->h) < 0) goto done; todo = sizeof(*query->addrno); done = 0; do { int r = safe_read(query->h, query->addrno + done, todo - done); if (r <= 0) goto done; done += r; } while (done < todo); assert(done == todo); *query->addr = mem_calloc(*query->addrno, sizeof(**query->addr)); if (!*query->addr) goto done; for (i = 0; i < *query->addrno; i++) { struct sockaddr_storage *addr = &(*query->addr)[i]; todo = sizeof(*addr); done = 0; do { int r = safe_read(query->h, addr + done, todo - done); if (r <= 0) goto done; done += r; } while (done < todo); assert(done == todo); } res = 0;done: if (res < 0 && query->addr) mem_free_set(&*query->addr, NULL); clear_handlers(query->h); close(query->h); query->xfn(query, res);}#endifstatic voidfailed_real_lookup(void *data){ struct dnsquery *query = (struct dnsquery *) data; clear_handlers(query->h); close(query->h); query->xfn(query, -1);}static intdo_lookup(struct dnsquery *query, int force_async){ int res; /* DBG("starting lookup for %s", query->name); */#ifndef NO_ASYNC_LOOKUP if (force_async || get_opt_bool("connection.async_dns")) { query->h = start_thread(lookup_fn, query->name, strlen(query->name) + 1); if (query->h != -1) { /* async lookup */ set_handlers(query->h, end_real_lookup, NULL, failed_real_lookup, query); return 1; } }#endif /* sync lookup */ res = do_real_lookup(query->name, query->addr, query->addrno, 0); query->xfn(query, res); return 0;}static intdo_queued_lookup(struct dnsquery *query){#ifdef THREAD_SAFE_LOOKUP query->next_in_queue = NULL; if (!dns_queue) { dns_queue = query; /* DBG("direct lookup"); */ return do_lookup(query, 0); } else { /* DBG("queuing lookup for %s", q->name); */ assertm(!dns_queue->next_in_queue, "DNS queue corrupted"); dns_queue->next_in_queue = query; dns_queue = query; return -1; }#else return do_lookup(query, 0);#endif}static intfind_in_dns_cache(unsigned char *name, struct dnsentry **dnsentry){ struct dnsentry *e; foreach (e, dns_cache) if (!strcasecmp(e->name, name)) { del_from_list(e); add_to_list(dns_cache, e); *dnsentry = e; return 0; } return -1;}static voidend_dns_lookup(struct dnsquery *query, int res){ struct dnsentry *dnsentry; void (*fn)(void *, int); void *data; int namelen; /* DBG("end lookup %s (%d)", query->name, res); */#ifdef THREAD_SAFE_LOOKUP if (query->next_in_queue) { /* DBG("processing next in queue: %s", query->next_in_queue->name); */ do_lookup(query->next_in_queue, 1); } else { dns_queue = NULL; }#endif if (!query->fn || !query->addr) { if (query->addr) mem_free_set(&*query->addr, NULL); mem_free(query); return; } if (find_in_dns_cache(query->name, &dnsentry) >= 0) { assert(dnsentry); if (res < 0) { int size; /* query->addr(no) is pointer to something already allocated */ assert(dnsentry->addrno > 0); size = dnsentry->addrno * sizeof(**query->addr); *query->addr = mem_alloc(size); if (!*query->addr) goto done; memcpy(*query->addr, dnsentry->addr, size); *query->addrno = dnsentry->addrno; res = 0; goto done; } del_from_list(dnsentry); mem_free(dnsentry->addr); mem_free(dnsentry); } if (res < 0) goto done; namelen = strlen(query->name); dnsentry = mem_calloc(1, sizeof(*dnsentry) + namelen); if (dnsentry) { int size; memcpy(dnsentry->name, query->name, namelen); /* calloc() sets nul char for us. */ assert(*query->addrno > 0); size = *query->addrno * sizeof(*dnsentry->addr); dnsentry->addr = mem_alloc(size); if (!dnsentry->addr) goto done; memcpy(dnsentry->addr, *query->addr, size);; dnsentry->addrno = *query->addrno; dnsentry->get_time = get_time(); add_to_list(dns_cache, dnsentry); }done: fn = query->fn; data = query->data; if (query->s) *query->s = NULL; /* query->addr is freed later by dns_found() */ mem_free(query); fn(data, res);}intfind_host_no_cache(unsigned char *name, struct sockaddr_storage **addr, int *addrno, void **query_p, void (*fn)(void *, int), void *data){ struct dnsquery *query; int namelen = strlen(name); query = mem_calloc(1, sizeof(*query) + namelen); if (!query) { fn(data, -1); return 0; } query->fn = fn; query->data = data; query->s = (struct dnsquery **) query_p; query->addr = addr; query->addrno = addrno; memcpy(query->name, name, namelen); /* calloc() sets nul char for us. */ if (query_p) *((struct dnsquery **) query_p) = query; query->xfn = end_dns_lookup; return do_queued_lookup(query);}intfind_host(unsigned char *name, struct sockaddr_storage **addr, int *addrno, void **query_p, void (*fn)(void *, int), void *data){ struct dnsentry *dnsentry; if (query_p) *query_p = NULL; if (find_in_dns_cache(name, &dnsentry) >= 0) { assert(dnsentry && dnsentry->addrno > 0); if (dnsentry->get_time + DNS_TIMEOUT >= get_time()) { int size = sizeof(**addr) * dnsentry->addrno; *addr = mem_alloc(size); if (*addr) { memcpy(*addr, dnsentry->addr, size); *addrno = dnsentry->addrno; fn(data, 0); } return 0; } } return find_host_no_cache(name, addr, addrno, query_p, fn, data);}voidkill_dns_request(void **qp){ struct dnsquery *query = *qp; query->fn = NULL; failed_real_lookup(query); *qp = NULL;}static voiddel_dns_cache_entry(struct dnsentry **d){ struct dnsentry *e = *d; *d = (*d)->prev; del_from_list(e); mem_free_if(e->addr); mem_free(e);}voidshrink_dns_cache(int whole){ struct dnsentry *d; if (whole) { foreach (d, dns_cache) del_dns_cache_entry(&d); } else { ttime oldest = get_time() - DNS_TIMEOUT; foreach (d, dns_cache) if (d->get_time < oldest) del_dns_cache_entry(&d); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -