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)
pull/863/head
Brad House 3 months ago committed by GitHub
parent c49a179124
commit 21b6b4a437
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      .github/workflows/alpine-latest.yml
  2. 2
      .github/workflows/coveralls.yml
  3. 12
      .github/workflows/ubuntu-latest.yml
  4. 8
      configure.ac
  5. 1
      src/lib/ares_sysconfig.c
  6. 6
      test/ares-test-ai.h
  7. 20
      test/ares-test-init.cc
  8. 8
      test/ares-test-mock-ai.cc
  9. 77
      test/ares-test-mock.cc
  10. 13
      test/ares-test-ns.cc
  11. 22
      test/ares-test.cc
  12. 54
      test/ares-test.h

@ -34,7 +34,7 @@ jobs:
- name: "CMake: build and test c-ares" - name: "CMake: build and test c-ares"
env: env:
BUILD_TYPE: CMAKE BUILD_TYPE: CMAKE
CMAKE_TEST_FLAGS: "-DCARES_BUILD_TESTS=ON" CMAKE_TEST_FLAGS: "-DCARES_BUILD_CONTAINER_TESTS=ON"
TEST_DEBUGGER: gdb TEST_DEBUGGER: gdb
run: | run: |
./ci/build.sh ./ci/build.sh
@ -51,7 +51,7 @@ jobs:
BUILD_TYPE: "ubsan" BUILD_TYPE: "ubsan"
CC: "clang" CC: "clang"
CXX: "clang++" CXX: "clang++"
CMAKE_TEST_FLAGS: "-DCARES_BUILD_TESTS=ON" CMAKE_TEST_FLAGS: "-DCARES_BUILD_CONTAINER_TESTS=ON"
CFLAGS: "-fsanitize=undefined -fno-sanitize-recover" CFLAGS: "-fsanitize=undefined -fno-sanitize-recover"
CXXFLAGS: "-fsanitize=undefined -fno-sanitize-recover" CXXFLAGS: "-fsanitize=undefined -fno-sanitize-recover"
LDFLAGS: "-fsanitize=undefined" LDFLAGS: "-fsanitize=undefined"
@ -64,7 +64,7 @@ jobs:
BUILD_TYPE: "asan" BUILD_TYPE: "asan"
CC: "clang" CC: "clang"
CXX: "clang++" CXX: "clang++"
CMAKE_TEST_FLAGS: "-DCARES_BUILD_TESTS=ON" CMAKE_TEST_FLAGS: "-DCARES_BUILD_CONTAINER_TESTS=ON"
CFLAGS: "-fsanitize=address" CFLAGS: "-fsanitize=address"
CXXFLAGS: "-fsanitize=address" CXXFLAGS: "-fsanitize=address"
LDFLAGS: "-fsanitize=address" LDFLAGS: "-fsanitize=address"

@ -19,7 +19,7 @@ jobs:
run: | run: |
mkdir build mkdir build
cd 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 ninja
- name: Test - name: Test
shell: bash shell: bash

