resolv_wrapper-1.1.7/README.install000644 001750 000144 00000004171 12427110221 017072 0ustar00asnusers000000 000000 Obtaining the sources ===================== Source tarballs for resolv_wrapper can be downloaded from https://ftp.samba.org/pub/cwrap/ The source code repository for socket wrapper is located under git://git.samba.org/resolv_wrapper.git To create a local copy, run $ git clone git://git.samba.org/resolv_wrapper.git $ cd resolv_wrapper Building from sources ===================== resolv_wrapper uses cmake (www.cmake.org) as its build system. In an unpacked sources base directory, create a directory to contain the build results, e.g. $ mkdir obj $ cd obj Note that "obj" is just an example. The directory can be named arbitrarily. Next, run cmake to configure the build, e.g. $ cmake -DCMAKE_INSTALL_PREFIX= .. or on a 64 bit red hat system: $ cmake -DCMAKE_INSTALL_PREFIX= -DLIB_SUFFIX=64 .. The "" should be replaced by the intended installation target prefix directory, typically /usr or /usr/local. Note that the target directory does not have to be a direct or indirect subdirectory of the source base directory: It can be an arbitrary directory in the system. In the general case, ".." has to be replaced by a relative or absolute path of the source base directory in the "cmake" command line. One can control the build type with "-DCMAKE_BUILD_TYPE=" where can be one of Debug, Release, RelWithDebInfo, and some more (see cmake.org). The default is "RelWithDebInfo". After configuring with cmake, run the build with $ make Unit testing ============ In order to support running the test suite after building, the cmocka unit test framework needs to be installed (cmocka.org), and you need to specify -DUNIT_TESTING=ON in the cmake run. Note that for unit testing, resolv_wrapper requires socket_wrapper to be installed. If socket_wrapper is installed in a non-standard location, this can be passed to cmake via -Dsocket_wrapper_DIR=//cmake/socket_wrapper After running "make", $ make test runs the test suite. Installing ========== resolv_wrapper is installed into the prefix directory after running "cmake" and "make" with $ make install resolv_wrapper-1.1.7/src/000755 001750 000144 00000000000 13721230076 015342 5ustar00asnusers000000 000000 resolv_wrapper-1.1.7/src/CMakeLists.txt000644 001750 000144 00000001574 13636077442 020124 0ustar00asnusers000000 000000 project(libresolv_wrapper C) add_library(resolv_wrapper SHARED resolv_wrapper.c) target_link_libraries(resolv_wrapper ${RWRAP_REQUIRED_LIBRARIES}) target_compile_options(resolv_wrapper PRIVATE ${DEFAULT_C_COMPILE_FLAGS} -D_GNU_SOURCE) target_include_directories(resolv_wrapper PRIVATE ${CMAKE_BINARY_DIR}) set_target_properties( resolv_wrapper PROPERTIES VERSION ${LIBRARY_VERSION} SOVERSION ${LIBRARY_SOVERSION} ) install(TARGETS resolv_wrapper RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) set(RESOLV_WRAPPER_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}resolv_wrapper${CMAKE_SHARED_LIBRARY_SUFFIX}" PARENT_SCOPE) resolv_wrapper-1.1.7/src/resolv_wrapper.c000644 001750 000144 00000143644 13720751265 020603 0ustar00asnusers000000 000000 /* * Copyright (c) 2014-2018 Andreas Schneider * Copyright (c) 2014-2016 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 #ifdef HAVE_ARPA_NAMESER_H #include #endif /* HAVE_ARPA_NAMESER_H */ #include #include #include #include #include #include #include #include #include #include #include #if defined(HAVE_RES_STATE_U_EXT_NSADDRS) || defined(HAVE_RES_SOCKADDR_UNION_SIN6) #define HAVE_RESOLV_IPV6_NSADDRS 1 #endif /* 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 */ #ifndef HAVE_NS_NAME_COMPRESS #define ns_name_compress dn_comp #endif #define ns_t_uri 256 enum rwrap_dbglvl_e { RWRAP_LOG_ERROR = 0, RWRAP_LOG_WARN, RWRAP_LOG_NOTICE, RWRAP_LOG_DEBUG, RWRAP_LOG_TRACE }; #ifndef HAVE_GETPROGNAME static const char *getprogname(void) { #if defined(HAVE_PROGRAM_INVOCATION_SHORT_NAME) return program_invocation_short_name; #elif defined(HAVE_GETEXECNAME) return getexecname(); #else return NULL; #endif /* HAVE_PROGRAM_INVOCATION_SHORT_NAME */ } #endif /* HAVE_GETPROGNAME */ 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; const char *prefix = NULL; const char *progname = NULL; d = getenv("RESOLV_WRAPPER_DEBUGLEVEL"); if (d != NULL) { lvl = atoi(d); } if (lvl < dbglvl) { return; } va_start(va, format); vsnprintf(buffer, sizeof(buffer), format, va); va_end(va); switch (dbglvl) { case RWRAP_LOG_ERROR: prefix = "RWRAP_ERROR"; break; case RWRAP_LOG_WARN: prefix = "RWRAP_WARN"; break; case RWRAP_LOG_NOTICE: prefix = "RWRAP_NOTICE"; break; case RWRAP_LOG_DEBUG: prefix = "RWRAP_DEBUG"; break; case RWRAP_LOG_TRACE: prefix = "RWRAP_TRACE"; break; } progname = getprogname(); if (progname == NULL) { progname = ""; } fprintf(stderr, "%s[%s (%u)] - %s: %s\n", prefix, progname, (unsigned int)getpid(), func, buffer); } #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); #define RWRAP_MAX_RECURSION 64 union rwrap_sockaddr { struct sockaddr sa; struct sockaddr_in in; struct sockaddr_in6 in6; }; /* 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 #define DFL_URI_PRIO 1 #define DFL_URI_WEIGHT 100 struct rwrap_srv_rrdata { uint16_t port; uint16_t prio; uint16_t weight; char hostname[MAXDNAME]; }; struct rwrap_uri_rrdata { uint16_t prio; uint16_t weight; char uri[MAXDNAME]; }; struct rwrap_soa_rrdata { uint32_t serial; uint32_t refresh; uint32_t retry; uint32_t expire; uint32_t minimum; char nameserver[MAXDNAME]; char mailbox[MAXDNAME]; }; struct rwrap_fake_rr { union fake_rrdata { struct in_addr a_rec; struct in6_addr aaaa_rec; struct rwrap_srv_rrdata srv_rec; struct rwrap_uri_rrdata uri_rec; struct rwrap_soa_rrdata soa_rec; char cname_rec[MAXDNAME]; char ptr_rec[MAXDNAME]; char txt_rec[MAXDNAME]; } rrdata; char key[MAXDNAME]; int type; /* ns_t_* */ }; static void rwrap_fake_rr_init(struct rwrap_fake_rr *rr, size_t len) { size_t i; for (i = 0; i < len; i++) { rr[i].type = ns_t_invalid; } } static int rwrap_create_fake_a_rr(const char *key, const char *value, struct rwrap_fake_rr *rr) { int ok; ok = inet_pton(AF_INET, value, &rr->rrdata.a_rec); if (!ok) { RWRAP_LOG(RWRAP_LOG_ERROR, "Failed to convert [%s] to binary\n", value); return -1; } memcpy(rr->key, key, strlen(key) + 1); rr->type = ns_t_a; return 0; } static int rwrap_create_fake_aaaa_rr(const char *key, const char *value, struct rwrap_fake_rr *rr) { int ok; ok = inet_pton(AF_INET6, value, &rr->rrdata.aaaa_rec); if (!ok) { RWRAP_LOG(RWRAP_LOG_ERROR, "Failed to convert [%s] to binary\n", value); return -1; } memcpy(rr->key, key, strlen(key) + 1); rr->type = ns_t_aaaa; return 0; } static int rwrap_create_fake_ns_rr(const char *key, const char *value, struct rwrap_fake_rr *rr) { memcpy(rr->rrdata.srv_rec.hostname, value, strlen(value) + 1); memcpy(rr->key, key, strlen(key) + 1); rr->type = ns_t_ns; return 0; } static int rwrap_create_fake_srv_rr(const char *key, const char *value, struct rwrap_fake_rr *rr) { char *str_prio; char *str_weight; char *str_port; const char *hostname; /* 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; } if (str_prio) { rr->rrdata.srv_rec.prio = atoi(str_prio); } else { rr->rrdata.srv_rec.prio = DFL_SRV_PRIO; } if (str_weight) { rr->rrdata.srv_rec.weight = atoi(str_weight); } else { rr->rrdata.srv_rec.weight = DFL_SRV_WEIGHT; } rr->rrdata.srv_rec.port = atoi(str_port); memcpy(rr->rrdata.srv_rec.hostname , hostname, strlen(hostname) + 1); memcpy(rr->key, key, strlen(key) + 1); rr->type = ns_t_srv; return 0; } static int rwrap_create_fake_uri_rr(const char *key, const char *value, struct rwrap_fake_rr *rr) { char *str_prio; char *str_weight; const char *uri; /* parse the value into priority, weight, and uri * and check the validity */ uri = value; NEXT_KEY(uri, str_prio); NEXT_KEY(str_prio, str_weight); if (uri == NULL) { RWRAP_LOG(RWRAP_LOG_ERROR, "Malformed URI entry []\n"); return -1; } if (str_prio) { rr->rrdata.uri_rec.prio = atoi(str_prio); } else { rr->rrdata.uri_rec.prio = DFL_URI_PRIO; } if (str_weight) { rr->rrdata.uri_rec.weight = atoi(str_weight); } else { rr->rrdata.uri_rec.weight = DFL_URI_WEIGHT; } memcpy(rr->rrdata.uri_rec.uri, uri, strlen(uri) + 1); memcpy(rr->key, key, strlen(key) + 1); rr->type = ns_t_uri; return 0; } static int rwrap_create_fake_txt_rr(const char *key, const char *value, struct rwrap_fake_rr *rr) { memcpy(rr->rrdata.txt_rec, value, strlen(value) + 1); memcpy(rr->key, key, strlen(key) + 1); rr->type = ns_t_txt; return 0; } static int rwrap_create_fake_soa_rr(const char *key, const char *value, struct rwrap_fake_rr *rr) { const char *nameserver; char *mailbox; char *str_serial; char *str_refresh; char *str_retry; char *str_expire; char *str_minimum; /* 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; } memcpy(rr->rrdata.soa_rec.nameserver, nameserver, strlen(nameserver)+1); memcpy(rr->rrdata.soa_rec.mailbox, mailbox, strlen(mailbox)+1); rr->rrdata.soa_rec.serial = atoi(str_serial); rr->rrdata.soa_rec.refresh = atoi(str_refresh); rr->rrdata.soa_rec.retry = atoi(str_retry); rr->rrdata.soa_rec.expire = atoi(str_expire); rr->rrdata.soa_rec.minimum = atoi(str_minimum); memcpy(rr->key, key, strlen(key) + 1); rr->type = ns_t_soa; return 0; } static int rwrap_create_fake_cname_rr(const char *key, const char *value, struct rwrap_fake_rr *rr) { memcpy(rr->rrdata.cname_rec , value, strlen(value) + 1); memcpy(rr->key, key, strlen(key) + 1); rr->type = ns_t_cname; return 0; } static int rwrap_create_fake_ptr_rr(const char *key, const char *value, struct rwrap_fake_rr *rr) { memcpy(rr->rrdata.ptr_rec , value, strlen(value) + 1); memcpy(rr->key, key, strlen(key) + 1); rr->type = ns_t_ptr; return 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 ancount, size_t arcount) { union { uint8_t *blob; HEADER *header; } h; if (remaining < NS_HFIXEDSZ) { RWRAP_LOG(RWRAP_LOG_ERROR, "Buffer too small!\n"); return -1; } h.blob = *header_blob; memset(h.blob, 0, NS_HFIXEDSZ); h.header->id = res_randomid(); /* random query ID */ h.header->qr = 1; /* response flag */ h.header->rd = 1; /* recursion desired */ h.header->ra = 1; /* recursion available */ h.header->qdcount = htons(1); /* no. of questions */ h.header->ancount = htons(ancount); /* no. of answers */ h.header->arcount = htons(arcount); /* no. of add'tl records */ /* move past the header */ *header_blob = h.blob += NS_HFIXEDSZ; 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) + rdata_size; } static ssize_t rwrap_fake_a(struct rwrap_fake_rr *rr, uint8_t *answer_ptr, size_t anslen) { uint8_t *a = answer_ptr; ssize_t resp_size; if (rr->type != ns_t_a) { RWRAP_LOG(RWRAP_LOG_ERROR, "Wrong type!\n"); return -1; } RWRAP_LOG(RWRAP_LOG_TRACE, "Adding A RR"); resp_size = rwrap_fake_rdata_common(ns_t_a, sizeof(struct in_addr), rr->key, anslen, &a); if (resp_size < 0) { return -1; } memcpy(a, &rr->rrdata.a_rec, sizeof(struct in_addr)); return resp_size; } static ssize_t rwrap_fake_aaaa(struct rwrap_fake_rr *rr, uint8_t *answer, size_t anslen) { uint8_t *a = answer; ssize_t resp_size; if (rr->type != ns_t_aaaa) { RWRAP_LOG(RWRAP_LOG_ERROR, "Wrong type!\n"); return -1; } RWRAP_LOG(RWRAP_LOG_TRACE, "Adding AAAA RR"); resp_size = rwrap_fake_rdata_common(ns_t_aaaa, sizeof(struct in6_addr), rr->key, anslen, &a); if (resp_size < 0) { return -1; } memcpy(a, &rr->rrdata.aaaa_rec, sizeof(struct in6_addr)); return resp_size; } static ssize_t rwrap_fake_ns(struct rwrap_fake_rr *rr, uint8_t *answer, size_t anslen) { uint8_t *a = answer; ssize_t resp_size = 0; size_t rdata_size; unsigned char hostname_compressed[MAXDNAME]; ssize_t compressed_len; if (rr->type != ns_t_ns) { RWRAP_LOG(RWRAP_LOG_ERROR, "Wrong type!\n"); return -1; } RWRAP_LOG(RWRAP_LOG_TRACE, "Adding NS RR"); /* Prepare the data to write */ compressed_len = ns_name_compress(rr->rrdata.srv_rec.hostname, hostname_compressed, MAXDNAME, NULL, NULL); if (compressed_len < 0) { return -1; } /* Is this enough? */ rdata_size = compressed_len; resp_size = rwrap_fake_rdata_common(ns_t_ns, rdata_size, rr->key, anslen, &a); if (resp_size < 0) { return -1; } memcpy(a, hostname_compressed, compressed_len); return resp_size; } static ssize_t rwrap_fake_srv(struct rwrap_fake_rr *rr, uint8_t *answer, size_t anslen) { uint8_t *a = answer; ssize_t resp_size; size_t rdata_size; unsigned char hostname_compressed[MAXDNAME]; ssize_t compressed_len; if (rr->type != ns_t_srv) { RWRAP_LOG(RWRAP_LOG_ERROR, "Wrong type!\n"); return -1; } RWRAP_LOG(RWRAP_LOG_TRACE, "Adding SRV RR"); rdata_size = 3 * sizeof(uint16_t); /* Prepare the data to write */ compressed_len = ns_name_compress(rr->rrdata.srv_rec.hostname, hostname_compressed, MAXDNAME, NULL, NULL); if (compressed_len < 0) { return -1; } rdata_size += compressed_len; resp_size = rwrap_fake_rdata_common(ns_t_srv, rdata_size, rr->key, anslen, &a); if (resp_size < 0) { return -1; } NS_PUT16(rr->rrdata.srv_rec.prio, a); NS_PUT16(rr->rrdata.srv_rec.weight, a); NS_PUT16(rr->rrdata.srv_rec.port, a); memcpy(a, hostname_compressed, compressed_len); return resp_size; } static ssize_t rwrap_fake_uri(struct rwrap_fake_rr *rr, uint8_t *answer, size_t anslen) { uint8_t *a = answer; ssize_t resp_size; size_t rdata_size; size_t uri_len; if (rr->type != ns_t_uri) { RWRAP_LOG(RWRAP_LOG_ERROR, "Wrong type!\n"); return -1; } RWRAP_LOG(RWRAP_LOG_TRACE, "Adding URI RR"); rdata_size = 3 * sizeof(uint16_t); uri_len = strlen(rr->rrdata.uri_rec.uri) + 1; rdata_size += uri_len; resp_size = rwrap_fake_rdata_common(ns_t_uri, rdata_size, rr->key, anslen, &a); if (resp_size < 0) { return -1; } NS_PUT16(rr->rrdata.uri_rec.prio, a); NS_PUT16(rr->rrdata.uri_rec.weight, a); memcpy(a, rr->rrdata.uri_rec.uri, uri_len); return resp_size; } static ssize_t rwrap_fake_txt(struct rwrap_fake_rr *rr, uint8_t *answer, size_t anslen) { uint8_t *a = answer; ssize_t resp_size; size_t rdata_size; size_t txt_len; if (rr->type != ns_t_txt) { RWRAP_LOG(RWRAP_LOG_ERROR, "Wrong type!\n"); return -1; } RWRAP_LOG(RWRAP_LOG_TRACE, "Adding TXT RR"); txt_len = strlen(rr->rrdata.txt_rec) + 1; rdata_size = txt_len; resp_size = rwrap_fake_rdata_common(ns_t_txt, rdata_size, rr->key, anslen, &a); if (resp_size < 0) { return -1; } memcpy(a, rr->rrdata.txt_rec, txt_len); return resp_size; } static ssize_t rwrap_fake_soa(struct rwrap_fake_rr *rr, uint8_t *answer, size_t anslen) { uint8_t *a = answer; ssize_t resp_size; size_t rdata_size; unsigned char nameser_compressed[MAXDNAME]; ssize_t compressed_ns_len; unsigned char mailbox_compressed[MAXDNAME]; ssize_t compressed_mb_len; if (rr->type != ns_t_soa) { RWRAP_LOG(RWRAP_LOG_ERROR, "Wrong type!\n"); return -1; } RWRAP_LOG(RWRAP_LOG_TRACE, "Adding SOA RR"); rdata_size = 5 * sizeof(uint16_t); compressed_ns_len = ns_name_compress(rr->rrdata.soa_rec.nameserver, nameser_compressed, MAXDNAME, NULL, NULL); if (compressed_ns_len < 0) { return -1; } rdata_size += compressed_ns_len; compressed_mb_len = ns_name_compress(rr->rrdata.soa_rec.mailbox, mailbox_compressed, MAXDNAME, NULL, NULL); if (compressed_mb_len < 0) { return -1; } rdata_size += compressed_mb_len; resp_size = rwrap_fake_rdata_common(ns_t_soa, rdata_size, rr->key, anslen, &a); if (resp_size < 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(rr->rrdata.soa_rec.serial, a); NS_PUT32(rr->rrdata.soa_rec.refresh, a); NS_PUT32(rr->rrdata.soa_rec.retry, a); NS_PUT32(rr->rrdata.soa_rec.expire, a); NS_PUT32(rr->rrdata.soa_rec.minimum, a); return resp_size; } static ssize_t rwrap_fake_cname(struct rwrap_fake_rr *rr, uint8_t *answer, size_t anslen) { uint8_t *a = answer; ssize_t resp_size; unsigned char hostname_compressed[MAXDNAME]; ssize_t rdata_size; if (rr->type != ns_t_cname) { RWRAP_LOG(RWRAP_LOG_ERROR, "Wrong type!\n"); return -1; } RWRAP_LOG(RWRAP_LOG_TRACE, "Adding CNAME RR"); /* Prepare the data to write */ rdata_size = ns_name_compress(rr->rrdata.cname_rec, hostname_compressed, MAXDNAME, NULL, NULL); if (rdata_size < 0) { return -1; } resp_size = rwrap_fake_rdata_common(ns_t_cname, rdata_size, rr->key, anslen, &a); if (resp_size < 0) { return -1; } memcpy(a, hostname_compressed, rdata_size); return resp_size; } static ssize_t rwrap_fake_ptr(struct rwrap_fake_rr *rr, uint8_t *answer, size_t anslen) { uint8_t *a = answer; ssize_t rdata_size; ssize_t resp_size; unsigned char hostname_compressed[MAXDNAME]; if (rr->type != ns_t_ptr) { RWRAP_LOG(RWRAP_LOG_ERROR, "Wrong type!\n"); return -1; } RWRAP_LOG(RWRAP_LOG_TRACE, "Adding PTR RR"); /* Prepare the data to write */ rdata_size = ns_name_compress(rr->rrdata.ptr_rec, hostname_compressed, MAXDNAME, NULL, NULL); if (rdata_size < 0) { return -1; } resp_size = rwrap_fake_rdata_common(ns_t_ptr, rdata_size, rr->key, anslen, &a); if (resp_size < 0) { return -1; } memcpy(a, hostname_compressed, rdata_size); return resp_size; } #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) && \ (strcasecmp(key, query)) == 0) static int rwrap_get_record(const char *hostfile, unsigned recursion, const char *query, int type, struct rwrap_fake_rr *rr); static int rwrap_uri_recurse(const char *hostfile, unsigned recursion, const char *query, struct rwrap_fake_rr *rr) { int rc; rc = rwrap_get_record(hostfile, recursion, query, ns_t_uri, rr); if (rc == ENOENT) { rc = 0; } return rc; } static int rwrap_srv_recurse(const char *hostfile, unsigned recursion, const char *query, struct rwrap_fake_rr *rr) { int rc; rc = rwrap_get_record(hostfile, recursion, query, ns_t_a, rr); if (rc == 0) return 0; rc = rwrap_get_record(hostfile, recursion, query, ns_t_aaaa, rr); if (rc == ENOENT) rc = 0; return rc; } static int rwrap_cname_recurse(const char *hostfile, unsigned recursion, const char *query, struct rwrap_fake_rr *rr) { int rc; rc = rwrap_get_record(hostfile, recursion, query, ns_t_a, rr); if (rc == 0) return 0; rc = rwrap_get_record(hostfile, recursion, query, ns_t_aaaa, rr); if (rc == 0) return 0; rc = rwrap_get_record(hostfile, recursion, query, ns_t_cname, rr); if (rc == ENOENT) rc = 0; return rc; } static int rwrap_get_record(const char *hostfile, unsigned recursion, const char *query, int type, struct rwrap_fake_rr *rr) { FILE *fp = NULL; char buf[BUFSIZ]; char *key = NULL; char *value = NULL; int rc = ENOENT; unsigned num_uris = 0; if (recursion >= RWRAP_MAX_RECURSION) { RWRAP_LOG(RWRAP_LOG_ERROR, "Recursed too deep!\n"); return -1; } RWRAP_LOG(RWRAP_LOG_TRACE, "Searching in fake hosts file %s for %s:%d\n", hostfile, query, type); fp = fopen(hostfile, "r"); if (fp == NULL) { RWRAP_LOG(RWRAP_LOG_WARN, "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); 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; } q = value; while(q[0] != '\n' && q[0] != '\0') { q++; } q[0] = '\0'; if (type == ns_t_uri && recursion > 0) { /* Skip non-URI records. */ if (!TYPE_MATCH(type, ns_t_uri, rec_type, "URI", key, query)) { continue; } /* Skip previous records based on the recurse depth. */ num_uris++; if (num_uris <= recursion) { continue; } } if (TYPE_MATCH(type, ns_t_a, rec_type, "A", key, query)) { rc = rwrap_create_fake_a_rr(key, value, rr); break; } else if (TYPE_MATCH(type, ns_t_aaaa, rec_type, "AAAA", key, query)) { rc = rwrap_create_fake_aaaa_rr(key, value, rr); break; } else if (TYPE_MATCH(type, ns_t_ns, rec_type, "NS", key, query)) { rc = rwrap_create_fake_ns_rr(key, value, rr); break; } else if (TYPE_MATCH(type, ns_t_srv, rec_type, "SRV", key, query)) { rc = rwrap_create_fake_srv_rr(key, value, rr); if (rc == 0) { rc = rwrap_srv_recurse(hostfile, recursion+1, rr->rrdata.srv_rec.hostname, rr + 1); } break; } else if (TYPE_MATCH(type, ns_t_uri, rec_type, "URI", key, query)) { rc = rwrap_create_fake_uri_rr(key, value, rr); if (rc == 0) { /* Recurse to collect multiple URI answers under a single key. */ rc = rwrap_uri_recurse(hostfile, recursion + 1, key, rr + 1); } break; } else if (TYPE_MATCH(type, ns_t_soa, rec_type, "SOA", key, query)) { rc = rwrap_create_fake_soa_rr(key, value, rr); break; } else if (TYPE_MATCH(type, ns_t_cname, rec_type, "CNAME", key, query)) { rc = rwrap_create_fake_cname_rr(key, value, rr); if (rc == 0) { rc = rwrap_cname_recurse(hostfile, recursion+1, value, rr + 1); } break; } else if (TYPE_MATCH(type, ns_t_a, rec_type, "CNAME", key, query)) { rc = rwrap_create_fake_cname_rr(key, value, rr); if (rc == 0) { rc = rwrap_cname_recurse(hostfile, recursion+1, value, rr + 1); } break; } else if (TYPE_MATCH(type, ns_t_ptr, rec_type, "PTR", key, query)) { rc = rwrap_create_fake_ptr_rr(key, value, rr); break; } else if (TYPE_MATCH(type, ns_t_txt, rec_type, "TXT", key, query)) { rc = rwrap_create_fake_txt_rr(key, value, rr); break; } } if (rc == ENOENT && recursion == 0 && key != NULL) { RWRAP_LOG(RWRAP_LOG_TRACE, "Record for [%s] not found\n", query); memcpy(rr->key, key, strlen(key) + 1); } fclose(fp); return rc; } static ssize_t rwrap_fake_empty(int type, const char *question, uint8_t *answer, size_t anslen) { ssize_t resp_data; size_t remaining = anslen; resp_data = rwrap_fake_header(&answer, remaining, 0, 0); if (resp_data < 0) { return -1; } remaining -= resp_data; resp_data += rwrap_fake_question(question, type, &answer, remaining); if (resp_data < 0) { return -1; } remaining -= resp_data; resp_data += rwrap_fake_rdata_common(type, 0, question, remaining, &answer); if (resp_data < 0) { return -1; } return resp_data; } static inline bool rwrap_known_type(int type) { switch (type) { case ns_t_a: case ns_t_aaaa: case ns_t_ns: case ns_t_srv: case ns_t_uri: case ns_t_soa: case ns_t_cname: case ns_t_ptr: case ns_t_txt: return true; } return false; } static int rwrap_ancount(struct rwrap_fake_rr *rrs, int qtype) { int i; int ancount = 0; /* For URI return the number of URIs. */ if (qtype == ns_t_uri) { for (i = 0; i < RWRAP_MAX_RECURSION; i++) { if (rwrap_known_type(rrs[i].type) && rrs[i].type == qtype) { ancount++; } } return ancount; } /* Include all RRs in the stack until the sought type * in the answer section. This is the case i.e. when looking * up an A record but the name points to a CNAME */ for (i = 0; i < RWRAP_MAX_RECURSION; i++) { ancount++; if (rwrap_known_type(rrs[i].type) && rrs[i].type == qtype) { break; } } /* Return 0 records if the sought type wasn't in the stack */ return i < RWRAP_MAX_RECURSION ? ancount : 0; } static int rwrap_arcount(struct rwrap_fake_rr *rrs, int ancount) { int i; int arcount = 0; /* start from index ancount */ for (i = ancount; i < RWRAP_MAX_RECURSION; i++) { if (rwrap_known_type(rrs[i].type)) { arcount++; } } return arcount; } static ssize_t rwrap_add_rr(struct rwrap_fake_rr *rr, uint8_t *answer, size_t anslen) { ssize_t resp_data; if (rr == NULL) { RWRAP_LOG(RWRAP_LOG_ERROR, "Internal error!\n"); return -1; } switch (rr->type) { case ns_t_a: resp_data = rwrap_fake_a(rr, answer, anslen); break; case ns_t_aaaa: resp_data = rwrap_fake_aaaa(rr, answer, anslen); break; case ns_t_ns: resp_data = rwrap_fake_ns(rr, answer, anslen); break; case ns_t_srv: resp_data = rwrap_fake_srv(rr, answer, anslen); break; case ns_t_uri: resp_data = rwrap_fake_uri(rr, answer, anslen); break; case ns_t_soa: resp_data = rwrap_fake_soa(rr, answer, anslen); break; case ns_t_cname: resp_data = rwrap_fake_cname(rr, answer, anslen); break; case ns_t_ptr: resp_data = rwrap_fake_ptr(rr, answer, anslen); break; case ns_t_txt: resp_data = rwrap_fake_txt(rr, answer, anslen); break; default: return -1; } return resp_data; } static ssize_t rwrap_fake_answer(struct rwrap_fake_rr *rrs, int type, uint8_t *answer, size_t anslen) { ssize_t resp_data; ssize_t rrlen; size_t remaining = anslen; int ancount; int arcount; int i; ancount = rwrap_ancount(rrs, type); arcount = rwrap_arcount(rrs, ancount); RWRAP_LOG(RWRAP_LOG_TRACE, "Got %d answers and %d additional records\n", ancount, arcount); resp_data = rwrap_fake_header(&answer, remaining, ancount, arcount); if (resp_data < 0) { return -1; } remaining -= resp_data; resp_data += rwrap_fake_question(rrs->key, rrs->type, &answer, remaining); if (resp_data < 0) { return -1; } remaining -= resp_data; /* answer */ for (i = 0; i < ancount; i++) { rrlen = rwrap_add_rr(&rrs[i], answer, remaining); if (rrlen < 0) { return -1; } remaining -= rrlen; answer += rrlen; resp_data += rrlen; } /* add authoritative NS here? */ /* additional records */ for (i = ancount; i < ancount + arcount; i++) { rrlen = rwrap_add_rr(&rrs[i], answer, remaining); if (rrlen < 0) { return -1; } remaining -= rrlen; answer += rrlen; resp_data += rrlen; } return resp_data; } /* Reads in a file in the following format: * TYPE RDATA * * Malformed entries 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) { int rc = ENOENT; char *query_name = NULL; size_t qlen = strlen(query); struct rwrap_fake_rr rrs[RWRAP_MAX_RECURSION]; ssize_t resp_size; RWRAP_LOG(RWRAP_LOG_TRACE, "Searching in fake hosts file %s\n", hostfile); if (qlen > 0 && query[qlen-1] == '.') { qlen--; } query_name = strndup(query, qlen); if (query_name == NULL) { return -1; } rwrap_fake_rr_init(rrs, RWRAP_MAX_RECURSION); rc = rwrap_get_record(hostfile, 0, query_name, type, rrs); switch (rc) { case 0: RWRAP_LOG(RWRAP_LOG_TRACE, "Found record for [%s]\n", query_name); resp_size = rwrap_fake_answer(rrs, type, answer, anslen); break; case ENOENT: RWRAP_LOG(RWRAP_LOG_TRACE, "No record for [%s]\n", query_name); resp_size = rwrap_fake_empty(type, rrs->key, answer, anslen); break; default: RWRAP_LOG(RWRAP_LOG_NOTICE, "Searching for [%s] did not return any results\n", query_name); free(query_name); return -1; } switch (resp_size) { case -1: RWRAP_LOG(RWRAP_LOG_ERROR, "Error faking answer for [%s]\n", query_name); break; default: RWRAP_LOG(RWRAP_LOG_TRACE, "Successfully faked answer for [%s]\n", query_name); break; } free(query_name); return resp_size; } /********************************************************* * RWRAP LOADING LIBC FUNCTIONS *********************************************************/ #include typedef int (*__libc_res_ninit)(struct __res_state *state); typedef int (*__libc___res_ninit)(struct __res_state *state); typedef void (*__libc_res_nclose)(struct __res_state *state); typedef void (*__libc___res_nclose)(struct __res_state *state); typedef int (*__libc_res_nquery)(struct __res_state *state, const char *dname, int class, int type, unsigned char *answer, int anslen); typedef int (*__libc___res_nquery)(struct __res_state *state, const char *dname, int class, int type, unsigned char *answer, int anslen); typedef int (*__libc_res_nsearch)(struct __res_state *state, const char *dname, int class, int type, unsigned char *answer, int anslen); typedef int (*__libc___res_nsearch)(struct __res_state *state, const char *dname, int class, int type, unsigned char *answer, int anslen); #define RWRAP_SYMBOL_ENTRY(i) \ union { \ __libc_##i f; \ void *obj; \ } _libc_##i struct rwrap_libc_symbols { RWRAP_SYMBOL_ENTRY(res_ninit); RWRAP_SYMBOL_ENTRY(__res_ninit); RWRAP_SYMBOL_ENTRY(res_nclose); RWRAP_SYMBOL_ENTRY(__res_nclose); RWRAP_SYMBOL_ENTRY(res_nquery); RWRAP_SYMBOL_ENTRY(__res_nquery); RWRAP_SYMBOL_ENTRY(res_nsearch); RWRAP_SYMBOL_ENTRY(__res_nsearch); }; #undef RWRAP_SYMBOL_ENTRY struct rwrap { struct { void *handle; struct rwrap_libc_symbols symbols; } libc; struct { void *handle; struct rwrap_libc_symbols symbols; } libresolv; bool initialised; bool enabled; char *socket_dir; }; static struct rwrap rwrap; enum rwrap_lib { RWRAP_LIBC, RWRAP_LIBRESOLV }; 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"; } static void *rwrap_load_lib_handle(enum rwrap_lib lib) { int flags = RTLD_LAZY; void *handle = NULL; int i; #ifdef RTLD_DEEPBIND const char *env_preload = getenv("LD_PRELOAD"); const char *env_deepbind = getenv("RESOLV_WRAPPER_DISABLE_DEEPBIND"); bool enable_deepbind = true; /* Don't do a deepbind if we run with libasan */ if (env_preload != NULL && strlen(env_preload) < 1024) { const char *p = strstr(env_preload, "libasan.so"); if (p != NULL) { enable_deepbind = false; } } if (env_deepbind != NULL && strlen(env_deepbind) >= 1) { enable_deepbind = false; } if (enable_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_bind_symbol(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_bind_symbol_libc(sym_name) \ if (rwrap.libc.symbols._libc_##sym_name.obj == NULL) { \ rwrap.libc.symbols._libc_##sym_name.obj = \ _rwrap_bind_symbol(RWRAP_LIBC, #sym_name); \ } #define rwrap_bind_symbol_libresolv(sym_name) \ if (rwrap.libresolv.symbols._libc_##sym_name.obj == NULL) { \ rwrap.libresolv.symbols._libc_##sym_name.obj = \ _rwrap_bind_symbol(RWRAP_LIBRESOLV, #sym_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. */ static int libc_res_ninit(struct __res_state *state) { #if !defined(res_ninit) && defined(HAVE_RES_NINIT) rwrap_bind_symbol_libresolv(res_ninit); return rwrap.libresolv.symbols._libc_res_ninit.f(state); #elif defined(HAVE___RES_NINIT) rwrap_bind_symbol_libresolv(__res_ninit); return rwrap.libresolv.symbols._libc___res_ninit.f(state); #else #error "No res_ninit function" #endif } static void libc_res_nclose(struct __res_state *state) { #if !defined(res_close) && defined(HAVE_RES_NCLOSE) rwrap_bind_symbol_libresolv(res_nclose); rwrap.libresolv.symbols._libc_res_nclose.f(state); return; #elif defined(HAVE___RES_NCLOSE) rwrap_bind_symbol_libresolv(__res_nclose); rwrap.libresolv.symbols._libc___res_nclose.f(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(res_nquery) && defined(HAVE_RES_NQUERY) rwrap_bind_symbol_libresolv(res_nquery); return rwrap.libresolv.symbols._libc_res_nquery.f(state, dname, class, type, answer, anslen); #elif defined(HAVE___RES_NQUERY) rwrap_bind_symbol_libresolv(__res_nquery); return rwrap.libresolv.symbols._libc___res_nquery.f(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(res_nsearch) && defined(HAVE_RES_NSEARCH) rwrap_bind_symbol_libresolv(res_nsearch); return rwrap.libresolv.symbols._libc_res_nsearch.f(state, dname, class, type, answer, anslen); #elif defined(HAVE___RES_NSEARCH) rwrap_bind_symbol_libresolv(__res_nsearch); return rwrap.libresolv.symbols._libc___res_nsearch.f(state, dname, class, type, answer, anslen); #else #error "No res_nsearch function" #endif } /**************************************************************************** * RES_HELPER ***************************************************************************/ static size_t rwrap_get_nameservers(struct __res_state *state, size_t nserv, union rwrap_sockaddr *nsaddrs) { #ifdef HAVE_RES_SOCKADDR_UNION_SIN union res_sockaddr_union set[MAXNS]; size_t i; int rc; memset(set, 0, sizeof(set)); memset(nsaddrs, 0, sizeof(*nsaddrs) * nserv); if (nserv > MAXNS) { nserv = MAXNS; } rc = res_getservers(state, set, nserv); if (rc <= 0) { return 0; } if (rc < nserv) { nserv = rc; } for (i = 0; i < nserv; i++) { switch (set[i].sin.sin_family) { case AF_INET: nsaddrs[i] = (union rwrap_sockaddr) { .in = set[i].sin, }; break; #ifdef HAVE_RES_SOCKADDR_UNION_SIN6 case AF_INET6: nsaddrs[i] = (union rwrap_sockaddr) { .in6 = set[i].sin6, }; break; #endif } } return nserv; #else /* ! HAVE_RES_SOCKADDR_UNION_SIN */ size_t i; memset(nsaddrs, 0, sizeof(*nsaddrs) * nserv); if (nserv > (size_t)state->nscount) { nserv = (size_t)state->nscount; } for (i = 0; i < nserv; i++) { #ifdef HAVE_RES_STATE_U_EXT_NSADDRS if (state->_u._ext.nsaddrs[i] != NULL) { nsaddrs[i] = (union rwrap_sockaddr) { .in6 = *state->_u._ext.nsaddrs[i], }; } else #endif /* HAVE_RES_STATE_U_EXT_NSADDRS */ { nsaddrs[i] = (union rwrap_sockaddr) { .in = state->nsaddr_list[i], }; } } return nserv; #endif /* ! HAVE_RES_SOCKADDR_UNION_SIN */ } static void rwrap_log_nameservers(enum rwrap_dbglvl_e dbglvl, const char *func, struct __res_state *state) { union rwrap_sockaddr nsaddrs[MAXNS]; size_t nserv = MAXNS; size_t i; memset(nsaddrs, 0, sizeof(nsaddrs)); nserv = rwrap_get_nameservers(state, nserv, nsaddrs); for (i = 0; i < nserv; i++) { char ip[INET6_ADDRSTRLEN]; switch (nsaddrs[i].sa.sa_family) { case AF_INET: inet_ntop(AF_INET, &(nsaddrs[i].in.sin_addr), ip, sizeof(ip)); break; case AF_INET6: inet_ntop(AF_INET6, &(nsaddrs[i].in6.sin6_addr), ip, sizeof(ip)); break; default: snprintf(ip, sizeof(ip), "nscount; i++) { if (state->_u._ext.nssocks[i] != -1) { close(state->_u._ext.nssocks[i]); state->_u._ext.nssocks[i] = -1; } SAFE_FREE(state->_u._ext.nsaddrs[i]); } memset(&state->_u._ext, 0, sizeof(state->_u._ext)); for (i = 0; i < MAXNS; i++) { state->_u._ext.nssocks[i] = -1; state->_u._ext.nsmap[i] = MAXNS + 1; } state->ipv6_unavail = false; #endif memset(state->nsaddr_list, 0, sizeof(state->nsaddr_list)); state->nscount = 0; #endif /* ! HAVE_RES_SOCKADDR_UNION_SIN */ } static int rwrap_set_nameservers(struct __res_state *state, size_t nserv, const union rwrap_sockaddr *nsaddrs) { #ifdef HAVE_RES_SOCKADDR_UNION_SIN union res_sockaddr_union set[MAXNS]; size_t i; memset(set, 0, sizeof(set)); if (nserv > MAXNS) { nserv = MAXNS; } rwrap_reset_nameservers(state); for (i = 0; i < nserv; i++) { switch (nsaddrs[i].sa.sa_family) { case AF_INET: set[i] = (union res_sockaddr_union) { .sin = nsaddrs[i].in, }; break; #ifdef HAVE_RES_SOCKADDR_UNION_SIN6 case AF_INET6: set[i] = (union res_sockaddr_union) { .sin6 = nsaddrs[i].in6, }; break; #endif default: RWRAP_LOG(RWRAP_LOG_ERROR, "Internal error unhandled sa_family=%d", nsaddrs[i].sa.sa_family); errno = ENOSYS; return -1; } } res_setservers(state, set, nserv); return 0; #else /* ! HAVE_RES_SOCKADDR_UNION_SIN */ size_t i; if (nserv > MAXNS) { nserv = MAXNS; } rwrap_reset_nameservers(state); for (i = 0; i < nserv; i++) { switch (nsaddrs[i].sa.sa_family) { case AF_INET: state->nsaddr_list[i] = nsaddrs[i].in; break; #ifdef HAVE_RES_STATE_U_EXT_NSADDRS case AF_INET6: state->_u._ext.nsaddrs[i] = malloc(sizeof(nsaddrs[i].in6)); if (state->_u._ext.nsaddrs[i] == NULL) { rwrap_reset_nameservers(state); errno = ENOMEM; return -1; } *state->_u._ext.nsaddrs[i] = nsaddrs[i].in6; state->_u._ext.nssocks[i] = -1; state->_u._ext.nsmap[i] = MAXNS + 1; state->_u._ext.nscount6++; break; #endif default: RWRAP_LOG(RWRAP_LOG_ERROR, "Internal error unhandled sa_family=%d", nsaddrs[i].sa.sa_family); rwrap_reset_nameservers(state); errno = ENOSYS; return -1; } } /* * note that state->_u._ext.nscount is left as 0, * this matches glibc and allows resolv wrapper * to work with most (maybe all) glibc versions. */ state->nscount = i; return 0; #endif /* ! HAVE_RES_SOCKADDR_UNION_SIN */ } static int rwrap_parse_resolv_conf(struct __res_state *state, const char *resolv_conf) { FILE *fp; char buf[BUFSIZ]; size_t nserv = 0; union rwrap_sockaddr nsaddrs[MAXNS]; memset(nsaddrs, 0, sizeof(nsaddrs)); fp = fopen(resolv_conf, "r"); if (fp == NULL) { RWRAP_LOG(RWRAP_LOG_WARN, "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; struct in6_addr a6; 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) { nsaddrs[nserv] = (union rwrap_sockaddr) { .in = { .sin_family = AF_INET, .sin_addr = a, .sin_port = htons(53), .sin_zero = { 0 }, }, }; nserv++; continue; } ok = inet_pton(AF_INET6, p, &a6); if (ok) { #ifdef HAVE_RESOLV_IPV6_NSADDRS nsaddrs[nserv] = (union rwrap_sockaddr) { .in6 = { .sin6_family = AF_INET6, .sin6_port = htons(53), .sin6_flowinfo = 0, .sin6_addr = a6, }, }; nserv++; continue; #else /* !HAVE_RESOLV_IPV6_NSADDRS */ RWRAP_LOG(RWRAP_LOG_WARN, "resolve_wrapper does not support " "IPv6 on this platform"); continue; #endif } RWRAP_LOG(RWRAP_LOG_ERROR, "Malformed DNS server[%s]", p); 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); if (nserv == 0) { RWRAP_LOG(RWRAP_LOG_WARN, "No usable nameservers found in %s", resolv_conf); errno = ESRCH; return -1; } return rwrap_set_nameservers(state, nserv, nsaddrs); } /**************************************************************************** * 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) { rc = rwrap_parse_resolv_conf(state, resolv_conf); } } return rc; } #if !defined(res_ninit) && 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(res_ninit) && 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) { rwrap_reset_nameservers(state); libc_res_nclose(state); } #if !defined(res_nclose) && 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; RWRAP_LOG(RWRAP_LOG_TRACE, "Resolve the domain name [%s] - class=%d, type=%d", dname, class, type); rwrap_log_nameservers(RWRAP_LOG_TRACE, __func__, state); 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(res_nquery) && 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(res_query) && 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; RWRAP_LOG(RWRAP_LOG_TRACE, "Resolve the domain name [%s] - class=%d, type=%d", dname, class, type); rwrap_log_nameservers(RWRAP_LOG_TRACE, __func__, state); 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(res_nsearch) && 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_SEARCH ***************************************************************************/ 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(res_search) && 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); } resolv_wrapper-1.1.7/CTestConfig.cmake000644 001750 000144 00000000433 13341215233 017721 0ustar00asnusers000000 000000 set(UPDATE_TYPE "true") set(CTEST_PROJECT_NAME "resolv_wrapper") set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC") set(CTEST_DROP_METHOD "https") set(CTEST_DROP_SITE "test.cmocka.org") set(CTEST_DROP_LOCATION "/submit.php?project=${CTEST_PROJECT_NAME}") set(CTEST_DROP_SITE_CDASH TRUE) resolv_wrapper-1.1.7/CHANGELOG000644 001750 000144 00000002527 13720751403 015774 0ustar00asnusers000000 000000 CHANGELOG ========= version 1.1.7 (released 2020-08-25) * Move debug messages from error to warning version 1.1.6 (released 2020-03-23) * Added support for running with Sanitizers * Added fake resolving of TXT records * Improved logging support * Fixed resolv.conf support on FreeBSD * Fixed alignment issues on FreeBSD * Fixed using IPv6 address with old glibc versions version 1.1.5 (released 2016-09-08) * Added support for faking PTR entries * Added support for faking URI entries version 1.1.4 (released 2016-05-31) * Added support for faking NS entries * Fixed some platform compatibility bugs version 1.1.3 (released 2015-01-13) * Fixed symbol detection if macros are used for res_* functions * Fixed strict aliasing warnings for symbol binding * Added missing tests for req_query and res_search version 1.1.2 (released 2015-01-13) * Fix detection for ns_name_compress. version 1.1.1 (released 2015-01-12) * Fixed building on older Linux distributions. * Fix a possible segfault. version 1.1.0 (released 2014-12-02) * Added case insensitive comparison of dns names (dns faking). * Added support complete dns names (trailing dot) (dns faking). * Added support for recursive name resolving (dns faking). * Fixed calculation of response size (dns faking). version 1.0.0 (released 2014-10-24) * Initial release resolv_wrapper-1.1.7/resolv_wrapper-config-version.cmake.in000644 001750 000144 00000000611 12421451254 024157 0ustar00asnusers000000 000000 set(PACKAGE_VERSION @APPLICATION_VERSION@) # Check whether the requested PACKAGE_FIND_VERSION is compatible if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") set(PACKAGE_VERSION_COMPATIBLE FALSE) else() set(PACKAGE_VERSION_COMPATIBLE TRUE) if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") set(PACKAGE_VERSION_EXACT TRUE) endif() endif() resolv_wrapper-1.1.7/CompilerChecks.cmake000644 001750 000144 00000012335 13636077442 020467 0ustar00asnusers000000 000000 include(AddCCompilerFlag) include(CheckCCompilerFlagSSP) if (UNIX) # # Check for -Werror turned on if possible # # This will prevent that compiler flags are detected incorrectly. # check_c_compiler_flag("-Werror" REQUIRED_FLAGS_WERROR) if (REQUIRED_FLAGS_WERROR) set(CMAKE_REQUIRED_FLAGS "-Werror") if (PICKY_DEVELOPER) list(APPEND SUPPORTED_COMPILER_FLAGS "-Werror") endif() endif() add_c_compiler_flag("-std=gnu99" SUPPORTED_COMPILER_FLAGS) #add_c_compiler_flag("-Wpedantic" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Wall" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Wshadow" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Wmissing-prototypes" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Wcast-align" SUPPORTED_COMPILER_FLAGS) #add_c_compiler_flag("-Wcast-qual" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Werror=address" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Wstrict-prototypes" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Werror=strict-prototypes" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Wwrite-strings" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Werror=write-strings" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Werror-implicit-function-declaration" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Wpointer-arith" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Werror=pointer-arith" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Wdeclaration-after-statement" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Werror=declaration-after-statement" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Wreturn-type" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Werror=return-type" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Wuninitialized" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Werror=uninitialized" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Wimplicit-fallthrough" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Werror=strict-overflow" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Wstrict-overflow=2" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Wno-format-zero-length" SUPPORTED_COMPILER_FLAGS) check_c_compiler_flag("-Wformat" REQUIRED_FLAGS_WFORMAT) if (REQUIRED_FLAGS_WFORMAT) list(APPEND SUPPORTED_COMPILER_FLAGS "-Wformat") set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Wformat") endif() add_c_compiler_flag("-Wformat-security" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Werror=format-security" SUPPORTED_COMPILER_FLAGS) # Allow zero for a variadic macro argument string(TOLOWER "${CMAKE_C_COMPILER_ID}" _C_COMPILER_ID) if ("${_C_COMPILER_ID}" STREQUAL "clang") add_c_compiler_flag("-Wno-gnu-zero-variadic-macro-arguments" SUPPORTED_COMPILER_FLAGS) endif() add_c_compiler_flag("-fno-common" SUPPORTED_COMPILER_FLAGS) if (CMAKE_BUILD_TYPE) string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER) if (CMAKE_BUILD_TYPE_LOWER MATCHES (release|relwithdebinfo|minsizerel)) add_c_compiler_flag("-Wp,-D_FORTIFY_SOURCE=2" SUPPORTED_COMPILER_FLAGS) endif() endif() if (NOT SOLARIS) check_c_compiler_flag_ssp("-fstack-protector-strong" WITH_STACK_PROTECTOR_STRONG) if (WITH_STACK_PROTECTOR_STRONG) list(APPEND SUPPORTED_COMPILER_FLAGS "-fstack-protector-strong") # This is needed as Solaris has a seperate libssp if (SOLARIS) list(APPEND SUPPORTED_LINKER_FLAGS "-fstack-protector-strong") endif() else (WITH_STACK_PROTECTOR_STRONG) check_c_compiler_flag_ssp("-fstack-protector" WITH_STACK_PROTECTOR) if (WITH_STACK_PROTECTOR) list(APPEND SUPPORTED_COMPILER_FLAGS "-fstack-protector") # This is needed as Solaris has a seperate libssp if (SOLARIS) list(APPEND SUPPORTED_LINKER_FLAGS "-fstack-protector") endif() endif() endif (WITH_STACK_PROTECTOR_STRONG) check_c_compiler_flag_ssp("-fstack-clash-protection" WITH_STACK_CLASH_PROTECTION) if (WITH_STACK_CLASH_PROTECTION) list(APPEND SUPPORTED_COMPILER_FLAGS "-fstack-clash-protection") endif() endif() if (PICKY_DEVELOPER) add_c_compiler_flag("-Wno-error=deprecated-declarations" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("-Wno-error=tautological-compare" SUPPORTED_COMPILER_FLAGS) endif() # Unset CMAKE_REQUIRED_FLAGS unset(CMAKE_REQUIRED_FLAGS) endif() if (MSVC) add_c_compiler_flag("/D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("/D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("/D _CRT_NONSTDC_NO_WARNINGS=1" SUPPORTED_COMPILER_FLAGS) add_c_compiler_flag("/D _CRT_SECURE_NO_WARNINGS=1" SUPPORTED_COMPILER_FLAGS) endif() if (SUPPORTED_COMPILER_FLAGS) set(DEFAULT_C_COMPILE_FLAGS ${SUPPORTED_COMPILER_FLAGS} CACHE INTERNAL "Default C Compiler Flags" FORCE) endif() if (SUPPORTED_LINKER_FLAGS) set(DEFAULT_LINK_FLAGS ${SUPPORTED_LINKER_FLAGS} CACHE INTERNAL "Default C Linker Flags" FORCE) endif() resolv_wrapper-1.1.7/tests/000755 001750 000144 00000000000 13721230076 015715 5ustar00asnusers000000 000000 resolv_wrapper-1.1.7/tests/test_res_init.c000644 001750 000144 00000015347 13657213747 020764 0ustar00asnusers000000 000000 #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define RWRAP_RESOLV_CONF_TMPL "rwrap_resolv_conf_XXXXXX" struct resolv_conf_test_state { int rc_fd; FILE *resolv_conf; char *resolv_conf_path; }; static int setup(void **state) { struct resolv_conf_test_state *test_state; test_state = malloc(sizeof(struct resolv_conf_test_state)); assert_non_null(test_state); test_state->rc_fd = -1; test_state->resolv_conf = NULL; test_state->resolv_conf_path = strdup(RWRAP_RESOLV_CONF_TMPL); assert_non_null(test_state->resolv_conf_path); test_state->rc_fd = mkstemp(test_state->resolv_conf_path); assert_return_code(test_state->rc_fd, errno); assert_non_null(test_state->resolv_conf_path); test_state->resolv_conf = fdopen(test_state->rc_fd, "a"); assert_non_null(test_state->resolv_conf); *state = test_state; return 0; } static int teardown(void **state) { struct resolv_conf_test_state *test_state; test_state = (struct resolv_conf_test_state *) *state; if (test_state == NULL) return -1; if (test_state->resolv_conf) { fclose(test_state->resolv_conf); } if (test_state->rc_fd != -1) { close(test_state->rc_fd); } if (test_state->resolv_conf_path) { unlink(test_state->resolv_conf_path); free(test_state->resolv_conf_path); } free(test_state); return 0; } static void test_res_ninit(void **state) { struct resolv_conf_test_state *test_state; struct __res_state dnsstate; /* * libc resolver only supports 3 name servers. Make sure the * extra are skipped for both v4 and v6. Also make sure there's * 'too many' nameservers even on platforms where v6 is not * supported. */ const char *nameservers[] = { "127.0.0.1", "10.10.10.1", "fd53:53:53:53::1011", "10.10.10.2", "10.10.10.3", "fd53:53:53:53::1012", NULL, }; int i; int rv; char straddr[INET6_ADDRSTRLEN] = { '\0' }; #ifdef HAVE_RES_SOCKADDR_UNION_SIN union res_sockaddr_union set[MAXNS*5]; #endif #ifdef HAVE_RES_STATE_U_EXT_NSADDRS struct sockaddr_in6 *sa6; #endif test_state = (struct resolv_conf_test_state *) *state; /* * Write a valid resolv.conf. * Make sure it's possible to skip comments */ fputs("# Hello world\n", test_state->resolv_conf); fputs("; This is resolv_wrapper\n", test_state->resolv_conf); for (i = 0; nameservers[i]; i++) { fputs("nameserver ", test_state->resolv_conf); fputs(nameservers[i], test_state->resolv_conf); fputs("\n", test_state->resolv_conf); } fflush(test_state->resolv_conf); rv = setenv("RESOLV_WRAPPER_CONF", test_state->resolv_conf_path, 1); assert_int_equal(rv, 0); memset(&dnsstate, 0, sizeof(dnsstate)); rv = res_ninit(&dnsstate); unsetenv("RESOLV_WRAPPER_CONF"); assert_int_equal(rv, 0); /* * Validate the number of parsed name servers. * * On platforms that don't support IPv6, the v6 address is skipped * and we end up reading three v4 addresses. * * test we have two v4 and one v6 server * * Note: This test assumes MAXNS == 3, which is the * case on all systems encountered so far. */ assert_int_equal(MAXNS, 3); #ifdef HAVE_RES_SOCKADDR_UNION_SIN memset(set, 0, sizeof(set)); rv = res_getservers(&dnsstate, set, MAXNS+5); assert_int_equal(rv, MAXNS); /* IPv4 */ assert_int_equal(set[0].sin.sin_family, AF_INET); assert_int_equal(set[0].sin.sin_port, htons(53)); inet_ntop(AF_INET, &(set[0].sin.sin_addr), straddr, INET6_ADDRSTRLEN); assert_string_equal(nameservers[0], straddr); assert_int_equal(set[1].sin.sin_family, AF_INET); assert_int_equal(set[1].sin.sin_port, htons(53)); inet_ntop(AF_INET, &(set[1].sin.sin_addr), straddr, INET6_ADDRSTRLEN); assert_string_equal(nameservers[1], straddr); #ifdef HAVE_RES_SOCKADDR_UNION_SIN6 /* IPv6 */ assert_int_equal(set[2].sin6.sin6_family, AF_INET6); assert_int_equal(set[2].sin6.sin6_port, htons(53)); inet_ntop(AF_INET6, &(set[2].sin6.sin6_addr), straddr, INET6_ADDRSTRLEN); assert_string_equal(nameservers[2], straddr); #else /* ! HAVE_RES_SOCKADDR_UNION_SIN6 */ /* * On platforms that don't support IPv6, the v6 address is skipped * and we end up reading three v4 addresses. */ assert_int_equal(set[2].sin.sin_family, AF_INET); assert_int_equal(set[2].sin.sin_port, htons(53)); inet_ntop(AF_INET, &(set[2].sin.sin_addr), straddr, INET6_ADDRSTRLEN); assert_string_equal(nameservers[3], straddr); #endif /* ! HAVE_RES_SOCKADDR_UNION_SIN6 */ #else /* ! HAVE_RES_SOCKADDR_UNION_SIN */ assert_int_equal(dnsstate.nscount, MAXNS); /* Validate the servers. */ /* IPv4 */ assert_int_equal(dnsstate.nsaddr_list[0].sin_family, AF_INET); assert_int_equal(dnsstate.nsaddr_list[0].sin_port, htons(53)); inet_ntop(AF_INET, &(dnsstate.nsaddr_list[0].sin_addr), straddr, INET6_ADDRSTRLEN); assert_string_equal(nameservers[0], straddr); #ifdef HAVE_RES_STATE_U_EXT_NSADDRS assert_null(dnsstate._u._ext.nsaddrs[0]); #endif assert_int_equal(dnsstate.nsaddr_list[1].sin_family, AF_INET); assert_int_equal(dnsstate.nsaddr_list[1].sin_port, htons(53)); inet_ntop(AF_INET, &(dnsstate.nsaddr_list[1].sin_addr), straddr, INET6_ADDRSTRLEN); assert_string_equal(nameservers[1], straddr); #ifdef HAVE_RES_STATE_U_EXT_NSADDRS assert_null(dnsstate._u._ext.nsaddrs[1]); #endif #ifdef HAVE_RES_STATE_U_EXT_NSADDRS /* IPv6 */ assert_non_null(dnsstate._u._ext.nsaddrs[2]); sa6 = dnsstate._u._ext.nsaddrs[2]; assert_int_equal(sa6->sin6_family, AF_INET6); assert_int_equal(sa6->sin6_port, htons(53)); inet_ntop(AF_INET6, &(sa6->sin6_addr), straddr, INET6_ADDRSTRLEN); assert_string_equal(nameservers[2], straddr); #else /* * On platforms that don't support IPv6, the v6 address is skipped * and we end up reading three v4 addresses. */ assert_int_equal(dnsstate.nsaddr_list[2].sin_family, AF_INET); assert_int_equal(dnsstate.nsaddr_list[2].sin_port, htons(53)); inet_ntop(AF_INET, &(dnsstate.nsaddr_list[2].sin_addr), straddr, INET6_ADDRSTRLEN); assert_string_equal(nameservers[3], straddr); #endif #endif /* ! HAVE_RES_SOCKADDR_UNION_SIN */ res_nclose(&dnsstate); } static void test_res_ninit_enoent(void **state) { int rv; struct __res_state dnsstate; (void) state; /* unused */ rv = setenv("RESOLV_WRAPPER_CONF", "/no/such/file", 1); assert_int_equal(rv, 0); /* Just make sure we don't crash, error is fine */ memset(&dnsstate, 0, sizeof(dnsstate)); rv = res_ninit(&dnsstate); unsetenv("RESOLV_WRAPPER_CONF"); assert_int_equal(rv, -1); } int main(void) { int rc; const struct CMUnitTest init_tests[] = { cmocka_unit_test_setup_teardown(test_res_ninit, setup, teardown), cmocka_unit_test(test_res_ninit_enoent), }; rc = cmocka_run_group_tests(init_tests, NULL, NULL); return rc; } resolv_wrapper-1.1.7/tests/fake_hosts.in000644 001750 000144 00000001342 13636077442 020406 0ustar00asnusers000000 000000 NS cwrap.org ns1.cwrap.org NS cwrap.org ns2.cwrap.org A brokenrecord.com A cwrap.org 127.0.0.21 AAAA cwrap6.org 2a00:1450:4013:c01::63 SRV _ldap._tcp.cwrap.org ldap.cwrap.org 389 1 5 SRV _krb5._tcp.cwrap.org krb5.cwrap.org 88 SOA cwrap.org ns1.cwrap.org admin.cwrap.org 2014100457 3600 300 1814400 600 CNAME rwrap.org web.cwrap.org CNAME web.cwrap.org www.cwrap.org A www.cwrap.org 127.0.0.22 A krb5.cwrap.org 127.0.0.23 A ns1.cwrap.org 127.0.0.24 A ns2.cwrap.org 127.0.0.25 URI _vpn.cwrap.org https://vpn.cwrap.org/VPN 2 5 URI _vpn.cwrap.org https://vpn2.cwrap.org/VPN 2 10 URI _vpn.cwrap.org https://vpn3.cwrap.org/VPN 2 20 URI _ftp.cwrap.org ftp://ftp.cwrap.org/public PTR 22.0.0.127.in-addr.arpa www.cwrap.org TXT cwrap.org v=spf1 mx resolv_wrapper-1.1.7/tests/CMakeLists.txt000644 001750 000144 00000007045 13636077442 020476 0ustar00asnusers000000 000000 project(tests C) set(TORTURE_LIBRARY torture) # A simple DNS server for testing add_executable(dns_srv dns_srv.c) target_compile_options(dns_srv PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) target_include_directories(dns_srv PRIVATE ${CMAKE_BINARY_DIR}) target_link_libraries(dns_srv ${RWRAP_REQUIRED_LIBRARIES}) add_executable(test_real_res_query test_real_res_query.c) target_compile_options(test_real_res_query PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) target_include_directories(test_real_res_query PRIVATE ${CMAKE_BINARY_DIR} ${CMOCKA_INCLUDE_DIR}) target_link_libraries(test_real_res_query ${RWRAP_REQUIRED_LIBRARIES} ${CMOCKA_LIBRARY}) configure_file(fake_hosts.in ${CMAKE_CURRENT_BINARY_DIR}/fake_hosts @ONLY) add_library(${TORTURE_LIBRARY} STATIC torture.c) target_compile_options(${TORTURE_LIBRARY} PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) target_include_directories(${TORTURE_LIBRARY} PRIVATE ${CMAKE_BINARY_DIR} ${CMOCKA_INCLUDE_DIR}) target_link_libraries(${TORTURE_LIBRARY} ${CMOCKA_LIBRARY} ${SWRAP_REQUIRED_LIBRARIES}) set(TESTSUITE_LIBRARIES ${RWRAP_REQUIRED_LIBRARIES} ${CMOCKA_LIBRARY}) # Some tests require socket_wrapper as well. find_package(socket_wrapper REQUIRED) set(RWRAP_TESTS test_res_init) if (HAVE_LIBRESOLV) set(RWRAP_TESTS ${RWRAP_TESTS} test_res_query_search) endif() function(ADD_CMOCKA_TEST_ENVIRONMENT _TEST_NAME) if (CMAKE_BUILD_TYPE) string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER) if (CMAKE_BUILD_TYPE_LOWER STREQUAL "addresssanitizer") find_library(ASAN_LIBRARY NAMES asan) if (NOT ASAN_LIBRARY) foreach(version RANGE 10 1) if (NOT ASAN_LIBRARY) find_library(ASAN_LIBRARY libasan.so.${version}) endif() endforeach() endif() endif() endif() if (ASAN_LIBRARY) list(APPEND PRELOAD_LIBRARIES ${ASAN_LIBRARY}) endif() list(APPEND PRELOAD_LIBRARIES ${RESOLV_WRAPPER_LOCATION}) list(APPEND PRELOAD_LIBRARIES ${SOCKET_WRAPPER_LIBRARY}) if (OSX) set(TORTURE_ENVIRONMENT "DYLD_FORCE_FLAT_NAMESPACE=1;DYLD_INSERT_LIBRARIES=${RESOLV_WRAPPER_LOCATION}:${SOCKET_WRAPPER_LIBRARY}") else () string(REPLACE ";" ":" _TMP_ENV "${PRELOAD_LIBRARIES}") set(TORTURE_ENVIRONMENT "LD_PRELOAD=${_TMP_ENV}") endif() list(APPEND TORTURE_ENVIRONMENT RESOLV_WRAPPER=1) foreach(_arg ${ARGN}) list(APPEND TORTURE_ENVIRONMENT ${_arg}) endforeach() set_property(TEST ${_TEST_NAME} PROPERTY ENVIRONMENT "${TORTURE_ENVIRONMENT}") endfunction() foreach(_RWRAP_TEST ${RWRAP_TESTS}) add_cmocka_test(${_RWRAP_TEST} SOURCES ${_RWRAP_TEST}.c COMPILE_OPTIONS ${DEFAULT_C_COMPILE_FLAGS} LINK_LIBRARIES ${TORTURE_LIBRARY} ${TESTSUITE_LIBRARIES} LINK_OPTIONS ${DEFAULT_LINK_FLAGS}) target_include_directories(${_RWRAP_TEST} PRIVATE ${CMAKE_BINARY_DIR} ${CMOCKA_INCLUDE_DIR}) add_cmocka_test_environment(${_RWRAP_TEST}) endforeach() add_cmocka_test(test_dns_fake SOURCES test_dns_fake.c COMPILE_OPTIONS ${DEFAULT_C_COMPILE_FLAGS} LINK_LIBRARIES ${TORTURE_LIBRARY} ${TESTSUITE_LIBRARIES} LINK_OPTIONS ${DEFAULT_LINK_FLAGS}) target_include_directories(test_dns_fake PRIVATE ${CMAKE_BINARY_DIR} ${CMOCKA_INCLUDE_DIR}) add_cmocka_test_environment(test_dns_fake RESOLV_WRAPPER_HOSTS=${CMAKE_CURRENT_BINARY_DIR}/fake_hosts) resolv_wrapper-1.1.7/tests/torture.c000644 001750 000144 00000017025 13636077462 017607 0ustar00asnusers000000 000000 /* * Copyright (C) Andreas Schneider 2013 * * 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 "torture.h" #include #include #include #include #include #include #include #include #include #include #include #include #define TORTURE_DNS_SRV_IPV4 "127.0.0.10" /* socket wrapper IPv6 prefix fd00::5357:5fxx */ #define TORTURE_DNS_SRV_IPV6 "fd00::5357:5f0a" #define TORTURE_DNS_SRV_PORT 53 #define TORTURE_SOCKET_DIR "/tmp/test_resolv_wrapper_XXXXXX" #define TORTURE_DNS_SRV_PIDFILE "dns_srv.pid" #define TORTURE_PCAP_FILE "socket_trace.pcap" #define RWRAP_RESOLV_CONF_TMPL "rwrap_resolv_conf_XXXXXX" const char *torture_server_address(int family) { switch (family) { case AF_INET: { const char *ip4 = getenv("TORTURE_SERVER_ADDRESS_IPV4"); if (ip4 != NULL && ip4[0] != '\0') { return ip4; } return TORTURE_DNS_SRV_IPV4; } #ifdef HAVE_IPV6 case AF_INET6: { const char *ip6 = getenv("TORTURE_SERVER_ADDRESS_IPV6"); if (ip6 != NULL && ip6[0] != '\0') { return ip6; } return TORTURE_DNS_SRV_IPV6; } #endif default: return NULL; } return NULL; } int torture_server_port(void) { char *env = getenv("TORTURE_SERVER_PORT"); if (env != NULL && env[0] != '\0' && strlen(env) < 6) { int port = atoi(env); if (port > 0 && port < 65536) { return port; } } return TORTURE_DNS_SRV_PORT; } void torture_setup_socket_dir(void **state) { struct torture_state *s; const char *p; size_t len; s = malloc(sizeof(struct torture_state)); assert_non_null(s); s->socket_dir = strdup(TORTURE_SOCKET_DIR); assert_non_null(s->socket_dir); p = mkdtemp(s->socket_dir); assert_non_null(p); /* pcap file */ len = strlen(p) + 1 + strlen(TORTURE_PCAP_FILE) + 1; s->pcap_file = malloc(len); assert_non_null(s->pcap_file); snprintf(s->pcap_file, len, "%s/%s", p, TORTURE_PCAP_FILE); /* pid file */ len = strlen(p) + 1 + strlen(TORTURE_DNS_SRV_PIDFILE) + 1; s->srv_pidfile = malloc(len); assert_non_null(s->srv_pidfile); snprintf(s->srv_pidfile, len, "%s/%s", p, TORTURE_DNS_SRV_PIDFILE); setenv("SOCKET_WRAPPER_DIR", p, 1); setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "170", 1); setenv("SOCKET_WRAPPER_PCAP_FILE", s->pcap_file, 1); *state = s; } const char *torture_server_resolv_conf(void **state) { struct torture_state *s = (struct torture_state *) *state; if (s == NULL) { return NULL; } return s->resolv_conf; } static char *torture_setup_resolv_conf(const char **nameservers, size_t num_ns) { char *path; int rc_fd; FILE *resolv_conf; size_t i; path = strdup(RWRAP_RESOLV_CONF_TMPL); assert_non_null(path); rc_fd = mkstemp(path); assert_return_code(rc_fd, errno); assert_non_null(path); resolv_conf = fdopen(rc_fd, "a"); assert_non_null(resolv_conf); for (i = 0; i < num_ns; i++) { fputs("nameserver ", resolv_conf); fputs(nameservers[i], resolv_conf); fputs("\n", resolv_conf); } fflush(resolv_conf); fclose(resolv_conf); close(rc_fd); return path; } static void torture_teardown_resolv_conf(char *resolv_conf_path) { unlink(resolv_conf_path); free(resolv_conf_path); } static void torture_setup_dns_srv_ip(void **state, int family, const char *ip, int port) { struct torture_state *s; char start_dns_srv[1024] = {0}; int count = 0; int rc; const char *nameservers[1] = { torture_server_address(family), }; torture_setup_socket_dir(state); s = (struct torture_state *) *state; /* set default iface for the server */ setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "10", 1); snprintf(start_dns_srv, sizeof(start_dns_srv), "%s/tests/dns_srv -b %s -p %d -D --pid %s", BINARYDIR, ip, port, s->srv_pidfile); rc = system(start_dns_srv); assert_int_equal(rc, 0); do { struct stat sb; count++; if (count > 100) { break; } rc = stat(s->srv_pidfile, &sb); /* Wait 200 ms before retrying */ usleep(200 * 1000); } while (rc != 0); assert_int_equal(rc, 0); /* set default iface for the client */ setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "170", 1); /* write a resolv.conf for the client */ s->resolv_conf = torture_setup_resolv_conf(nameservers, 1); } void torture_setup_dns_srv_ipv4(void **state) { torture_setup_dns_srv_ip(state, AF_INET, "0.0.0.0", torture_server_port()); } void torture_setup_dns_srv_ipv6(void **state) { torture_setup_dns_srv_ip(state, AF_INET6, "::", torture_server_port()); } void torture_teardown_socket_dir(void **state) { struct torture_state *s = (struct torture_state *) *state; char *env = getenv("TORTURE_SKIP_CLEANUP"); char remove_cmd[1024] = {0}; int rc; if (env != NULL && env[0] == '1') { fprintf(stderr, ">>> Skipping cleanup of %s", s->socket_dir); } else { snprintf(remove_cmd, sizeof(remove_cmd), "rm -rf %s", s->socket_dir); rc = system(remove_cmd); if (rc < 0) { fprintf(stderr, "%s failed: %s", remove_cmd, strerror(errno)); } } free(s->socket_dir); free(s->pcap_file); free(s->srv_pidfile); free(s); } void torture_teardown_dns_srv(void **state) { struct torture_state *s = (struct torture_state *) *state; char buf[8] = {0}; long int tmp; ssize_t rc; pid_t pid; int fd; bool is_running = true; int count; /* read the pidfile */ fd = open(s->srv_pidfile, O_RDONLY); if (fd < 0) { goto done; } rc = read(fd, buf, sizeof(buf)); close(fd); if (rc <= 0) { goto done; } buf[sizeof(buf) - 1] = '\0'; tmp = strtol(buf, NULL, 10); if (tmp == 0 || tmp > 0xFFFF || errno == ERANGE) { goto done; } pid = (pid_t)(tmp & 0xFFFF); /* Make sure the daemon goes away! */ for (count = 0; count < 10; count++) { kill(pid, SIGTERM); usleep(200); rc = kill(pid, 0); if (rc != 0) { is_running = false; break; } } if (is_running) { fprintf(stderr, "WARNING the DNS server is still running!\n"); } done: torture_teardown_resolv_conf(s->resolv_conf); torture_teardown_socket_dir(state); } void torture_generate_random_buffer(uint8_t *out, int len) { int i; srand(time(NULL)); for (i = 0; i < len; i++) { out[i] = (uint8_t)rand(); } } resolv_wrapper-1.1.7/tests/test_dns_fake.c000644 001750 000144 00000054266 13636077442 020722 0ustar00asnusers000000 000000 /* * Copyright (C) Jakub Hrozek 2014 * * 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 #include #include #include #include "config.h" #include #include #include #include #include #include #include #include #define ANSIZE 256 #define ns_t_uri 256 static void test_res_fake_a_query(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE]; char addr[INET_ADDRSTRLEN]; ns_msg handle; ns_rr rr; /* expanded resource record */ (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); rv = res_nquery(&dnsstate, "cwrap.org", ns_c_in, ns_t_a, answer, sizeof(answer)); assert_in_range(rv, 1, 100); ns_initparse(answer, sizeof(answer), &handle); /* The query must finish w/o an error, have one answer and the answer * must be a parseable RR of type A and have the address that our * fake hosts file contains */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 1); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_a); assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr), addr, sizeof(addr))); assert_string_equal(addr, "127.0.0.21"); } static void test_res_fake_a_query_case_insensitive(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE]; char addr[INET_ADDRSTRLEN]; ns_msg handle; ns_rr rr; /* expanded resource record */ (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); rv = res_nquery(&dnsstate, "CWRAP.ORG", ns_c_in, ns_t_a, answer, sizeof(answer)); assert_in_range(rv, 1, 100); ns_initparse(answer, sizeof(answer), &handle); /* The query must finish w/o an error, have one answer and the answer * must be a parseable RR of type A and have the address that our * fake hosts file contains. Case does not matter. */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 1); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_a); assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr), addr, sizeof(addr))); assert_string_equal(addr, "127.0.0.21"); res_nclose(&dnsstate); } static void test_res_fake_a_query_trailing_dot(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE]; char addr[INET_ADDRSTRLEN]; ns_msg handle; ns_rr rr; /* expanded resource record */ (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); rv = res_nquery(&dnsstate, "cwrap.org.", ns_c_in, ns_t_a, answer, ANSIZE); assert_in_range(rv, 1, 100); ns_initparse(answer, sizeof(answer), &handle); /* The query must finish w/o an error, have one answer and the answer * must be a parseable RR of type A and have the address that our * fake hosts file contains */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 1); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_a); assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr), addr, sizeof(addr))); assert_string_equal(addr, "127.0.0.21"); res_nclose(&dnsstate); } static void test_res_fake_a_query_notfound(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE]; ns_msg handle; (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); rv = res_nquery(&dnsstate, "nosuchentry.org", ns_c_in, ns_t_a, answer, sizeof(answer)); assert_in_range(rv, 1, 100); ns_initparse(answer, sizeof(answer), &handle); /* The query must finish w/o an error and have no answer */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 0); } static void test_res_fake_aaaa_query(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE]; char addr[INET6_ADDRSTRLEN]; ns_msg handle; ns_rr rr; /* expanded resource record */ (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); rv = res_nquery(&dnsstate, "cwrap6.org", ns_c_in, ns_t_aaaa, answer, sizeof(answer)); assert_in_range(rv, 1, 100); ns_initparse(answer, sizeof(answer), &handle); /* The query must finish w/o an error, have one answer and the answer * must be a parseable RR of type AAAA and have the address that our * fake hosts file contains */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 1); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_aaaa); assert_non_null(inet_ntop(AF_INET6, ns_rr_rdata(rr), addr, sizeof(addr))); assert_string_equal(addr, "2a00:1450:4013:c01::63"); } static void test_res_fake_aaaa_query_notfound(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE]; ns_msg handle; (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); rv = res_nquery(&dnsstate, "nosuchentry.org", ns_c_in, ns_t_aaaa, answer, sizeof(answer)); assert_in_range(rv, 1, 100); ns_initparse(answer, sizeof(answer), &handle); /* The query must finish w/o an error and have no answer */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 0); } static void test_res_fake_srv_query(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE]; ns_msg handle; ns_rr rr; /* expanded resource record */ const uint8_t *rrdata; int prio; int weight; int port; char hostname[MAXDNAME]; (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); rv = res_nquery(&dnsstate, "_ldap._tcp.cwrap.org", ns_c_in, ns_t_srv, answer, sizeof(answer)); assert_in_range(rv, 1, 100); ns_initparse(answer, sizeof(answer), &handle); /* * The query must finish w/o an error, have one answer and the answer * must be a parseable RR of type SRV and have the priority, weight, * port and hostname as in the fake hosts file */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 1); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_srv); rrdata = ns_rr_rdata(rr); NS_GET16(prio, rrdata); NS_GET16(weight, rrdata); NS_GET16(port, rrdata); rv = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), rrdata, hostname, MAXDNAME); assert_int_not_equal(rv, -1); assert_int_equal(prio, 1); assert_int_equal(weight, 5); assert_int_equal(port, 389); assert_string_equal(hostname, "ldap.cwrap.org"); } /* * Test the case of a SRV record query where the * fake hosts file entry is minimal in the sense * that it omits the priority and weight entries. * The server then fills in some default values. */ static void test_res_fake_srv_query_minimal(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE]; ns_msg handle; ns_rr rr; /* expanded resource record */ const uint8_t *rrdata; int prio; int weight; int port; char hostname[MAXDNAME]; char addr[INET_ADDRSTRLEN]; (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); rv = res_nquery(&dnsstate, "_krb5._tcp.cwrap.org", ns_c_in, ns_t_srv, answer, sizeof(answer)); assert_in_range(rv, 1, 256); ns_initparse(answer, sizeof(answer), &handle); /* * The query must finish w/o an error, have one answer and the answer * must be a parseable RR of type SRV and have the priority, weight, * port and hostname as in the fake hosts file */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 1); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_srv); rrdata = ns_rr_rdata(rr); NS_GET16(prio, rrdata); NS_GET16(weight, rrdata); NS_GET16(port, rrdata); rv = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), rrdata, hostname, MAXDNAME); assert_int_not_equal(rv, -1); assert_int_equal(prio, 1); assert_int_equal(weight, 100); assert_int_equal(port, 88); assert_string_equal(hostname, "krb5.cwrap.org"); /* The additional section contains the A record of krb5.cwrap.org */ assert_int_equal(ns_msg_count(handle, ns_s_ar), 1); assert_int_equal(ns_parserr(&handle, ns_s_ar, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_a); assert_string_equal(ns_rr_name(rr), "krb5.cwrap.org"); assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr), addr, sizeof(addr))); assert_string_equal(addr, "127.0.0.23"); } static void test_res_fake_uri_query(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE]; ns_msg handle; ns_rr rr; /* expanded resource record */ const uint8_t *rrdata; int prio; int weight; (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); rv = res_nquery(&dnsstate, "_vpn.cwrap.org", ns_c_in, ns_t_uri, answer, sizeof(answer)); assert_in_range(rv, 1, ANSIZE); ns_initparse(answer, sizeof(answer), &handle); /* * The query must finish w/o an error, have three answers and they must be * a parseable RR of type URI and have the priority, weight, and URI string * as in the hosts file. */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 3); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_uri); rrdata = ns_rr_rdata(rr); NS_GET16(prio, rrdata); NS_GET16(weight, rrdata); assert_int_equal(prio, 2); assert_int_equal(weight, 5); assert_string_equal(rrdata, "https://vpn.cwrap.org/VPN"); assert_int_equal(ns_parserr(&handle, ns_s_an, 1, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_uri); rrdata = ns_rr_rdata(rr); NS_GET16(prio, rrdata); NS_GET16(weight, rrdata); assert_int_equal(prio, 2); assert_int_equal(weight, 10); assert_string_equal(rrdata, "https://vpn2.cwrap.org/VPN"); assert_int_equal(ns_parserr(&handle, ns_s_an, 2, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_uri); rrdata = ns_rr_rdata(rr); NS_GET16(prio, rrdata); NS_GET16(weight, rrdata); assert_int_equal(prio, 2); assert_int_equal(weight, 20); assert_string_equal(rrdata, "https://vpn3.cwrap.org/VPN"); } /* * Test the case of a URI record query where the * fake hosts file entry is minimal in the sense * that it omits the priority and weight entries. * The server then fills in some default values. */ static void test_res_fake_uri_query_minimal(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE]; ns_msg handle; ns_rr rr; /* expanded resource record */ const uint8_t *rrdata; int prio; int weight; (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); rv = res_nquery(&dnsstate, "_ftp.cwrap.org", ns_c_in, ns_t_uri, answer, sizeof(answer)); assert_in_range(rv, 1, 256); ns_initparse(answer, sizeof(answer), &handle); /* * The query must finish w/o an error, have one answer and the answer * must be a parseable RR of type URI and have the priority, weight, and * URI string as in the fake hosts file */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 1); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_uri); rrdata = ns_rr_rdata(rr); NS_GET16(prio, rrdata); NS_GET16(weight, rrdata); assert_int_equal(prio, 1); assert_int_equal(weight, 100); assert_string_equal(rrdata, "ftp://ftp.cwrap.org/public"); } static void test_res_fake_soa_query(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE]; ns_msg handle; ns_rr rr; /* expanded resource record */ const uint8_t *rrdata; char nameser[MAXDNAME]; char admin[MAXDNAME]; int serial; int refresh; int retry; int expire; int minimum; (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); rv = res_nquery(&dnsstate, "cwrap.org", ns_c_in, ns_t_soa, answer, sizeof(answer)); assert_in_range(rv, 1, 100); ns_initparse(answer, sizeof(answer), &handle); /* * The query must finish w/o an error, have one answer and the answer * must be a parseable RR of type SOA and have the data as in the fake * hosts file */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 1); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_soa); rrdata = ns_rr_rdata(rr); rv = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), rrdata, nameser, MAXDNAME); assert_int_not_equal(rv, -1); rrdata += rv; rv = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), rrdata, admin, MAXDNAME); assert_int_not_equal(rv, -1); rrdata += rv; NS_GET32(serial, rrdata); NS_GET32(refresh, rrdata); NS_GET32(retry, rrdata); NS_GET32(expire, rrdata); NS_GET32(minimum, rrdata); assert_string_equal(nameser, "ns1.cwrap.org"); assert_string_equal(admin, "admin.cwrap.org"); assert_int_equal(serial, 2014100457); assert_int_equal(refresh, 3600); assert_int_equal(retry, 300); assert_int_equal(expire, 1814400); assert_int_equal(minimum, 600); } static void test_res_fake_cname_query(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE]; ns_msg handle; ns_rr rr; /* expanded resource record */ const uint8_t *rrdata; char cname[MAXDNAME]; char addr[INET_ADDRSTRLEN]; (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); rv = res_nquery(&dnsstate, "rwrap.org", ns_c_in, ns_t_cname, answer, sizeof(answer)); assert_in_range(rv, 1, 256); ns_initparse(answer, 256, &handle); ns_initparse(answer, sizeof(answer), &handle); /* * The query must finish w/o an error, have one answer and the answer * must be a parseable RR of type CNAME and have the cname as in the * fake hosts file */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 1); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_cname); rrdata = ns_rr_rdata(rr); rv = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), rrdata, cname, MAXDNAME); assert_int_not_equal(rv, -1); assert_string_equal(cname, "web.cwrap.org"); /* The CNAME points to an A record that's present in the additional * section */ assert_int_equal(ns_msg_count(handle, ns_s_ar), 2); assert_int_equal(ns_parserr(&handle, ns_s_ar, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_cname); assert_string_equal(ns_rr_name(rr), "web.cwrap.org"); rrdata = ns_rr_rdata(rr); rv = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), rrdata, cname, MAXDNAME); assert_int_not_equal(rv, -1); assert_string_equal(cname, "www.cwrap.org"); assert_int_equal(ns_parserr(&handle, ns_s_ar, 1, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_a); assert_string_equal(ns_rr_name(rr), "www.cwrap.org"); assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr), addr, sizeof(addr))); assert_string_equal(addr, "127.0.0.22"); } static void test_res_fake_a_via_cname(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE]; ns_msg handle; ns_rr rr; /* expanded resource record */ const uint8_t *rrdata; char cname[MAXDNAME]; char addr[INET_ADDRSTRLEN]; (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); /* Query for A record, but the key is a CNAME. The expected result is * that the whole chain of CNAMEs will be included in the answer section * along with the resulting A */ rv = res_nquery(&dnsstate, "rwrap.org", ns_c_in, ns_t_a, answer, sizeof(answer)); assert_in_range(rv, 1, 256); ns_initparse(answer, sizeof(answer), &handle); /* * The query must finish w/o an error, have three answers and the answers * must be a parseable RR of type CNAME and have the cname as in the * fake hosts file */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 3); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_cname); rrdata = ns_rr_rdata(rr); rv = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), rrdata, cname, MAXDNAME); assert_int_not_equal(rv, -1); assert_string_equal(cname, "web.cwrap.org"); assert_int_equal(ns_parserr(&handle, ns_s_an, 1, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_cname); rrdata = ns_rr_rdata(rr); rv = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), rrdata, cname, MAXDNAME); assert_int_not_equal(rv, -1); assert_string_equal(cname, "www.cwrap.org"); assert_int_equal(ns_parserr(&handle, ns_s_an, 2, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_a); assert_string_equal(ns_rr_name(rr), "www.cwrap.org"); assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr), addr, sizeof(addr))); assert_string_equal(addr, "127.0.0.22"); } static void test_res_fake_ptr_query(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE]; const uint8_t *rrdata; char ptrname[MAXDNAME]; ns_msg handle; ns_rr rr; /* expanded resource record */ (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); rv = res_nquery(&dnsstate, "22.0.0.127.in-addr.arpa", ns_c_in, ns_t_ptr, answer, sizeof(answer)); assert_in_range(rv, 1, 100); ns_initparse(answer, sizeof(answer), &handle); /* * The query must finish w/o an error, have one answer and the answer * must be a parseable RR of type PTR and have the name that our * fake hosts file contains */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 1); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_ptr); rrdata = ns_rr_rdata(rr); rv = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), rrdata, ptrname, MAXDNAME); assert_int_not_equal(rv, -1); assert_string_equal(ptrname, "www.cwrap.org"); } static void test_res_fake_txt_query(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE]; ns_msg handle; ns_rr rr; /* expanded resource record */ const uint8_t *rrdata; (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); rv = res_nquery(&dnsstate, "cwrap.org", ns_c_in, ns_t_txt, answer, sizeof(answer)); assert_in_range(rv, 1, 256); ns_initparse(answer, sizeof(answer), &handle); /* * The query must finish w/o an error, have one answer and the answer * must be a parseable RR of type TXT */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 1); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_txt); rrdata = ns_rr_rdata(rr); assert_string_equal(rrdata, "v=spf1 mx"); } int main(void) { int rc; const struct CMUnitTest fake_tests[] = { cmocka_unit_test(test_res_fake_a_query), cmocka_unit_test(test_res_fake_a_query_case_insensitive), cmocka_unit_test(test_res_fake_a_query_trailing_dot), cmocka_unit_test(test_res_fake_a_query_notfound), cmocka_unit_test(test_res_fake_aaaa_query), cmocka_unit_test(test_res_fake_aaaa_query_notfound), cmocka_unit_test(test_res_fake_srv_query), cmocka_unit_test(test_res_fake_srv_query_minimal), cmocka_unit_test(test_res_fake_uri_query), cmocka_unit_test(test_res_fake_uri_query_minimal), cmocka_unit_test(test_res_fake_soa_query), cmocka_unit_test(test_res_fake_cname_query), cmocka_unit_test(test_res_fake_a_via_cname), cmocka_unit_test(test_res_fake_ptr_query), cmocka_unit_test(test_res_fake_txt_query), }; rc = cmocka_run_group_tests(fake_tests, NULL, NULL); return rc; } resolv_wrapper-1.1.7/tests/test_res_query_search.c000644 001750 000144 00000014642 13552354002 022470 0ustar00asnusers000000 000000 /* * Copyright (C) Jakub Hrozek 2014 * * 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 #include #include #include #include "config.h" #include "torture.h" #include #include #include #include #include #include #include #include #define ANSIZE 256 static int setup_dns_srv_ipv4(void **state) { torture_setup_dns_srv_ipv4(state); setenv("RESOLV_WRAPPER_CONF", torture_server_resolv_conf(state), 1); return 0; } static int teardown(void **state) { torture_teardown_dns_srv(state); return 0; } static void test_res_nquery(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE]; char addr[INET_ADDRSTRLEN]; ns_msg handle; ns_rr rr; /* expanded resource record */ (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); rv = res_nquery(&dnsstate, "www.cwrap.org", ns_c_in, ns_t_a, answer, sizeof(answer)); assert_int_not_equal(rv, -1); ns_initparse(answer, sizeof(answer), &handle); /* * The query must finish w/o an error, have one answer and the answer * must be a parseable RR of type A and have the address that our * test server sends. */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 1); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_a); assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr), addr, sizeof(addr))); assert_string_equal(addr, "127.0.10.10"); res_nclose(&dnsstate); } static void test_res_query(void **state) { int rv; unsigned char answer[ANSIZE]; char addr[INET_ADDRSTRLEN]; ns_msg handle; ns_rr rr; /* expanded resource record */ (void) state; /* unused */ rv = res_query("www.cwrap.org", ns_c_in, ns_t_a, answer, sizeof(answer)); assert_int_not_equal(rv, -1); ns_initparse(answer, sizeof(answer), &handle); /* * The query must finish w/o an error, have one answer and the answer * must be a parseable RR of type A and have the address that our * test server sends. */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 1); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_a); assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr), addr, sizeof(addr))); assert_string_equal(addr, "127.0.10.10"); res_close(); } static void test_res_nsearch(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE]; char addr[INET_ADDRSTRLEN]; ns_msg handle; ns_rr rr; /* expanded resource record */ (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); rv = res_nsearch(&dnsstate, "www.cwrap.org", ns_c_in, ns_t_a, answer, sizeof(answer)); assert_int_not_equal(rv, -1); ns_initparse(answer, sizeof(answer), &handle); /* The query must finish w/o an error, have one answer and the answer * must be a parseable RR of type A and have the address that our * test server sends */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 1); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_a); assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr), addr, sizeof(addr))); assert_string_equal(addr, "127.0.10.10"); res_nclose(&dnsstate); } static void test_res_search(void **state) { int rv; unsigned char answer[ANSIZE]; char addr[INET_ADDRSTRLEN]; ns_msg handle; ns_rr rr; /* expanded resource record */ (void) state; /* unused */ rv = res_search("www.cwrap.org", ns_c_in, ns_t_a, answer, sizeof(answer)); assert_int_not_equal(rv, -1); ns_initparse(answer, sizeof(answer), &handle); /* The query must finish w/o an error, have one answer and the answer * must be a parseable RR of type A and have the address that our * test server sends */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 1); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_a); assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr), addr, sizeof(addr))); assert_string_equal(addr, "127.0.10.10"); res_close(); } int main(void) { int rc; const struct CMUnitTest res_tests[] = { cmocka_unit_test_setup_teardown(test_res_nquery, setup_dns_srv_ipv4, teardown), cmocka_unit_test_setup_teardown(test_res_query, setup_dns_srv_ipv4, teardown), cmocka_unit_test_setup_teardown(test_res_nsearch, setup_dns_srv_ipv4, teardown), cmocka_unit_test_setup_teardown(test_res_search, setup_dns_srv_ipv4, teardown), }; rc = cmocka_run_group_tests(res_tests, NULL, NULL); return rc; } resolv_wrapper-1.1.7/tests/torture.h000644 001750 000144 00000005211 12421451254 017570 0ustar00asnusers000000 000000 /* * Copyright (C) Andreas Schneider 2013 * * 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. */ #ifndef _TORTURE_H #define _TORTURE_H #include "config.h" #include #include #include #include #include #include #include #include #include struct torture_address { socklen_t sa_socklen; union { struct sockaddr s; struct sockaddr_in in; #ifdef HAVE_IPV6 struct sockaddr_in6 in6; #endif struct sockaddr_un un; struct sockaddr_storage ss; } sa; }; struct torture_state { char *socket_dir; char *pcap_file; char *srv_pidfile; char *resolv_conf; }; #ifndef ZERO_STRUCT #define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x)) #endif const char *torture_server_address(int domain); int torture_server_port(void); const char *torture_server_resolv_conf(void **state); void torture_setup_socket_dir(void **state); void torture_setup_dns_srv_ipv4(void **state); void torture_setup_dns_srv_ipv6(void **state); void torture_teardown_socket_dir(void **state); void torture_teardown_dns_srv(void **state); void torture_generate_random_buffer(uint8_t *out, int len); #endif /* _TORTURE_H */ resolv_wrapper-1.1.7/tests/dns_srv.c000644 001750 000144 00000032513 13636077442 017556 0ustar00asnusers000000 000000 /* * Copyright (C) Jakub Hrozek 2014 * * 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 #include #include #include #include #include #ifndef PIDFILE #define PIDFILE "dns_srv.pid" #endif /* PIDFILE */ #define DNS_PORT 53 #define DFL_TTL 30 #ifndef BUFSIZE #define BUFSIZE 1024 #endif /* BUFSIZE */ #ifndef discard_const #define discard_const(ptr) ((void *)((uintptr_t)(ptr))) #endif #ifndef discard_const_p #define discard_const_p(type, ptr) ((type *)discard_const(ptr)) #endif #ifndef ZERO_STRUCT #define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x)) #endif /* The macros below are taken from c-ares */ #define DNS__16BIT(p) ((unsigned short)((unsigned int) 0xffff & \ (((unsigned int)((unsigned char)(p)[0]) << 8U) | \ ((unsigned int)((unsigned char)(p)[1]))))) #define DNS__SET16BIT(p, v) (((p)[0] = (unsigned char)(((v) >> 8) & 0xff)), \ ((p)[1] = (unsigned char)((v) & 0xff))) #define DNS__SET32BIT(p, v) (((p)[0] = (unsigned char)(((v) >> 24) & 0xff)), \ ((p)[1] = (unsigned char)(((v) >> 16) & 0xff)), \ ((p)[2] = (unsigned char)(((v) >> 8) & 0xff)), \ ((p)[3] = (unsigned char)((v) & 0xff))); /* Macros for parsing a DNS header */ #define DNS_HEADER_QID(h) DNS__16BIT(h) #define DNS_HEADER_OPCODE(h) (((h)[2] >> 3) & 0xf) #define DNS_HEADER_TC(h) (((h)[2] >> 1) & 0x1) #define DNS_HEADER_QDCOUNT(h) DNS__16BIT((h) + 4) /* Macros for parsing the fixed part of a DNS question */ #define DNS_QUESTION_TYPE(q) DNS__16BIT(q) #define DNS_QUESTION_CLASS(q) DNS__16BIT((q) + 2) /* Macros for constructing a DNS header */ #define DNS_HEADER_SET_QID(h, v) DNS__SET16BIT(h, v) #define DNS_HEADER_SET_QR(h, v) ((h)[2] |= (unsigned char)(((v) & 0x1) << 7)) #define DNS_HEADER_SET_RD(h, v) ((h)[2] |= (unsigned char)((v) & 0x1)) #define DNS_HEADER_SET_RA(h, v) ((h)[3] |= (unsigned char)(((v) & 0x1) << 7)) #define DNS_HEADER_SET_QDCOUNT(h, v) DNS__SET16BIT((h) + 4, v) #define DNS_HEADER_SET_ANCOUNT(h, v) DNS__SET16BIT((h) + 6, v) /* Macros for constructing the fixed part of a DNS question */ #define DNS_QUESTION_SET_TYPE(q, v) DNS__SET16BIT(q, v) #define DNS_QUESTION_SET_CLASS(q, v) DNS__SET16BIT((q) + 2, v) /* Macros for constructing the fixed part of a DNS resource record */ #define DNS_RR_SET_TYPE(r, v) DNS__SET16BIT(r, v) #define DNS_RR_SET_CLASS(r, v) DNS__SET16BIT((r) + 2, v) #define DNS_RR_SET_TTL(r, v) DNS__SET32BIT((r) + 4, v) #define DNS_RR_SET_LEN(r, v) DNS__SET16BIT((r) + 8, v) #define DEFAULT_A_REC "127.0.10.10" struct dns_srv_opts { char *bind; bool daemon; int port; const char *pidfile; }; struct dns_query { char *query; uint16_t id; uint16_t qtype; uint16_t qclass; unsigned char *reply; size_t reply_len; }; static void free_dns_query(struct dns_query *query) { free(query->query); free(query->reply); memset(query, 0, sizeof(struct dns_query)); } static size_t encode_name(unsigned char *buffer, const char *name) { const char *p, *dot; unsigned char *bp; size_t len; p = name; bp = buffer; len = 0; while ((dot = strchr(p, '.')) != NULL) { *bp++ = dot - p; len++; while (p < dot) { *bp++ = *p++; len++; } p = dot + 1; /* move past the dot */ } *bp = '\0'; len++; return len; } static void fake_header(struct dns_query *query) { DNS_HEADER_SET_QID(query->reply, query->id); DNS_HEADER_SET_QR(query->reply, 1); DNS_HEADER_SET_RD(query->reply, 1); DNS_HEADER_SET_RA(query->reply, 1); DNS_HEADER_SET_QDCOUNT(query->reply, 1); DNS_HEADER_SET_ANCOUNT(query->reply, 1); } static size_t fake_question(struct dns_query *query, unsigned char **pout) { unsigned char *p; size_t len; p = *pout; len = encode_name(p, query->query); p += len; DNS_QUESTION_SET_TYPE(p, query->qtype); len += sizeof(uint16_t); DNS_QUESTION_SET_CLASS(p, query->qclass); len += sizeof(uint16_t); p += 2 * sizeof(uint16_t); *pout = p; return len; } static size_t fake_answer(struct dns_query *query, unsigned char **pout) { unsigned char *p; size_t len; size_t rlen; char *val; struct in_addr a_rec; p = *pout; len = encode_name(p, query->query); p += len; DNS_RR_SET_TYPE(p, query->qtype); len += sizeof(uint16_t); DNS_RR_SET_CLASS(p, query->qclass); len += sizeof(uint16_t); DNS_RR_SET_TTL(p, DFL_TTL); len += sizeof(uint32_t); switch (query->qtype) { case ns_t_a: val = getenv("RWRAP_TEST_A_REC"); inet_pton(AF_INET, val ? val : DEFAULT_A_REC, &a_rec); rlen = sizeof(struct in_addr); break; default: /* Unhandled record */ return -1; } DNS_RR_SET_LEN(p, rlen); len += sizeof(uint16_t); /* Move to the RDATA section */ p += sizeof(uint16_t) + /* type */ sizeof(uint16_t) + /* class */ sizeof(uint32_t) + /* ttl */ sizeof(uint16_t); /* rlen */ /* Copy RDATA */ memcpy(p, &a_rec, sizeof(struct in_addr)); len += rlen; *pout = p; return len; } static int fake_reply(struct dns_query *query) { unsigned char *p; query->reply = malloc(BUFSIZE); if (query->reply == NULL) { return ENOMEM; } memset(query->reply, 0, BUFSIZE); p = query->reply; fake_header(query); query->reply_len = NS_HFIXEDSZ; p += NS_HFIXEDSZ; /* advances p internally */ query->reply_len += fake_question(query, &p); query->reply_len += fake_answer(query, &p); return 0; } static char *extract_name(char **buffer, size_t maxlen) { char *query, *qp, *bp; unsigned int len; unsigned int i; query = malloc(maxlen); if (query == NULL) return NULL; i = 0; qp = query; bp = *buffer; do { len = *bp; bp++; if (len > (maxlen - (qp - query))) { /* label is past the buffer */ free(query); return NULL; } for (i = 0; i < len; i++) { *qp++ = *bp++; } if (len > 0) { *qp++ = '.'; } else { *qp = '\0'; } } while (len > 0); *buffer = bp; return query; } static int parse_query(unsigned char *buffer, size_t len, struct dns_query *query) { unsigned char *p; p = buffer; if (len < NS_HFIXEDSZ) { /* Message too short */ return EBADMSG; } if (DNS_HEADER_OPCODE(p) != 0) { /* Queries must have the opcode set to 0 */ return EBADMSG; } if (DNS_HEADER_QDCOUNT(p) != 1) { /* We only support one query */ return EBADMSG; } if (len < NS_HFIXEDSZ + 2 * sizeof(uint16_t)) { /* No room for class and type */ return EBADMSG; } /* Need to remember the query to respond with the same */ query->id = DNS_HEADER_QID(p); /* Done with the header, move past it */ p += NS_HFIXEDSZ; query->query = extract_name((char **) &p, len - NS_HFIXEDSZ); if (query->query == NULL) { return EIO; } query->qclass = DNS_QUESTION_CLASS(p); if (query->qclass != ns_c_in) { /* We only support Internet queries */ return EBADMSG; } query->qtype = DNS_QUESTION_TYPE(p); return 0; } static void dns(int sock) { struct sockaddr_storage css; socklen_t addrlen = sizeof(css); ssize_t bret; unsigned char buf[BUFSIZE]; struct dns_query query; int rv; ZERO_STRUCT(query); while (1) { free_dns_query(&query); /* for advanced features, use recvmsg here */ ZERO_STRUCT(buf); bret = recvfrom(sock, buf, BUFSIZE, 0, (struct sockaddr *) &css, &addrlen); if (bret == -1) { perror("recvfrom"); continue; } /* parse query */ rv = parse_query(buf, bret, &query); if (rv != 0) { continue; } /* Construct the reply */ rv = fake_reply(&query); if (rv != 0) { continue; } /* send reply back */ bret = sendto(sock, query.reply, query.reply_len, 0, (struct sockaddr *) &css, addrlen); if (bret == -1) { perror("sendto"); continue; } } } static int pidfile(const char *path) { int err; int fd; char pid_str[32] = { 0 }; ssize_t nwritten; size_t len; fd = open(path, O_RDONLY, 0644); err = errno; if (fd != -1) { close(fd); return EEXIST; } else if (err != ENOENT) { return err; } fd = open(path, O_CREAT | O_WRONLY | O_EXCL, 0644); err = errno; if (fd == -1) { return err; } snprintf(pid_str, sizeof(pid_str) -1, "%u\n", (unsigned int) getpid()); len = strlen(pid_str); nwritten = write(fd, pid_str, len); close(fd); if (nwritten != (ssize_t)len) { return EIO; } return 0; } static int become_daemon(void) { int ret; pid_t child_pid; int fd; int i; if (getppid() == 1) { return 0; } child_pid = fork(); if (child_pid == -1) { ret = errno; perror("fork"); return ret; } else if (child_pid > 0) { exit(0); } /* If a working directory was defined, go there */ #ifdef WORKING_DIR chdir(WORKING_DIR); #endif ret = setsid(); if (ret == -1) { ret = errno; perror("setsid"); return ret; } for (fd = getdtablesize(); fd >= 0; --fd) { close(fd); } for (i = 0; i < 3; i++) { fd = open("/dev/null", O_RDWR, 0); if (fd < 0) { fd = open("/dev/null", O_WRONLY, 0); } if (fd < 0) { ret = errno; perror("Can't open /dev/null"); return ret; } if (fd != i) { perror("Didn't get correct fd"); close(fd); return EINVAL; } } umask(0177); return 0; } /* * Returns 0 on success, errno on failure. * If successful, sock is a ready to use socket. */ static int setup_srv(struct dns_srv_opts *opts, int *_sock) { struct addrinfo hints; struct addrinfo *res, *ri; char svc[6]; int ret; int sock; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_PASSIVE; snprintf(svc, sizeof(svc), "%d", opts->port); ret = getaddrinfo(opts->bind, svc, &hints, &res); if (ret != 0) { return errno; } for (ri = res; ri != NULL; ri = ri->ai_next) { sock = socket(ri->ai_family, ri->ai_socktype, ri->ai_protocol); if (sock == -1) { ret = errno; freeaddrinfo(res); perror("socket"); return ret; } ret = bind(sock, ri->ai_addr, ri->ai_addrlen); if (ret == 0) { break; } close(sock); } freeaddrinfo(res); if (ri == NULL) { fprintf(stderr, "Could not bind\n"); return EFAULT; } *_sock = sock; return 0; } int main(int argc, char **argv) { int ret; int sock = -1; struct dns_srv_opts opts; int opt; int optindex; static struct option long_options[] = { { discard_const_p(char, "bind-addr"), required_argument, 0, 'b' }, { discard_const_p(char, "daemon"), no_argument, 0, 'D' }, { discard_const_p(char, "port"), required_argument, 0, 'p' }, { discard_const_p(char, "pid"), required_argument, 0, 0 }, { 0, 0, 0, 0 } }; opts.bind = NULL; opts.pidfile = PIDFILE; opts.daemon = false; opts.port = DNS_PORT; while ((opt = getopt_long(argc, argv, "Db:p:", long_options, &optindex)) != -1) { switch (opt) { case 0: if (optindex == 3) { opts.pidfile = optarg; } break; case 'b': opts.bind = optarg; break; case 'D': opts.daemon = true; break; case 'p': opts.port = atoi(optarg); break; default: /* '?' */ fprintf(stderr, "Usage: %s [-p port] [-b bind_addr] " "[-D] [--pid pidfile]\n" "-D tells the server to become a " "deamon and write a PIDfile\n" "The default PIDfile is '%s' " "in the current directory\n", PIDFILE, argv[0]); ret = 1; goto done; } } if (opts.daemon) { ret = become_daemon(); if (ret != 0) { fprintf(stderr, "Cannot become daemon: %s\n", strerror(ret)); goto done; } } ret = setup_srv(&opts, &sock); if (ret != 0) { fprintf(stderr, "Cannot setup server: %s\n", strerror(ret)); goto done; } if (opts.daemon) { if (opts.pidfile == NULL) { fprintf(stderr, "Error: pidfile == NULL\n"); ret = -1; goto done; } ret = pidfile(opts.pidfile); if (ret != 0) { fprintf(stderr, "Cannot create pidfile %s: %s\n", opts.pidfile, strerror(ret)); goto done; } } if (sock != -1) { dns(sock); close(sock); } if (opts.daemon) { unlink(opts.pidfile); } ret = 0; done: return ret; } resolv_wrapper-1.1.7/tests/test_real_res_query.c000644 001750 000144 00000013462 12721063307 022147 0ustar00asnusers000000 000000 /* * Copyright (C) Jakub Hrozek 2014 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "config.h" #include #include #include #include #include #include #include #include #include #ifndef MIN #define MIN(a,b) ((a)<(b)?(a):(b)) #endif #define ANSIZE 256 static void print_asc(const uint8_t *buf, uint32_t len) { uint32_t i; for (i = 0; i < len; i++) { printf("%c", isprint(buf[i])?buf[i]:'.'); } } static void dump_data(const uint8_t *buf, int len) { int i=0; static const uint8_t empty[16] = { 0, }; if (len<=0) return; for (i=0; i 0) && (len > i+16) && (memcmp(&buf[i], &empty, 16) == 0)) { i +=16; continue; } if (i8) printf(" "); while (n--) printf(" "); n = MIN(8,i%16); print_asc(&buf[i-(i%16)],n); printf( " " ); n = (i%16) - n; if (n>0) print_asc(&buf[i-n],n); printf("\n"); } } static void test_res_query_a_record(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE] = { 0 }; char addr[INET_ADDRSTRLEN]; ns_msg handle; ns_rr rr; /* expanded resource record */ (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); rv = res_nquery(&dnsstate, "cwrap.org", ns_c_in, ns_t_a, answer, sizeof(answer)); assert_in_range(rv, 1, 100); printf("dump answer:\n"); dump_data(answer, rv); ns_initparse(answer, sizeof(answer), &handle); /* The query must finish w/o an error, have one answer and the answer * must be a parseable RR of type A and have the address that our * fake hosts file contains */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 1); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_a); assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr), addr, sizeof(addr))); assert_string_equal(addr, "78.46.80.163"); } static void test_res_query_ns_record(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE] = { 0 }; char addr[INET_ADDRSTRLEN]; ns_msg handle; ns_rr rr; /* expanded resource record */ (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); rv = res_nquery(&dnsstate, "cwrap.org", ns_c_in, ns_t_ns, answer, sizeof(answer)); assert_in_range(rv, 1, 150); printf("dump answer:\n"); dump_data(answer, rv); ns_initparse(answer, sizeof(answer), &handle); /* The query must finish w/o an error, have two answers and the answer * must be a parseable RR of type A and have the address that our * fake hosts file contains */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 2); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_ns); assert_non_null(inet_ntop(AF_INET, ns_rr_rdata(rr), addr, sizeof(addr))); /*assert_string_equal(addr, "3.110.115.50");*/ } static void test_res_query_srv_record(void **state) { int rv; struct __res_state dnsstate; unsigned char answer[ANSIZE] = { 0 }; ns_msg handle; ns_rr rr; /* expanded resource record */ const uint8_t *rrdata; int prio; int weight; int port; char hostname[MAXDNAME]; (void) state; /* unused */ memset(&dnsstate, 0, sizeof(struct __res_state)); rv = res_ninit(&dnsstate); assert_int_equal(rv, 0); rv = res_nquery(&dnsstate, "_http._tcp.mxtoolbox.com", ns_c_in, ns_t_srv, answer, sizeof(answer)); assert_in_range(rv, 1, 100); printf("dump answer:\n"); dump_data(answer, rv); ns_initparse(answer, sizeof(answer), &handle); /* * The query must finish w/o an error, have one answer and the answer * must be a parseable RR of type SRV and have the priority, weight, * port and hostname as in the fake hosts file */ assert_int_equal(ns_msg_getflag(handle, ns_f_rcode), ns_r_noerror); assert_int_equal(ns_msg_count(handle, ns_s_an), 1); assert_int_equal(ns_parserr(&handle, ns_s_an, 0, &rr), 0); assert_int_equal(ns_rr_type(rr), ns_t_srv); rrdata = ns_rr_rdata(rr); NS_GET16(prio, rrdata); NS_GET16(weight, rrdata); NS_GET16(port, rrdata); rv = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), rrdata, hostname, MAXDNAME); assert_int_not_equal(rv, -1); assert_int_equal(prio, 10); assert_int_equal(weight, 100); assert_int_equal(port, 80); assert_string_equal(hostname, "mxtoolbox.com"); } int main(void) { int rc; const struct CMUnitTest real_tests[] = { cmocka_unit_test(test_res_query_a_record), cmocka_unit_test(test_res_query_ns_record), cmocka_unit_test(test_res_query_srv_record), }; rc = cmocka_run_group_tests(real_tests, NULL, NULL); return rc; } resolv_wrapper-1.1.7/doc/000755 001750 000144 00000000000 13721230076 015320 5ustar00asnusers000000 000000 resolv_wrapper-1.1.7/doc/CMakeLists.txt000644 001750 000144 00000000121 13636077442 020065 0ustar00asnusers000000 000000 install(FILES resolv_wrapper.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) resolv_wrapper-1.1.7/doc/resolv_wrapper.1.txt000644 001750 000144 00000004226 13636077442 021311 0ustar00asnusers000000 000000 resolv_wrapper(1) ================= :author: The Samba Team :revdate: 2020-01-31 NAME ---- resolv_wrapper - A wrapper for dns name resolving or dns faking. SYNOPSIS -------- LD_PRELOAD=libresolv_wrapper.so RESOLV_WRAPPER_CONF="/path/to/resolv.conf" *./myapplication* DESCRIPTION ----------- resolv_wrapper makes it possible on most UNIX platforms to contact your own DNS implementation in your test environment. It requires socket_wrapper to be able to contact it. If it doesn't work on a special platform the wrapper is able to fake DNS queries and return valid responses to your application. - Redirects name queries to the nameservers specified in your resolv.conf - Can fake DNS queries using a simple formatted DNS hosts file. ENVIRONMENT VARIABLES --------------------- *RESOLV_WRAPPER_CONF*:: This is used to specify the resolv.conf to use. The format of the resolv.conf file is defined in the manpage 'resolv.conf(5)'. Currently only the *namserver* directive is supported. *RESOLV_WRAPPER_HOSTS*:: This environment variable is used for faking DNS queries. It must point to a hosts-like text file that specifies fake records for custom queries. The format of the file looks like this: TYPE RECORD_NAME RECORD_VALUE For example: A dc.cwrap.org 127.0.0.10 AAAA dc.cwrap.org fd00::5357:5f0a CNAME kerberos.cwrap.org dc.cwrap.org SRV _kerberos._tcp.cwrap.org kerberos.cwrap.org 88 URI _vpn.cwrap.org https://vpn.cwrap.org/VPN TXT cwrap.org v=spf1 mx *RESOLV_WRAPPER_DEBUGLEVEL*:: If you need to see what is going on in resolv_wrapper itself or try to find a bug, you can enable logging support in resolv_wrapper if you built it with debug symbols. - 0 = ERROR - 1 = WARNING - 2 = DEBUG - 3 = TRACE *RESOLV_WRAPPER_DISABLE_DEEPBIND*:: This allows you to disable deep binding in resolv_wrapper. This is useful for running valgrind tools or sanitizers like (address, undefined, thread). EXAMPLE ------- The following command would trick 'kinit(1)' into using DNS servers from "./resolv.conf" for Kerberos service resolution: $ LD_PRELOAD=libresolv_wrapper.so RESOLV_WRAPPER_CONF="./resolv.conf" kinit user@EXAMPLE.COM resolv_wrapper-1.1.7/doc/README000644 001750 000144 00000000204 12421451254 016173 0ustar00asnusers000000 000000 The manpage is written with asciidoc. To generate the manpage use: a2x --doctype manpage --format manpage doc/resolv_wrapper.1.txt resolv_wrapper-1.1.7/doc/resolv_wrapper.1000644 001750 000144 00000007775 13636077442 020507 0ustar00asnusers000000 000000 '\" t .\" Title: resolv_wrapper .\" Author: The Samba Team .\" Generator: DocBook XSL Stylesheets vsnapshot .\" Date: 2020-01-31 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" .TH "RESOLV_WRAPPER" "1" "2020\-01\-31" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" resolv_wrapper \- A wrapper for dns name resolving or dns faking\&. .SH "SYNOPSIS" .sp LD_PRELOAD=libresolv_wrapper\&.so RESOLV_WRAPPER_CONF="/path/to/resolv\&.conf" \fB\&./myapplication\fR .SH "DESCRIPTION" .sp resolv_wrapper makes it possible on most UNIX platforms to contact your own DNS implementation in your test environment\&. It requires socket_wrapper to be able to contact it\&. If it doesn\(cqt work on a special platform the wrapper is able to fake DNS queries and return valid responses to your application\&. .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Redirects name queries to the nameservers specified in your resolv\&.conf .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Can fake DNS queries using a simple formatted DNS hosts file\&. .RE .SH "ENVIRONMENT VARIABLES" .PP \fBRESOLV_WRAPPER_CONF\fR .RS 4 This is used to specify the resolv\&.conf to use\&. The format of the resolv\&.conf file is defined in the manpage \fIresolv\&.conf(5)\fR\&. Currently only the \fBnamserver\fR directive is supported\&. .RE .PP \fBRESOLV_WRAPPER_HOSTS\fR .RS 4 This environment variable is used for faking DNS queries\&. It must point to a hosts\-like text file that specifies fake records for custom queries\&. The format of the file looks like this: .sp .if n \{\ .RS 4 .\} .nf TYPE RECORD_NAME RECORD_VALUE .fi .if n \{\ .RE .\} .RE .sp For example: .sp .if n \{\ .RS 4 .\} .nf A dc\&.cwrap\&.org 127\&.0\&.0\&.10 AAAA dc\&.cwrap\&.org fd00::5357:5f0a CNAME kerberos\&.cwrap\&.org dc\&.cwrap\&.org SRV _kerberos\&._tcp\&.cwrap\&.org kerberos\&.cwrap\&.org 88 URI _vpn\&.cwrap\&.org https://vpn\&.cwrap\&.org/VPN TXT cwrap\&.org v=spf1 mx .fi .if n \{\ .RE .\} .PP \fBRESOLV_WRAPPER_DEBUGLEVEL\fR .RS 4 If you need to see what is going on in resolv_wrapper itself or try to find a bug, you can enable logging support in resolv_wrapper if you built it with debug symbols\&. .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} 0 = ERROR .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} 1 = WARNING .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} 2 = DEBUG .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} 3 = TRACE .RE .RE .PP \fBRESOLV_WRAPPER_DISABLE_DEEPBIND\fR .RS 4 This allows you to disable deep binding in resolv_wrapper\&. This is useful for running valgrind tools or sanitizers like (address, undefined, thread)\&. .RE .SH "EXAMPLE" .sp The following command would trick \fIkinit(1)\fR into using DNS servers from "\&./resolv\&.conf" for Kerberos service resolution: .sp .if n \{\ .RS 4 .\} .nf $ LD_PRELOAD=libresolv_wrapper\&.so RESOLV_WRAPPER_CONF="\&./resolv\&.conf" kinit user@EXAMPLE\&.COM .fi .if n \{\ .RE .\} .SH "AUTHOR" .PP \fBThe Samba Team\fR .RS 4 Author. .RE resolv_wrapper-1.1.7/CMakeLists.txt000644 001750 000144 00000005107 13720751417 017324 0ustar00asnusers000000 000000 # Required cmake version cmake_minimum_required(VERSION 3.5.0) cmake_policy(SET CMP0048 NEW) # Specify search path for CMake modules to be loaded by include() # and find_package() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules") # Add defaults for cmake # Those need to be set before the project() call. include(DefineCMakeDefaults) include(DefineCompilerFlags) project(resolv_wrapper VERSION 1.1.7 LANGUAGES C) # global needed variables set(APPLICATION_NAME ${PROJECT_NAME}) # SOVERSION scheme: MAJOR.MINOR.PATCH # If there was an incompatible interface change: # Increment MAJOR. Set MINOR and PATCH to 0 # If there was a compatible interface change: # Increment MINOR. Set PATCH to 0 # If the source code was changed, but there were no interface changes: # Increment PATCH. set(LIBRARY_VERSION_MAJOR 0) set(LIBRARY_VERSION_MINOR 0) set(LIBRARY_VERSION_PATCH 7) set(LIBRARY_VERSION "${LIBRARY_VERSION_MAJOR}.${LIBRARY_VERSION_MINOR}.${LIBRARY_VERSION_PATCH}") set(LIBRARY_SOVERSION ${LIBRARY_VERSION_MAJOR}) # where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules ) # add definitions include(DefinePlatformDefaults) include(GNUInstallDirs) include(DefineOptions.cmake) include(CPackConfig.cmake) include(CompilerChecks.cmake) # Find out if we have threading available set(CMAKE_THREAD_PREFER_PTHREADS ON) find_package(Threads) # config.h checks include(ConfigureChecks.cmake) configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) # check subdirectories add_subdirectory(src) # pkg-config file get_filename_component(RESOLV_WRAPPER_LIB ${RESOLV_WRAPPER_LOCATION} NAME) configure_file(resolv_wrapper.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/resolv_wrapper.pc @ONLY) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/resolv_wrapper.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT pkgconfig ) # cmake config files configure_file(resolv_wrapper-config-version.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/resolv_wrapper-config-version.cmake @ONLY) configure_file(resolv_wrapper-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/resolv_wrapper-config.cmake @ONLY) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/resolv_wrapper-config-version.cmake ${CMAKE_CURRENT_BINARY_DIR}/resolv_wrapper-config.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/resolv_wrapper COMPONENT devel ) add_subdirectory(doc) if (UNIT_TESTING) find_package(cmocka 1.1.0 REQUIRED) include(AddCMockaTest) add_subdirectory(tests) endif (UNIT_TESTING) resolv_wrapper-1.1.7/ConfigureChecks.cmake000644 001750 000144 00000014610 13674136411 020626 0ustar00asnusers000000 000000 include(CheckIncludeFile) include(CheckSymbolExists) include(CheckFunctionExists) include(CheckLibraryExists) include(CheckTypeSize) include(CheckStructHasMember) include(CheckPrototypeDefinition) include(TestBigEndian) set(BINARYDIR ${resolv_wrapper_BINARY_DIR}) set(SOURCEDIR ${resolv_wrapper_SOURCE_DIR}) function(COMPILER_DUMPVERSION _OUTPUT_VERSION) # Remove whitespaces from the argument. # This is needed for CC="ccache gcc" cmake .. string(REPLACE " " "" _C_COMPILER_ARG "${CMAKE_C_COMPILER_ARG1}") execute_process( COMMAND ${CMAKE_C_COMPILER} ${_C_COMPILER_ARG} -dumpversion OUTPUT_VARIABLE _COMPILER_VERSION ) string(REGEX REPLACE "([0-9])\\.([0-9])(\\.[0-9])?" "\\1\\2" _COMPILER_VERSION "${_COMPILER_VERSION}") set(${_OUTPUT_VERSION} ${_COMPILER_VERSION} PARENT_SCOPE) endfunction() if(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW AND NOT OS2) compiler_dumpversion(GNUCC_VERSION) if (NOT GNUCC_VERSION EQUAL 34) set(CMAKE_REQUIRED_FLAGS "-fvisibility=hidden") check_c_source_compiles( "void __attribute__((visibility(\"default\"))) test() {} int main(void){ return 0; } " WITH_VISIBILITY_HIDDEN) unset(CMAKE_REQUIRED_FLAGS) endif (NOT GNUCC_VERSION EQUAL 34) endif(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW AND NOT OS2) # HEADERS check_include_file(sys/types.h HAVE_SYS_TYPES_H) check_include_file(resolv.h HAVE_RESOLV_H) check_include_file(arpa/nameser.h HAVE_ARPA_NAMESER_H) # SYMBOLS set(CMAKE_REQUIRED_FLAGS -D_GNU_SOURCE) check_symbol_exists(program_invocation_short_name "errno.h" HAVE_PROGRAM_INVOCATION_SHORT_NAME) unset(CMAKE_REQUIRED_FLAGS) # FUNCTIONS check_function_exists(getprogname HAVE_GETPROGNAME) check_function_exists(getexecname HAVE_GETEXECNAME) find_library(RESOLV_LIRBRARY resolv) if (RESOLV_LIRBRARY) set(HAVE_LIBRESOLV TRUE) # If we have a libresolv, we need to check functions linking the library list(APPEND _REQUIRED_LIBRARIES ${RESOLV_LIRBRARY}) else() message(STATUS "libresolv not found on ${CMAKE_SYSTEM_NAME}: Only dns faking will be available") endif() set(CMAKE_REQUIRED_LIBRARIES ${RESOLV_LIRBRARY}) check_function_exists(res_init HAVE_RES_INIT) check_function_exists(__res_init HAVE___RES_INIT) unset(CMAKE_REQUIRED_LIBRARIES) set(CMAKE_REQUIRED_LIBRARIES ${RESOLV_LIRBRARY}) check_function_exists(res_ninit HAVE_RES_NINIT) check_function_exists(__res_ninit HAVE___RES_NINIT) unset(CMAKE_REQUIRED_LIBRARIES) set(CMAKE_REQUIRED_LIBRARIES ${RESOLV_LIRBRARY}) check_function_exists(res_close HAVE_RES_CLOSE) check_function_exists(__res_close HAVE___RES_CLOSE) unset(CMAKE_REQUIRED_LIBRARIES) set(CMAKE_REQUIRED_LIBRARIES ${RESOLV_LIRBRARY}) check_function_exists(res_nclose HAVE_RES_NCLOSE) check_function_exists(__res_nclose HAVE___RES_NCLOSE) unset(CMAKE_REQUIRED_LIBRARIES) set(CMAKE_REQUIRED_LIBRARIES ${RESOLV_LIRBRARY}) check_function_exists(res_query HAVE_RES_QUERY) check_function_exists(__res_query HAVE___RES_QUERY) unset(CMAKE_REQUIRED_LIBRARIES) set(CMAKE_REQUIRED_LIBRARIES ${RESOLV_LIRBRARY}) check_function_exists(res_nquery HAVE_RES_NQUERY) check_function_exists(__res_nquery HAVE___RES_NQUERY) unset(CMAKE_REQUIRED_LIBRARIES) set(CMAKE_REQUIRED_LIBRARIES ${RESOLV_LIRBRARY}) check_function_exists(res_search HAVE_RES_SEARCH) check_function_exists(__res_search HAVE___RES_SEARCH) unset(CMAKE_REQUIRED_LIBRARIES) set(CMAKE_REQUIRED_LIBRARIES ${RESOLV_LIRBRARY}) check_function_exists(res_nsearch HAVE_RES_NSEARCH) check_function_exists(__res_nsearch HAVE___RES_NSEARCH) unset(CMAKE_REQUIRED_LIBRARIES) check_symbol_exists(ns_name_compress "sys/types.h;arpa/nameser.h" HAVE_NS_NAME_COMPRESS) if (UNIX) if (NOT LINUX) # libsocket (Solaris) find_library(SOCKET_LIBRARY socket) if (SOCKET_LIBRARY) check_library_exists(${SOCKET_LIBRARY} getaddrinfo "" HAVE_LIBSOCKET) if (HAVE_LIBSOCKET) list(APPEND _REQUIRED_LIBRARIES ${SOCKET_LIBRARY}) endif() endif() # libnsl/inet_pton (Solaris) find_library(NSL_LIBRARY nsl) if (NSL_LIBRARY) check_library_exists(${NSL_LIBRARY} inet_pton "" HAVE_LIBNSL) if (HAVE_LIBNSL) list(APPEND _REQUIRED_LIBRARIES ${NSL_LIBRARY}) endif() endif() endif (NOT LINUX) check_function_exists(getaddrinfo HAVE_GETADDRINFO) endif (UNIX) find_library(DLFCN_LIBRARY dl) if (DLFCN_LIBRARY) list(APPEND _REQUIRED_LIBRARIES ${DLFCN_LIBRARY}) else() check_function_exists(dlopen HAVE_DLOPEN) if (NOT HAVE_DLOPEN) message(FATAL_ERROR "FATAL: No dlopen() function detected") endif() endif() # IPV6 check_c_source_compiles(" #include #include #include #include #include int main(void) { struct sockaddr_storage sa_store; struct addrinfo *ai = NULL; struct in6_addr in6addr; int idx = if_nametoindex(\"iface1\"); int s = socket(AF_INET6, SOCK_STREAM, 0); int ret = getaddrinfo(NULL, NULL, NULL, &ai); if (ret != 0) { const char *es = gai_strerror(ret); } freeaddrinfo(ai); { int val = 1; #ifdef HAVE_LINUX_IPV6_V6ONLY_26 #define IPV6_V6ONLY 26 #endif ret = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (const void *)&val, sizeof(val)); } return 0; }" HAVE_IPV6) check_struct_has_member("struct __res_state" _u._ext.nsaddrs "sys/socket.h;netinet/in.h;resolv.h" HAVE_RES_STATE_U_EXT_NSADDRS) check_struct_has_member("union res_sockaddr_union" sin "sys/socket.h;netinet/in.h;resolv.h" HAVE_RES_SOCKADDR_UNION_SIN) check_struct_has_member("union res_sockaddr_union" sin6 "sys/socket.h;netinet/in.h;resolv.h" HAVE_RES_SOCKADDR_UNION_SIN6) check_c_source_compiles(" void log_fn(const char *format, ...) __attribute__ ((format (printf, 1, 2))); int main(void) { return 0; }" HAVE_ATTRIBUTE_PRINTF_FORMAT) check_c_source_compiles(" void test_destructor_attribute(void) __attribute__ ((destructor)); void test_destructor_attribute(void) { return; } int main(void) { return 0; }" HAVE_DESTRUCTOR_ATTRIBUTE) # ENDIAN test_big_endian(WORDS_BIGENDIAN) set(RWRAP_REQUIRED_LIBRARIES ${_REQUIRED_LIBRARIES} CACHE INTERNAL "resolv_wrapper required system libraries") resolv_wrapper-1.1.7/cmake/000755 001750 000144 00000000000 13721230076 015633 5ustar00asnusers000000 000000 resolv_wrapper-1.1.7/cmake/Toolchain-Debian-mips.cmake000644 001750 000144 00000001173 13636077442 022720 0ustar00asnusers000000 000000 include(CMakeForceCompiler) set(TOOLCHAIN_PREFIX mips-linux-gnu) set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_VERSION 1) set(CMAKE_SYSTEM_PROCESSOR mips) # This is the location of the mips toolchain set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++) # This is the file system root of the target set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) # Search for programs in the build host directories set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # For libraries and headers in the target directories set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) resolv_wrapper-1.1.7/cmake/Modules/000755 001750 000144 00000000000 13721230076 017243 5ustar00asnusers000000 000000 resolv_wrapper-1.1.7/cmake/Modules/DefineCMakeDefaults.cmake000644 001750 000144 00000001422 13636077442 024042 0ustar00asnusers000000 000000 # Always include srcdir and builddir in include path # This saves typing ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY} in # about every subdir # since cmake 2.4.0 set(CMAKE_INCLUDE_CURRENT_DIR ON) # Put the include dirs which are in the source or build tree # before all other include dirs, so the headers in the sources # are prefered over the already installed ones # since cmake 2.4.1 set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON) # Use colored output # since cmake 2.4.0 set(CMAKE_COLOR_MAKEFILE ON) # Create the compile command database for clang by default set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Always build with -fPIC set(CMAKE_POSITION_INDEPENDENT_CODE ON) # Avoid source tree pollution set(CMAKE_DISABLE_SOURCE_CHANGES ON) set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) resolv_wrapper-1.1.7/cmake/Modules/COPYING-CMAKE-SCRIPTS000644 001750 000144 00000002457 12422105604 022244 0ustar00asnusers000000 000000 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 copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. resolv_wrapper-1.1.7/cmake/Modules/AddCCompilerFlag.cmake000644 001750 000144 00000001515 13636077442 023342 0ustar00asnusers000000 000000 # # add_c_compiler_flag("-Werror" SUPPORTED_CFLAGS) # # Copyright (c) 2018 Andreas Schneider # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. include(CheckCCompilerFlag) macro(add_c_compiler_flag _COMPILER_FLAG _OUTPUT_VARIABLE) string(TOUPPER ${_COMPILER_FLAG} _COMPILER_FLAG_NAME) string(REGEX REPLACE "^-" "" _COMPILER_FLAG_NAME "${_COMPILER_FLAG_NAME}") string(REGEX REPLACE "(-|=|\ )" "_" _COMPILER_FLAG_NAME "${_COMPILER_FLAG_NAME}") check_c_compiler_flag("${_COMPILER_FLAG}" WITH_${_COMPILER_FLAG_NAME}_FLAG) if (WITH_${_COMPILER_FLAG_NAME}_FLAG) #string(APPEND ${_OUTPUT_VARIABLE} "${_COMPILER_FLAG} ") list(APPEND ${_OUTPUT_VARIABLE} ${_COMPILER_FLAG}) endif() endmacro() resolv_wrapper-1.1.7/cmake/Modules/AddCMockaTest.cmake000644 001750 000144 00000006250 13636077442 022671 0ustar00asnusers000000 000000 # # Copyright (c) 2007 Daniel Gollub # Copyright (c) 2007-2018 Andreas Schneider # Copyright (c) 2018 Anderson Toshiyuki Sasaki # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. #.rst: # AddCMockaTest # ------------- # # This file provides a function to add a test # # Functions provided # ------------------ # # :: # # add_cmocka_test(target_name # SOURCES src1 src2 ... srcN # [COMPILE_OPTIONS opt1 opt2 ... optN] # [LINK_LIBRARIES lib1 lib2 ... libN] # [LINK_OPTIONS lopt1 lop2 .. loptN] # ) # # ``target_name``: # Required, expects the name of the test which will be used to define a target # # ``SOURCES``: # Required, expects one or more source files names # # ``COMPILE_OPTIONS``: # Optional, expects one or more options to be passed to the compiler # # ``LINK_LIBRARIES``: # Optional, expects one or more libraries to be linked with the test # executable. # # ``LINK_OPTIONS``: # Optional, expects one or more options to be passed to the linker # # # Example: # # .. code-block:: cmake # # add_cmocka_test(my_test # SOURCES my_test.c other_source.c # COMPILE_OPTIONS -g -Wall # LINK_LIBRARIES mylib # LINK_OPTIONS -Wl,--enable-syscall-fixup # ) # # Where ``my_test`` is the name of the test, ``my_test.c`` and # ``other_source.c`` are sources for the binary, ``-g -Wall`` are compiler # options to be used, ``mylib`` is a target of a library to be linked, and # ``-Wl,--enable-syscall-fixup`` is an option passed to the linker. # enable_testing() include(CTest) if (CMAKE_CROSSCOMPILING) if (WIN32) find_program(WINE_EXECUTABLE NAMES wine) set(TARGET_SYSTEM_EMULATOR ${WINE_EXECUTABLE} CACHE INTERNAL "") endif() endif() function(ADD_CMOCKA_TEST _TARGET_NAME) set(one_value_arguments ) set(multi_value_arguments SOURCES COMPILE_OPTIONS LINK_LIBRARIES LINK_OPTIONS ) cmake_parse_arguments(_add_cmocka_test "" "${one_value_arguments}" "${multi_value_arguments}" ${ARGN} ) if (NOT DEFINED _add_cmocka_test_SOURCES) message(FATAL_ERROR "No sources provided for target ${_TARGET_NAME}") endif() add_executable(${_TARGET_NAME} ${_add_cmocka_test_SOURCES}) if (DEFINED _add_cmocka_test_COMPILE_OPTIONS) target_compile_options(${_TARGET_NAME} PRIVATE ${_add_cmocka_test_COMPILE_OPTIONS} ) endif() if (DEFINED _add_cmocka_test_LINK_LIBRARIES) target_link_libraries(${_TARGET_NAME} PRIVATE ${_add_cmocka_test_LINK_LIBRARIES} ) endif() if (DEFINED _add_cmocka_test_LINK_OPTIONS) set_target_properties(${_TARGET_NAME} PROPERTIES LINK_FLAGS ${_add_cmocka_test_LINK_OPTIONS} ) endif() add_test(${_TARGET_NAME} ${TARGET_SYSTEM_EMULATOR} ${_TARGET_NAME} ) endfunction (ADD_CMOCKA_TEST) resolv_wrapper-1.1.7/cmake/Modules/DefinePlatformDefaults.cmake000644 001750 000144 00000001312 12421451254 024630 0ustar00asnusers000000 000000 # Set system vars if (CMAKE_SYSTEM_NAME MATCHES "Linux") set(LINUX TRUE) endif(CMAKE_SYSTEM_NAME MATCHES "Linux") if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") set(FREEBSD TRUE) set(BSD TRUE) endif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") if (CMAKE_SYSTEM_NAME MATCHES "OpenBSD") set(OPENBSD TRUE) set(BSD TRUE) endif (CMAKE_SYSTEM_NAME MATCHES "OpenBSD") if (CMAKE_SYSTEM_NAME MATCHES "NetBSD") set(NETBSD TRUE) set(BSD TRUE) endif (CMAKE_SYSTEM_NAME MATCHES "NetBSD") if (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") set(SOLARIS TRUE) endif (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") if (CMAKE_SYSTEM_NAME MATCHES "OS2") set(OS2 TRUE) endif (CMAKE_SYSTEM_NAME MATCHES "OS2") resolv_wrapper-1.1.7/cmake/Modules/CheckCCompilerFlagSSP.cmake000644 001750 000144 00000003560 13636077442 024257 0ustar00asnusers000000 000000 # - Check whether the C compiler supports a given flag in the # context of a stack checking compiler option. # CHECK_C_COMPILER_FLAG_SSP(FLAG VARIABLE) # # FLAG - the compiler flag # VARIABLE - variable to store the result # # This actually calls check_c_source_compiles. # See help for CheckCSourceCompiles for a listing of variables # that can modify the build. # Copyright (c) 2006, Alexander Neundorf, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # Requires cmake 3.10 #include_guard(GLOBAL) include(CheckCSourceCompiles) include(CMakeCheckCompilerFlagCommonPatterns) macro(CHECK_C_COMPILER_FLAG_SSP _FLAG _RESULT) set(SAFE_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") set(CMAKE_REQUIRED_FLAGS "${_FLAG}") # Normalize locale during test compilation. set(_CheckCCompilerFlag_LOCALE_VARS LC_ALL LC_MESSAGES LANG) foreach(v ${_CheckCCompilerFlag_LOCALE_VARS}) set(_CheckCCompilerFlag_SAVED_${v} "$ENV{${v}}") set(ENV{${v}} C) endforeach() CHECK_COMPILER_FLAG_COMMON_PATTERNS(_CheckCCompilerFlag_COMMON_PATTERNS) check_c_source_compiles("int main(int argc, char **argv) { char buffer[256]; return buffer[argc]=0;}" ${_RESULT} # Some compilers do not fail with a bad flag FAIL_REGEX "command line option .* is valid for .* but not for C" # GNU ${_CheckCCompilerFlag_COMMON_PATTERNS}) foreach(v ${_CheckCCompilerFlag_LOCALE_VARS}) set(ENV{${v}} ${_CheckCCompilerFlag_SAVED_${v}}) unset(_CheckCCompilerFlag_SAVED_${v}) endforeach() unset(_CheckCCompilerFlag_LOCALE_VARS) unset(_CheckCCompilerFlag_COMMON_PATTERNS) set(CMAKE_REQUIRED_FLAGS "${SAFE_CMAKE_REQUIRED_FLAGS}") endmacro(CHECK_C_COMPILER_FLAG_SSP) resolv_wrapper-1.1.7/cmake/Modules/DefineCompilerFlags.cmake000644 001750 000144 00000005551 13636077442 024130 0ustar00asnusers000000 000000 if (UNIX AND NOT WIN32) # Activate with: -DCMAKE_BUILD_TYPE=Profiling set(CMAKE_C_FLAGS_PROFILING "-O0 -g -fprofile-arcs -ftest-coverage" CACHE STRING "Flags used by the C compiler during PROFILING builds.") set(CMAKE_CXX_FLAGS_PROFILING "-O0 -g -fprofile-arcs -ftest-coverage" CACHE STRING "Flags used by the CXX compiler during PROFILING builds.") set(CMAKE_SHARED_LINKER_FLAGS_PROFILING "-fprofile-arcs -ftest-coverage" CACHE STRING "Flags used by the linker during the creation of shared libraries during PROFILING builds.") set(CMAKE_MODULE_LINKER_FLAGS_PROFILING "-fprofile-arcs -ftest-coverage" CACHE STRING "Flags used by the linker during the creation of shared libraries during PROFILING builds.") set(CMAKE_EXEC_LINKER_FLAGS_PROFILING "-fprofile-arcs -ftest-coverage" CACHE STRING "Flags used by the linker during PROFILING builds.") # Activate with: -DCMAKE_BUILD_TYPE=AddressSanitizer set(CMAKE_C_FLAGS_ADDRESSSANITIZER "-g -O1 -fsanitize=address -fno-omit-frame-pointer" CACHE STRING "Flags used by the C compiler during ADDRESSSANITIZER builds.") set(CMAKE_CXX_FLAGS_ADDRESSSANITIZER "-g -O1 -fsanitize=address -fno-omit-frame-pointer" CACHE STRING "Flags used by the CXX compiler during ADDRESSSANITIZER builds.") set(CMAKE_SHARED_LINKER_FLAGS_ADDRESSSANITIZER "-fsanitize=address" CACHE STRING "Flags used by the linker during the creation of shared libraries during ADDRESSSANITIZER builds.") set(CMAKE_MODULE_LINKER_FLAGS_ADDRESSSANITIZER "-fsanitize=address" CACHE STRING "Flags used by the linker during the creation of shared libraries during ADDRESSSANITIZER builds.") set(CMAKE_EXEC_LINKER_FLAGS_ADDRESSSANITIZER "-fsanitize=address" CACHE STRING "Flags used by the linker during ADDRESSSANITIZER builds.") # Activate with: -DCMAKE_BUILD_TYPE=UndefinedSanitizer set(CMAKE_C_FLAGS_UNDEFINEDSANITIZER "-g -O1 -fsanitize=undefined -fsanitize=null -fsanitize=alignment -fno-sanitize-recover" CACHE STRING "Flags used by the C compiler during UNDEFINEDSANITIZER builds.") set(CMAKE_CXX_FLAGS_UNDEFINEDSANITIZER "-g -O1 -fsanitize=undefined -fsanitize=null -fsanitize=alignment -fno-sanitize-recover" CACHE STRING "Flags used by the CXX compiler during UNDEFINEDSANITIZER builds.") set(CMAKE_SHARED_LINKER_FLAGS_UNDEFINEDSANITIZER "-fsanitize=undefined" CACHE STRING "Flags used by the linker during the creation of shared libraries during UNDEFINEDSANITIZER builds.") set(CMAKE_MODULE_LINKER_FLAGS_UNDEFINEDSANITIZER "-fsanitize=undefined" CACHE STRING "Flags used by the linker during the creation of shared libraries during UNDEFINEDSANITIZER builds.") set(CMAKE_EXEC_LINKER_FLAGS_UNDEFINEDSANITIZER "-fsanitize=undefined" CACHE STRING "Flags used by the linker during UNDEFINEDSANITIZER builds.") endif() resolv_wrapper-1.1.7/config.h.cmake000644 001750 000144 00000003461 13636077442 017267 0ustar00asnusers000000 000000 /* Name of package */ #cmakedefine PACKAGE "${PROJECT_NAME}" /* Version number of package */ #cmakedefine VERSION "${PROJECT_VERSION}" #cmakedefine BINARYDIR "${BINARYDIR}" #cmakedefine SOURCEDIR "${SOURCEDIR}" /************************** HEADER FILES *************************/ #cmakedefine HAVE_SYS_TYPES_H 1 #cmakedefine HAVE_RESOLV_H 1 #cmakedefine HAVE_ARPA_NAMESER_H 1 /**************************** SYMBOLS ****************************/ #cmakedefine HAVE_PROGRAM_INVOCATION_SHORT_NAME 1 /*************************** FUNCTIONS ***************************/ #cmakedefine HAVE_GETPROGNAME 1 #cmakedefine HAVE_GETEXECNAME 1 #cmakedefine HAVE_RES_INIT 1 #cmakedefine HAVE___RES_INIT 1 #cmakedefine HAVE_RES_NINIT 1 #cmakedefine HAVE___RES_NINIT 1 #cmakedefine HAVE_RES_CLOSE 1 #cmakedefine HAVE___RES_CLOSE 1 #cmakedefine HAVE_RES_NCLOSE 1 #cmakedefine HAVE___RES_NCLOSE 1 #cmakedefine HAVE_RES_QUERY 1 #cmakedefine HAVE___RES_QUERY 1 #cmakedefine HAVE_RES_SEARCH 1 #cmakedefine HAVE___RES_SEARCH 1 #cmakedefine HAVE_RES_NQUERY 1 #cmakedefine HAVE___RES_NQUERY 1 #cmakedefine HAVE_RES_NSEARCH 1 #cmakedefine HAVE___RES_NSEARCH 1 #cmakedefine HAVE_NS_NAME_COMPRESS 1 /*************************** LIBRARIES ***************************/ #cmakedefine HAVE_LIBRESOLV 1 /**************************** OPTIONS ****************************/ #cmakedefine HAVE_IPV6 1 #cmakedefine HAVE_RES_STATE_U_EXT_NSADDRS 1 #cmakedefine HAVE_RES_SOCKADDR_UNION_SIN 1 #cmakedefine HAVE_RES_SOCKADDR_UNION_SIN6 1 #cmakedefine HAVE_ATTRIBUTE_PRINTF_FORMAT 1 #cmakedefine HAVE_DESTRUCTOR_ATTRIBUTE 1 /*************************** ENDIAN *****************************/ /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #cmakedefine WORDS_BIGENDIAN 1 resolv_wrapper-1.1.7/resolv_wrapper.pc.cmake000644 001750 000144 00000000217 13636077442 021243 0ustar00asnusers000000 000000 Name: @PROJECT_NAME@ Description: The resolv_wrapper library Version: @PROJECT_VERSION@ Libs: @CMAKE_INSTALL_FULL_LIBDIR@/@RESOLV_WRAPPER_LIB@ resolv_wrapper-1.1.7/.gitlab-ci.yml000644 001750 000144 00000012424 13720751241 017213 0ustar00asnusers000000 000000 variables: GIT_DEPTH: 3 BUILD_IMAGES_PROJECT: cmocka/gitlab-build-images FEDORA_BUILD: buildenv-fedora CENTOS7_BUILD: buildenv-centos7 TUMBLEWEED_BUILD: buildenv-tumbleweed MINGW_BUILD: buildenv-mingw UBUNTU_BUILD: buildenv-ubuntu centos7/x86_64: image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS7_BUILD script: - mkdir -p obj && cd obj && cmake3 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DPICKY_DEVELOPER=ON -DUNIT_TESTING=ON .. && make -j$(nproc) && ctest --output-on-failure tags: - shared except: - tags artifacts: expire_in: 1 week when: on_failure paths: - obj/ fedora/x86_64: image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD script: - mkdir -p obj && cd obj && cmake -DCMAKE_INSTALL_PREFIX=/tmp/local -DCMAKE_BUILD_TYPE=RelWithDebInfo -DPICKY_DEVELOPER=ON -DUNIT_TESTING=ON .. && make -j$(nproc) && ctest --output-on-failure && make install tags: - shared except: - tags artifacts: expire_in: 1 week when: on_failure paths: - obj/ fedora/address-sanitizer: image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD script: - mkdir -p obj && cd obj && cmake -DCMAKE_BUILD_TYPE=AddressSanitizer -DPICKY_DEVELOPER=ON -DUNIT_TESTING=ON .. && make -j$(nproc) && ctest --output-on-failure tags: - shared except: - tags artifacts: expire_in: 1 week when: on_failure paths: - obj/ fedora/undefined-sanitizer: image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD script: - mkdir -p obj && cd obj && cmake -DCMAKE_BUILD_TYPE=UndefinedSanitizer -DPICKY_DEVELOPER=ON -DUNIT_TESTING=ON .. && make -j$(nproc) && ctest --output-on-failure tags: - shared except: - tags artifacts: expire_in: 1 week when: on_failure paths: - obj/ fedora/csbuild: variables: GIT_DEPTH: 20 image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD script: - | if [[ -z "$CI_COMMIT_BEFORE_SHA" ]]; then export CI_COMMIT_BEFORE_SHA=$(git rev-parse "${CI_COMMIT_SHA}~15") fi # Check if the commit exists in this branch # This is not the case for a force push git branch --contains $CI_COMMIT_BEFORE_SHA 2>/dev/null || export CI_COMMIT_BEFORE_SHA=$(git rev-parse "${CI_COMMIT_SHA}~15") export CI_COMMIT_RANGE="$CI_COMMIT_BEFORE_SHA..$CI_COMMIT_SHA" - csbuild --build-dir=obj-csbuild --prep-cmd="cmake -DCMAKE_BUILD_TYPE=Debug -DPICKY_DEVELOPER=ON -DUNIT_TESTING=ON @SRCDIR@" --build-cmd "make clean && make -j$(nproc)" --git-commit-range $CI_COMMIT_RANGE --color --print-current --print-fixed tags: - shared except: - tags artifacts: expire_in: 1 week when: on_failure paths: - obj-csbuild/ freebsd/x86_64: image: script: - mkdir -p obj && cd obj && cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DPICKY_DEVELOPER=ON -DUNIT_TESTING=ON .. && make && ctest --output-on-failure tags: - freebsd except: - tags only: - branches@cwrap/resolv_wrapper - branches@cryptomilk/resolv_wrapper - branches@metze/resolv_wrapper artifacts: expire_in: 1 week when: on_failure paths: - obj/ tumbleweed/x86_64/gcc: image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$TUMBLEWEED_BUILD script: - mkdir -p obj && cd obj && cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DPICKY_DEVELOPER=ON -DUNIT_TESTING=ON .. && make -j$(nproc) && ctest --output-on-failure tags: - shared except: - tags artifacts: expire_in: 1 week when: on_failure paths: - obj/ tumbleweed/x86_64/gcc7: image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$TUMBLEWEED_BUILD script: - mkdir -p obj && cd obj && cmake -DCMAKE_C_COMPILER=gcc-7 -DCMAKE_CXX_COMPILER=g++-7 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DPICKY_DEVELOPER=ON -DUNIT_TESTING=ON .. && make -j$(nproc) && ctest --output-on-failure tags: - shared except: - tags artifacts: expire_in: 1 week when: on_failure paths: - obj/ tumbleweed/x86_64/clang: image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$TUMBLEWEED_BUILD script: - mkdir -p obj && cd obj && cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DPICKY_DEVELOPER=ON -DUNIT_TESTING=ON .. && make -j$(nproc) && ctest --output-on-failure tags: - shared except: - tags artifacts: expire_in: 1 week when: on_failure paths: - obj/ tumbleweed/static-analysis: image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$TUMBLEWEED_BUILD script: - export CCC_CC=clang - export CCC_CXX=clang++ - mkdir -p obj && cd obj && scan-build cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DPICKY_DEVELOPER=ON -DUNIT_TESTING=ON .. && scan-build --status-bugs -o scan make -j$(nproc) tags: - shared except: - tags artifacts: expire_in: 1 week when: on_failure paths: - obj/scan ubuntu/x86_64: image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$UBUNTU_BUILD script: - mkdir -p obj && cd obj && cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DPICKY_DEVELOPER=ON -DUNIT_TESTING=ON .. && make -j$(nproc) && ctest --output-on-failure tags: - shared except: - tags artifacts: expire_in: 1 week when: on_failure paths: - obj/ resolv_wrapper-1.1.7/resolv_wrapper-config.cmake.in000644 001750 000144 00000000115 13636077442 022507 0ustar00asnusers000000 000000 set(RESOLV_WRAPPER_LIBRARY @CMAKE_INSTALL_FULL_LIBDIR@/@RESOLV_WRAPPER_LIB@) resolv_wrapper-1.1.7/4.patch000644 001750 000144 00000074462 13657213711 015761 0ustar00asnusers000000 000000 From 94b70c525eaa623a455bb31240546d6cb2bab249 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 15 Feb 2019 17:24:57 +0100 Subject: [PATCH 1/7] tests/test_res_init.c: avoid using public ipv6 addresses from google in tests Signed-off-by: Stefan Metzmacher --- tests/test_res_init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_res_init.c b/tests/test_res_init.c index 5cd3591..7f73378 100644 --- a/tests/test_res_init.c +++ b/tests/test_res_init.c @@ -83,10 +83,10 @@ static void test_res_ninit(void **state) const char *nameservers[] = { "127.0.0.1", "10.10.10.1", - "2607:f8b0:4009:802::1011", + "fd53:53:53:53::1011", "10.10.10.2", "10.10.10.3", - "2607:f8b0:4009:802::1012", + "fd53:53:53:53::1012", NULL, }; int i; -- 2.26.2 From 8400a73d75df840b5eed7103fff52ba35f3b5f81 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 14 Feb 2019 15:46:22 +0100 Subject: [PATCH 2/7] rwrap: fix resolv wrapper with ipv6 addresses and old glibc versions The handling of __res_state._u._ext was different before this glibc commit (e.g. glibc-2.19): commit 2212c1420c92a33b0e0bd9a34938c9814a56c0f7 Author: Andreas Schwab AuthorDate: Thu Feb 19 15:52:08 2015 +0100 Commit: Andreas Schwab CommitDate: Thu May 21 15:16:37 2015 +0200 Simplify handling of nameserver configuration in resolver Remove use of ext.nsmap member of struct __res_state and always use an identity mapping betwen the nsaddr_list array and the ext.nsaddrs array. The fact that a nameserver has an IPv6 address is signalled by setting nsaddr_list[].sin_family to zero. As a result of fixing this, it's now possible to run 'test_res_init' even without using resolv_wrapper. Signed-off-by: Stefan Metzmacher --- src/resolv_wrapper.c | 53 +++++++++++++++++++++++++------------------ tests/test_res_init.c | 26 ++++++++++----------- 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/src/resolv_wrapper.c b/src/resolv_wrapper.c index cbca248..20ba2d1 100644 --- a/src/resolv_wrapper.c +++ b/src/resolv_wrapper.c @@ -1645,14 +1645,13 @@ static int rwrap_parse_resolv_conf(struct __res_state *state, ok = inet_pton(AF_INET, p, &a); if (ok) { - state->nsaddr_list[state->nscount] = (struct sockaddr_in) { + state->nsaddr_list[nserv] = (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 @@ -1673,11 +1672,11 @@ static int rwrap_parse_resolv_conf(struct __res_state *state, 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.nsaddrs[nserv] = sa6; + state->_u._ext.nssocks[nserv] = -1; + state->_u._ext.nsmap[nserv] = MAXNS + 1; - state->_u._ext.nscount++; + state->_u._ext.nscount6++; nserv++; } else { RWRAP_LOG(RWRAP_LOG_ERROR, @@ -1700,6 +1699,13 @@ static int rwrap_parse_resolv_conf(struct __res_state *state, } /* TODO: match other keywords */ } + /* + * note that state->_u._ext.nscount is left as 0, + * this matches glibc and allows resolv wrapper + * to work with most (maybe all) glibc versions. + */ + state->nscount = nserv; + if (ferror(fp)) { RWRAP_LOG(RWRAP_LOG_ERROR, "Reading from %s failed", @@ -1725,21 +1731,36 @@ static int rwrap_res_ninit(struct __res_state *state) const char *resolv_conf = getenv("RESOLV_WRAPPER_CONF"); if (resolv_conf != NULL) { + /* Delete name servers */ +#ifdef HAVE_RESOLV_IPV6_NSADDRS uint16_t i; - (void)i; /* maybe unused */ + for (i = 0; i < state->nscount; i++) { + if (state->_u._ext.nssocks[i] != -1) { + close(state->_u._ext.nssocks[i]); + state->_u._ext.nssocks[i] = -1; + } + + SAFE_FREE(state->_u._ext.nsaddrs[i]); + } +#endif - /* Delete name servers */ state->nscount = 0; memset(state->nsaddr_list, 0, sizeof(state->nsaddr_list)); #ifdef HAVE_RESOLV_IPV6_NSADDRS + state->ipv6_unavail = false; + state->_u._ext.nsinit = 0; state->_u._ext.nscount = 0; - for (i = 0; i < state->_u._ext.nscount; i++) { - SAFE_FREE(state->_u._ext.nsaddrs[i]); + state->_u._ext.nscount6 = 0; + for (i = 0; i < MAXNS; i++) { + state->_u._ext.nsaddrs[i] = NULL; + state->_u._ext.nssocks[i] = -1; + state->_u._ext.nsmap[i] = MAXNS; } #endif + /* And parse the new name servers */ rc = rwrap_parse_resolv_conf(state, resolv_conf); } } @@ -1786,19 +1807,7 @@ int __res_init(void) 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(res_nclose) && defined(HAVE_RES_NCLOSE) diff --git a/tests/test_res_init.c b/tests/test_res_init.c index 7f73378..f0cd2ee 100644 --- a/tests/test_res_init.c +++ b/tests/test_res_init.c @@ -121,26 +121,17 @@ static void test_res_ninit(void **state) /* * Validate the number of parsed name servers. - */ - - assert_int_equal(dnsstate.nscount + dnsstate._u._ext.nscount, MAXNS); - -#ifndef HAVE_RESOLV_IPV6_NSADDRS - /* + * * On platforms that don't support IPv6, the v6 address is skipped * and we end up reading three v4 addresses. - */ - assert_int_equal(dnsstate.nscount, MAXNS); -#else - /* + * * test we have two v4 and one v6 server * * Note: This test assumes MAXNS == 3, which is the * case on all systems encountered so far. */ - assert_int_equal(dnsstate.nscount, 2); - assert_int_equal(dnsstate._u._ext.nscount, 1); -#endif /* HAVE_RESOLV_IPV6_NSADDRS */ + assert_int_equal(MAXNS, 3); + assert_int_equal(dnsstate.nscount, MAXNS); /* Validate the servers. */ @@ -150,12 +141,18 @@ static void test_res_ninit(void **state) inet_ntop(AF_INET, &(dnsstate.nsaddr_list[0].sin_addr), straddr, INET6_ADDRSTRLEN); assert_string_equal(nameservers[0], straddr); +#ifdef HAVE_RESOLV_IPV6_NSADDRS + assert_null(dnsstate._u._ext.nsaddrs[0]); +#endif assert_int_equal(dnsstate.nsaddr_list[1].sin_family, AF_INET); assert_int_equal(dnsstate.nsaddr_list[1].sin_port, htons(53)); inet_ntop(AF_INET, &(dnsstate.nsaddr_list[1].sin_addr), straddr, INET6_ADDRSTRLEN); assert_string_equal(nameservers[1], straddr); +#ifdef HAVE_RESOLV_IPV6_NSADDRS + assert_null(dnsstate._u._ext.nsaddrs[1]); +#endif #ifndef HAVE_RESOLV_IPV6_NSADDRS /* @@ -169,7 +166,8 @@ static void test_res_ninit(void **state) assert_string_equal(nameservers[3], straddr); #else /* IPv6 */ - sa6 = dnsstate._u._ext.nsaddrs[0]; + assert_non_null(dnsstate._u._ext.nsaddrs[2]); + sa6 = dnsstate._u._ext.nsaddrs[2]; assert_int_equal(sa6->sin6_family, AF_INET6); assert_int_equal(sa6->sin6_port, htons(53)); inet_ntop(AF_INET6, &(sa6->sin6_addr), straddr, INET6_ADDRSTRLEN); -- 2.26.2 From d2090f1407852215b5dc0656b9cd81acb33f0841 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 18 Mar 2020 17:01:48 +0100 Subject: [PATCH 3/7] rwrap: let configure use define HAVE_RES_STATE_U_EXT_NSADDRS The configure check should describe what it checked for. Let the code logic decide if that means we expect HAVE_RESOLV_IPV6_NSADDRS to be defined. We'll get another condition that sets HAVE_RESOLV_IPV6_NSADDRS in the following commits. Signed-off-by: Stefan Metzmacher --- ConfigureChecks.cmake | 4 +++- config.h.cmake | 2 +- src/resolv_wrapper.c | 4 ++++ tests/test_res_init.c | 24 ++++++++++++------------ 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake index ae9437b..4752cd4 100644 --- a/ConfigureChecks.cmake +++ b/ConfigureChecks.cmake @@ -173,7 +173,9 @@ int main(void) { return 0; }" HAVE_IPV6) -check_struct_has_member("struct __res_state" _u._ext.nsaddrs resolv.h HAVE_RESOLV_IPV6_NSADDRS) +check_struct_has_member("struct __res_state" _u._ext.nsaddrs + "sys/socket.h;netinet/in.h;resolv.h" + HAVE_RES_STATE_U_EXT_NSADDRS) check_c_source_compiles(" void log_fn(const char *format, ...) __attribute__ ((format (printf, 1, 2))); diff --git a/config.h.cmake b/config.h.cmake index 0c2fa35..75e6902 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -55,7 +55,7 @@ /**************************** OPTIONS ****************************/ #cmakedefine HAVE_IPV6 1 -#cmakedefine HAVE_RESOLV_IPV6_NSADDRS 1 +#cmakedefine HAVE_RES_STATE_U_EXT_NSADDRS 1 #cmakedefine HAVE_ATTRIBUTE_PRINTF_FORMAT 1 #cmakedefine HAVE_DESTRUCTOR_ATTRIBUTE 1 diff --git a/src/resolv_wrapper.c b/src/resolv_wrapper.c index 20ba2d1..0ee9fee 100644 --- a/src/resolv_wrapper.c +++ b/src/resolv_wrapper.c @@ -52,6 +52,10 @@ #include +#ifdef HAVE_RES_STATE_U_EXT_NSADDRS +#define HAVE_RESOLV_IPV6_NSADDRS 1 +#endif + /* GCC has printf type attribute check. */ #ifdef HAVE_ATTRIBUTE_PRINTF_FORMAT #define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b))) diff --git a/tests/test_res_init.c b/tests/test_res_init.c index f0cd2ee..eb0eee7 100644 --- a/tests/test_res_init.c +++ b/tests/test_res_init.c @@ -92,7 +92,7 @@ static void test_res_ninit(void **state) int i; int rv; char straddr[INET6_ADDRSTRLEN] = { '\0' }; -#ifdef HAVE_RESOLV_IPV6_NSADDRS +#ifdef HAVE_RES_STATE_U_EXT_NSADDRS struct sockaddr_in6 *sa6; #endif @@ -141,7 +141,7 @@ static void test_res_ninit(void **state) inet_ntop(AF_INET, &(dnsstate.nsaddr_list[0].sin_addr), straddr, INET6_ADDRSTRLEN); assert_string_equal(nameservers[0], straddr); -#ifdef HAVE_RESOLV_IPV6_NSADDRS +#ifdef HAVE_RES_STATE_U_EXT_NSADDRS assert_null(dnsstate._u._ext.nsaddrs[0]); #endif @@ -150,11 +150,19 @@ static void test_res_ninit(void **state) inet_ntop(AF_INET, &(dnsstate.nsaddr_list[1].sin_addr), straddr, INET6_ADDRSTRLEN); assert_string_equal(nameservers[1], straddr); -#ifdef HAVE_RESOLV_IPV6_NSADDRS +#ifdef HAVE_RES_STATE_U_EXT_NSADDRS assert_null(dnsstate._u._ext.nsaddrs[1]); #endif -#ifndef HAVE_RESOLV_IPV6_NSADDRS +#ifdef HAVE_RES_STATE_U_EXT_NSADDRS + /* IPv6 */ + assert_non_null(dnsstate._u._ext.nsaddrs[2]); + sa6 = dnsstate._u._ext.nsaddrs[2]; + assert_int_equal(sa6->sin6_family, AF_INET6); + assert_int_equal(sa6->sin6_port, htons(53)); + inet_ntop(AF_INET6, &(sa6->sin6_addr), straddr, INET6_ADDRSTRLEN); + assert_string_equal(nameservers[2], straddr); +#else /* * On platforms that don't support IPv6, the v6 address is skipped * and we end up reading three v4 addresses. @@ -164,14 +172,6 @@ static void test_res_ninit(void **state) inet_ntop(AF_INET, &(dnsstate.nsaddr_list[2].sin_addr), straddr, INET6_ADDRSTRLEN); assert_string_equal(nameservers[3], straddr); -#else - /* IPv6 */ - assert_non_null(dnsstate._u._ext.nsaddrs[2]); - sa6 = dnsstate._u._ext.nsaddrs[2]; - assert_int_equal(sa6->sin6_family, AF_INET6); - assert_int_equal(sa6->sin6_port, htons(53)); - inet_ntop(AF_INET6, &(sa6->sin6_addr), straddr, INET6_ADDRSTRLEN); - assert_string_equal(nameservers[2], straddr); #endif res_nclose(&dnsstate); -- 2.26.2 From 628f6e47449ac5d5a72fb48e5384bda504bf97a4 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 10 Mar 2020 13:07:25 +0100 Subject: [PATCH 4/7] rwrap: split out a rwrap_reset_nameservers() function This will make it easier to add support for ipv6 nameservers on FreeBSD in the following commits. Signed-off-by: Stefan Metzmacher --- src/resolv_wrapper.c | 56 ++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/src/resolv_wrapper.c b/src/resolv_wrapper.c index 0ee9fee..7c5bf81 100644 --- a/src/resolv_wrapper.c +++ b/src/resolv_wrapper.c @@ -1606,6 +1606,29 @@ static int libc_res_nsearch(struct __res_state *state, * RES_HELPER ***************************************************************************/ +static void rwrap_reset_nameservers(struct __res_state *state) +{ +#ifdef HAVE_RES_STATE_U_EXT_NSADDRS + size_t i; + + for (i = 0; i < (size_t)state->nscount; i++) { + if (state->_u._ext.nssocks[i] != -1) { + close(state->_u._ext.nssocks[i]); + state->_u._ext.nssocks[i] = -1; + } + SAFE_FREE(state->_u._ext.nsaddrs[i]); + } + memset(&state->_u._ext, 0, sizeof(state->_u._ext)); + for (i = 0; i < MAXNS; i++) { + state->_u._ext.nssocks[i] = -1; + state->_u._ext.nsmap[i] = MAXNS + 1; + } + state->ipv6_unavail = false; +#endif + memset(state->nsaddr_list, 0, sizeof(state->nsaddr_list)); + state->nscount = 0; +} + static int rwrap_parse_resolv_conf(struct __res_state *state, const char *resolv_conf) { @@ -1613,6 +1636,8 @@ static int rwrap_parse_resolv_conf(struct __res_state *state, char buf[BUFSIZ]; int nserv = 0; + rwrap_reset_nameservers(state); + fp = fopen(resolv_conf, "r"); if (fp == NULL) { RWRAP_LOG(RWRAP_LOG_ERROR, @@ -1735,36 +1760,6 @@ static int rwrap_res_ninit(struct __res_state *state) const char *resolv_conf = getenv("RESOLV_WRAPPER_CONF"); if (resolv_conf != NULL) { - /* Delete name servers */ -#ifdef HAVE_RESOLV_IPV6_NSADDRS - uint16_t i; - - for (i = 0; i < state->nscount; i++) { - if (state->_u._ext.nssocks[i] != -1) { - close(state->_u._ext.nssocks[i]); - state->_u._ext.nssocks[i] = -1; - } - - SAFE_FREE(state->_u._ext.nsaddrs[i]); - } -#endif - - state->nscount = 0; - memset(state->nsaddr_list, 0, sizeof(state->nsaddr_list)); - -#ifdef HAVE_RESOLV_IPV6_NSADDRS - state->ipv6_unavail = false; - state->_u._ext.nsinit = 0; - state->_u._ext.nscount = 0; - state->_u._ext.nscount6 = 0; - for (i = 0; i < MAXNS; i++) { - state->_u._ext.nsaddrs[i] = NULL; - state->_u._ext.nssocks[i] = -1; - state->_u._ext.nsmap[i] = MAXNS; - } -#endif - - /* And parse the new name servers */ rc = rwrap_parse_resolv_conf(state, resolv_conf); } } @@ -1811,6 +1806,7 @@ int __res_init(void) static void rwrap_res_nclose(struct __res_state *state) { + rwrap_reset_nameservers(state); libc_res_nclose(state); } -- 2.26.2 From dea2e9eeb9677b145a26ff23d9a01226dbd9b6ae Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 10 Mar 2020 13:07:25 +0100 Subject: [PATCH 5/7] rwrap: split out rwrap_{get,log}_nameservers() functions This will make it easier to add support for ipv6 nameservers on FreeBSD in the next step. Signed-off-by: Stefan Metzmacher --- src/resolv_wrapper.c | 98 ++++++++++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 26 deletions(-) diff --git a/src/resolv_wrapper.c b/src/resolv_wrapper.c index 7c5bf81..c97ad26 100644 --- a/src/resolv_wrapper.c +++ b/src/resolv_wrapper.c @@ -177,6 +177,12 @@ static void rwrap_log(enum rwrap_dbglvl_e dbglvl, #define RWRAP_MAX_RECURSION 64 +union rwrap_sockaddr { + struct sockaddr sa; + struct sockaddr_in in; + struct sockaddr_in6 in6; +}; + /* Priority and weight can be omitted from the hosts file, but need to be part * of the output */ @@ -1606,6 +1612,70 @@ static int libc_res_nsearch(struct __res_state *state, * RES_HELPER ***************************************************************************/ +static size_t rwrap_get_nameservers(struct __res_state *state, + size_t nserv, + union rwrap_sockaddr *nsaddrs) +{ + size_t i; + + memset(nsaddrs, 0, sizeof(*nsaddrs) * nserv); + + if (nserv > (size_t)state->nscount) { + nserv = (size_t)state->nscount; + } + + for (i = 0; i < nserv; i++) { +#ifdef HAVE_RES_STATE_U_EXT_NSADDRS + if (state->_u._ext.nsaddrs[i] != NULL) { + nsaddrs[i] = (union rwrap_sockaddr) { + .in6 = *state->_u._ext.nsaddrs[i], + }; + } else +#endif /* HAVE_RES_STATE_U_EXT_NSADDRS */ + { + nsaddrs[i] = (union rwrap_sockaddr) { + .in = state->nsaddr_list[i], + }; + } + } + + return nserv; +} + +static void rwrap_log_nameservers(enum rwrap_dbglvl_e dbglvl, + const char *func, + struct __res_state *state) +{ + union rwrap_sockaddr nsaddrs[MAXNS]; + size_t nserv = MAXNS; + size_t i; + + memset(nsaddrs, 0, sizeof(nsaddrs)); + nserv = rwrap_get_nameservers(state, nserv, nsaddrs); + for (i = 0; i < nserv; i++) { + char ip[INET6_ADDRSTRLEN]; + + switch (nsaddrs[i].sa.sa_family) { + case AF_INET: + inet_ntop(AF_INET, &(nsaddrs[i].in.sin_addr), + ip, sizeof(ip)); + break; + case AF_INET6: + inet_ntop(AF_INET6, &(nsaddrs[i].in6.sin6_addr), + ip, sizeof(ip)); + break; + default: + snprintf(ip, sizeof(ip), "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 + rwrap_log_nameservers(RWRAP_LOG_TRACE, __func__, state); fake_hosts = getenv("RESOLV_WRAPPER_HOSTS"); if (fake_hosts != NULL) { @@ -1959,23 +2017,11 @@ static int rwrap_res_nsearch(struct __res_state *state, { 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 + rwrap_log_nameservers(RWRAP_LOG_TRACE, __func__, state); fake_hosts = getenv("RESOLV_WRAPPER_HOSTS"); if (fake_hosts != NULL) { -- 2.26.2 From 1eb3d53e07e029e317fda3715370d779f0dbadbb Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 10 Mar 2020 13:07:25 +0100 Subject: [PATCH 6/7] rwrap: split out a rwrap_set_nameservers() function This will make it easier to add support for ipv6 nameservers on FreeBSD in the next step. Signed-off-by: Stefan Metzmacher --- src/resolv_wrapper.c | 140 +++++++++++++++++++++++++++---------------- 1 file changed, 90 insertions(+), 50 deletions(-) diff --git a/src/resolv_wrapper.c b/src/resolv_wrapper.c index c97ad26..e1903c9 100644 --- a/src/resolv_wrapper.c +++ b/src/resolv_wrapper.c @@ -1699,14 +1699,65 @@ static void rwrap_reset_nameservers(struct __res_state *state) state->nscount = 0; } +static int rwrap_set_nameservers(struct __res_state *state, + size_t nserv, + const union rwrap_sockaddr *nsaddrs) +{ + size_t i; + + if (nserv > MAXNS) { + nserv = MAXNS; + } + rwrap_reset_nameservers(state); + + for (i = 0; i < nserv; i++) { + switch (nsaddrs[i].sa.sa_family) { + case AF_INET: + state->nsaddr_list[i] = nsaddrs[i].in; + break; +#ifdef HAVE_RES_STATE_U_EXT_NSADDRS + case AF_INET6: + state->_u._ext.nsaddrs[i] = malloc(sizeof(nsaddrs[i].in6)); + if (state->_u._ext.nsaddrs[i] == NULL) { + rwrap_reset_nameservers(state); + errno = ENOMEM; + return -1; + } + *state->_u._ext.nsaddrs[i] = nsaddrs[i].in6; + state->_u._ext.nssocks[i] = -1; + state->_u._ext.nsmap[i] = MAXNS + 1; + state->_u._ext.nscount6++; + break; +#endif + default: + RWRAP_LOG(RWRAP_LOG_ERROR, + "Internal error unhandled sa_family=%d", + nsaddrs[i].sa.sa_family); + rwrap_reset_nameservers(state); + errno = ENOSYS; + return -1; + } + } + + /* + * note that state->_u._ext.nscount is left as 0, + * this matches glibc and allows resolv wrapper + * to work with most (maybe all) glibc versions. + */ + state->nscount = i; + + return 0; +} + static int rwrap_parse_resolv_conf(struct __res_state *state, const char *resolv_conf) { FILE *fp; char buf[BUFSIZ]; - int nserv = 0; + size_t nserv = 0; + union rwrap_sockaddr nsaddrs[MAXNS]; - rwrap_reset_nameservers(state); + memset(nsaddrs, 0, sizeof(nsaddrs)); fp = fopen(resolv_conf, "r"); if (fp == NULL) { @@ -1726,6 +1777,7 @@ static int rwrap_parse_resolv_conf(struct __res_state *state, if (RESOLV_MATCH(buf, "nameserver") && nserv < MAXNS) { struct in_addr a; + struct in6_addr a6; char *q; int ok; @@ -1744,67 +1796,46 @@ static int rwrap_parse_resolv_conf(struct __res_state *state, ok = inet_pton(AF_INET, p, &a); if (ok) { - state->nsaddr_list[nserv] = (struct sockaddr_in) { - .sin_family = AF_INET, - .sin_addr = a, - .sin_port = htons(53), - .sin_zero = { 0 }, + nsaddrs[nserv] = (union rwrap_sockaddr) { + .in = { + .sin_family = AF_INET, + .sin_addr = a, + .sin_port = htons(53), + .sin_zero = { 0 }, + }, }; nserv++; - } else { + continue; + } + + ok = inet_pton(AF_INET6, p, &a6); + if (ok) { #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[nserv] = sa6; - state->_u._ext.nssocks[nserv] = -1; - state->_u._ext.nsmap[nserv] = MAXNS + 1; - - state->_u._ext.nscount6++; - nserv++; - } else { - RWRAP_LOG(RWRAP_LOG_ERROR, - "Malformed DNS server"); - continue; - } + nsaddrs[nserv] = (union rwrap_sockaddr) { + .in6 = { + + .sin6_family = AF_INET6, + .sin6_port = htons(53), + .sin6_flowinfo = 0, + .sin6_addr = a6, + }, + }; + nserv++; + 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; + continue; #endif } + + RWRAP_LOG(RWRAP_LOG_ERROR, "Malformed DNS server[%s]", p); continue; } /* TODO: match other keywords */ } - /* - * note that state->_u._ext.nscount is left as 0, - * this matches glibc and allows resolv wrapper - * to work with most (maybe all) glibc versions. - */ - state->nscount = nserv; - if (ferror(fp)) { RWRAP_LOG(RWRAP_LOG_ERROR, "Reading from %s failed", @@ -1814,7 +1845,16 @@ static int rwrap_parse_resolv_conf(struct __res_state *state, } fclose(fp); - return 0; + + if (nserv == 0) { + RWRAP_LOG(RWRAP_LOG_ERROR, + "No usable nameservers found in %s", + resolv_conf); + errno = ESRCH; + return -1; + } + + return rwrap_set_nameservers(state, nserv, nsaddrs); } /**************************************************************************** -- 2.26.2 From a3f6bba5805860184fb8a4fa8c6591b326784f83 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 10 Mar 2020 13:11:40 +0100 Subject: [PATCH 7/7] rwrap: make use of res_{get,set}servers() for FreeBSD This way don't depend on the opaque structure on FreeBSD and have support for ipv6 nameservers. Signed-off-by: Stefan Metzmacher --- ConfigureChecks.cmake | 6 +++ config.h.cmake | 2 + src/resolv_wrapper.c | 85 ++++++++++++++++++++++++++++++++++++++++++- tests/test_res_init.c | 40 ++++++++++++++++++++ 4 files changed, 132 insertions(+), 1 deletion(-) diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake index 4752cd4..8444232 100644 --- a/ConfigureChecks.cmake +++ b/ConfigureChecks.cmake @@ -176,6 +176,12 @@ int main(void) { check_struct_has_member("struct __res_state" _u._ext.nsaddrs "sys/socket.h;netinet/in.h;resolv.h" HAVE_RES_STATE_U_EXT_NSADDRS) +check_struct_has_member("union res_sockaddr_union" sin + "sys/socket.h;netinet/in.h;resolv.h" + HAVE_RES_SOCKADDR_UNION_SIN) +check_struct_has_member("union res_sockaddr_union" sin6 + "sys/socket.h;netinet/in.h;resolv.h" + HAVE_RES_SOCKADDR_UNION_SIN6) check_c_source_compiles(" void log_fn(const char *format, ...) __attribute__ ((format (printf, 1, 2))); diff --git a/config.h.cmake b/config.h.cmake index 75e6902..8eba17b 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -56,6 +56,8 @@ #cmakedefine HAVE_IPV6 1 #cmakedefine HAVE_RES_STATE_U_EXT_NSADDRS 1 +#cmakedefine HAVE_RES_SOCKADDR_UNION_SIN 1 +#cmakedefine HAVE_RES_SOCKADDR_UNION_SIN6 1 #cmakedefine HAVE_ATTRIBUTE_PRINTF_FORMAT 1 #cmakedefine HAVE_DESTRUCTOR_ATTRIBUTE 1 diff --git a/src/resolv_wrapper.c b/src/resolv_wrapper.c index e1903c9..0d3f34c 100644 --- a/src/resolv_wrapper.c +++ b/src/resolv_wrapper.c @@ -52,7 +52,7 @@ #include -#ifdef HAVE_RES_STATE_U_EXT_NSADDRS +#if defined(HAVE_RES_STATE_U_EXT_NSADDRS) || defined(HAVE_RES_SOCKADDR_UNION_SIN6) #define HAVE_RESOLV_IPV6_NSADDRS 1 #endif @@ -1616,6 +1616,45 @@ static size_t rwrap_get_nameservers(struct __res_state *state, size_t nserv, union rwrap_sockaddr *nsaddrs) { +#ifdef HAVE_RES_SOCKADDR_UNION_SIN + union res_sockaddr_union set[MAXNS]; + size_t i; + int rc; + + memset(set, 0, sizeof(set)); + memset(nsaddrs, 0, sizeof(*nsaddrs) * nserv); + + if (nserv > MAXNS) { + nserv = MAXNS; + } + + rc = res_getservers(state, set, nserv); + if (rc <= 0) { + return 0; + } + if (rc < nserv) { + nserv = rc; + } + + for (i = 0; i < nserv; i++) { + switch (set[i].sin.sin_family) { + case AF_INET: + nsaddrs[i] = (union rwrap_sockaddr) { + .in = set[i].sin, + }; + break; +#ifdef HAVE_RES_SOCKADDR_UNION_SIN6 + case AF_INET6: + nsaddrs[i] = (union rwrap_sockaddr) { + .in6 = set[i].sin6, + }; + break; +#endif + } + } + + return nserv; +#else /* ! HAVE_RES_SOCKADDR_UNION_SIN */ size_t i; memset(nsaddrs, 0, sizeof(*nsaddrs) * nserv); @@ -1640,6 +1679,7 @@ static size_t rwrap_get_nameservers(struct __res_state *state, } return nserv; +#endif /* ! HAVE_RES_SOCKADDR_UNION_SIN */ } static void rwrap_log_nameservers(enum rwrap_dbglvl_e dbglvl, @@ -1678,6 +1718,9 @@ static void rwrap_log_nameservers(enum rwrap_dbglvl_e dbglvl, static void rwrap_reset_nameservers(struct __res_state *state) { +#ifdef HAVE_RES_SOCKADDR_UNION_SIN + res_setservers(state, NULL, 0); +#else /* ! HAVE_RES_SOCKADDR_UNION_SIN */ #ifdef HAVE_RES_STATE_U_EXT_NSADDRS size_t i; @@ -1697,12 +1740,51 @@ static void rwrap_reset_nameservers(struct __res_state *state) #endif memset(state->nsaddr_list, 0, sizeof(state->nsaddr_list)); state->nscount = 0; +#endif /* ! HAVE_RES_SOCKADDR_UNION_SIN */ } static int rwrap_set_nameservers(struct __res_state *state, size_t nserv, const union rwrap_sockaddr *nsaddrs) { +#ifdef HAVE_RES_SOCKADDR_UNION_SIN + union res_sockaddr_union set[MAXNS]; + size_t i; + + memset(set, 0, sizeof(set)); + + if (nserv > MAXNS) { + nserv = MAXNS; + } + + rwrap_reset_nameservers(state); + + for (i = 0; i < nserv; i++) { + switch (nsaddrs[i].sa.sa_family) { + case AF_INET: + set[i] = (union res_sockaddr_union) { + .sin = nsaddrs[i].in, + }; + break; +#ifdef HAVE_RES_SOCKADDR_UNION_SIN6 + case AF_INET6: + set[i] = (union res_sockaddr_union) { + .sin6 = nsaddrs[i].in6, + }; + break; +#endif + default: + RWRAP_LOG(RWRAP_LOG_ERROR, + "Internal error unhandled sa_family=%d", + nsaddrs[i].sa.sa_family); + errno = ENOSYS; + return -1; + } + } + + res_setservers(state, set, nserv); + return 0; +#else /* ! HAVE_RES_SOCKADDR_UNION_SIN */ size_t i; if (nserv > MAXNS) { @@ -1747,6 +1829,7 @@ static int rwrap_set_nameservers(struct __res_state *state, state->nscount = i; return 0; +#endif /* ! HAVE_RES_SOCKADDR_UNION_SIN */ } static int rwrap_parse_resolv_conf(struct __res_state *state, diff --git a/tests/test_res_init.c b/tests/test_res_init.c index eb0eee7..b79e28c 100644 --- a/tests/test_res_init.c +++ b/tests/test_res_init.c @@ -92,6 +92,9 @@ static void test_res_ninit(void **state) int i; int rv; char straddr[INET6_ADDRSTRLEN] = { '\0' }; +#ifdef HAVE_RES_SOCKADDR_UNION_SIN + union res_sockaddr_union set[MAXNS*5]; +#endif #ifdef HAVE_RES_STATE_U_EXT_NSADDRS struct sockaddr_in6 *sa6; #endif @@ -131,6 +134,41 @@ static void test_res_ninit(void **state) * case on all systems encountered so far. */ assert_int_equal(MAXNS, 3); +#ifdef HAVE_RES_SOCKADDR_UNION_SIN + memset(set, 0, sizeof(set)); + rv = res_getservers(&dnsstate, set, MAXNS+5); + assert_int_equal(rv, MAXNS); + + /* IPv4 */ + assert_int_equal(set[0].sin.sin_family, AF_INET); + assert_int_equal(set[0].sin.sin_port, htons(53)); + inet_ntop(AF_INET, &(set[0].sin.sin_addr), + straddr, INET6_ADDRSTRLEN); + assert_string_equal(nameservers[0], straddr); + + assert_int_equal(set[1].sin.sin_family, AF_INET); + assert_int_equal(set[1].sin.sin_port, htons(53)); + inet_ntop(AF_INET, &(set[1].sin.sin_addr), straddr, INET6_ADDRSTRLEN); + assert_string_equal(nameservers[1], straddr); + +#ifdef HAVE_RES_SOCKADDR_UNION_SIN6 + /* IPv6 */ + assert_int_equal(set[2].sin6.sin6_family, AF_INET6); + assert_int_equal(set[2].sin6.sin6_port, htons(53)); + inet_ntop(AF_INET6, &(set[2].sin6.sin6_addr), straddr, INET6_ADDRSTRLEN); + assert_string_equal(nameservers[2], straddr); +#else /* ! HAVE_RES_SOCKADDR_UNION_SIN6 */ + /* + * On platforms that don't support IPv6, the v6 address is skipped + * and we end up reading three v4 addresses. + */ + assert_int_equal(set[2].sin.sin_family, AF_INET); + assert_int_equal(set[2].sin.sin_port, htons(53)); + inet_ntop(AF_INET, &(set[2].sin.sin_addr), straddr, INET6_ADDRSTRLEN); + assert_string_equal(nameservers[3], straddr); +#endif /* ! HAVE_RES_SOCKADDR_UNION_SIN6 */ + +#else /* ! HAVE_RES_SOCKADDR_UNION_SIN */ assert_int_equal(dnsstate.nscount, MAXNS); /* Validate the servers. */ @@ -174,6 +212,8 @@ static void test_res_ninit(void **state) assert_string_equal(nameservers[3], straddr); #endif +#endif /* ! HAVE_RES_SOCKADDR_UNION_SIN */ + res_nclose(&dnsstate); } -- 2.26.2 resolv_wrapper-1.1.7/README.md000644 001750 000144 00000000720 13636077442 016044 0ustar00asnusers000000 000000 RESOLV_WRAPPER =============== This is a wrapper for dns resolving. DESCRIPTION ----------- More details can be found in the manpage: man -l ./doc/resolv_wrapper.1 or the raw text version: less ./doc/resolv_wrapper.1.txt For installation instructions please take a look at the README.install file. MAILINGLIST ----------- As the mailing list samba-technical is used and can be found here: https://lists.samba.org/mailman/listinfo/samba-technical resolv_wrapper-1.1.7/CPackConfig.cmake000644 001750 000144 00000003327 13720751241 017672 0ustar00asnusers000000 000000 # For help take a look at: # http://www.cmake.org/Wiki/CMake:CPackConfiguration ### general settings set(CPACK_PACKAGE_NAME ${PROJECT_NAME}) set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "The resolv wrapper") set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README.md") set(CPACK_PACKAGE_VENDOR "The Samba Team") set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME}) set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE") ### versions set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}") ### source generator set(CPACK_SOURCE_GENERATOR "TGZ") set(CPACK_SOURCE_IGNORE_FILES "~$;[.]swp$;/[.]svn/;/[.]git/;.gitignore;/build*;/obj*;tags;cscope.*") set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") if (WIN32) set(CPACK_GENERATOR "ZIP") ### nsis generator find_package(NSIS) if (NSIS_MAKE) set(CPACK_GENERATOR "${CPACK_GENERATOR};NSIS") set(CPACK_NSIS_DISPLAY_NAME "The resolv wrapper") set(CPACK_NSIS_COMPRESSOR "/SOLID zlib") set(CPACK_NSIS_MENU_LINKS "http://www.samba.org/" "Samba homepage") endif (NSIS_MAKE) endif (WIN32) set(CPACK_PACKAGE_INSTALL_DIRECTORY "libssh") set(CPACK_PACKAGE_FILE_NAME ${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}) set(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME "Libraries") set(CPACK_COMPONENT_HEADERS_DISPLAY_NAME "C/C++ Headers") set(CPACK_COMPONENT_LIBRARIES_DESCRIPTION "Libraries used to build programs which use libssh") set(CPACK_COMPONENT_HEADERS_DESCRIPTION "C/C++ header files for use with libssh") set(CPACK_COMPONENT_HEADERS_DEPENDS libraries) #set(CPACK_COMPONENT_APPLICATIONS_GROUP "Runtime") set(CPACK_COMPONENT_LIBRARIES_GROUP "Development") set(CPACK_COMPONENT_HEADERS_GROUP "Development") include(CPack) resolv_wrapper-1.1.7/DefineOptions.cmake000644 001750 000144 00000000160 13636077442 020333 0ustar00asnusers000000 000000 option(UNIT_TESTING "Build with unit tests" OFF) option(PICKY_DEVELOPER "Build with picky developer flags" OFF) resolv_wrapper-1.1.7/AUTHORS000644 001750 000144 00000000037 12427110221 015612 0ustar00asnusers000000 000000 Andreas Schneider Jakub Hrozek resolv_wrapper-1.1.7/LICENSE000644 001750 000144 00000003055 13341215233 015557 0ustar00asnusers000000 000000 BSD 3-Clause License Copyright (c) 2014-2018, Andreas Schneider Copyright (c) 2014-2016, 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.