diff options
author | Anas Nashif <anas.nashif@intel.com> | 2012-12-07 02:53:31 -0800 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2012-12-07 02:53:31 -0800 |
commit | cbb6286cb92020dd7ae88798ed831ed76fd2130e (patch) | |
tree | 782a01c00d5e064aa67ea3f9241a8ef1de1060c6 /dns.c | |
download | links-upstream/2.6.tar.gz links-upstream/2.6.tar.bz2 links-upstream/2.6.zip |
Imported Upstream version 2.6upstream/2.6upstream
Diffstat (limited to 'dns.c')
-rw-r--r-- | dns.c | 355 |
1 files changed, 355 insertions, 0 deletions
@@ -0,0 +1,355 @@ +/* dns.c + * (c) 2002 Mikulas Patocka + * This file is a part of the Links program, released under GPL + */ + +#include "links.h" + +#if defined(HAVE_GETHOSTBYNAME_BUG) || !defined(HAVE_GETHOSTBYNAME) +#define EXTERNAL_LOOKUP +#endif + +struct dnsentry { + struct dnsentry *next; + struct dnsentry *prev; + ttime get_time; + ip__address addr; + unsigned char name[1]; +}; + +#ifndef THREAD_SAFE_LOOKUP +struct dnsquery *dns_queue = NULL; +#endif + +struct dnsquery { +#ifndef THREAD_SAFE_LOOKUP + struct dnsquery *next_in_queue; +#endif + void (*fn)(void *, int); + void *data; + void (*xfn)(struct dnsquery *, int); + int h; + struct dnsquery **s; + ip__address *addr; + unsigned char name[1]; +}; + +static struct list_head dns_cache = {&dns_cache, &dns_cache}; + +static int get_addr_byte(unsigned char **ptr, unsigned char *res, unsigned char stp) +{ + unsigned u = 0; + if (!(**ptr >= '0' && **ptr <= '9')) return -1; + while (**ptr >= '0' && **ptr <= '9') { + u = u * 10 + **ptr - '0'; + if (u >= 256) return -1; + (*ptr)++; + } + if (stp != 255 && **ptr != stp) return -1; + (*ptr)++; + *res = u; + return 0; +} + +int numeric_ip_address(unsigned char *name, ip__address *host) +{ + ip__address dummy; + if (!host) host = &dummy; + if (get_addr_byte(&name, ((unsigned char *)host + 0), '.')) return -1; + if (get_addr_byte(&name, ((unsigned char *)host + 1), '.')) return -1; + if (get_addr_byte(&name, ((unsigned char *)host + 2), '.')) return -1; + if (get_addr_byte(&name, ((unsigned char *)host + 3), 0)) return -1; + return 0; +} + +#ifdef EXTERNAL_LOOKUP + +static int do_external_lookup(unsigned char *name, ip__address *host) +{ + unsigned char buffer[1024]; + unsigned char sink[16]; + int rd; + int pi[2]; + pid_t f; + unsigned char *n; + int rs; + if (c_pipe(pi) == -1) + return -1; + EINTRLOOP(f, fork()); + if (f == -1) { + EINTRLOOP(rs, close(pi[0])); + EINTRLOOP(rs, close(pi[1])); + return -1; + } + if (!f) { + EINTRLOOP(rs, close(pi[0])); + EINTRLOOP(rs, dup2(pi[1], 1)); + if (rs == -1) _exit(1); + EINTRLOOP(rs, dup2(pi[1], 2)); + if (rs == -1) _exit(1); + EINTRLOOP(rs, close(pi[1])); + EINTRLOOP(rs, execlp("host", "host", name, NULL)); + EINTRLOOP(rs, execl("/usr/sbin/host", "host", name, NULL)); + _exit(1); + } + EINTRLOOP(rs, close(pi[1])); + rd = hard_read(pi[0], buffer, sizeof buffer - 1); + if (rd >= 0) buffer[rd] = 0; + if (rd > 0) { + while (hard_read(pi[0], sink, sizeof sink) > 0); + } + EINTRLOOP(rs, close(pi[0])); + /* Don't wait for the process, we already have sigchld handler that + * does cleanup. + * waitpid(f, NULL, 0); */ + if (rd < 0) return -1; + /*fprintf(stderr, "query: '%s', result: %s\n", name, buffer);*/ + while ((n = strstr(buffer, name))) { + memset(n, '-', strlen(name)); + } + for (n = buffer; n < buffer + rd; n++) { + if (*n >= '0' && *n <= '9') { + if (get_addr_byte(&n, ((unsigned char *)host + 0), '.')) goto skip_addr; + if (get_addr_byte(&n, ((unsigned char *)host + 1), '.')) goto skip_addr; + if (get_addr_byte(&n, ((unsigned char *)host + 2), '.')) goto skip_addr; + if (get_addr_byte(&n, ((unsigned char *)host + 3), 255)) goto skip_addr; + return 0; +skip_addr: + if (n >= buffer + rd) break; + } + } + return -1; +} + +#endif + +int do_real_lookup(unsigned char *name, ip__address *host) +{ + unsigned char *n; + struct hostent *hst; + if (!*name) return -1; + for (n = name; *n; n++) if (*n != '.' && (*n < '0' || *n > '9')) goto nogethostbyaddr; + if (!numeric_ip_address(name, host)) return 0; +#ifdef HAVE_GETHOSTBYADDR + if (!(hst = gethostbyaddr(name, strlen(name), AF_INET))) +#endif + { + nogethostbyaddr: +#ifdef HAVE_GETHOSTBYNAME + if (!(hst = gethostbyname(name))) +#endif + { +#ifdef EXTERNAL_LOOKUP + return do_external_lookup(name, host); +#endif + return -1; + } + } + memcpy(host, hst->h_addr_list[0], sizeof(ip__address)); + return 0; +} + +#ifndef NO_ASYNC_LOOKUP +static void lookup_fn(unsigned char *name, int h) +{ + ip__address host; + if (do_real_lookup(name, &host)) return; + hard_write(h, (unsigned char *)&host, sizeof(ip__address)); +} + +static void end_real_lookup(struct dnsquery *q) +{ + int r = 1; + int rs; + if (!q->addr || hard_read(q->h, (unsigned char *)q->addr, sizeof(ip__address)) != sizeof(ip__address)) goto end; + r = 0; + + end: + set_handlers(q->h, NULL, NULL, NULL, NULL); + EINTRLOOP(rs, close(q->h)); + q->xfn(q, r); +} + +static void failed_real_lookup(struct dnsquery *q) +{ + int rs; + set_handlers(q->h, NULL, NULL, NULL, NULL); + EINTRLOOP(rs, close(q->h)); + q->xfn(q, -1); +} +#endif + +static int do_lookup(struct dnsquery *q, int force_async) +{ + /*debug("starting lookup for %s", q->name);*/ +#ifndef NO_ASYNC_LOOKUP + if (!async_lookup && !force_async) { +#endif + int r; +#ifndef NO_ASYNC_LOOKUP + sync_lookup: +#endif + r = do_real_lookup(q->name, q->addr); + q->xfn(q, r); + return 0; +#ifndef NO_ASYNC_LOOKUP + } else { + if ((q->h = start_thread((void (*)(void *, int))lookup_fn, q->name, strlen(q->name) + 1)) == -1) goto sync_lookup; + set_handlers(q->h, (void (*)(void *))end_real_lookup, NULL, (void (*)(void *))failed_real_lookup, q); + return 1; + } +#endif +} + +static int do_queued_lookup(struct dnsquery *q) +{ +#ifndef THREAD_SAFE_LOOKUP + q->next_in_queue = NULL; + if (!dns_queue) { + dns_queue = q; + /*debug("direct lookup");*/ +#endif + return do_lookup(q, 0); +#ifndef THREAD_SAFE_LOOKUP + } else { + /*debug("queuing lookup for %s", q->name);*/ + if (dns_queue->next_in_queue) internal("DNS queue corrupted"); + dns_queue->next_in_queue = q; + dns_queue = q; + return 1; + } +#endif +} + +static int find_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 void end_dns_lookup(struct dnsquery *q, int a) +{ + struct dnsentry *dnsentry; + void (*fn)(void *, int); + void *data; + /*debug("end lookup %s", q->name);*/ +#ifndef THREAD_SAFE_LOOKUP + if (q->next_in_queue) { + /*debug("processing next in queue: %s", q->next_in_queue->name);*/ + do_lookup(q->next_in_queue, 1); + } else dns_queue = NULL; +#endif + if (!q->fn || !q->addr) { + free(q); + return; + } + if (!find_in_dns_cache(q->name, &dnsentry)) { + if (a) { + memcpy(q->addr, &dnsentry->addr, sizeof(ip__address)); + a = 0; + goto e; + } + del_from_list(dnsentry); + mem_free(dnsentry); + } + if (a) goto e; + dnsentry = mem_alloc(sizeof(struct dnsentry) + strlen(q->name) + 1); + strcpy(dnsentry->name, q->name); + memcpy(&dnsentry->addr, q->addr, sizeof(ip__address)); + dnsentry->get_time = get_time(); + add_to_list(dns_cache, dnsentry); + e: + if (q->s) *q->s = NULL; + fn = q->fn; + data = q->data; + free(q); + fn(data, a); +} + +int find_host_no_cache(unsigned char *name, ip__address *addr, void **qp, void (*fn)(void *, int), void *data) +{ + struct dnsquery *q; + retry: + if (!(q = (struct dnsquery *)malloc(sizeof(struct dnsquery) + strlen(name) + 1))) { + if (out_of_memory(NULL, 0)) + goto retry; + fn(data, -1); + return 0; + } + q->fn = fn; + q->data = data; + q->s = (struct dnsquery **)qp; + q->addr = addr; + strcpy(q->name, name); + if (qp) *(struct dnsquery **) qp = q; + q->xfn = end_dns_lookup; + return do_queued_lookup(q); +} + +int find_host(unsigned char *name, ip__address *addr, void **qp, void (*fn)(void *, int), void *data) +{ + struct dnsentry *dnsentry; + if (qp) *qp = NULL; + if (!find_in_dns_cache(name, &dnsentry)) { + if ((uttime)get_time() - (uttime)dnsentry->get_time > DNS_TIMEOUT) goto timeout; + memcpy(addr, &dnsentry->addr, sizeof(ip__address)); + fn(data, 0); + return 0; + } + timeout: + return find_host_no_cache(name, addr, qp, fn, data); +} + +void kill_dns_request(void **qp) +{ + struct dnsquery *q = *qp; + q->fn = NULL; + q->addr = NULL; + *qp = NULL; +} + +unsigned long dns_info(int type) +{ + switch (type) { + case CI_FILES: { + unsigned long n = 0; + struct dnsentry *e; + foreach(e, dns_cache) n++; + return n; + } + default: + internal("dns_info: bad request"); + } + return 0; +} + +static int shrink_dns_cache(int u) +{ + struct dnsentry *d, *e; + int f = 0; + if (u == SH_FREE_SOMETHING && !list_empty(dns_cache)) { + d = dns_cache.prev; + goto free_e; + } + foreach(d, dns_cache) if (u == SH_FREE_ALL || (uttime)get_time() - (uttime)d->get_time > DNS_TIMEOUT) { + free_e: + e = d; + d = d->prev; + del_from_list(e); + mem_free(e); + f = ST_SOMETHING_FREED; + } + return f | (list_empty(dns_cache) ? ST_CACHE_EMPTY : 0); +} + +void init_dns(void) +{ + register_cache_upcall(shrink_dns_cache, "dns"); +} |