@ -32,7 +32,7 @@ jobs:
- name: "CMake: build and test c-ares" - name: "CMake: build and test c-ares"
env: env:
BUILD_TYPE: CMAKE BUILD_TYPE: CMAKE
CMAKE_TEST_FLAGS: "-DCARES_BUILD_TESTS=ON" CMAKE_TEST_FLAGS: "-DCARES_BUILD_CONTAINER_TESTS=ON"
TEST_DEBUGGER: gdb TEST_DEBUGGER: gdb
run: | run: |
./ci/build.sh ./ci/build.sh
@ -49,7 +49,7 @@ jobs:
env: env:
BUILD_TYPE: CMAKE BUILD_TYPE: CMAKE
CMAKE_FLAGS: "-DCMAKE_BUILD_TYPE=DEBUG -DCARES_STATIC=ON -DCARES_STATIC_PIC=ON -DCARES_THREADS=OFF -G Ninja" 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 TEST_DEBUGGER: gdb
run: | run: |
./ci/build.sh ./ci/build.sh
@ -58,7 +58,7 @@ jobs:
env: env:
BUILD_TYPE: CMAKE BUILD_TYPE: CMAKE
CMAKE_FLAGS: "-DCMAKE_BUILD_TYPE=DEBUG -DCARES_STATIC=OFF -DCARES_SYMBOL_HIDING=ON -G Ninja" 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 TEST_DEBUGGER: gdb
run: | run: |
./ci/build.sh ./ci/build.sh
@ -68,7 +68,7 @@ jobs:
BUILD_TYPE: "ubsan" BUILD_TYPE: "ubsan"
CC: "clang" CC: "clang"
CXX: "clang++" CXX: "clang++"
CMAKE_TEST_FLAGS: "-DCARES_BUILD_TESTS=ON" CMAKE_TEST_FLAGS: "-DCARES_BUILD_CONTAINER_TESTS=ON"
CFLAGS: "-fsanitize=undefined -fno-sanitize-recover" CFLAGS: "-fsanitize=undefined -fno-sanitize-recover"
CXXFLAGS: "-fsanitize=undefined -fno-sanitize-recover" CXXFLAGS: "-fsanitize=undefined -fno-sanitize-recover"
LDFLAGS: "-fsanitize=undefined" LDFLAGS: "-fsanitize=undefined"
@ -81,7 +81,7 @@ jobs:
BUILD_TYPE: "asan" BUILD_TYPE: "asan"
CC: "clang" CC: "clang"
CXX: "clang++" CXX: "clang++"
CMAKE_TEST_FLAGS: "-DCARES_BUILD_TESTS=ON" CMAKE_TEST_FLAGS: "-DCARES_BUILD_CONTAINER_TESTS=ON"
CFLAGS: "-fsanitize=address" CFLAGS: "-fsanitize=address"
CXXFLAGS: "-fsanitize=address" CXXFLAGS: "-fsanitize=address"
LDFLAGS: "-fsanitize=address" LDFLAGS: "-fsanitize=address"
@ -114,7 +114,7 @@ jobs:
- name: "CMake: No TCP FastOpen" - name: "CMake: No TCP FastOpen"
env: env:
BUILD_TYPE: CMAKE BUILD_TYPE: CMAKE
CMAKE_TEST_FLAGS: "-DCARES_BUILD_TESTS=ON" CMAKE_TEST_FLAGS: "-DCARES_BUILD_CONTAINER_TESTS=ON"
TEST_DEBUGGER: gdb TEST_DEBUGGER: gdb
run: | run: |
sudo sysctl -w net.ipv4.tcp_fastopen=0 sudo sysctl -w net.ipv4.tcp_fastopen=0

@ -153,8 +153,6 @@ _EOF
]) ])
AX_CODE_COVERAGE AX_CODE_COVERAGE
AX_CHECK_USER_NAMESPACE
AX_CHECK_UTS_NAMESPACE
AC_SYS_LARGEFILE AC_SYS_LARGEFILE
case $host_os in case $host_os in
@ -819,6 +817,12 @@ if test "x$build_tests" != "xno" ; then
else else
AC_MSG_ERROR([tests require gmock]) AC_MSG_ERROR([tests require gmock])
fi 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
fi fi
if test "x$build_tests" != "xno" ; then if test "x$build_tests" != "xno" ; then

@ -494,6 +494,7 @@ ares_status_t ares__init_by_sysconfig(ares_channel_t *channel)
ares_sysconfig_t sysconfig; ares_sysconfig_t sysconfig;
memset(&sysconfig, 0, sizeof(sysconfig)); memset(&sysconfig, 0, sizeof(sysconfig));
sysconfig.ndots = 1; /* Default value if not otherwise set */
#if defined(USE_WINSOCK) #if defined(USE_WINSOCK)
status = ares__init_sysconfig_windows(&sysconfig); status = ares__init_sysconfig_windows(&sysconfig);

