diff options
author | 2012-04-14 09:24:18 +0000 | |
---|---|---|
committer | 2012-04-14 09:24:18 +0000 | |
commit | b44da6278dfc5a9ac4bcab6662bc7def6c347a68 (patch) | |
tree | cbe5fcbe427706b86755f57914f80402178c4fa0 /lib/libc/asr/getnetnamadr_async.c | |
parent | Free tmp buffer in case the cluster allocation failed. Found by David Hill. (diff) | |
download | wireguard-openbsd-b44da6278dfc5a9ac4bcab6662bc7def6c347a68.tar.xz wireguard-openbsd-b44da6278dfc5a9ac4bcab6662bc7def6c347a68.zip |
Import asr, an experimental async resolver implementation.
The idea is to eventually replace the existing resolver with
something better. Time to start working on it in tree.
ok deraadt@
Diffstat (limited to 'lib/libc/asr/getnetnamadr_async.c')
-rw-r--r-- | lib/libc/asr/getnetnamadr_async.c | 418 |
1 files changed, 418 insertions, 0 deletions
diff --git a/lib/libc/asr/getnetnamadr_async.c b/lib/libc/asr/getnetnamadr_async.c new file mode 100644 index 00000000000..0bc8a36cfb0 --- /dev/null +++ b/lib/libc/asr/getnetnamadr_async.c @@ -0,0 +1,418 @@ +/* $OpenBSD: getnetnamadr_async.c,v 1.1 2012/04/14 09:24:18 eric Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <err.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "asr.h" +#include "asr_private.h" + +#define MAXALIASES 16 + +ssize_t addr_as_fqdn(const char *, int, char *, size_t); + +static int getnetnamadr_async_run(struct async *, struct async_res *); +static struct netent *netent_alloc(int); +static int netent_set_cname(struct netent *, const char *, int); +static int netent_add_alias(struct netent *, const char *, int); +static int netent_file_match(FILE *, int, const char *, char **, int); +static int netent_from_packet(struct netent *, int, char *, size_t); + +struct async * +getnetbyname_async(const char *name, struct asr *asr) +{ + struct asr_ctx *ac; + struct async *as; + + /* The current resolver segfaults. */ + if (name == NULL) { + errno = EINVAL; + return (NULL); + } + + ac = asr_use_resolver(asr); + if ((as = async_new(ac, ASR_GETNETBYNAME)) == NULL) + goto abort; /* errno set */ + as->as_run = getnetnamadr_async_run; + + as->as.netnamadr.family = AF_INET; + as->as.netnamadr.name = strdup(name); + if (as->as.netnamadr.name == NULL) + goto abort; /* errno set */ + + asr_ctx_unref(ac); + return (as); + + abort: + if (as) + async_free(as); + asr_ctx_unref(ac); + return (NULL); +} + +struct async * +getnetbyaddr_async(in_addr_t net, int family, struct asr *asr) +{ + struct asr_ctx *ac; + struct async *as; + + ac = asr_use_resolver(asr); + if ((as = async_new(ac, ASR_GETNETBYADDR)) == NULL) + goto abort; /* errno set */ + as->as_run = getnetnamadr_async_run; + + as->as.netnamadr.family = family; + as->as.netnamadr.addr = net; + + asr_ctx_unref(ac); + return (as); + + abort: + if (as) + async_free(as); + asr_ctx_unref(ac); + return (NULL); +} + +static int +getnetnamadr_async_run(struct async *as, struct async_res *ar) +{ + struct netent *e; + int i, n, r, type; + FILE *f; + char *toks[MAXTOKEN], dname[MAXDNAME], *name, *data; + in_addr_t in; + + next: + switch(as->as_state) { + + case ASR_STATE_INIT: + + if (as->as.netnamadr.family != AF_INET) { + ar->ar_h_errno = NETDB_INTERNAL; + ar->ar_errno = EAFNOSUPPORT; + async_set_state(as, ASR_STATE_HALT); + break; + } + + async_set_state(as, ASR_STATE_NEXT_DB); + break; + + case ASR_STATE_NEXT_DB: + + if (asr_iter_db(as) == -1) { + async_set_state(as, ASR_STATE_NOT_FOUND); + break; + } + + switch(AS_DB(as)) { + case ASR_DB_DNS: + + if (as->as_type == ASR_GETNETBYNAME) { + type = T_A; + /* + * I think we want to do the former, but our + * resolver is doing the following, so let's + * preserve bugward-compatibility there. + */ + type = T_PTR; + name = as->as.netnamadr.name; + as->as.netnamadr.subq = res_search_async_ctx( + name, C_IN, type, NULL, 0, as->as_ctx); + } else { + type = T_PTR; + name = dname; + + in = htonl(as->as.netnamadr.addr); + addr_as_fqdn((char*)&in, + as->as.netnamadr.family, + dname, sizeof(dname)); + as->as.netnamadr.subq = res_query_async_ctx( + name, C_IN, type, NULL, 0, as->as_ctx); + } + + if (as->as.netnamadr.subq == NULL) { + ar->ar_errno = errno; + ar->ar_h_errno = NETDB_INTERNAL; + async_set_state(as, ASR_STATE_HALT); + } + async_set_state(as, ASR_STATE_SUBQUERY); + break; + + case ASR_DB_FILE: + + if ((f = fopen("/etc/networks", "r")) == NULL) + break; + + if (as->as_type == ASR_GETNETBYNAME) + data = as->as.netnamadr.name; + else + data = (void*)&as->as.netnamadr.addr; + n = netent_file_match(f, as->as_type, data, toks, + MAXTOKEN); + if (n == -1) { + fclose(f); + break; + } + e = netent_alloc(as->as.netnamadr.family); + if (e == NULL) { + ar->ar_errno = errno; + ar->ar_h_errno = NETDB_INTERNAL; + async_set_state(as, ASR_STATE_HALT); + break; + } + netent_set_cname(e, toks[0], 0); + for (i = 2; i < n; i ++) + netent_add_alias(e, toks[i], 0); + e->n_net = inet_network(toks[1]); + fclose(f); + + ar->ar_h_errno = NETDB_SUCCESS; + ar->ar_netent = e; + async_set_state(as, ASR_STATE_HALT); + break; + } + break; + + case ASR_STATE_SUBQUERY: + + if ((r = async_run(as->as.netnamadr.subq, ar)) == ASYNC_COND) + return (ASYNC_COND); + as->as.netnamadr.subq = NULL; + + if (ar->ar_datalen == -1) { + async_set_state(as, ASR_STATE_NEXT_DB); + break; + } + + /* Got packet, but no answer */ + if (ar->ar_count == 0) { + free(ar->ar_data); + async_set_state(as, ASR_STATE_NEXT_DB); + break; + } + + if ((e = netent_alloc(as->as.netnamadr.family)) == NULL) { + ar->ar_errno = errno; + ar->ar_h_errno = NETDB_INTERNAL; + free(ar->ar_data); + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (as->as_type == ASR_GETNETBYADDR) + e->n_net = as->as.netnamadr.addr; + + netent_from_packet(e, as->as_type, ar->ar_data, ar->ar_datalen); + free(ar->ar_data); + + /* + * No address found in the dns packet. The blocking version + * reports this as an error. + */ + if (as->as_type == ASR_GETNETBYNAME && e->n_net == 0) { + /* XXX wrong */ + freenetent(e); + async_set_state(as, ASR_STATE_NEXT_DB); + } else { + ar->ar_h_errno = NETDB_SUCCESS; + ar->ar_netent = e; + async_set_state(as, ASR_STATE_HALT); + } + break; + + case ASR_STATE_NOT_FOUND: + + ar->ar_errno = 0; + ar->ar_h_errno = HOST_NOT_FOUND; + async_set_state(as, ASR_STATE_HALT); + break; + + case ASR_STATE_HALT: + + if (ar->ar_h_errno) + ar->ar_netent = NULL; + else + ar->ar_errno = 0; + return (ASYNC_DONE); + + default: + ar->ar_errno = EOPNOTSUPP; + ar->ar_h_errno = NETDB_INTERNAL; + ar->ar_gai_errno = EAI_SYSTEM; + async_set_state(as, ASR_STATE_HALT); + break; + } + goto next; +} + +static int +netent_file_match(FILE *f, int type, const char *data, char **tokens, + int ntokens) +{ + int n, i; + in_addr_t net; + + for(;;) { + n = asr_parse_namedb_line(f, tokens, MAXTOKEN); + if (n == -1) + return (-1); + + if (type == ASR_GETNETBYADDR) { + net = inet_network(tokens[1]); + if (memcmp(&net, data, sizeof net) == 0) + return (n); + } else { + for (i = 0; i < n; i++) { + if (i == 1) + continue; + if (strcasecmp(data, tokens[i])) + continue; + return (n); + } + } + } +} + +static int +netent_from_packet(struct netent *n, int action, char *pkt, size_t pktlen) +{ + struct packed p; + struct header hdr; + struct query q; + struct rr rr; + int r; + + packed_init(&p, pkt, pktlen); + unpack_header(&p, &hdr); + for(; hdr.qdcount; hdr.qdcount--) + unpack_query(&p, &q); + + for(; hdr.ancount; hdr.ancount--) { + unpack_rr(&p, &rr); + if (rr.rr_class != C_IN) + continue; + switch (rr.rr_type) { + case T_CNAME: + if (action == ASR_GETNETBYNAME) + r = netent_add_alias(n, rr.rr_dname, 1); + else + r = netent_set_cname(n, rr.rr_dname, 1); + break; + case T_PTR: + if (action != ASR_GETNETBYADDR) + continue; + r = netent_set_cname(n, rr.rr.ptr.ptrname, 1); + /* XXX See if we need to have MULTI_PTRS_ARE_ALIASES */ + break; + case T_A: + if (n->n_addrtype != AF_INET) + break; + r = netent_set_cname(n, rr.rr_dname, 1); + n->n_net = ntohl(rr.rr.in_a.addr.s_addr); + break; + } + } + + return (0); +} + +static struct netent * +netent_alloc(int family) +{ + struct netent *n; + + n = calloc(1, sizeof *n); + if (n == NULL) + return (NULL); + + n->n_aliases = calloc(MAXALIASES, sizeof *n->n_aliases); + if (n->n_aliases == NULL) { + freenetent(n); + return (NULL); + } + n->n_addrtype = family; + + return (n); +} + +static int +netent_set_cname(struct netent *n, const char *name, int isdname) +{ + char buf[MAXDNAME]; + + if (n->n_name) + return (0); + + if (isdname) { + asr_strdname(name, buf, sizeof buf); + buf[strlen(buf) - 1] = '\0'; + n->n_name = strdup(buf); + } else { + n->n_name = strdup(name); + } + if (n->n_name == NULL) + return (-1); + + return (0); +} + +static int +netent_add_alias(struct netent *n, const char *name, int isdname) +{ + char buf[MAXDNAME]; + size_t i; + + for (i = 0; i < MAXALIASES; i++) + if (n->n_aliases[i] == NULL) + break; + if (i == MAXALIASES) + return (0); + + if (isdname) { + asr_strdname(name, buf, sizeof buf); + buf[strlen(buf)-1] = '\0'; + n->n_aliases[i] = strdup(buf); + } else { + n->n_aliases[i] = strdup(name); + } + if (n->n_aliases[i] == NULL) + return (-1); + + return (0); +} + +void +freenetent(struct netent *n) +{ + char **c; + + free(n->n_name); + for (c = n->n_aliases; *c; c++) + free(*c); + free(n->n_aliases); + free(n); +} |