From: Andreas Schneider Date: Wed, 3 Dec 2014 14:44:58 +0000 (+0100) Subject: lib: Add resolv_wrapper version 1.0.0. X-Git-Tag: ph-ms-prot-tests-ok~16701 X-Git-Url: http://git.samba.org/samba.git/?a=commitdiff_plain;h=8bf949f343f8da7431d130395e3ac29e4be39889;p=slow%2Fsamba.git lib: Add resolv_wrapper version 1.0.0. Signed-off-by: Andreas Schneider Reviewed-by: Andrew Bartlett --- diff --git a/lib/resolv_wrapper/resolv_wrapper.c b/lib/resolv_wrapper/resolv_wrapper.c new file mode 100644 index 00000000000..35f426dec81 --- /dev/null +++ b/lib/resolv_wrapper/resolv_wrapper.c @@ -0,0 +1,1377 @@ +/* + * Copyright (c) 2014 Andreas Schneider + * Copyright (c) 2014 Jakub Hrozek + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* GCC has printf type attribute check. */ +#ifdef HAVE_ATTRIBUTE_PRINTF_FORMAT +#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b))) +#else +#define PRINTF_ATTRIBUTE(a,b) +#endif /* HAVE_ATTRIBUTE_PRINTF_FORMAT */ + +#ifdef HAVE_DESTRUCTOR_ATTRIBUTE +#define DESTRUCTOR_ATTRIBUTE __attribute__ ((destructor)) +#else +#define DESTRUCTOR_ATTRIBUTE +#endif /* HAVE_DESTRUCTOR_ATTRIBUTE */ + +#ifndef RWRAP_DEFAULT_FAKE_TTL +#define RWRAP_DEFAULT_FAKE_TTL 600 +#endif /* RWRAP_DEFAULT_FAKE_TTL */ + +enum rwrap_dbglvl_e { + RWRAP_LOG_ERROR = 0, + RWRAP_LOG_WARN, + RWRAP_LOG_DEBUG, + RWRAP_LOG_TRACE +}; + +#ifdef NDEBUG +# define RWRAP_LOG(...) +#else /* NDEBUG */ + +static void rwrap_log(enum rwrap_dbglvl_e dbglvl, const char *func, const char *format, ...) PRINTF_ATTRIBUTE(3, 4); +# define RWRAP_LOG(dbglvl, ...) rwrap_log((dbglvl), __func__, __VA_ARGS__) + +static void rwrap_log(enum rwrap_dbglvl_e dbglvl, + const char *func, + const char *format, ...) +{ + char buffer[1024]; + va_list va; + const char *d; + unsigned int lvl = 0; + int pid = getpid(); + + d = getenv("RESOLV_WRAPPER_DEBUGLEVEL"); + if (d != NULL) { + lvl = atoi(d); + } + + va_start(va, format); + vsnprintf(buffer, sizeof(buffer), format, va); + va_end(va); + + if (lvl >= dbglvl) { + switch (dbglvl) { + case RWRAP_LOG_ERROR: + fprintf(stderr, + "RWRAP_ERROR(%d) - %s: %s\n", + pid, func, buffer); + break; + case RWRAP_LOG_WARN: + fprintf(stderr, + "RWRAP_WARN(%d) - %s: %s\n", + pid, func, buffer); + break; + case RWRAP_LOG_DEBUG: + fprintf(stderr, + "RWRAP_DEBUG(%d) - %s: %s\n", + pid, func, buffer); + break; + case RWRAP_LOG_TRACE: + fprintf(stderr, + "RWRAP_TRACE(%d) - %s: %s\n", + pid, func, buffer); + break; + } + } +} +#endif /* NDEBUG RWRAP_LOG */ + +#ifndef SAFE_FREE +#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); (x)=NULL;} } while(0) +#endif + +#define NEXT_KEY(buf, key) do { \ + (key) = (buf) ? strpbrk((buf), " \t") : NULL; \ + if ((key) != NULL) { \ + (key)[0] = '\0'; \ + (key)++; \ + } \ + while ((key) != NULL \ + && (isblank((int)(key)[0]))) { \ + (key)++; \ + } \ +} while(0); + + +/* Prepares a fake header with a single response. Advances header_blob */ +static ssize_t rwrap_fake_header(uint8_t **header_blob, size_t remaining, + size_t rdata_size) +{ + uint8_t *hb; + HEADER *h; + int answers; + + /* If rdata_size is zero, the answer is empty */ + answers = rdata_size > 0 ? 1 : 0; + + if (remaining < NS_HFIXEDSZ) { + RWRAP_LOG(RWRAP_LOG_ERROR, "Buffer too small!\n"); + return -1; + } + + hb = *header_blob; + memset(hb, 0, NS_HFIXEDSZ); + + h = (HEADER *) hb; + h->id = res_randomid(); /* random query ID */ + h->qr = 1; /* response flag */ + h->rd = 1; /* recursion desired */ + h->ra = 1; /* resursion available */ + + h->qdcount = htons(1); /* no. of questions */ + h->ancount = htons(answers); /* no. of answers */ + + hb += NS_HFIXEDSZ; /* move past the header */ + *header_blob = hb; + + return NS_HFIXEDSZ; +} + +static ssize_t rwrap_fake_question(const char *question, + uint16_t type, + uint8_t **question_ptr, + size_t remaining) +{ + uint8_t *qb = *question_ptr; + int n; + + n = ns_name_compress(question, qb, remaining, NULL, NULL); + if (n < 0) { + RWRAP_LOG(RWRAP_LOG_ERROR, + "Failed to compress [%s]\n", question); + return -1; + } + + qb += n; + remaining -= n; + + if (remaining < 2 * sizeof(uint16_t)) { + RWRAP_LOG(RWRAP_LOG_ERROR, "Buffer too small!\n"); + return -1; + } + + NS_PUT16(type, qb); + NS_PUT16(ns_c_in, qb); + + *question_ptr = qb; + return n + 2 * sizeof(uint16_t); +} + +static ssize_t rwrap_fake_rdata_common(uint16_t type, + size_t rdata_size, + const char *key, + size_t remaining, + uint8_t **rdata_ptr) +{ + uint8_t *rd = *rdata_ptr; + ssize_t written = 0; + + written = ns_name_compress(key, rd, remaining, NULL, NULL); + if (written < 0) { + RWRAP_LOG(RWRAP_LOG_ERROR, + "Failed to compress [%s]\n", key); + return -1; + } + rd += written; + remaining -= written; + + if (remaining < 3 * sizeof(uint16_t) + sizeof(uint32_t)) { + RWRAP_LOG(RWRAP_LOG_ERROR, "Buffer too small\n"); + return -1; + } + + NS_PUT16(type, rd); + NS_PUT16(ns_c_in, rd); + NS_PUT32(RWRAP_DEFAULT_FAKE_TTL, rd); + NS_PUT16(rdata_size, rd); + + if (remaining < rdata_size) { + RWRAP_LOG(RWRAP_LOG_ERROR, "Buffer too small\n"); + return -1; + } + + *rdata_ptr = rd; + return written + 3 * sizeof(uint16_t) + sizeof(uint32_t); +} + +static ssize_t rwrap_fake_common(uint16_t type, + const char *question, + size_t rdata_size, + uint8_t **answer_ptr, + size_t anslen) +{ + uint8_t *a = *answer_ptr; + ssize_t written; + size_t remaining; + + remaining = anslen; + + written = rwrap_fake_header(&a, remaining, rdata_size); + if (written < 0) { + return -1; + } + remaining -= written; + + written = rwrap_fake_question(question, type, &a, remaining); + if (written < 0) { + return -1; + } + remaining -= written; + + /* rdata_size = 0 denotes an empty answer */ + if (rdata_size > 0) { + written = rwrap_fake_rdata_common(type, rdata_size, question, + remaining, &a); + if (written < 0) { + return -1; + } + } + + *answer_ptr = a; + return written; +} + +static int rwrap_fake_a(const char *key, + const char *value, + uint8_t *answer_ptr, + size_t anslen) +{ + uint8_t *a = answer_ptr; + struct in_addr a_rec; + int rc; + int ok; + + if (value == NULL) { + RWRAP_LOG(RWRAP_LOG_ERROR, "Malformed record, no value!\n"); + return -1; + } + + rc = rwrap_fake_common(ns_t_a, key, sizeof(a_rec), &a, anslen); + if (rc < 0) { + return -1; + } + + ok = inet_pton(AF_INET, value, &a_rec); + if (!ok) { + RWRAP_LOG(RWRAP_LOG_ERROR, + "Failed to convert [%s] to binary\n", value); + return -1; + } + memcpy(a, &a_rec, sizeof(struct in_addr)); + + return 0; +} + +static int rwrap_fake_aaaa(const char *key, + const char *value, + uint8_t *answer, + size_t anslen) +{ + uint8_t *a = answer; + struct in6_addr aaaa_rec; + int rc; + int ok; + + if (value == NULL) { + RWRAP_LOG(RWRAP_LOG_ERROR, "Malformed record, no value!\n"); + return -1; + } + + rc = rwrap_fake_common(ns_t_aaaa, key, sizeof(aaaa_rec), &a, anslen); + if (rc < 0) { + return -1; + } + + ok = inet_pton(AF_INET6, value, &aaaa_rec); + if (!ok) { + RWRAP_LOG(RWRAP_LOG_ERROR, + "Failed to convert [%s] to binary\n", value); + return -1; + } + memcpy(a, &aaaa_rec, sizeof(struct in6_addr)); + + return 0; +} + +/* + * Priority and weight can be omitted from the hosts file, but need to be part + * of the output + */ +#define DFL_SRV_PRIO 1 +#define DFL_SRV_WEIGHT 100 + +static int rwrap_fake_srv(const char *key, + const char *value, + uint8_t *answer, + size_t anslen) +{ + uint8_t *a = answer; + int rv; + size_t rdata_size; + char *str_prio; + char *str_weight; + char *str_port; + const char *hostname; + unsigned char hostname_compressed[MAXDNAME]; + ssize_t compressed_len; + + /* + * Parse the value into priority, weight, port and hostname + * and check the validity. + */ + hostname = value; + NEXT_KEY(hostname, str_port); + NEXT_KEY(str_port, str_prio); + NEXT_KEY(str_prio, str_weight); + if (str_port == NULL || hostname == NULL) { + RWRAP_LOG(RWRAP_LOG_ERROR, + "Malformed SRV entry [%s]\n", value); + return -1; + } + rdata_size = 3 * sizeof(uint16_t); + + /* Prepare the data to write */ + compressed_len = ns_name_compress(hostname, + hostname_compressed, MAXDNAME, + NULL, NULL); + if (compressed_len < 0) { + return -1; + } + rdata_size += compressed_len; + + rv = rwrap_fake_common(ns_t_srv, key, rdata_size, &a, anslen); + if (rv < 0) { + return -1; + } + + if (str_prio) { + NS_PUT16(atoi(str_prio), a); + } else { + NS_PUT16(DFL_SRV_PRIO, a); + } + if (str_weight) { + NS_PUT16(atoi(str_weight), a); + } else { + NS_PUT16(DFL_SRV_WEIGHT, a); + } + NS_PUT16(atoi(str_port), a); + memcpy(a, hostname_compressed, compressed_len); + + return 0; +} + +static int rwrap_fake_soa(const char *key, + const char *value, + uint8_t *answer, + size_t anslen) +{ + uint8_t *a = answer; + int rv; + const char *nameserver; + char *mailbox; + char *str_serial; + char *str_refresh; + char *str_retry; + char *str_expire; + char *str_minimum; + size_t rdata_size; + unsigned char nameser_compressed[MAXDNAME]; + ssize_t compressed_ns_len; + unsigned char mailbox_compressed[MAXDNAME]; + ssize_t compressed_mb_len; + + /* + * parse the value into nameserver, mailbox, serial, refresh, + * retry, expire, minimum and check the validity + */ + nameserver = value; + NEXT_KEY(nameserver, mailbox); + NEXT_KEY(mailbox, str_serial); + NEXT_KEY(str_serial, str_refresh); + NEXT_KEY(str_refresh, str_retry); + NEXT_KEY(str_retry, str_expire); + NEXT_KEY(str_expire, str_minimum); + if (nameserver == NULL || mailbox == NULL || str_serial == NULL || + str_refresh == NULL || str_retry == NULL || str_expire == NULL || + str_minimum == NULL) + { + RWRAP_LOG(RWRAP_LOG_ERROR, + "Malformed SOA entry [%s]\n", value); + return -1; + } + rdata_size = 5 * sizeof(uint16_t); + + compressed_ns_len = ns_name_compress(nameserver, nameser_compressed, + MAXDNAME, NULL, NULL); + if (compressed_ns_len < 0) { + return -1; + } + rdata_size += compressed_ns_len; + + compressed_mb_len = ns_name_compress(mailbox, mailbox_compressed, + MAXDNAME, NULL, NULL); + if (compressed_mb_len < 0) { + return -1; + } + rdata_size += compressed_mb_len; + + rv = rwrap_fake_common(ns_t_soa, key, rdata_size, &a, anslen); + if (rv < 0) { + return -1; + } + + memcpy(a, nameser_compressed, compressed_ns_len); + a += compressed_ns_len; + memcpy(a, mailbox_compressed, compressed_mb_len); + a += compressed_mb_len; + NS_PUT32(atoi(str_serial), a); + NS_PUT32(atoi(str_refresh), a); + NS_PUT32(atoi(str_retry), a); + NS_PUT32(atoi(str_expire), a); + NS_PUT32(atoi(str_minimum), a); + + return 0; +} + +static int rwrap_fake_cname(const char *key, + const char *value, + uint8_t *answer, + size_t anslen) +{ + uint8_t *a = answer; + int rv; + unsigned char hostname_compressed[MAXDNAME]; + ssize_t rdata_size; + + if (value == NULL) { + RWRAP_LOG(RWRAP_LOG_ERROR, "Malformed record, no value!\n"); + return -1; + } + + /* Prepare the data to write */ + rdata_size = ns_name_compress(value, + hostname_compressed, MAXDNAME, + NULL, NULL); + if (rdata_size < 0) { + return -1; + } + + rv = rwrap_fake_common(ns_t_cname, key, rdata_size, &a, anslen); + if (rv < 0) { + return -1; + } + + memcpy(a, hostname_compressed, rdata_size); + + return 0; +} + +static int rwrap_fake_empty_query(const char *key, + uint16_t type, + uint8_t *answer, + size_t anslen) +{ + int rc; + + rc = rwrap_fake_common(type, key, 0, &answer, anslen); + if (rc < 0) { + return -1; + } + + return 0; +} + +#define RESOLV_MATCH(line, name) \ + (strncmp(line, name, sizeof(name) - 1) == 0 && \ + (line[sizeof(name) - 1] == ' ' || \ + line[sizeof(name) - 1] == '\t')) + +#define TYPE_MATCH(type, ns_type, rec_type, str_type, key, query) \ + ((type) == (ns_type) && \ + (strncmp((rec_type), (str_type), sizeof(str_type)) == 0) && \ + (strcmp(key, query)) == 0) + + +/* Reads in a file in the following format: + * TYPE RDATA + * + * Malformed entried are silently skipped. + * Allocates answer buffer of size anslen that has to be freed after use. + */ +static int rwrap_res_fake_hosts(const char *hostfile, + const char *query, + int type, + unsigned char *answer, + size_t anslen) +{ + FILE *fp = NULL; + char buf[BUFSIZ]; + int rc = ENOENT; + char *key = NULL; + char *value = NULL; + + RWRAP_LOG(RWRAP_LOG_TRACE, + "Searching in fake hosts file %s\n", hostfile); + + fp = fopen(hostfile, "r"); + if (fp == NULL) { + RWRAP_LOG(RWRAP_LOG_ERROR, + "Opening %s failed: %s", + hostfile, strerror(errno)); + return -1; + } + + while (fgets(buf, sizeof(buf), fp) != NULL) { + char *rec_type; + char *q; + + rec_type = buf; + key = value = NULL; + + NEXT_KEY(rec_type, key); + NEXT_KEY(key, value); + + q = value; + while(q[0] != '\n' && q[0] != '\0') { + q++; + } + q[0] = '\0'; + + if (key == NULL || value == NULL) { + RWRAP_LOG(RWRAP_LOG_WARN, + "Malformed line: not enough parts, use \"rec_type key data\n" + "For example \"A cwrap.org 10.10.10.10\""); + continue; + } + + if (TYPE_MATCH(type, ns_t_a, rec_type, "A", key, query)) { + rc = rwrap_fake_a(key, value, answer, anslen); + break; + } else if (TYPE_MATCH(type, ns_t_aaaa, + rec_type, "AAAA", key, query)) { + rc = rwrap_fake_aaaa(key, value, answer, anslen); + break; + } else if (TYPE_MATCH(type, ns_t_srv, + rec_type, "SRV", key, query)) { + rc = rwrap_fake_srv(key, value, answer, anslen); + break; + } else if (TYPE_MATCH(type, ns_t_soa, + rec_type, "SOA", key, query)) { + rc = rwrap_fake_soa(key, value, answer, anslen); + break; + } else if (TYPE_MATCH(type, ns_t_cname, + rec_type, "CNAME", key, query)) { + rc = rwrap_fake_cname(key, value, answer, anslen); + break; + } + } + + switch (rc) { + case 0: + RWRAP_LOG(RWRAP_LOG_TRACE, + "Successfully faked answer for [%s]\n", query); + break; + case -1: + RWRAP_LOG(RWRAP_LOG_ERROR, + "Error faking answer for [%s]\n", query); + break; + case ENOENT: + RWRAP_LOG(RWRAP_LOG_TRACE, + "Record for [%s] not found\n", query); + rc = rwrap_fake_empty_query(key, type, answer, anslen); + break; + } + + fclose(fp); + return rc; +} + +/********************************************************* + * RWRAP LOADING LIBC FUNCTIONS + *********************************************************/ + +#include + +struct rwrap_libc_fns { + int (*libc_res_init)(void); + int (*libc___res_init)(void); + int (*libc_res_ninit)(struct __res_state *state); + int (*libc___res_ninit)(struct __res_state *state); + void (*libc_res_nclose)(struct __res_state *state); + void (*libc___res_nclose)(struct __res_state *state); + void (*libc_res_close)(void); + void (*libc___res_close)(void); + int (*libc_res_nquery)(struct __res_state *state, + const char *dname, + int class, + int type, + unsigned char *answer, + int anslen); + int (*libc___res_nquery)(struct __res_state *state, + const char *dname, + int class, + int type, + unsigned char *answer, + int anslen); + int (*libc_res_nsearch)(struct __res_state *state, + const char *dname, + int class, + int type, + unsigned char *answer, + int anslen); + int (*libc___res_nsearch)(struct __res_state *state, + const char *dname, + int class, + int type, + unsigned char *answer, + int anslen); +}; + +struct rwrap { + void *libc_handle; + void *libresolv_handle; + + bool initialised; + bool enabled; + + char *socket_dir; + + struct rwrap_libc_fns fns; +}; + +static struct rwrap rwrap; + +enum rwrap_lib { + RWRAP_LIBC, + RWRAP_LIBRESOLV +}; + +#ifndef NDEBUG +static const char *rwrap_str_lib(enum rwrap_lib lib) +{ + switch (lib) { + case RWRAP_LIBC: + return "libc"; + case RWRAP_LIBRESOLV: + return "libresolv"; + } + + /* Compiler would warn us about unhandled enum value if we get here */ + return "unknown"; +} +#endif + +static void *rwrap_load_lib_handle(enum rwrap_lib lib) +{ + int flags = RTLD_LAZY; + void *handle = NULL; + int i; + +#ifdef RTLD_DEEPBIND + flags |= RTLD_DEEPBIND; +#endif + + switch (lib) { + case RWRAP_LIBRESOLV: +#ifdef HAVE_LIBRESOLV + handle = rwrap.libresolv_handle; + if (handle == NULL) { + for (i = 10; i >= 0; i--) { + char soname[256] = {0}; + + snprintf(soname, sizeof(soname), "libresolv.so.%d", i); + handle = dlopen(soname, flags); + if (handle != NULL) { + break; + } + } + + rwrap.libresolv_handle = handle; + } + break; +#endif + /* FALL TROUGH */ + case RWRAP_LIBC: + handle = rwrap.libc_handle; +#ifdef LIBC_SO + if (handle == NULL) { + handle = dlopen(LIBC_SO, flags); + + rwrap.libc_handle = handle; + } +#endif + if (handle == NULL) { + for (i = 10; i >= 0; i--) { + char soname[256] = {0}; + + snprintf(soname, sizeof(soname), "libc.so.%d", i); + handle = dlopen(soname, flags); + if (handle != NULL) { + break; + } + } + + rwrap.libc_handle = handle; + } + break; + } + + if (handle == NULL) { +#ifdef RTLD_NEXT + handle = rwrap.libc_handle = rwrap.libresolv_handle = RTLD_NEXT; +#else + RWRAP_LOG(RWRAP_LOG_ERROR, + "Failed to dlopen library: %s\n", + dlerror()); + exit(-1); +#endif + } + + return handle; +} + +static void *_rwrap_load_lib_function(enum rwrap_lib lib, const char *fn_name) +{ + void *handle; + void *func; + + handle = rwrap_load_lib_handle(lib); + + func = dlsym(handle, fn_name); + if (func == NULL) { + RWRAP_LOG(RWRAP_LOG_ERROR, + "Failed to find %s: %s\n", + fn_name, dlerror()); + exit(-1); + } + + RWRAP_LOG(RWRAP_LOG_TRACE, + "Loaded %s from %s", + fn_name, rwrap_str_lib(lib)); + return func; +} + +#define rwrap_load_lib_function(lib, fn_name) \ + if (rwrap.fns.libc_##fn_name == NULL) { \ + *(void **) (&rwrap.fns.libc_##fn_name) = \ + _rwrap_load_lib_function(lib, #fn_name); \ + } + +/* + * IMPORTANT + * + * Functions especially from libc need to be loaded individually, you can't load + * all at once or gdb will segfault at startup. The same applies to valgrind and + * has probably something todo with with the linker. + * So we need load each function at the point it is called the first time. + */ +#if 0 +static int libc_res_init(void) +{ +#if defined(HAVE_RES_INIT) + rwrap_load_lib_function(RWRAP_LIBRESOLV, res_init); + + return rwrap.fns.libc_res_init(); +#elif defined(HAVE___RES_INIT) + rwrap_load_lib_function(RWRAP_LIBRESOLV, __res_init); + + return rwrap.fns.libc___res_init(); +#endif +} +#endif + +static int libc_res_ninit(struct __res_state *state) +{ +#if defined(HAVE_RES_NINIT) + +#if defined(HAVE_RES_NINIT_IN_LIBRESOLV) + rwrap_load_lib_function(RWRAP_LIBRESOLV, res_ninit); +#else /* HAVE_RES_NINIT_IN_LIBRESOLV */ + rwrap_load_lib_function(RWRAP_LIBC, res_ninit); +#endif /* HAVE_RES_NINIT_IN_LIBRESOLV */ + + return rwrap.fns.libc_res_ninit(state); +#elif defined(HAVE___RES_NINIT) + rwrap_load_lib_function(RWRAP_LIBC, __res_ninit); + + return rwrap.fns.libc___res_ninit(state); +#else +#error "No res_ninit function" +#endif +} + +static void libc_res_nclose(struct __res_state *state) +{ +#if defined(HAVE_RES_NCLOSE) + +#if defined(HAVE_RES_NCLOSE_IN_LIBRESOLV) + rwrap_load_lib_function(RWRAP_LIBRESOLV, res_nclose); +#else /* HAVE_RES_NCLOSE_IN_LIBRESOLV */ + rwrap_load_lib_function(RWRAP_LIBC, res_nclose); +#endif /* HAVE_RES_NCLOSE_IN_LIBRESOLV */ + + rwrap.fns.libc_res_nclose(state); +#elif defined(HAVE___RES_NCLOSE) + rwrap_load_lib_function(RWRAP_LIBC, __res_nclose); + + rwrap.fns.libc___res_nclose(state); +#else +#error "No res_nclose function" +#endif +} + +static int libc_res_nquery(struct __res_state *state, + const char *dname, + int class, + int type, + unsigned char *answer, + int anslen) +{ +#if defined(HAVE_RES_NQUERY) + rwrap_load_lib_function(RWRAP_LIBRESOLV, res_nquery); + + return rwrap.fns.libc_res_nquery(state, + dname, + class, + type, + answer, + anslen); +#elif defined(HAVE___RES_NQUERY) + rwrap_load_lib_function(RWRAP_LIBRESOLV, __res_nquery); + + return rwrap.fns.libc___res_nquery(state, + dname, + class, + type, + answer, + anslen); +#else +#error "No res_nquery function" +#endif +} + +static int libc_res_nsearch(struct __res_state *state, + const char *dname, + int class, + int type, + unsigned char *answer, + int anslen) +{ +#if defined(HAVE_RES_NSEARCH) + rwrap_load_lib_function(RWRAP_LIBRESOLV, res_nsearch); + + return rwrap.fns.libc_res_nsearch(state, + dname, + class, + type, + answer, + anslen); +#elif defined(HAVE___RES_NSEARCH) + rwrap_load_lib_function(RWRAP_LIBRESOLV, __res_nsearch); + + return rwrap.fns.libc___res_nsearch(state, + dname, + class, + type, + answer, + anslen); +#else +#error "No res_nsearch function" +#endif +} + +/**************************************************************************** + * RES_HELPER + ***************************************************************************/ + +static int rwrap_parse_resolv_conf(struct __res_state *state, + const char *resolv_conf) +{ + FILE *fp; + char buf[BUFSIZ]; + int nserv = 0; + + fp = fopen(resolv_conf, "r"); + if (fp == NULL) { + RWRAP_LOG(RWRAP_LOG_ERROR, + "Opening %s failed: %s", + resolv_conf, strerror(errno)); + return -1; + } + + while(fgets(buf, sizeof(buf), fp) != NULL) { + char *p; + + /* Ignore comments */ + if (buf[0] == '#' || buf[0] == ';') { + continue; + } + + if (RESOLV_MATCH(buf, "nameserver") && nserv < MAXNS) { + struct in_addr a; + char *q; + int ok; + + p = buf + strlen("nameserver"); + + /* Skip spaces and tabs */ + while(isblank((int)p[0])) { + p++; + } + + q = p; + while(q[0] != '\n' && q[0] != '\0') { + q++; + } + q[0] = '\0'; + + ok = inet_pton(AF_INET, p, &a); + if (ok) { + state->nsaddr_list[state->nscount] = (struct sockaddr_in) { + .sin_family = AF_INET, + .sin_addr = a, + .sin_port = htons(53), + .sin_zero = { 0 }, + }; + + state->nscount++; + nserv++; + } else { +#ifdef HAVE_RESOLV_IPV6_NSADDRS + /* IPv6 */ + struct in6_addr a6; + ok = inet_pton(AF_INET6, p, &a6); + if (ok) { + struct sockaddr_in6 *sa6; + + sa6 = malloc(sizeof(*sa6)); + if (sa6 == NULL) { + fclose(fp); + return -1; + } + + sa6->sin6_family = AF_INET6; + sa6->sin6_port = htons(53); + sa6->sin6_flowinfo = 0; + sa6->sin6_addr = a6; + + state->_u._ext.nsaddrs[state->_u._ext.nscount] = sa6; + state->_u._ext.nssocks[state->_u._ext.nscount] = -1; + state->_u._ext.nsmap[state->_u._ext.nscount] = MAXNS + 1; + + state->_u._ext.nscount++; + nserv++; + } else { + RWRAP_LOG(RWRAP_LOG_ERROR, + "Malformed DNS server"); + continue; + } +#else /* !HAVE_RESOLV_IPV6_NSADDRS */ + /* + * BSD uses an opaque structure to store the + * IPv6 addresses. So we can not simply store + * these addresses the same way as above. + */ + RWRAP_LOG(RWRAP_LOG_WARN, + "resolve_wrapper does not support " + "IPv6 on this platform"); + continue; +#endif + } + continue; + } /* TODO: match other keywords */ + } + + if (ferror(fp)) { + RWRAP_LOG(RWRAP_LOG_ERROR, + "Reading from %s failed", + resolv_conf); + fclose(fp); + return -1; + } + + fclose(fp); + return 0; +} + +/**************************************************************************** + * RES_NINIT + ***************************************************************************/ + +static int rwrap_res_ninit(struct __res_state *state) +{ + int rc; + + rc = libc_res_ninit(state); + if (rc == 0) { + const char *resolv_conf = getenv("RESOLV_WRAPPER_CONF"); + + if (resolv_conf != NULL) { + uint16_t i; + + (void)i; /* maybe unused */ + + /* Delete name servers */ + state->nscount = 0; + memset(state->nsaddr_list, 0, sizeof(state->nsaddr_list)); + + state->_u._ext.nscount = 0; +#ifdef HAVE_RESOLV_IPV6_NSADDRS + for (i = 0; i < state->_u._ext.nscount; i++) { + SAFE_FREE(state->_u._ext.nsaddrs[i]); + } +#endif + + rc = rwrap_parse_resolv_conf(state, resolv_conf); + } + } + + return rc; +} + +#if defined(HAVE_RES_NINIT) +int res_ninit(struct __res_state *state) +#elif defined(HAVE___RES_NINIT) +int __res_ninit(struct __res_state *state) +#endif +{ + return rwrap_res_ninit(state); +} + +/**************************************************************************** + * RES_INIT + ***************************************************************************/ + +static struct __res_state rwrap_res_state; + +static int rwrap_res_init(void) +{ + int rc; + + rc = rwrap_res_ninit(&rwrap_res_state); + + return rc; +} + +#if defined(HAVE_RES_INIT) +int res_init(void) +#elif defined(HAVE___RES_INIT) +int __res_init(void) +#endif +{ + return rwrap_res_init(); +} + +/**************************************************************************** + * RES_NCLOSE + ***************************************************************************/ + +static void rwrap_res_nclose(struct __res_state *state) +{ +#ifdef HAVE_RESOLV_IPV6_NSADDRS + int i; +#endif + + libc_res_nclose(state); + +#ifdef HAVE_RESOLV_IPV6_NSADDRS + if (state != NULL) { + for (i = 0; i < state->_u._ext.nscount; i++) { + SAFE_FREE(state->_u._ext.nsaddrs[i]); + } + } +#endif +} + +#if defined(HAVE_RES_NCLOSE) +void res_nclose(struct __res_state *state) +#elif defined(HAVE___RES_NCLOSE) +void __res_nclose(struct __res_state *state) +#endif +{ + rwrap_res_nclose(state); +} + +/**************************************************************************** + * RES_CLOSE + ***************************************************************************/ + +static void rwrap_res_close(void) +{ + rwrap_res_nclose(&rwrap_res_state); +} + +#if defined(HAVE_RES_CLOSE) +void res_close(void) +#elif defined(HAVE___RES_CLOSE) +void __res_close(void) +#endif +{ + rwrap_res_close(); +} + +/**************************************************************************** + * RES_NQUERY + ***************************************************************************/ + +static int rwrap_res_nquery(struct __res_state *state, + const char *dname, + int class, + int type, + unsigned char *answer, + int anslen) +{ + int rc; + const char *fake_hosts; +#ifndef NDEBUG + int i; +#endif + + RWRAP_LOG(RWRAP_LOG_TRACE, + "Resolve the domain name [%s] - class=%d, type=%d", + dname, class, type); +#ifndef NDEBUG + for (i = 0; i < state->nscount; i++) { + char ip[INET6_ADDRSTRLEN]; + + inet_ntop(AF_INET, &state->nsaddr_list[i].sin_addr, ip, sizeof(ip)); + RWRAP_LOG(RWRAP_LOG_TRACE, + " nameserver: %s", + ip); + } +#endif + + fake_hosts = getenv("RESOLV_WRAPPER_HOSTS"); + if (fake_hosts != NULL) { + rc = rwrap_res_fake_hosts(fake_hosts, dname, type, answer, anslen); + } else { + rc = libc_res_nquery(state, dname, class, type, answer, anslen); + } + + + RWRAP_LOG(RWRAP_LOG_TRACE, + "The returned response length is: %d", + rc); + + return rc; +} + +#if defined(HAVE_RES_NQUERY) +int res_nquery(struct __res_state *state, + const char *dname, + int class, + int type, + unsigned char *answer, + int anslen) +#elif defined(HAVE___RES_NQUERY) +int __res_nquery(struct __res_state *state, + const char *dname, + int class, + int type, + unsigned char *answer, + int anslen) +#endif +{ + return rwrap_res_nquery(state, dname, class, type, answer, anslen); +} + +/**************************************************************************** + * RES_QUERY + ***************************************************************************/ + +static int rwrap_res_query(const char *dname, + int class, + int type, + unsigned char *answer, + int anslen) +{ + int rc; + + rc = rwrap_res_ninit(&rwrap_res_state); + if (rc != 0) { + return rc; + } + + rc = rwrap_res_nquery(&rwrap_res_state, + dname, + class, + type, + answer, + anslen); + + return rc; +} + +#if defined(HAVE_RES_QUERY) +int res_query(const char *dname, + int class, + int type, + unsigned char *answer, + int anslen) +#elif defined(HAVE___RES_QUERY) +int __res_query(const char *dname, + int class, + int type, + unsigned char *answer, + int anslen) +#endif +{ + return rwrap_res_query(dname, class, type, answer, anslen); +} + +/**************************************************************************** + * RES_NSEARCH + ***************************************************************************/ + +static int rwrap_res_nsearch(struct __res_state *state, + const char *dname, + int class, + int type, + unsigned char *answer, + int anslen) +{ + int rc; + const char *fake_hosts; +#ifndef NDEBUG + int i; +#endif + + RWRAP_LOG(RWRAP_LOG_TRACE, + "Resolve the domain name [%s] - class=%d, type=%d", + dname, class, type); +#ifndef NDEBUG + for (i = 0; i < state->nscount; i++) { + char ip[INET6_ADDRSTRLEN]; + + inet_ntop(AF_INET, &state->nsaddr_list[i].sin_addr, ip, sizeof(ip)); + RWRAP_LOG(RWRAP_LOG_TRACE, + " nameserver: %s", + ip); + } +#endif + + fake_hosts = getenv("RESOLV_WRAPPER_HOSTS"); + if (fake_hosts != NULL) { + rc = rwrap_res_fake_hosts(fake_hosts, dname, type, answer, anslen); + } else { + rc = libc_res_nsearch(state, dname, class, type, answer, anslen); + } + + RWRAP_LOG(RWRAP_LOG_TRACE, + "The returned response length is: %d", + rc); + + return rc; +} + +#if defined(HAVE_RES_NSEARCH) +int res_nsearch(struct __res_state *state, + const char *dname, + int class, + int type, + unsigned char *answer, + int anslen) +#elif defined(HAVE___RES_NSEARCH) +int __res_nsearch(struct __res_state *state, + const char *dname, + int class, + int type, + unsigned char *answer, + int anslen) +#endif +{ + return rwrap_res_nsearch(state, dname, class, type, answer, anslen); +} + +/**************************************************************************** + * RES_QUERY + ***************************************************************************/ + +static int rwrap_res_search(const char *dname, + int class, + int type, + unsigned char *answer, + int anslen) +{ + int rc; + + rc = rwrap_res_ninit(&rwrap_res_state); + if (rc != 0) { + return rc; + } + + rc = rwrap_res_nsearch(&rwrap_res_state, + dname, + class, + type, + answer, + anslen); + + return rc; +} + +#if defined(HAVE_RES_SEARCH) +int res_search(const char *dname, + int class, + int type, + unsigned char *answer, + int anslen) +#elif defined(HAVE___RES_SEARCH) +int __res_search(const char *dname, + int class, + int type, + unsigned char *answer, + int anslen) +#endif +{ + return rwrap_res_search(dname, class, type, answer, anslen); +} diff --git a/lib/resolv_wrapper/wscript b/lib/resolv_wrapper/wscript new file mode 100644 index 00000000000..d72ec182eb3 --- /dev/null +++ b/lib/resolv_wrapper/wscript @@ -0,0 +1,94 @@ +#!/usr/bin/env python + +import os + +VERSION="1.0.0" + +def configure(conf): + if conf.CHECK_BUNDLED_SYSTEM('resolv_wrapper', minversion=VERSION, set_target=False): + conf.DEFINE('USING_SYSTEM_RESOLV_WRAPPER', 1) + libresolv_wrapper_so_path = 'libresolv_wrapper.so' + else: + # check HAVE_GCC_THREAD_LOCAL_STORAGE + conf.CHECK_CODE(''' + __thread int tls; + + int main(void) { + return 0; + } + ''', + 'HAVE_GCC_THREAD_LOCAL_STORAGE', + addmain=False, + msg='Checking for thread local storage') + + # check HAVE_DESTRUCTOR_ATTRIBUTE + conf.CHECK_CODE(''' + void test_destructor_attribute(void) __attribute__ ((destructor)); + + void test_destructor_attribute(void) + { + return; + } + + int main(void) { + return 0; + } + ''', + 'HAVE_DESTRUCTOR_ATTRIBUTE', + addmain=False, + msg='Checking for library destructor support') + + # check HAVE_FUNCTION_ATTRIBUTE_FORMAT + conf.CHECK_CODE(''' + void log_fn(const char *format, ...) __attribute__ ((format (printf, 1, 2))); + + int main(void) { + return 0; + } + ''', + 'HAVE_FUNCTION_ATTRIBUTE_FORMAT', + addmain=False, + msg='Checking for printf format validation support') + + conf.CHECK_HEADERS('resolv.h') + + conf.CHECK_STRUCTURE_MEMBER('struct _res_state', + '_u._ext.nsaddrs', + headers='resolv.h', + define='HAVE_RESOLV_IPV6_NSADDRS') + + conf.CHECK_FUNCS_IN('res_ninit', 'resolv') + if conf.CONFIG_SET('HAVE_RES_NINIT'): + conf.DEFINE('HAVE_RES_NINIT_IN_LIBRESOLV', 1) + + conf.CHECK_FUNCS_IN('res_nclose', 'resolv') + if conf.CONFIG_SET('HAVE_RES_NCLOSE'): + conf.DEFINE('HAVE_RES_NCLOSE_IN_LIBRESOLV', 1) + + conf.CHECK_FUNCS_IN('res_init __res_init', 'resolv', checklibc=True) + conf.CHECK_FUNCS_IN('res_ninit __res_ninit', 'resolv', checklibc=True) + conf.CHECK_FUNCS_IN('res_close __res_close', 'resolv', checklibc=True) + conf.CHECK_FUNCS_IN('res_nclose __res_nclose', 'resolv', checklibc=True) + conf.CHECK_FUNCS_IN('res_query __res_query', 'resolv', checklibc=True) + conf.CHECK_FUNCS_IN('res_nquery __res_nquery', 'resolv', checklibc=True) + conf.CHECK_FUNCS_IN('res_search __res_search', 'resolv', checklibc=True) + conf.CHECK_FUNCS_IN('res_nsearch __res_nsearch', 'resolv', checklibc=True) + + # Create full path to resolv_wrapper + srcdir = os.path.realpath(conf.srcdir) + libresolv_wrapper_so_path = srcdir + '/bin/default/lib/resolv_wrapper/libresolv-wrapper.so' + + conf.DEFINE('LIBRESOLV_WRAPPER_SO_PATH', libresolv_wrapper_so_path) + conf.DEFINE('RESOLV_WRAPPER', 1) + +def build(bld): + if bld.CONFIG_SET("HAVE_RESOLV_H") and not bld.CONFIG_SET("USING_SYSTEM_RESOLV_WRAPPER"): + # We need to do it this way or the library wont work. + # Using private_library=True will add symbol version which + # breaks preloading! + bld.SAMBA_LIBRARY('resolv_wrapper', + source='resolv_wrapper.c', + cflags='-DNDEBUG', + deps='dl resolv', + install=False, + realname='libresolv-wrapper.so') diff --git a/wscript b/wscript index 872177e20b9..d851885f869 100644 --- a/wscript +++ b/wscript @@ -158,6 +158,7 @@ def configure(conf): conf.RECURSE('source4/auth') conf.RECURSE('lib/nss_wrapper') conf.RECURSE('nsswitch') + conf.RECURSE('lib/resolv_wrapper') conf.RECURSE('lib/socket_wrapper') conf.RECURSE('lib/uid_wrapper') conf.RECURSE('lib/subunit/c') diff --git a/wscript_build b/wscript_build index e74841e96a1..fc4197441f4 100644 --- a/wscript_build +++ b/wscript_build @@ -71,6 +71,7 @@ bld.RECURSE('source4/lib/events') bld.RECURSE('source4/lib/cmdline') bld.RECURSE('source4/lib/http') bld.RECURSE('lib/socket_wrapper') +bld.RECURSE('lib/resolv_wrapper') bld.RECURSE('lib/nss_wrapper') bld.RECURSE('lib/uid_wrapper') if bld.CHECK_FOR_THIRD_PARTY():