From f34b4540fd1b860ecff38f9caaa6f4c48197a18a Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 10 Mar 2020 13:11:40 +0100 Subject: [PATCH] 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 Reviewed-by: Andreas Schneider --- 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.34.1