@ -39,7 +39,7 @@ class MockChannelTestAI
public ::testing::WithParamInterface<std::pair<int, bool>> { public ::testing::WithParamInterface<std::pair<int, bool>> {
public: public:
MockChannelTestAI() 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, class MockUDPChannelTestAI : public MockChannelOptsTest,
public ::testing::WithParamInterface<int> { public ::testing::WithParamInterface<int> {
public: 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, class MockTCPChannelTestAI : public MockChannelOptsTest,
public ::testing::WithParamInterface<int> { public ::testing::WithParamInterface<int> {
public: public:
MockTCPChannelTestAI() : MockChannelOptsTest(1, GetParam(), true, nullptr, 0) MockTCPChannelTestAI() : MockChannelOptsTest(1, GetParam(), true, false, nullptr, 0)
{ {
} }
}; };

@ -417,7 +417,8 @@ CONTAINED_TEST_F(LibraryTest, ContainerChannelInit,
CONTAINED_TEST_F(LibraryTest, ContainerSortlistOptionInit, CONTAINED_TEST_F(LibraryTest, ContainerSortlistOptionInit,
"myhostname", "mydomainname.org", filelist) { "myhostname", "mydomainname.org", filelist) {
ares_channel_t *channel = nullptr; ares_channel_t *channel = nullptr;
struct ares_options opts = {0}; struct ares_options opts;
memset(&opts, 0, sizeof(opts));
int optmask = 0; int optmask = 0;
optmask |= ARES_OPT_SORTLIST; optmask |= ARES_OPT_SORTLIST;
opts.nsort = 0; opts.nsort = 0;
@ -461,7 +462,8 @@ CONTAINED_TEST_F(LibraryTest, ContainerMyResolvConfInit,
"myhostname", "mydomain.org", myresolvconf) { "myhostname", "mydomain.org", myresolvconf) {
char filename[] = "/tmp/myresolv.cnf"; char filename[] = "/tmp/myresolv.cnf";
ares_channel_t *channel = nullptr; ares_channel_t *channel = nullptr;
struct ares_options options = {0}; struct ares_options options;
memset(&options, 0, sizeof(options));
options.resolvconf_path = strdup(filename); options.resolvconf_path = strdup(filename);
int optmask = ARES_OPT_RESOLVCONF; int optmask = ARES_OPT_RESOLVCONF;
EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel, &options, optmask)); EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel, &options, optmask));
@ -487,11 +489,12 @@ CONTAINED_TEST_F(LibraryTest, ContainerMyHostsInit,
"myhostname", "mydomain.org", myhosts) { "myhostname", "mydomain.org", myhosts) {
char filename[] = "/tmp/hosts"; char filename[] = "/tmp/hosts";
ares_channel_t *channel = nullptr; ares_channel_t *channel = nullptr;
struct ares_options options = {0}; struct ares_options options;
options.hosts_path = strdup(filename); options.hosts_path = strdup(filename);
int optmask = ARES_OPT_HOSTS_FILE; int optmask = ARES_OPT_HOSTS_FILE;
EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel, &options, optmask)); EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel, &options, optmask));
memset(&options, 0, sizeof(options));
optmask = 0; optmask = 0;
free(options.hosts_path); free(options.hosts_path);
options.hosts_path = NULL; options.hosts_path = NULL;
@ -595,7 +598,8 @@ CONTAINED_TEST_F(LibraryTest, ContainerRotateInit,
CONTAINED_TEST_F(LibraryTest, ContainerRotateOverride, CONTAINED_TEST_F(LibraryTest, ContainerRotateOverride,
"myhostname", "mydomainname.org", rotateenv) { "myhostname", "mydomainname.org", rotateenv) {
ares_channel_t *channel = nullptr; ares_channel_t *channel = nullptr;
struct ares_options opts = {0}; struct ares_options opts;
memset(&opts, 0, sizeof(opts));
int optmask = ARES_OPT_NOROTATE; int optmask = ARES_OPT_NOROTATE;
EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel, &opts, optmask)); EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel, &opts, optmask));
optmask = 0; optmask = 0;
@ -687,7 +691,8 @@ CONTAINED_TEST_F(LibraryTest, ContainerEmptyInit,
CONTAINED_TEST_F(LibraryTest, ContainerNoDfltSvrEmptyInit, CONTAINED_TEST_F(LibraryTest, ContainerNoDfltSvrEmptyInit,
"myhostname", "mydomainname.org", empty) { "myhostname", "mydomainname.org", empty) {
ares_channel_t *channel = nullptr; ares_channel_t *channel = nullptr;
struct ares_options opts = {0}; struct ares_options opts;
memset(&opts, 0, sizeof(opts));
int optmask = ARES_OPT_FLAGS; int optmask = ARES_OPT_FLAGS;
opts.flags = ARES_FLAG_NO_DFLT_SVR; opts.flags = ARES_FLAG_NO_DFLT_SVR;
EXPECT_EQ(ARES_ENOSERVER, ares_init_options(&channel, &opts, optmask)); EXPECT_EQ(ARES_ENOSERVER, ares_init_options(&channel, &opts, optmask));
@ -700,7 +705,8 @@ CONTAINED_TEST_F(LibraryTest, ContainerNoDfltSvrEmptyInit,
CONTAINED_TEST_F(LibraryTest, ContainerNoDfltSvrFullInit, CONTAINED_TEST_F(LibraryTest, ContainerNoDfltSvrFullInit,
"myhostname", "mydomainname.org", filelist) { "myhostname", "mydomainname.org", filelist) {
ares_channel_t *channel = nullptr; ares_channel_t *channel = nullptr;
struct ares_options opts = {0}; struct ares_options opts;
memset(&opts, 0, sizeof(opts));
int optmask = ARES_OPT_FLAGS; int optmask = ARES_OPT_FLAGS;
opts.flags = ARES_FLAG_NO_DFLT_SVR; opts.flags = ARES_FLAG_NO_DFLT_SVR;
EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel, &opts, optmask)); EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel, &opts, optmask));

@ -266,7 +266,7 @@ class MockExtraOptsTestAI
public ::testing::WithParamInterface< std::pair<int, bool> > { public ::testing::WithParamInterface< std::pair<int, bool> > {
public: public:
MockExtraOptsTestAI() MockExtraOptsTestAI()
: MockChannelOptsTest(1, GetParam().first, GetParam().second, : MockChannelOptsTest(1, GetParam().first, GetParam().second, false,
FillOptions(&opts_), FillOptions(&opts_),
ARES_OPT_SOCK_SNDBUF|ARES_OPT_SOCK_RCVBUF) {} ARES_OPT_SOCK_SNDBUF|ARES_OPT_SOCK_RCVBUF) {}
static struct ares_options* FillOptions(struct ares_options * opts) { static struct ares_options* FillOptions(struct ares_options * opts) {
@ -311,7 +311,7 @@ class MockExtraOptsNDotsTestAI
public ::testing::WithParamInterface< std::pair<int, bool> > { public ::testing::WithParamInterface< std::pair<int, bool> > {
public: public:
MockExtraOptsNDotsTestAI(int ndots) MockExtraOptsNDotsTestAI(int ndots)
: MockChannelOptsTest(1, GetParam().first, GetParam().second, : MockChannelOptsTest(1, GetParam().first, GetParam().second, false,
FillOptions(&opts_, ndots), FillOptions(&opts_, ndots),
ARES_OPT_SOCK_SNDBUF|ARES_OPT_SOCK_RCVBUF|ARES_OPT_NDOTS) {} ARES_OPT_SOCK_SNDBUF|ARES_OPT_SOCK_RCVBUF|ARES_OPT_NDOTS) {}
static struct ares_options* FillOptions(struct ares_options * opts, int ndots) { static struct ares_options* FillOptions(struct ares_options * opts, int ndots) {
@ -409,7 +409,7 @@ class MockFlagsChannelOptsTestAI
public ::testing::WithParamInterface< std::pair<int, bool> > { public ::testing::WithParamInterface< std::pair<int, bool> > {
public: public:
MockFlagsChannelOptsTestAI(int flags) MockFlagsChannelOptsTestAI(int flags)
: MockChannelOptsTest(1, GetParam().first, GetParam().second, : MockChannelOptsTest(1, GetParam().first, GetParam().second, false,
FillOptions(&opts_, flags), ARES_OPT_FLAGS) {} FillOptions(&opts_, flags), ARES_OPT_FLAGS) {}
static struct ares_options* FillOptions(struct ares_options * opts, int flags) { static struct ares_options* FillOptions(struct ares_options * opts, int flags) {
memset(opts, 0, sizeof(struct ares_options)); memset(opts, 0, sizeof(struct ares_options));
@ -704,7 +704,7 @@ class MockMultiServerChannelTestAI
public ::testing::WithParamInterface< std::pair<int, bool> > { public ::testing::WithParamInterface< std::pair<int, bool> > {
public: public:
MockMultiServerChannelTestAI(ares_options *opts, int optmask) 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() { void CheckExample() {
AddrInfoResult result; AddrInfoResult result;
struct ares_addrinfo_hints hints = {0, 0, 0, 0}; struct ares_addrinfo_hints hints = {0, 0, 0, 0};

@ -45,7 +45,7 @@ class NoDNS0x20MockTest
public ::testing::WithParamInterface<int> { public ::testing::WithParamInterface<int> {
public: public:
NoDNS0x20MockTest() NoDNS0x20MockTest()
: MockChannelOptsTest(1, GetParam(), false, : MockChannelOptsTest(1, GetParam(), false, false,
FillOptions(&opts_), FillOptions(&opts_),
ARES_OPT_FLAGS) {} ARES_OPT_FLAGS) {}
static struct ares_options* FillOptions(struct ares_options * opts) { static struct ares_options* FillOptions(struct ares_options * opts) {
@ -453,7 +453,7 @@ class MockUDPMaxQueriesTest
public ::testing::WithParamInterface<int> { public ::testing::WithParamInterface<int> {
public: public:
MockUDPMaxQueriesTest() MockUDPMaxQueriesTest()
: MockChannelOptsTest(1, GetParam(), false, : MockChannelOptsTest(1, GetParam(), false, false,
FillOptions(&opts_), FillOptions(&opts_),
ARES_OPT_UDP_MAX_QUERIES) {} ARES_OPT_UDP_MAX_QUERIES) {}
static struct ares_options* FillOptions(struct ares_options * opts) { static struct ares_options* FillOptions(struct ares_options * opts) {
@ -500,7 +500,7 @@ class CacheQueriesTest
public ::testing::WithParamInterface<int> { public ::testing::WithParamInterface<int> {
public: public:
CacheQueriesTest() CacheQueriesTest()
: MockChannelOptsTest(1, GetParam(), false, : MockChannelOptsTest(1, GetParam(), false, false,
FillOptions(&opts_), FillOptions(&opts_),
ARES_OPT_QUERY_CACHE) {} ARES_OPT_QUERY_CACHE) {}
static struct ares_options* FillOptions(struct ares_options * opts) { static struct ares_options* FillOptions(struct ares_options * opts) {
@ -665,7 +665,7 @@ class MockExtraOptsTest
public ::testing::WithParamInterface< std::pair<int, bool> > { public ::testing::WithParamInterface< std::pair<int, bool> > {
public: public:
MockExtraOptsTest() MockExtraOptsTest()
: MockChannelOptsTest(1, GetParam().first, GetParam().second, : MockChannelOptsTest(1, GetParam().first, GetParam().second, false,
FillOptions(&opts_), FillOptions(&opts_),
ARES_OPT_SOCK_SNDBUF|ARES_OPT_SOCK_RCVBUF) {} ARES_OPT_SOCK_SNDBUF|ARES_OPT_SOCK_RCVBUF) {}
static struct ares_options* FillOptions(struct ares_options * opts) { static struct ares_options* FillOptions(struct ares_options * opts) {
@ -707,7 +707,7 @@ class MockFlagsChannelOptsTest
public ::testing::WithParamInterface< std::pair<int, bool> > { public ::testing::WithParamInterface< std::pair<int, bool> > {
public: public:
MockFlagsChannelOptsTest(int flags) MockFlagsChannelOptsTest(int flags)
: MockChannelOptsTest(1, GetParam().first, GetParam().second, : MockChannelOptsTest(1, GetParam().first, GetParam().second, false,
FillOptions(&opts_, flags), ARES_OPT_FLAGS) {} FillOptions(&opts_, flags), ARES_OPT_FLAGS) {}
static struct ares_options* FillOptions(struct ares_options * opts, int flags) { static struct ares_options* FillOptions(struct ares_options * opts, int flags) {
memset(opts, 0, sizeof(struct ares_options)); 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()); 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<std::pair<int, bool>> {
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 // Issue #858
TEST_P(CacheQueriesTest, BlankName) { TEST_P(CacheQueriesTest, BlankName) {
DNSPacket rsp; DNSPacket rsp;
@ -1933,7 +1994,7 @@ class MockMultiServerChannelTest
public ::testing::WithParamInterface< std::pair<int, bool> > { public ::testing::WithParamInterface< std::pair<int, bool> > {
public: public:
MockMultiServerChannelTest(ares_options *opts, int optmask) 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() { void CheckExample() {
HostResult result; HostResult result;
ares_gethostbyname(channel_, "www.example.com.", AF_INET, HostCallback, &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); 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, MockUDPChannelTest, ::testing::ValuesIn(ares::test::families), PrintFamily);
INSTANTIATE_TEST_SUITE_P(AddressFamilies, MockUDPMaxQueriesTest, ::testing::ValuesIn(ares::test::families), PrintFamily); INSTANTIATE_TEST_SUITE_P(AddressFamilies, MockUDPMaxQueriesTest, ::testing::ValuesIn(ares::test::families), PrintFamily);

@ -155,8 +155,8 @@ int RunInContainer(ContainerFilesystem* fs, const std::string& hostname,
std::stringstream contentss; std::stringstream contentss;
contentss << "0 " << getuid() << " 1" << std::endl; contentss << "0 " << getuid() << " 1" << std::endl;
std::string content = contentss.str(); std::string content = contentss.str();
int rc = write(fd, content.c_str(), content.size()); ssize_t rc = write(fd, content.c_str(), content.size());
if (rc != (int)content.size()) { if (rc != (ssize_t)content.size()) {
std::cerr << "Failed to write uid map to '" << mapfile << "'" << std::endl; std::cerr << "Failed to write uid map to '" << mapfile << "'" << std::endl;
} }
close(fd); close(fd);
@ -181,8 +181,13 @@ ContainerFilesystem::ContainerFilesystem(NameContentList files, const std::strin
dirs_.push_front(rootdir_); dirs_.push_front(rootdir_);
for (const auto& nc : files) { for (const auto& nc : files) {
std::string fullpath = rootdir_ + nc.first; std::string fullpath = rootdir_ + nc.first;
int idx = fullpath.rfind('/'); size_t idx = fullpath.rfind('/');
std::string dir = fullpath.substr(0, idx); std::string dir;
if (idx != SIZE_MAX) {
dir = fullpath.substr(0, idx);
} else {
dir = fullpath;
}
EnsureDirExists(dir); EnsureDirExists(dir);
files_.push_back(std::unique_ptr<TransientFile>( files_.push_back(std::unique_ptr<TransientFile>(
new TransientFile(fullpath, nc.second))); new TransientFile(fullpath, nc.second)));

22
test/ares-test.cc vendored

@ -766,11 +766,13 @@ MockChannelOptsTest::NiceMockServers MockChannelOptsTest::BuildServers(int count
MockChannelOptsTest::MockChannelOptsTest(int count, MockChannelOptsTest::MockChannelOptsTest(int count,
int family, int family,
bool force_tcp, bool force_tcp,
bool honor_sysconfig,
struct ares_options* givenopts, struct ares_options* givenopts,
int optmask) int optmask)
: servers_(BuildServers(count, family, mock_port)), : servers_(BuildServers(count, family, mock_port)),
server_(*servers_[0].get()), channel_(nullptr) { server_(*servers_[0].get()), channel_(nullptr) {
// Set up channel options. // Set up channel options.
const char *domains[3] = {"first.com", "second.org", "third.gov"};
struct ares_options opts; struct ares_options opts;
if (givenopts) { if (givenopts) {
memcpy(&opts, givenopts, sizeof(opts)); memcpy(&opts, givenopts, sizeof(opts));
@ -778,12 +780,8 @@ MockChannelOptsTest::MockChannelOptsTest(int count,
memset(&opts, 0, sizeof(opts)); memset(&opts, 0, sizeof(opts));
} }
// Point the library at the first mock server by default (overridden below). /* Honor items from resolv.conf except the dns server itself */
opts.udp_port = server_.udpport(); if (!honor_sysconfig) {
optmask |= ARES_OPT_UDP_PORT;
opts.tcp_port = server_.tcpport();
optmask |= ARES_OPT_TCP_PORT;
if (!(optmask & (ARES_OPT_TIMEOUTMS|ARES_OPT_TIMEOUT))) { if (!(optmask & (ARES_OPT_TIMEOUTMS|ARES_OPT_TIMEOUT))) {
// Reduce timeouts significantly to shorten test times. // Reduce timeouts significantly to shorten test times.
opts.timeout = 250; opts.timeout = 250;
@ -794,17 +792,13 @@ MockChannelOptsTest::MockChannelOptsTest(int count,
opts.tries = 3; opts.tries = 3;
optmask |= ARES_OPT_TRIES; optmask |= ARES_OPT_TRIES;
} }
// If not already overridden, set search domains. // If not already overridden, set search domains.
const char *domains[3] = {"first.com", "second.org", "third.gov"};
if (!(optmask & ARES_OPT_DOMAINS)) { if (!(optmask & ARES_OPT_DOMAINS)) {
opts.ndomains = 3; opts.ndomains = 3;
opts.domains = (char**)domains; opts.domains = (char**)domains;
optmask |= ARES_OPT_DOMAINS; optmask |= ARES_OPT_DOMAINS;
} }
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 /* Tests expect ndots=1 in general, the system config may not default to this
* so we don't want to inherit that. */ * so we don't want to inherit that. */
@ -812,6 +806,12 @@ MockChannelOptsTest::MockChannelOptsTest(int count,
opts.ndots = 1; opts.ndots = 1;
optmask |= ARES_OPT_NDOTS; optmask |= ARES_OPT_NDOTS;
} }
}
if (force_tcp) {
opts.flags |= ARES_FLAG_USEVC;
optmask |= ARES_OPT_FLAGS;
}
/* Disable the query cache for tests unless explicitly enabled. As of /* 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 * c-ares 1.31.0, the query cache is enabled by default so we have to set

54
test/ares-test.h vendored

@ -311,7 +311,7 @@ private:
// Test fixture that uses a mock DNS server. // Test fixture that uses a mock DNS server.
class MockChannelOptsTest : public LibraryTest { class MockChannelOptsTest : public LibraryTest {
public: 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); struct ares_options *givenopts, int optmask);
~MockChannelOptsTest(); ~MockChannelOptsTest();
@ -341,7 +341,7 @@ class MockChannelTest
public ::testing::WithParamInterface<std::pair<int, bool>> { public ::testing::WithParamInterface<std::pair<int, bool>> {
public: public:
MockChannelTest() 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, class MockUDPChannelTest : public MockChannelOptsTest,
public ::testing::WithParamInterface<int> { public ::testing::WithParamInterface<int> {
public: 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, class MockTCPChannelTest : public MockChannelOptsTest,
public ::testing::WithParamInterface<int> { public ::testing::WithParamInterface<int> {
public: 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, MockEventThreadOptsTest(int count, ares_evsys_t evsys, int family,
bool force_tcp, struct ares_options *givenopts, bool force_tcp, struct ares_options *givenopts,
int optmask) int optmask)
: MockChannelOptsTest(count, family, force_tcp, : MockChannelOptsTest(count, family, force_tcp, false,
FillOptionsET(&evopts_, givenopts, evsys), FillOptionsET(&evopts_, givenopts, evsys),
optmask | ARES_OPT_EVENT_THREAD) optmask | ARES_OPT_EVENT_THREAD)
{ {
@ -764,6 +764,50 @@ int RunInContainer(ContainerFilesystem *fs, const std::string &hostname,
} \ } \
int ICLASS_NAME(casename, testname)::InnerTestBody() 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<test_suite_name>( \
GTEST_STRINGIFY_(test_suite_name), \
::testing::internal::CodeLocation(__FILE__, __LINE__)) \
->AddTestPattern( \
GTEST_STRINGIFY_(test_suite_name), GTEST_STRINGIFY_(test_name), \
new ::testing::internal::TestMetaFactory<GTEST_TEST_CLASS_NAME_( \
test_suite_name, test_name)>(), \
::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 #endif
/* Assigns virtual IO functions to a channel. These functions simply call /* Assigns virtual IO functions to a channel. These functions simply call

Loading…
Cancel
Save