diff --git a/test/ares-test-init.cc b/test/ares-test-init.cc index f97fdff4..0d4bd278 100644 --- a/test/ares-test-init.cc +++ b/test/ares-test-init.cc @@ -56,7 +56,7 @@ TEST_F(LibraryTest, BasicChannelInit) { TEST_F(LibraryTest, OptionsChannelInit) { struct ares_options opts = {0}; int optmask = 0; - opts.flags = ARES_FLAG_USEVC; + opts.flags = ARES_FLAG_USEVC | ARES_FLAG_PRIMARY; optmask |= ARES_OPT_FLAGS; opts.timeout = 2000; optmask |= ARES_OPT_TIMEOUTMS; @@ -97,7 +97,7 @@ TEST_F(LibraryTest, OptionsChannelInit) { struct ares_options opts2 = {0}; int optmask2 = 0; - ares_save_options(channel2, &opts2, &optmask2); + EXPECT_EQ(ARES_SUCCESS, ares_save_options(channel2, &opts2, &optmask2)); // Note that not all opts-settable fields are saved (e.g. // ednspsz, socket_{send,receive}_buffer_size). @@ -107,9 +107,8 @@ TEST_F(LibraryTest, OptionsChannelInit) { EXPECT_EQ(opts.ndots, opts2.ndots); EXPECT_EQ(opts.udp_port, opts2.udp_port); EXPECT_EQ(opts.tcp_port, opts2.tcp_port); - EXPECT_EQ(opts.nservers, opts2.nservers); + EXPECT_EQ(1, opts2.nservers); // Truncated by ARES_FLAG_PRIMARY EXPECT_EQ(opts.servers[0].s_addr, opts2.servers[0].s_addr); - EXPECT_EQ(opts.servers[1].s_addr, opts2.servers[1].s_addr); EXPECT_EQ(opts.ndomains, opts2.ndomains); EXPECT_EQ(std::string(opts.domains[0]), std::string(opts2.domains[0])); EXPECT_EQ(std::string(opts.domains[1]), std::string(opts2.domains[1])); @@ -121,6 +120,95 @@ TEST_F(LibraryTest, OptionsChannelInit) { ares_destroy(channel2); } +TEST_F(LibraryTest, ChannelAllocFail) { + ares_channel channel; + for (int ii = 1; ii <= 16; ii++) { + ClearFails(); + SetAllocFail(ii); + channel = nullptr; + int rc = ares_init(&channel); + // The number of allocations depends on local environment, so don't expect ENOMEM. + if (rc == ARES_ENOMEM) { + EXPECT_EQ(nullptr, channel); + } else { + ares_destroy(channel); + } + } +} + +TEST_F(LibraryTest, OptionsChannelAllocFail) { + struct ares_options opts = {0}; + int optmask = 0; + opts.flags = ARES_FLAG_USEVC; + optmask |= ARES_OPT_FLAGS; + opts.timeout = 2; + optmask |= ARES_OPT_TIMEOUT; + opts.tries = 2; + optmask |= ARES_OPT_TRIES; + opts.ndots = 4; + optmask |= ARES_OPT_NDOTS; + opts.udp_port = 54; + optmask |= ARES_OPT_UDP_PORT; + opts.tcp_port = 54; + optmask |= ARES_OPT_TCP_PORT; + opts.socket_send_buffer_size = 514; + optmask |= ARES_OPT_SOCK_SNDBUF; + opts.socket_receive_buffer_size = 514; + optmask |= ARES_OPT_SOCK_RCVBUF; + opts.ednspsz = 1280; + optmask |= ARES_OPT_EDNSPSZ; + opts.nservers = 2; + opts.servers = (struct in_addr *)malloc(opts.nservers * sizeof(struct in_addr)); + opts.servers[0].s_addr = htonl(0x01020304); + opts.servers[1].s_addr = htonl(0x02030405); + optmask |= ARES_OPT_SERVERS; + opts.ndomains = 2; + opts.domains = (char **)malloc(opts.ndomains * sizeof(char *)); + opts.domains[0] = strdup("example.com"); + opts.domains[1] = strdup("example2.com"); + optmask |= ARES_OPT_DOMAINS; + opts.lookups = strdup("b"); + optmask |= ARES_OPT_LOOKUPS; + optmask |= ARES_OPT_ROTATE; + + ares_channel channel = nullptr; + for (int ii = 1; ii <= 8; ii++) { + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, ares_init_options(&channel, &opts, optmask)) << ii; + EXPECT_EQ(nullptr, channel); + } + + EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel, &opts, optmask)); + EXPECT_NE(nullptr, channel); + + // Add some servers and a sortlist for flavour. + EXPECT_EQ(ARES_SUCCESS, + ares_set_servers_csv(channel, "1.2.3.4,0102:0304:0506:0708:0910:1112:1314:1516,2.3.4.5")); + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel, "1.2.3.4 2.3.4.5")); + + ares_channel channel2 = nullptr; + for (int ii = 1; ii <= 18; ii++) { + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, ares_dup(&channel2, channel)) << ii; + EXPECT_EQ(nullptr, channel2) << ii; + } + + struct ares_options opts2; + int optmask2 = 0; + for (int ii = 1; ii <= 6; ii++) { + memset(&opts2, 0, sizeof(opts2)); + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, ares_save_options(channel, &opts2, &optmask2)) << ii; + // May still have allocations even after ARES_ENOMEM return code. + ares_destroy_options(&opts2); + } + ares_destroy_options(&opts); + ares_destroy(channel); +} + TEST_F(LibraryTest, FailChannelInit) { EXPECT_EQ(ARES_SUCCESS, ares_library_init_mem(ARES_LIB_INIT_ALL, @@ -134,6 +222,37 @@ TEST_F(LibraryTest, FailChannelInit) { ares_library_cleanup(); } +TEST_F(DefaultChannelTest, SetAddresses) { + ares_set_local_ip4(channel_, 0x01020304); + byte addr6[16] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}; + ares_set_local_ip6(channel_, addr6); + ares_set_local_dev(channel_, "dummy"); +} + +TEST_F(DefaultChannelTest, SetSortlistFailures) { + EXPECT_EQ(ARES_ENODATA, ares_set_sortlist(nullptr, "1.2.3.4")); + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "xyzzy ; lwk")); + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "xyzzy ; 0x123")); +} + +TEST_F(DefaultChannelTest, SetSortlistVariants) { + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "1.2.3.4")); + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "1.2.3.4 ; 2.3.4.5")); + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "129.1.1.1")); + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "192.1.1.1")); + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "224.1.1.1")); + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "225.1.1.1")); +} + +TEST_F(DefaultChannelTest, SetSortlistAllocFail) { + for (int ii = 1; ii <= 3; ii++) { + ClearFails(); + SetAllocFail(ii); + EXPECT_EQ(ARES_ENOMEM, ares_set_sortlist(channel_, "12.13.0.0/16 1234::5678/40 1.2.3.4")) << ii; + } +} + #ifdef USE_WINSOCK TEST(Init, NoLibraryInit) { ares_channel channel = nullptr; diff --git a/test/ares-test-live.cc b/test/ares-test-live.cc index 11d590d1..abfb946a 100644 --- a/test/ares-test-live.cc +++ b/test/ares-test-live.cc @@ -287,7 +287,55 @@ TEST_F(DefaultChannelTest, LiveGetNameInfoV4) { if (verbose) std::cerr << "8.8.8.8:53 => " << result.node_ << "/" << result.service_ << std::endl; } -TEST_F(DefaultChannelTest, LiveGetNameInfoV6) { +TEST_F(DefaultChannelTest, LiveGetNameInfoV4NoPort) { + NameInfoResult result; + struct sockaddr_in sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(0); + sockaddr.sin_addr.s_addr = htonl(0x08080808); + ares_getnameinfo(channel_, (const struct sockaddr*)&sockaddr, sizeof(sockaddr), + ARES_NI_LOOKUPHOST|ARES_NI_LOOKUPSERVICE|ARES_NI_UDP, + NameInfoCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); + if (verbose) std::cerr << "8.8.8.8:0 => " << result.node_ << "/" << result.service_ << std::endl; +} + +TEST_F(DefaultChannelTest, LiveGetNameInfoV4UnassignedPort) { + NameInfoResult result; + struct sockaddr_in sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(4); // Unassigned at IANA + sockaddr.sin_addr.s_addr = htonl(0x08080808); + ares_getnameinfo(channel_, (const struct sockaddr*)&sockaddr, sizeof(sockaddr), + ARES_NI_LOOKUPHOST|ARES_NI_LOOKUPSERVICE|ARES_NI_UDP, + NameInfoCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); + if (verbose) std::cerr << "8.8.8.8:4 => " << result.node_ << "/" << result.service_ << std::endl; +} + +TEST_F(DefaultChannelTest, LiveGetNameInfoV6Both) { + NameInfoResult result; + struct sockaddr_in6 sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin6_family = AF_INET6; + sockaddr.sin6_port = htons(53); + memcpy(sockaddr.sin6_addr.s6_addr, gdns_addr6, 16); + ares_getnameinfo(channel_, (const struct sockaddr*)&sockaddr, sizeof(sockaddr), + ARES_NI_TCP|ARES_NI_LOOKUPHOST|ARES_NI_LOOKUPSERVICE|ARES_NI_NOFQDN, + NameInfoCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_SUCCESS, result.status_); + if (verbose) std::cerr << "[2001:4860:4860::8888]:53 => " << result.node_ << "/" << result.service_ << std::endl; +} + +TEST_F(DefaultChannelTest, LiveGetNameInfoV6Neither) { NameInfoResult result; struct sockaddr_in6 sockaddr; memset(&sockaddr, 0, sizeof(sockaddr)); diff --git a/test/ares-test-misc.cc b/test/ares-test-misc.cc index d58de50d..fca66139 100644 --- a/test/ares-test-misc.cc +++ b/test/ares-test-misc.cc @@ -91,6 +91,12 @@ TEST_F(DefaultChannelTest, SetServersCSV) { ares_set_servers_csv(channel_, "1.2.3.4:54,[0102:0304:0506:0708:0910:1112:1314:1516]:80,2.3.4.5:55")); EXPECT_EQ(expected, GetNameServers(channel_)); + // Should survive duplication + ares_channel channel2; + EXPECT_EQ(ARES_SUCCESS, ares_dup(&channel2, channel_)); + EXPECT_EQ(expected, GetNameServers(channel2)); + ares_destroy(channel2); + // Allocation failure cases for (int fail = 1; fail <= 5; fail++) { SetAllocFail(fail); diff --git a/test/ares-test-mock.cc b/test/ares-test-mock.cc index 370026ec..2a235acb 100644 --- a/test/ares-test-mock.cc +++ b/test/ares-test-mock.cc @@ -7,6 +7,7 @@ #include using testing::InvokeWithoutArgs; +using testing::DoAll; namespace ares { namespace test { @@ -51,6 +52,202 @@ TEST_P(MockChannelTest, Basic) { EXPECT_EQ("{'www.google.com' aliases=[] addrs=[1.2.3.4]}", ss.str()); } +// UDP only so mock server doesn't get confused by concatenated requests +TEST_P(MockUDPChannelTest, ParallelLookups) { + DNSPacket rsp1; + rsp1.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)) + .add_answer(new DNSARR("www.google.com", 100, {2, 3, 4, 5})); + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReply(&server_, &rsp1)) + .WillOnce(SetReply(&server_, &rsp1)); + DNSPacket rsp2; + rsp2.set_response().set_aa() + .add_question(new DNSQuestion("www.example.com", ns_t_a)) + .add_answer(new DNSARR("www.example.com", 100, {1, 2, 3, 4})); + EXPECT_CALL(server_, OnRequest("www.example.com", ns_t_a)) + .WillOnce(SetReply(&server_, &rsp2)); + + HostResult result1; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result1); + HostResult result2; + ares_gethostbyname(channel_, "www.example.com.", AF_INET, HostCallback, &result2); + HostResult result3; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result3); + Process(); + EXPECT_TRUE(result1.done_); + EXPECT_TRUE(result2.done_); + EXPECT_TRUE(result3.done_); + std::stringstream ss1; + ss1 << result1.host_; + EXPECT_EQ("{'www.google.com' aliases=[] addrs=[2.3.4.5]}", ss1.str()); + std::stringstream ss2; + ss2 << result2.host_; + EXPECT_EQ("{'www.example.com' aliases=[] addrs=[1.2.3.4]}", ss2.str()); + std::stringstream ss3; + ss3 << result3.host_; + EXPECT_EQ("{'www.google.com' aliases=[] addrs=[2.3.4.5]}", ss3.str()); +} + +static int sock_cb_count = 0; +static int SocketCallback(ares_socket_t fd, int type, void *data) { + if (verbose) std::cerr << "SocketCallback(" << fd << ") invoked" << std::endl; + sock_cb_count++; + return ARES_SUCCESS; +} + +TEST_P(MockChannelTest, SockCallback) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)) + .add_answer(new DNSARR("www.google.com", 100, {2, 3, 4, 5})); + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReply(&server_, &rsp)); + + // Get notified of new sockets + ares_set_socket_callback(channel_, SocketCallback, nullptr); + + HostResult result; + sock_cb_count = 0; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_EQ(1, sock_cb_count); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'www.google.com' aliases=[] addrs=[2.3.4.5]}", ss.str()); +} + +// TCP only to prevent retries +TEST_P(MockTCPChannelTest, FormErrResponse) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)); + rsp.set_rcode(ns_r_formerr); + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReply(&server_, &rsp)); + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_EFORMERR, result.status_); +} + +TEST_P(MockTCPChannelTest, ServFailResponse) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)); + rsp.set_rcode(ns_r_servfail); + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReply(&server_, &rsp)); + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + // ARES_FLAG_NOCHECKRESP not set, so SERVFAIL consumed + EXPECT_EQ(ARES_ECONNREFUSED, result.status_); +} + +TEST_P(MockTCPChannelTest, NotImplResponse) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)); + rsp.set_rcode(ns_r_notimpl); + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReply(&server_, &rsp)); + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + // ARES_FLAG_NOCHECKRESP not set, so NOTIMPL consumed + EXPECT_EQ(ARES_ECONNREFUSED, result.status_); +} + +TEST_P(MockTCPChannelTest, RefusedResponse) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)); + rsp.set_rcode(ns_r_refused); + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReply(&server_, &rsp)); + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + // ARES_FLAG_NOCHECKRESP not set, so REFUSED consumed + EXPECT_EQ(ARES_ECONNREFUSED, result.status_); +} + +TEST_P(MockTCPChannelTest, YXDomainResponse) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)); + rsp.set_rcode(ns_r_yxdomain); + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReply(&server_, &rsp)); + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_ENODATA, result.status_); +} + +class MockNoCheckRespChannelTest + : public MockChannelOptsTest, + public ::testing::WithParamInterface { + public: + MockNoCheckRespChannelTest() : MockChannelOptsTest(GetParam(), true, FillOptions(&opts_), ARES_OPT_FLAGS) {} + static struct ares_options* FillOptions(struct ares_options * opts) { + memset(opts, 0, sizeof(struct ares_options)); + opts->flags = ARES_FLAG_NOCHECKRESP; + return opts; + } + private: + struct ares_options opts_; +}; + +TEST_P(MockNoCheckRespChannelTest, ServFailResponse) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)); + rsp.set_rcode(ns_r_servfail); + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReply(&server_, &rsp)); + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_ESERVFAIL, result.status_); +} + +TEST_P(MockNoCheckRespChannelTest, NotImplResponse) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)); + rsp.set_rcode(ns_r_notimpl); + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReply(&server_, &rsp)); + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_ENOTIMP, result.status_); +} + +TEST_P(MockNoCheckRespChannelTest, RefusedResponse) { + DNSPacket rsp; + rsp.set_response().set_aa() + .add_question(new DNSQuestion("www.google.com", ns_t_a)); + rsp.set_rcode(ns_r_refused); + EXPECT_CALL(server_, OnRequest("www.google.com", ns_t_a)) + .WillOnce(SetReply(&server_, &rsp)); + HostResult result; + ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + EXPECT_EQ(ARES_EREFUSED, result.status_); +} + TEST_P(MockChannelTest, SearchDomains) { DNSPacket nofirst; nofirst.set_response().set_aa().set_rcode(ns_r_nxdomain) @@ -78,6 +275,38 @@ TEST_P(MockChannelTest, SearchDomains) { EXPECT_EQ("{'www.third.gov' aliases=[] addrs=[2.3.4.5]}", ss.str()); } +// Relies on retries so is UDP-only +TEST_P(MockUDPChannelTest, SearchDomainsWithResentReply) { + DNSPacket nofirst; + nofirst.set_response().set_aa().set_rcode(ns_r_nxdomain) + .add_question(new DNSQuestion("www.first.com", ns_t_a)); + EXPECT_CALL(server_, OnRequest("www.first.com", ns_t_a)) + .WillOnce(SetReply(&server_, &nofirst)); + DNSPacket nosecond; + nosecond.set_response().set_aa().set_rcode(ns_r_nxdomain) + .add_question(new DNSQuestion("www.second.org", ns_t_a)); + EXPECT_CALL(server_, OnRequest("www.second.org", ns_t_a)) + .WillOnce(SetReply(&server_, &nosecond)); + DNSPacket yesthird; + yesthird.set_response().set_aa() + .add_question(new DNSQuestion("www.third.gov", ns_t_a)) + .add_answer(new DNSARR("www.third.gov", 0x0200, {2, 3, 4, 5})); + // Before sending the real answer, resend an earlier reply + EXPECT_CALL(server_, OnRequest("www.third.gov", ns_t_a)) + .WillOnce(DoAll(SetReply(&server_, &nofirst), + SetReplyQID(&server_, 123))) + .WillOnce(DoAll(SetReply(&server_, &yesthird), + SetReplyQID(&server_, -1))); + + HostResult result; + ares_gethostbyname(channel_, "www", AF_INET, HostCallback, &result); + Process(); + EXPECT_TRUE(result.done_); + std::stringstream ss; + ss << result.host_; + EXPECT_EQ("{'www.third.gov' aliases=[] addrs=[2.3.4.5]}", ss.str()); +} + TEST_P(MockChannelTest, SearchDomainsBare) { DNSPacket nofirst; nofirst.set_response().set_aa().set_rcode(ns_r_nxdomain) @@ -295,7 +524,7 @@ TEST_P(MockChannelTest, SortListV4) { .WillByDefault(SetReply(&server_, &rsp)); { - ares_set_sortlist(channel_, "12.13.0.0/255.255.0.0 1234::5678"); + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "12.13.0.0/255.255.0.0 1234::5678")); HostResult result; ares_gethostbyname(channel_, "example.com.", AF_INET, HostCallback, &result); Process(); @@ -305,7 +534,7 @@ TEST_P(MockChannelTest, SortListV4) { EXPECT_EQ("{'example.com' aliases=[] addrs=[12.13.14.15, 22.23.24.25, 2.3.4.5]}", ss.str()); } { - ares_set_sortlist(channel_, "2.3.0.0/16 130.140.150.160/26"); + EXPECT_EQ(ARES_SUCCESS, ares_set_sortlist(channel_, "2.3.0.0/16 130.140.150.160/26")); HostResult result; ares_gethostbyname(channel_, "example.com.", AF_INET, HostCallback, &result); Process(); @@ -568,5 +797,11 @@ INSTANTIATE_TEST_CASE_P(AddressFamilies, MockChannelTest, INSTANTIATE_TEST_CASE_P(AddressFamilies, MockUDPChannelTest, ::testing::Values(AF_INET, AF_INET6)); +INSTANTIATE_TEST_CASE_P(AddressFamilies, MockTCPChannelTest, + ::testing::Values(AF_INET, AF_INET6)); + +INSTANTIATE_TEST_CASE_P(AddressFamilies, MockNoCheckRespChannelTest, + ::testing::Values(AF_INET, AF_INET6)); + } // namespace test } // namespace ares diff --git a/test/ares-test-parse-txt.cc b/test/ares-test-parse-txt.cc index 7854db73..193fe272 100644 --- a/test/ares-test-parse-txt.cc +++ b/test/ares-test-parse-txt.cc @@ -38,6 +38,96 @@ TEST_F(LibraryTest, ParseTxtReplyOK) { ares_free_data(txt); } +TEST_F(LibraryTest, ParseTxtMalformedReply1) { + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x10, // type TXT + 0x00, 0x01, // class IN + // Answer 1 + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x10, // RR type + 0x00, 0x01, // class IN + 0x01, 0x02, 0x03, 0x04, // TTL + 0x00, 0x03, // rdata length + 0x12, 'a', 'b', // invalid length + }; + + struct ares_txt_reply* txt = nullptr; + EXPECT_EQ(ARES_EBADRESP, ares_parse_txt_reply(data.data(), data.size(), &txt)); + ASSERT_EQ(nullptr, txt); +} + +TEST_F(LibraryTest, ParseTxtMalformedReply2) { + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x10, // type TXT + 0x00, 0x01, // class IN + // Answer 1 + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x10, // RR type + // truncated + }; + + struct ares_txt_reply* txt = nullptr; + EXPECT_EQ(ARES_EBADRESP, ares_parse_txt_reply(data.data(), data.size(), &txt)); + ASSERT_EQ(nullptr, txt); +} + +TEST_F(LibraryTest, ParseTxtMalformedReply3) { + std::vector data = { + 0x12, 0x34, // qid + 0x84, // response + query + AA + not-TC + not-RD + 0x00, // not-RA + not-Z + not-AD + not-CD + rc=NoError + 0x00, 0x01, // num questions + 0x00, 0x01, // num answer RRs + 0x00, 0x00, // num authority RRs + 0x00, 0x00, // num additional RRs + // Question + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x10, // type TXT + 0x00, 0x01, // class IN + // Answer 1 + 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x10, // RR type + 0x00, 0x01, // class IN + 0x01, 0x02, 0x03, 0x04, // TTL + 0x00, 0x13, // rdata length INVALID + 0x02, 'a', 'b', + }; + + struct ares_txt_reply* txt = nullptr; + EXPECT_EQ(ARES_EBADRESP, ares_parse_txt_reply(data.data(), data.size(), &txt)); + ASSERT_EQ(nullptr, txt); +} + TEST_F(LibraryTest, ParseTxtReplyErrors) { DNSPacket pkt; std::string expected1 = "txt1.example.com"; diff --git a/test/ares-test.cc b/test/ares-test.cc index 356218e9..b0bbe5f7 100644 --- a/test/ares-test.cc +++ b/test/ares-test.cc @@ -131,7 +131,7 @@ void DefaultChannelModeTest::Process() { ProcessWork(channel_, NoExtraFDs, nullptr); } -MockServer::MockServer(int family, bool tcp, int port) : tcp_(tcp), port_(port) { +MockServer::MockServer(int family, bool tcp, int port) : tcp_(tcp), port_(port), qid_(-1) { if (tcp_) { // Create a TCP socket to receive data on. sockfd_ = socket(family, SOCK_STREAM, 0); @@ -291,7 +291,11 @@ void MockServer::ProcessRequest(int fd, struct sockaddr_storage* addr, int addrl } std::vector reply = reply_; - // Overwrite the query ID with the value from the argument. + if (qid_ >= 0) { + // Use the explicitly specified query ID. + qid = qid_; + } + // Overwrite the query ID. reply[0] = (byte)((qid >> 8) & 0xff); reply[1] = (byte)(qid & 0xff); if (verbose) std::cerr << "sending reply " << PacketToString(reply) << std::endl; @@ -359,7 +363,7 @@ MockChannelOptsTest::MockChannelOptsTest(int family, optmask |= ARES_OPT_DOMAINS; } if (force_tcp) { - opts.flags = ARES_FLAG_USEVC; + opts.flags |= ARES_FLAG_USEVC; optmask |= ARES_OPT_FLAGS; } diff --git a/test/ares-test.h b/test/ares-test.h index e4417c75..4618de2d 100644 --- a/test/ares-test.h +++ b/test/ares-test.h @@ -125,6 +125,7 @@ class MockServer { // with the value from the request. void SetReplyData(const std::vector& reply) { reply_ = reply; } void SetReply(const DNSPacket* reply) { SetReplyData(reply->data()); } + void SetReplyQID(int qid) { qid_ = qid; } // The set of file descriptors that the server handles. std::set fds() const; @@ -144,6 +145,7 @@ class MockServer { int sockfd_; std::set connfds_; std::vector reply_; + int qid_; }; // Test fixture that uses a mock DNS server. @@ -174,6 +176,13 @@ class MockUDPChannelTest MockUDPChannelTest() : MockChannelOptsTest(GetParam(), false, nullptr, 0) {} }; +class MockTCPChannelTest + : public MockChannelOptsTest, + public ::testing::WithParamInterface { + public: + MockTCPChannelTest() : MockChannelOptsTest(GetParam(), true, nullptr, 0) {} +}; + // gMock action to set the reply for a mock server. ACTION_P2(SetReplyData, mockserver, data) { mockserver->SetReplyData(data); @@ -181,6 +190,9 @@ ACTION_P2(SetReplyData, mockserver, data) { ACTION_P2(SetReply, mockserver, reply) { mockserver->SetReply(reply); } +ACTION_P2(SetReplyQID, mockserver, qid) { + mockserver->SetReplyQID(qid); +} // gMock action to cancel a channel. ACTION_P2(CancelChannel, mockserver, channel) { ares_cancel(channel);