From 21b6b4a437b7d612d0f6e79bb773235b6f2a6596 Mon Sep 17 00:00:00 2001 From: Brad House Date: Thu, 22 Aug 2024 19:24:06 -0400 Subject: [PATCH] Fix Sysconfig ndots default value and add test case (#862) As per #852 searching is failing, partially it is due to the ndots value not defaulting to a proper value on linux, and partially due to systemd-resolved returning the wrong error codes. This PR fixes the first issue and adds containerized test cases to validate the behavior and prevent issues in the future. Reported-By: Hans-Christian Egtvedt (@egtvedt) and Mikael Lindemann(@mikaellindemann) Authored-By: Brad House (@bradh352) --- .github/workflows/alpine-latest.yml | 6 +-- .github/workflows/coveralls.yml | 2 +- .github/workflows/ubuntu-latest.yml | 12 ++--- configure.ac | 8 ++- src/lib/ares_sysconfig.c | 1 + test/ares-test-ai.h | 6 +-- test/ares-test-init.cc | 20 +++++--- test/ares-test-mock-ai.cc | 8 +-- test/ares-test-mock.cc | 77 ++++++++++++++++++++++++++--- test/ares-test-ns.cc | 13 +++-- test/ares-test.cc | 56 ++++++++++----------- test/ares-test.h | 54 ++++++++++++++++++-- 12 files changed, 194 insertions(+), 69 deletions(-) diff --git a/.github/workflows/alpine-latest.yml b/.github/workflows/alpine-latest.yml index 916c8d4b..85539d9f 100644 --- a/.github/workflows/alpine-latest.yml +++ b/.github/workflows/alpine-latest.yml @@ -34,7 +34,7 @@ jobs: - name: "CMake: build and test c-ares" env: BUILD_TYPE: CMAKE - CMAKE_TEST_FLAGS: "-DCARES_BUILD_TESTS=ON" + CMAKE_TEST_FLAGS: "-DCARES_BUILD_CONTAINER_TESTS=ON" TEST_DEBUGGER: gdb run: | ./ci/build.sh @@ -51,7 +51,7 @@ jobs: BUILD_TYPE: "ubsan" CC: "clang" CXX: "clang++" - CMAKE_TEST_FLAGS: "-DCARES_BUILD_TESTS=ON" + CMAKE_TEST_FLAGS: "-DCARES_BUILD_CONTAINER_TESTS=ON" CFLAGS: "-fsanitize=undefined -fno-sanitize-recover" CXXFLAGS: "-fsanitize=undefined -fno-sanitize-recover" LDFLAGS: "-fsanitize=undefined" @@ -64,7 +64,7 @@ jobs: BUILD_TYPE: "asan" CC: "clang" CXX: "clang++" - CMAKE_TEST_FLAGS: "-DCARES_BUILD_TESTS=ON" + CMAKE_TEST_FLAGS: "-DCARES_BUILD_CONTAINER_TESTS=ON" CFLAGS: "-fsanitize=address" CXXFLAGS: "-fsanitize=address" LDFLAGS: "-fsanitize=address" diff --git a/.github/workflows/coveralls.yml b/.github/workflows/coveralls.yml index 80300776..d5e66397 100644 --- a/.github/workflows/coveralls.yml +++ b/.github/workflows/coveralls.yml @@ -19,7 +19,7 @@ jobs: run: | mkdir build cd build - cmake -DCMAKE_BUILD_TYPE=DEBUG -DCARES_BUILD_TESTS=ON -DCARES_COVERAGE=ON -DCARES_BUILD_TOOLS=OFF -G Ninja .. + cmake -DCMAKE_BUILD_TYPE=DEBUG -DCARES_BUILD_CONTAINER_TESTS=ON -DCARES_COVERAGE=ON -DCARES_BUILD_TOOLS=OFF -G Ninja .. ninja - name: Test shell: bash diff --git a/.github/workflows/ubuntu-latest.yml b/.github/workflows/ubuntu-latest.yml index ac9a09c1..2aada5f6 100644 --- a/.github/workflows/ubuntu-latest.yml +++ b/.github/workflows/ubuntu-latest.yml @@ -32,7 +32,7 @@ jobs: - name: "CMake: build and test c-ares" env: BUILD_TYPE: CMAKE - CMAKE_TEST_FLAGS: "-DCARES_BUILD_TESTS=ON" + CMAKE_TEST_FLAGS: "-DCARES_BUILD_CONTAINER_TESTS=ON" TEST_DEBUGGER: gdb run: | ./ci/build.sh @@ -49,7 +49,7 @@ jobs: env: BUILD_TYPE: CMAKE CMAKE_FLAGS: "-DCMAKE_BUILD_TYPE=DEBUG -DCARES_STATIC=ON -DCARES_STATIC_PIC=ON -DCARES_THREADS=OFF -G Ninja" - CMAKE_TEST_FLAGS: "-DCARES_BUILD_TESTS=ON" + CMAKE_TEST_FLAGS: "-DCARES_BUILD_CONTAINER_TESTS=ON" TEST_DEBUGGER: gdb run: | ./ci/build.sh @@ -58,7 +58,7 @@ jobs: env: BUILD_TYPE: CMAKE CMAKE_FLAGS: "-DCMAKE_BUILD_TYPE=DEBUG -DCARES_STATIC=OFF -DCARES_SYMBOL_HIDING=ON -G Ninja" - CMAKE_TEST_FLAGS: "-DCARES_BUILD_TESTS=ON" + CMAKE_TEST_FLAGS: "-DCARES_BUILD_CONTAINER_TESTS=ON" TEST_DEBUGGER: gdb run: | ./ci/build.sh @@ -68,7 +68,7 @@ jobs: BUILD_TYPE: "ubsan" CC: "clang" CXX: "clang++" - CMAKE_TEST_FLAGS: "-DCARES_BUILD_TESTS=ON" + CMAKE_TEST_FLAGS: "-DCARES_BUILD_CONTAINER_TESTS=ON" CFLAGS: "-fsanitize=undefined -fno-sanitize-recover" CXXFLAGS: "-fsanitize=undefined -fno-sanitize-recover" LDFLAGS: "-fsanitize=undefined" @@ -81,7 +81,7 @@ jobs: BUILD_TYPE: "asan" CC: "clang" CXX: "clang++" - CMAKE_TEST_FLAGS: "-DCARES_BUILD_TESTS=ON" + CMAKE_TEST_FLAGS: "-DCARES_BUILD_CONTAINER_TESTS=ON" CFLAGS: "-fsanitize=address" CXXFLAGS: "-fsanitize=address" LDFLAGS: "-fsanitize=address" @@ -114,7 +114,7 @@ jobs: - name: "CMake: No TCP FastOpen" env: BUILD_TYPE: CMAKE - CMAKE_TEST_FLAGS: "-DCARES_BUILD_TESTS=ON" + CMAKE_TEST_FLAGS: "-DCARES_BUILD_CONTAINER_TESTS=ON" TEST_DEBUGGER: gdb run: | sudo sysctl -w net.ipv4.tcp_fastopen=0 diff --git a/configure.ac b/configure.ac index be891e40..3b555fbc 100644 --- a/configure.ac +++ b/configure.ac @@ -153,8 +153,6 @@ _EOF ]) AX_CODE_COVERAGE -AX_CHECK_USER_NAMESPACE -AX_CHECK_UTS_NAMESPACE AC_SYS_LARGEFILE case $host_os in @@ -819,6 +817,12 @@ if test "x$build_tests" != "xno" ; then else AC_MSG_ERROR([tests require gmock]) fi + else + PKG_CHECK_MODULES([GMOCK112], [gmock >= 1.12.0], [ have_gmock_v112=yes ], [ have_gmock_v112=no ]) + if test "x$have_gmock_v112" = "xyes" ; then + AX_CHECK_USER_NAMESPACE + AX_CHECK_UTS_NAMESPACE + fi fi fi if test "x$build_tests" != "xno" ; then diff --git a/src/lib/ares_sysconfig.c b/src/lib/ares_sysconfig.c index 317b3a52..61e6a423 100644 --- a/src/lib/ares_sysconfig.c +++ b/src/lib/ares_sysconfig.c @@ -494,6 +494,7 @@ ares_status_t ares__init_by_sysconfig(ares_channel_t *channel) ares_sysconfig_t sysconfig; memset(&sysconfig, 0, sizeof(sysconfig)); + sysconfig.ndots = 1; /* Default value if not otherwise set */ #if defined(USE_WINSOCK) status = ares__init_sysconfig_windows(&sysconfig); diff --git a/test/ares-test-ai.h b/test/ares-test-ai.h index 95eeb885..f127fae7 100644 --- a/test/ares-test-ai.h +++ b/test/ares-test-ai.h @@ -39,7 +39,7 @@ class MockChannelTestAI public ::testing::WithParamInterface> { public: MockChannelTestAI() - : MockChannelOptsTest(1, GetParam().first, GetParam().second, nullptr, 0) + : MockChannelOptsTest(1, GetParam().first, GetParam().second, false, nullptr, 0) { } }; @@ -47,7 +47,7 @@ public: class MockUDPChannelTestAI : public MockChannelOptsTest, public ::testing::WithParamInterface { public: - MockUDPChannelTestAI() : MockChannelOptsTest(1, GetParam(), false, nullptr, 0) + MockUDPChannelTestAI() : MockChannelOptsTest(1, GetParam(), false, false, nullptr, 0) { } }; @@ -55,7 +55,7 @@ public: class MockTCPChannelTestAI : public MockChannelOptsTest, public ::testing::WithParamInterface { public: - MockTCPChannelTestAI() : MockChannelOptsTest(1, GetParam(), true, nullptr, 0) + MockTCPChannelTestAI() : MockChannelOptsTest(1, GetParam(), true, false, nullptr, 0) { } }; diff --git a/test/ares-test-init.cc b/test/ares-test-init.cc index 4fd5cff6..453848f3 100644 --- a/test/ares-test-init.cc +++ b/test/ares-test-init.cc @@ -417,7 +417,8 @@ CONTAINED_TEST_F(LibraryTest, ContainerChannelInit, CONTAINED_TEST_F(LibraryTest, ContainerSortlistOptionInit, "myhostname", "mydomainname.org", filelist) { ares_channel_t *channel = nullptr; - struct ares_options opts = {0}; + struct ares_options opts; + memset(&opts, 0, sizeof(opts)); int optmask = 0; optmask |= ARES_OPT_SORTLIST; opts.nsort = 0; @@ -461,7 +462,8 @@ CONTAINED_TEST_F(LibraryTest, ContainerMyResolvConfInit, "myhostname", "mydomain.org", myresolvconf) { char filename[] = "/tmp/myresolv.cnf"; ares_channel_t *channel = nullptr; - struct ares_options options = {0}; + struct ares_options options; + memset(&options, 0, sizeof(options)); options.resolvconf_path = strdup(filename); int optmask = ARES_OPT_RESOLVCONF; EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel, &options, optmask)); @@ -487,11 +489,12 @@ CONTAINED_TEST_F(LibraryTest, ContainerMyHostsInit, "myhostname", "mydomain.org", myhosts) { char filename[] = "/tmp/hosts"; ares_channel_t *channel = nullptr; - struct ares_options options = {0}; + struct ares_options options; + options.hosts_path = strdup(filename); int optmask = ARES_OPT_HOSTS_FILE; EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel, &options, optmask)); - + memset(&options, 0, sizeof(options)); optmask = 0; free(options.hosts_path); options.hosts_path = NULL; @@ -595,7 +598,8 @@ CONTAINED_TEST_F(LibraryTest, ContainerRotateInit, CONTAINED_TEST_F(LibraryTest, ContainerRotateOverride, "myhostname", "mydomainname.org", rotateenv) { ares_channel_t *channel = nullptr; - struct ares_options opts = {0}; + struct ares_options opts; + memset(&opts, 0, sizeof(opts)); int optmask = ARES_OPT_NOROTATE; EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel, &opts, optmask)); optmask = 0; @@ -687,7 +691,8 @@ CONTAINED_TEST_F(LibraryTest, ContainerEmptyInit, CONTAINED_TEST_F(LibraryTest, ContainerNoDfltSvrEmptyInit, "myhostname", "mydomainname.org", empty) { ares_channel_t *channel = nullptr; - struct ares_options opts = {0}; + struct ares_options opts; + memset(&opts, 0, sizeof(opts)); int optmask = ARES_OPT_FLAGS; opts.flags = ARES_FLAG_NO_DFLT_SVR; EXPECT_EQ(ARES_ENOSERVER, ares_init_options(&channel, &opts, optmask)); @@ -700,7 +705,8 @@ CONTAINED_TEST_F(LibraryTest, ContainerNoDfltSvrEmptyInit, CONTAINED_TEST_F(LibraryTest, ContainerNoDfltSvrFullInit, "myhostname", "mydomainname.org", filelist) { ares_channel_t *channel = nullptr; - struct ares_options opts = {0}; + struct ares_options opts; + memset(&opts, 0, sizeof(opts)); int optmask = ARES_OPT_FLAGS; opts.flags = ARES_FLAG_NO_DFLT_SVR; EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel, &opts, optmask)); diff --git a/test/ares-test-mock-ai.cc b/test/ares-test-mock-ai.cc index 0b61a166..8d6e7f78 100644 --- a/test/ares-test-mock-ai.cc +++ b/test/ares-test-mock-ai.cc @@ -266,7 +266,7 @@ class MockExtraOptsTestAI public ::testing::WithParamInterface< std::pair > { public: MockExtraOptsTestAI() - : MockChannelOptsTest(1, GetParam().first, GetParam().second, + : MockChannelOptsTest(1, GetParam().first, GetParam().second, false, FillOptions(&opts_), ARES_OPT_SOCK_SNDBUF|ARES_OPT_SOCK_RCVBUF) {} static struct ares_options* FillOptions(struct ares_options * opts) { @@ -311,7 +311,7 @@ class MockExtraOptsNDotsTestAI public ::testing::WithParamInterface< std::pair > { public: MockExtraOptsNDotsTestAI(int ndots) - : MockChannelOptsTest(1, GetParam().first, GetParam().second, + : MockChannelOptsTest(1, GetParam().first, GetParam().second, false, FillOptions(&opts_, ndots), ARES_OPT_SOCK_SNDBUF|ARES_OPT_SOCK_RCVBUF|ARES_OPT_NDOTS) {} static struct ares_options* FillOptions(struct ares_options * opts, int ndots) { @@ -409,7 +409,7 @@ class MockFlagsChannelOptsTestAI public ::testing::WithParamInterface< std::pair > { public: MockFlagsChannelOptsTestAI(int flags) - : MockChannelOptsTest(1, GetParam().first, GetParam().second, + : MockChannelOptsTest(1, GetParam().first, GetParam().second, false, FillOptions(&opts_, flags), ARES_OPT_FLAGS) {} static struct ares_options* FillOptions(struct ares_options * opts, int flags) { memset(opts, 0, sizeof(struct ares_options)); @@ -704,7 +704,7 @@ class MockMultiServerChannelTestAI public ::testing::WithParamInterface< std::pair > { public: MockMultiServerChannelTestAI(ares_options *opts, int optmask) - : MockChannelOptsTest(3, GetParam().first, GetParam().second, opts, optmask) {} + : MockChannelOptsTest(3, GetParam().first, GetParam().second, false, opts, optmask) {} void CheckExample() { AddrInfoResult result; struct ares_addrinfo_hints hints = {0, 0, 0, 0}; diff --git a/test/ares-test-mock.cc b/test/ares-test-mock.cc index af591a87..6d66b9bb 100644 --- a/test/ares-test-mock.cc +++ b/test/ares-test-mock.cc @@ -45,7 +45,7 @@ class NoDNS0x20MockTest public ::testing::WithParamInterface { public: NoDNS0x20MockTest() - : MockChannelOptsTest(1, GetParam(), false, + : MockChannelOptsTest(1, GetParam(), false, false, FillOptions(&opts_), ARES_OPT_FLAGS) {} static struct ares_options* FillOptions(struct ares_options * opts) { @@ -453,7 +453,7 @@ class MockUDPMaxQueriesTest public ::testing::WithParamInterface { public: MockUDPMaxQueriesTest() - : MockChannelOptsTest(1, GetParam(), false, + : MockChannelOptsTest(1, GetParam(), false, false, FillOptions(&opts_), ARES_OPT_UDP_MAX_QUERIES) {} static struct ares_options* FillOptions(struct ares_options * opts) { @@ -500,7 +500,7 @@ class CacheQueriesTest public ::testing::WithParamInterface { public: CacheQueriesTest() - : MockChannelOptsTest(1, GetParam(), false, + : MockChannelOptsTest(1, GetParam(), false, false, FillOptions(&opts_), ARES_OPT_QUERY_CACHE) {} static struct ares_options* FillOptions(struct ares_options * opts) { @@ -665,7 +665,7 @@ class MockExtraOptsTest public ::testing::WithParamInterface< std::pair > { public: MockExtraOptsTest() - : MockChannelOptsTest(1, GetParam().first, GetParam().second, + : MockChannelOptsTest(1, GetParam().first, GetParam().second, false, FillOptions(&opts_), ARES_OPT_SOCK_SNDBUF|ARES_OPT_SOCK_RCVBUF) {} static struct ares_options* FillOptions(struct ares_options * opts) { @@ -707,7 +707,7 @@ class MockFlagsChannelOptsTest public ::testing::WithParamInterface< std::pair > { public: MockFlagsChannelOptsTest(int flags) - : MockChannelOptsTest(1, GetParam().first, GetParam().second, + : MockChannelOptsTest(1, GetParam().first, GetParam().second, false, FillOptions(&opts_, flags), ARES_OPT_FLAGS) {} static struct ares_options* FillOptions(struct ares_options * opts, int flags) { memset(opts, 0, sizeof(struct ares_options)); @@ -817,6 +817,67 @@ TEST_P(MockChannelTest, SearchDomains) { EXPECT_EQ("{'www.third.gov' aliases=[] addrs=[2.3.4.5]}", ss.str()); } +#ifdef HAVE_CONTAINER +// Issue #852 +class ContainedMockChannelSysConfig + : public MockChannelOptsTest, + public ::testing::WithParamInterface> { + public: + ContainedMockChannelSysConfig() + : MockChannelOptsTest(1, GetParam().first, GetParam().second, true, nullptr, 0) {} +}; + +NameContentList files_no_ndots = { + {"/etc/resolv.conf", "nameserver 1.2.3.4\n" // Will be replaced + "search example.com example.org\n" + "options edns0 trust-ad\n"}, // ndots:1 is default + {"/etc/hosts", "3.4.5.6 ahostname.com\n"}, + {"/etc/nsswitch.conf", "hosts: files dns\n"}}; +CONTAINED_TEST_P(ContainedMockChannelSysConfig, SysConfigNdotsDefault, + "myhostname", "mydomainname.org", files_no_ndots) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www.example.com", T_A)) + .add_answer(new DNSARR("www.example.com", 0x0200, {2, 3, 4, 5})); + EXPECT_CALL(server_, OnRequest("www.example.com", T_A)) + .WillOnce(SetReply(&server_, &rsp)); + + HostResult result; + ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'www.example.com' aliases=[] addrs=[2.3.4.5]}", ss.str()); + return HasFailure(); +} + +NameContentList files_ndots0 = { + {"/etc/resolv.conf", "nameserver 1.2.3.4\n" // Will be replaced + "search example.com example.org\n" + "options edns0 trust-ad ndots:0\n"}, // ndots:1 is default + {"/etc/hosts", "3.4.5.6 ahostname.com\n"}, + {"/etc/nsswitch.conf", "hosts: files dns\n"}}; +CONTAINED_TEST_P(ContainedMockChannelSysConfig, SysConfigNdots0, + "myhostname", "mydomainname.org", files_ndots0) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www", T_A)) + .add_answer(new DNSARR("www", 0x0200, {1, 2, 3, 4})); + EXPECT_CALL(server_, OnRequest("www", T_A)) + .WillOnce(SetReply(&server_, &rsp)); + + HostResult result; + ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'www' aliases=[] addrs=[1.2.3.4]}", ss.str()); + return HasFailure(); +} +#endif + // Issue #858 TEST_P(CacheQueriesTest, BlankName) { DNSPacket rsp; @@ -1933,7 +1994,7 @@ class MockMultiServerChannelTest public ::testing::WithParamInterface< std::pair > { public: MockMultiServerChannelTest(ares_options *opts, int optmask) - : MockChannelOptsTest(3, GetParam().first, GetParam().second, opts, optmask) {} + : MockChannelOptsTest(3, GetParam().first, GetParam().second, false, opts, optmask) {} void CheckExample() { HostResult result; ares_gethostbyname(channel_, "www.example.com.", AF_INET, HostCallback, &result); @@ -2253,6 +2314,10 @@ INSTANTIATE_TEST_SUITE_P(AddressFamilies, NoDNS0x20MockTest, ::testing::ValuesIn INSTANTIATE_TEST_SUITE_P(AddressFamilies, MockChannelTest, ::testing::ValuesIn(ares::test::families_modes), PrintFamilyMode); +#ifdef HAVE_CONTAINER +INSTANTIATE_TEST_SUITE_P(AddressFamilies, ContainedMockChannelSysConfig, ::testing::ValuesIn(ares::test::families_modes), PrintFamilyMode); +#endif + INSTANTIATE_TEST_SUITE_P(AddressFamilies, MockUDPChannelTest, ::testing::ValuesIn(ares::test::families), PrintFamily); INSTANTIATE_TEST_SUITE_P(AddressFamilies, MockUDPMaxQueriesTest, ::testing::ValuesIn(ares::test::families), PrintFamily); diff --git a/test/ares-test-ns.cc b/test/ares-test-ns.cc index 88370387..3ff9b9ad 100644 --- a/test/ares-test-ns.cc +++ b/test/ares-test-ns.cc @@ -155,8 +155,8 @@ int RunInContainer(ContainerFilesystem* fs, const std::string& hostname, std::stringstream contentss; contentss << "0 " << getuid() << " 1" << std::endl; std::string content = contentss.str(); - int rc = write(fd, content.c_str(), content.size()); - if (rc != (int)content.size()) { + ssize_t rc = write(fd, content.c_str(), content.size()); + if (rc != (ssize_t)content.size()) { std::cerr << "Failed to write uid map to '" << mapfile << "'" << std::endl; } close(fd); @@ -181,8 +181,13 @@ ContainerFilesystem::ContainerFilesystem(NameContentList files, const std::strin dirs_.push_front(rootdir_); for (const auto& nc : files) { std::string fullpath = rootdir_ + nc.first; - int idx = fullpath.rfind('/'); - std::string dir = fullpath.substr(0, idx); + size_t idx = fullpath.rfind('/'); + std::string dir; + if (idx != SIZE_MAX) { + dir = fullpath.substr(0, idx); + } else { + dir = fullpath; + } EnsureDirExists(dir); files_.push_back(std::unique_ptr( new TransientFile(fullpath, nc.second))); diff --git a/test/ares-test.cc b/test/ares-test.cc index b41cc57d..99ab0a00 100644 --- a/test/ares-test.cc +++ b/test/ares-test.cc @@ -766,11 +766,13 @@ MockChannelOptsTest::NiceMockServers MockChannelOptsTest::BuildServers(int count MockChannelOptsTest::MockChannelOptsTest(int count, int family, bool force_tcp, + bool honor_sysconfig, struct ares_options* givenopts, int optmask) : servers_(BuildServers(count, family, mock_port)), server_(*servers_[0].get()), channel_(nullptr) { // Set up channel options. + const char *domains[3] = {"first.com", "second.org", "third.gov"}; struct ares_options opts; if (givenopts) { memcpy(&opts, givenopts, sizeof(opts)); @@ -778,41 +780,39 @@ MockChannelOptsTest::MockChannelOptsTest(int count, memset(&opts, 0, sizeof(opts)); } - // Point the library at the first mock server by default (overridden below). - opts.udp_port = server_.udpport(); - optmask |= ARES_OPT_UDP_PORT; - opts.tcp_port = server_.tcpport(); - optmask |= ARES_OPT_TCP_PORT; + /* Honor items from resolv.conf except the dns server itself */ + if (!honor_sysconfig) { + if (!(optmask & (ARES_OPT_TIMEOUTMS|ARES_OPT_TIMEOUT))) { + // Reduce timeouts significantly to shorten test times. + opts.timeout = 250; + optmask |= ARES_OPT_TIMEOUTMS; + } + // If not already overridden, set 3 retries. + if (!(optmask & ARES_OPT_TRIES)) { + opts.tries = 3; + optmask |= ARES_OPT_TRIES; + } - if (!(optmask & (ARES_OPT_TIMEOUTMS|ARES_OPT_TIMEOUT))) { - // Reduce timeouts significantly to shorten test times. - opts.timeout = 250; - optmask |= ARES_OPT_TIMEOUTMS; - } - // If not already overridden, set 3 retries. - if (!(optmask & ARES_OPT_TRIES)) { - opts.tries = 3; - optmask |= ARES_OPT_TRIES; - } - // If not already overridden, set search domains. - const char *domains[3] = {"first.com", "second.org", "third.gov"}; - if (!(optmask & ARES_OPT_DOMAINS)) { - opts.ndomains = 3; - opts.domains = (char**)domains; - optmask |= ARES_OPT_DOMAINS; + // If not already overridden, set search domains. + if (!(optmask & ARES_OPT_DOMAINS)) { + opts.ndomains = 3; + opts.domains = (char**)domains; + optmask |= ARES_OPT_DOMAINS; + } + + /* Tests expect ndots=1 in general, the system config may not default to this + * so we don't want to inherit that. */ + if (!(optmask & ARES_OPT_NDOTS)) { + opts.ndots = 1; + optmask |= ARES_OPT_NDOTS; + } } + if (force_tcp) { opts.flags |= ARES_FLAG_USEVC; optmask |= ARES_OPT_FLAGS; } - /* Tests expect ndots=1 in general, the system config may not default to this - * so we don't want to inherit that. */ - if (!(optmask & ARES_OPT_NDOTS)) { - opts.ndots = 1; - optmask |= ARES_OPT_NDOTS; - } - /* Disable the query cache for tests unless explicitly enabled. As of * c-ares 1.31.0, the query cache is enabled by default so we have to set * the option and set the TTL to 0 to effectively disable it. */ diff --git a/test/ares-test.h b/test/ares-test.h index ea25498d..7342daaa 100644 --- a/test/ares-test.h +++ b/test/ares-test.h @@ -311,7 +311,7 @@ private: // Test fixture that uses a mock DNS server. class MockChannelOptsTest : public LibraryTest { public: - MockChannelOptsTest(int count, int family, bool force_tcp, + MockChannelOptsTest(int count, int family, bool force_tcp, bool honor_sysconfig, struct ares_options *givenopts, int optmask); ~MockChannelOptsTest(); @@ -341,7 +341,7 @@ class MockChannelTest public ::testing::WithParamInterface> { public: MockChannelTest() - : MockChannelOptsTest(1, GetParam().first, GetParam().second, nullptr, 0) + : MockChannelOptsTest(1, GetParam().first, GetParam().second, false, nullptr, 0) { } }; @@ -349,7 +349,7 @@ public: class MockUDPChannelTest : public MockChannelOptsTest, public ::testing::WithParamInterface { public: - MockUDPChannelTest() : MockChannelOptsTest(1, GetParam(), false, nullptr, 0) + MockUDPChannelTest() : MockChannelOptsTest(1, GetParam(), false, false, nullptr, 0) { } }; @@ -357,7 +357,7 @@ public: class MockTCPChannelTest : public MockChannelOptsTest, public ::testing::WithParamInterface { public: - MockTCPChannelTest() : MockChannelOptsTest(1, GetParam(), true, nullptr, 0) + MockTCPChannelTest() : MockChannelOptsTest(1, GetParam(), true, false, nullptr, 0) { } }; @@ -367,7 +367,7 @@ public: MockEventThreadOptsTest(int count, ares_evsys_t evsys, int family, bool force_tcp, struct ares_options *givenopts, int optmask) - : MockChannelOptsTest(count, family, force_tcp, + : MockChannelOptsTest(count, family, force_tcp, false, FillOptionsET(&evopts_, givenopts, evsys), optmask | ARES_OPT_EVENT_THREAD) { @@ -764,6 +764,50 @@ int RunInContainer(ContainerFilesystem *fs, const std::string &hostname, } \ int ICLASS_NAME(casename, testname)::InnerTestBody() +#define CONTAINED_TEST_P(test_suite_name, test_name, hostname, domainname, files) \ + class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + : public test_suite_name { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() {} \ + int InnerTestBody(); \ + void TestBody() \ + { \ + ContainerFilesystem chroot(files, ".."); \ + VoidToIntFn fn = \ + [this](void) -> int { \ + ares_reinit(this->channel_); \ + ares_sleep_time(100); \ + return this->InnerTestBody(); \ + }; \ + EXPECT_EQ(0, RunInContainer(&chroot, hostname, domainname, fn)); \ + } \ + \ + private: \ + static int AddToRegistry() { \ + ::testing::UnitTest::GetInstance() \ + ->parameterized_test_registry() \ + .GetTestSuitePatternHolder( \ + GTEST_STRINGIFY_(test_suite_name), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)) \ + ->AddTestPattern( \ + GTEST_STRINGIFY_(test_suite_name), GTEST_STRINGIFY_(test_name), \ + new ::testing::internal::TestMetaFactory(), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)); \ + return 0; \ + } \ + static int gtest_registering_dummy_ GTEST_ATTRIBUTE_UNUSED_; \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + (const GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &) = delete; \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=( \ + const GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name) &) = delete; /* NOLINT */ \ + }; \ + int GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)::gtest_registering_dummy_ = \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::AddToRegistry(); \ + int GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::InnerTestBody() + #endif /* Assigns virtual IO functions to a channel. These functions simply call