summaryrefslogtreecommitdiff
path: root/test/ares-test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'test/ares-test.cc')
-rw-r--r--test/ares-test.cc667
1 files changed, 0 insertions, 667 deletions
diff --git a/test/ares-test.cc b/test/ares-test.cc
deleted file mode 100644
index b86cba4..0000000
--- a/test/ares-test.cc
+++ /dev/null
@@ -1,667 +0,0 @@
-#include "ares-test.h"
-#include "dns-proto.h"
-
-// Include ares internal files for DNS protocol details
-#include "nameser.h"
-#include "ares_dns.h"
-
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_NETINET_TCP_H
-#include <netinet/tcp.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <functional>
-#include <sstream>
-
-#ifdef WIN32
-#define BYTE_CAST (char *)
-#define sclose(x) closesocket(x)
-#define mkdir_(d, p) mkdir(d)
-#else
-#define BYTE_CAST
-#define sclose(x) close(x)
-#define mkdir_(d, p) mkdir(d, p)
-#endif
-
-namespace ares {
-namespace test {
-
-bool verbose = false;
-int mock_port = 5300;
-
-unsigned long long LibraryTest::fails_ = 0;
-std::map<size_t, int> LibraryTest::size_fails_;
-
-void ProcessWork(ares_channel channel,
- std::function<std::set<int>()> get_extrafds,
- std::function<void(int)> process_extra) {
- int nfds, count;
- fd_set readers, writers;
- struct timeval tv;
- while (true) {
- // Retrieve the set of file descriptors that the library wants us to monitor.
- FD_ZERO(&readers);
- FD_ZERO(&writers);
- nfds = ares_fds(channel, &readers, &writers);
- if (nfds == 0) // no work left to do in the library
- return;
-
- // Add in the extra FDs if present.
- std::set<int> extrafds = get_extrafds();
- for (int extrafd : extrafds) {
- FD_SET(extrafd, &readers);
- if (extrafd >= nfds) {
- nfds = extrafd + 1;
- }
- }
-
- // Wait for activity or timeout.
- tv.tv_sec = 0;
- tv.tv_usec = 100000; // 100ms
- count = select(nfds, &readers, &writers, nullptr, &tv);
- if (count < 0) {
- fprintf(stderr, "select() failed, errno %d\n", errno);
- return;
- }
-
- // Let the library process any activity.
- ares_process(channel, &readers, &writers);
-
- // Let the provided callback process any activity on the extra FD.
- for (int extrafd : extrafds) {
- if (FD_ISSET(extrafd, &readers)) {
- process_extra(extrafd);
- }
- }
- }
-}
-
-// static
-void LibraryTest::SetAllocFail(int nth) {
- assert(nth > 0);
- assert(nth <= (int)(8 * sizeof(fails_)));
- fails_ |= (1LL << (nth - 1));
-}
-
-// static
-void LibraryTest::SetAllocSizeFail(size_t size) {
- size_fails_[size]++;
-}
-
-// static
-void LibraryTest::ClearFails() {
- fails_ = 0;
- size_fails_.clear();
-}
-
-
-// static
-bool LibraryTest::ShouldAllocFail(size_t size) {
- bool fail = (fails_ & 0x01);
- fails_ >>= 1;
- if (size_fails_[size] > 0) {
- size_fails_[size]--;
- fail = true;
- }
- return fail;
-}
-
-// static
-void* LibraryTest::amalloc(size_t size) {
- if (ShouldAllocFail(size)) {
- if (verbose) std::cerr << "Failing malloc(" << size << ") request" << std::endl;
- return nullptr;
- } else {
- return malloc(size);
- }
-}
-
-// static
-void* LibraryTest::arealloc(void *ptr, size_t size) {
- if (ShouldAllocFail(size)) {
- if (verbose) std::cerr << "Failing realloc(" << ptr << ", " << size << ") request" << std::endl;
- return nullptr;
- } else {
- return realloc(ptr, size);
- }
-}
-
-// static
-void LibraryTest::afree(void *ptr) {
- free(ptr);
-}
-
-std::set<int> NoExtraFDs() {
- return std::set<int>();
-}
-
-void DefaultChannelTest::Process() {
- ProcessWork(channel_, NoExtraFDs, nullptr);
-}
-
-void DefaultChannelModeTest::Process() {
- ProcessWork(channel_, NoExtraFDs, nullptr);
-}
-
-MockServer::MockServer(int family, int port, int tcpport)
- : udpport_(port), tcpport_(tcpport ? tcpport : udpport_), qid_(-1) {
- // Create a TCP socket to receive data on.
- tcpfd_ = socket(family, SOCK_STREAM, 0);
- EXPECT_NE(-1, tcpfd_);
- int optval = 1;
- setsockopt(tcpfd_, SOL_SOCKET, SO_REUSEADDR,
- BYTE_CAST &optval , sizeof(int));
- // Send TCP data right away.
- setsockopt(tcpfd_, IPPROTO_TCP, TCP_NODELAY,
- BYTE_CAST &optval , sizeof(int));
-
- // Create a UDP socket to receive data on.
- udpfd_ = socket(family, SOCK_DGRAM, 0);
- EXPECT_NE(-1, udpfd_);
-
- // Bind the sockets to the given port.
- if (family == AF_INET) {
- struct sockaddr_in addr;
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = htonl(INADDR_ANY);
- addr.sin_port = htons(tcpport_);
- int tcprc = bind(tcpfd_, (struct sockaddr*)&addr, sizeof(addr));
- EXPECT_EQ(0, tcprc) << "Failed to bind AF_INET to TCP port " << tcpport_;
- addr.sin_port = htons(udpport_);
- int udprc = bind(udpfd_, (struct sockaddr*)&addr, sizeof(addr));
- EXPECT_EQ(0, udprc) << "Failed to bind AF_INET to UDP port " << udpport_;
- } else {
- EXPECT_EQ(AF_INET6, family);
- struct sockaddr_in6 addr;
- memset(&addr, 0, sizeof(addr));
- addr.sin6_family = AF_INET6;
- memset(&addr.sin6_addr, 0, sizeof(addr.sin6_addr)); // in6addr_any
- addr.sin6_port = htons(tcpport_);
- int tcprc = bind(tcpfd_, (struct sockaddr*)&addr, sizeof(addr));
- EXPECT_EQ(0, tcprc) << "Failed to bind AF_INET6 to TCP port " << tcpport_;
- addr.sin6_port = htons(udpport_);
- int udprc = bind(udpfd_, (struct sockaddr*)&addr, sizeof(addr));
- EXPECT_EQ(0, udprc) << "Failed to bind AF_INET6 to UDP port " << udpport_;
- }
- if (verbose) std::cerr << "Configured "
- << (family == AF_INET ? "IPv4" : "IPv6")
- << " mock server with TCP socket " << tcpfd_
- << " on port " << tcpport_
- << " and UDP socket " << udpfd_
- << " on port " << udpport_ << std::endl;
-
- // For TCP, also need to listen for connections.
- EXPECT_EQ(0, listen(tcpfd_, 5)) << "Failed to listen for TCP connections";
-}
-
-MockServer::~MockServer() {
- for (int fd : connfds_) {
- sclose(fd);
- }
- sclose(tcpfd_);
- sclose(udpfd_);
-}
-
-void MockServer::ProcessFD(int fd) {
- if (fd != tcpfd_ && fd != udpfd_ && connfds_.find(fd) == connfds_.end()) {
- // Not one of our FDs.
- return;
- }
- if (fd == tcpfd_) {
- int connfd = accept(tcpfd_, NULL, NULL);
- if (connfd < 0) {
- std::cerr << "Error accepting connection on fd " << fd << std::endl;
- } else {
- connfds_.insert(connfd);
- }
- return;
- }
-
- // Activity on a data-bearing file descriptor.
- struct sockaddr_storage addr;
- socklen_t addrlen = sizeof(addr);
- byte buffer[2048];
- int len = recvfrom(fd, BYTE_CAST buffer, sizeof(buffer), 0,
- (struct sockaddr *)&addr, &addrlen);
- byte* data = buffer;
- if (fd != udpfd_) {
- if (len == 0) {
- connfds_.erase(std::find(connfds_.begin(), connfds_.end(), fd));
- sclose(fd);
- return;
- }
- if (len < 2) {
- std::cerr << "Packet too short (" << len << ")" << std::endl;
- return;
- }
- int tcplen = (data[0] << 8) + data[1];
- data += 2;
- len -= 2;
- if (tcplen != len) {
- std::cerr << "Warning: TCP length " << tcplen
- << " doesn't match remaining data length " << len << std::endl;
- }
- }
-
- // Assume the packet is a well-formed DNS request and extract the request
- // details.
- if (len < NS_HFIXEDSZ) {
- std::cerr << "Packet too short (" << len << ")" << std::endl;
- return;
- }
- int qid = DNS_HEADER_QID(data);
- if (DNS_HEADER_QR(data) != 0) {
- std::cerr << "Not a request" << std::endl;
- return;
- }
- if (DNS_HEADER_OPCODE(data) != ns_o_query) {
- std::cerr << "Not a query (opcode " << DNS_HEADER_OPCODE(data)
- << ")" << std::endl;
- return;
- }
- if (DNS_HEADER_QDCOUNT(data) != 1) {
- std::cerr << "Unexpected question count (" << DNS_HEADER_QDCOUNT(data)
- << ")" << std::endl;
- return;
- }
- byte* question = data + 12;
- int qlen = len - 12;
-
- char *name = nullptr;
- long enclen;
- ares_expand_name(question, data, len, &name, &enclen);
- if (!name) {
- std::cerr << "Failed to retrieve name" << std::endl;
- return;
- }
- qlen -= enclen;
- question += enclen;
- std::string namestr(name);
- free(name);
-
- if (qlen < 4) {
- std::cerr << "Unexpected question size (" << qlen
- << " bytes after name)" << std::endl;
- return;
- }
- if (DNS_QUESTION_CLASS(question) != ns_c_in) {
- std::cerr << "Unexpected question class (" << DNS_QUESTION_CLASS(question)
- << ")" << std::endl;
- return;
- }
- int rrtype = DNS_QUESTION_TYPE(question);
-
- if (verbose) {
- std::vector<byte> req(data, data + len);
- std::cerr << "received " << (fd == udpfd_ ? "UDP" : "TCP") << " request " << PacketToString(req)
- << " on port " << (fd == udpfd_ ? udpport_ : tcpport_) << std::endl;
- std::cerr << "ProcessRequest(" << qid << ", '" << namestr
- << "', " << RRTypeToString(rrtype) << ")" << std::endl;
- }
- ProcessRequest(fd, &addr, addrlen, qid, namestr, rrtype);
-}
-
-std::set<int> MockServer::fds() const {
- std::set<int> result = connfds_;
- result.insert(tcpfd_);
- result.insert(udpfd_);
- return result;
-}
-
-void MockServer::ProcessRequest(int fd, struct sockaddr_storage* addr, int addrlen,
- int qid, const std::string& name, int rrtype) {
- // Before processing, let gMock know the request is happening.
- OnRequest(name, rrtype);
-
- if (reply_.size() == 0) {
- return;
- }
-
- // Make a local copy of the current pending reply.
- std::vector<byte> reply = reply_;
-
- if (qid_ >= 0) {
- // Use the explicitly specified query ID.
- qid = qid_;
- }
- if (reply.size() >= 2) {
- // Overwrite the query ID if space to do so.
- reply[0] = (byte)((qid >> 8) & 0xff);
- reply[1] = (byte)(qid & 0xff);
- }
- if (verbose) std::cerr << "sending reply " << PacketToString(reply)
- << " on port " << ((fd == udpfd_) ? udpport_ : tcpport_) << std::endl;
-
- // Prefix with 2-byte length if TCP.
- if (fd != udpfd_) {
- int len = reply.size();
- std::vector<byte> vlen = {(byte)((len & 0xFF00) >> 8), (byte)(len & 0xFF)};
- reply.insert(reply.begin(), vlen.begin(), vlen.end());
- // Also, don't bother with the destination address.
- addr = nullptr;
- addrlen = 0;
- }
-
- int rc = sendto(fd, BYTE_CAST reply.data(), reply.size(), 0,
- (struct sockaddr *)addr, addrlen);
- if (rc < static_cast<int>(reply.size())) {
- std::cerr << "Failed to send full reply, rc=" << rc << std::endl;
- }
-}
-
-// static
-MockChannelOptsTest::NiceMockServers MockChannelOptsTest::BuildServers(int count, int family, int base_port) {
- NiceMockServers servers;
- assert(count > 0);
- for (int ii = 0; ii < count; ii++) {
- std::unique_ptr<NiceMockServer> server(new NiceMockServer(family, base_port + ii));
- servers.push_back(std::move(server));
- }
- return servers;
-}
-
-MockChannelOptsTest::MockChannelOptsTest(int count,
- int family,
- bool force_tcp,
- struct ares_options* givenopts,
- int optmask)
- : servers_(BuildServers(count, family, mock_port)),
- server_(*servers_[0].get()), channel_(nullptr) {
- // Set up channel options.
- struct ares_options opts;
- if (givenopts) {
- memcpy(&opts, givenopts, sizeof(opts));
- } else {
- memset(&opts, 0, sizeof(opts));
- }
-
- // Point the library at the first mock server by default (overridden below).
- opts.udp_port = mock_port;
- optmask |= ARES_OPT_UDP_PORT;
- opts.tcp_port = mock_port;
- optmask |= ARES_OPT_TCP_PORT;
-
- // If not already overridden, set short-ish timeouts.
- if (!(optmask & (ARES_OPT_TIMEOUTMS|ARES_OPT_TIMEOUT))) {
- opts.timeout = 1500;
- 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 (force_tcp) {
- opts.flags |= ARES_FLAG_USEVC;
- optmask |= ARES_OPT_FLAGS;
- }
-
- EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel_, &opts, optmask));
- EXPECT_NE(nullptr, channel_);
-
- // Set up servers after construction so we can set individual ports
- struct ares_addr_port_node* prev = nullptr;
- struct ares_addr_port_node* first = nullptr;
- for (const auto& server : servers_) {
- struct ares_addr_port_node* node = (struct ares_addr_port_node*)malloc(sizeof(*node));
- if (prev) {
- prev->next = node;
- } else {
- first = node;
- }
- node->next = nullptr;
- node->family = family;
- node->udp_port = server->udpport();
- node->tcp_port = server->tcpport();
- if (family == AF_INET) {
- node->addr.addr4.s_addr = htonl(0x7F000001);
- } else {
- memset(&node->addr.addr6, 0, sizeof(node->addr.addr6));
- node->addr.addr6._S6_un._S6_u8[15] = 1;
- }
- prev = node;
- }
- EXPECT_EQ(ARES_SUCCESS, ares_set_servers_ports(channel_, first));
-
- while (first) {
- prev = first;
- first = first->next;
- free(prev);
- }
- if (verbose) {
- std::cerr << "Configured library with servers:";
- std::vector<std::string> servers = GetNameServers(channel_);
- for (const auto& server : servers) {
- std::cerr << " " << server;
- }
- std::cerr << std::endl;
- }
-}
-
-MockChannelOptsTest::~MockChannelOptsTest() {
- if (channel_) {
- ares_destroy(channel_);
- }
- channel_ = nullptr;
-}
-
-std::set<int> MockChannelOptsTest::fds() const {
- std::set<int> fds;
- for (const auto& server : servers_) {
- std::set<int> serverfds = server->fds();
- fds.insert(serverfds.begin(), serverfds.end());
- }
- return fds;
-}
-
-void MockChannelOptsTest::ProcessFD(int fd) {
- for (auto& server : servers_) {
- server->ProcessFD(fd);
- }
-}
-
-void MockChannelOptsTest::Process() {
- using namespace std::placeholders;
- ProcessWork(channel_,
- std::bind(&MockChannelOptsTest::fds, this),
- std::bind(&MockChannelOptsTest::ProcessFD, this, _1));
-}
-
-std::ostream& operator<<(std::ostream& os, const HostResult& result) {
- os << '{';
- if (result.done_) {
- os << StatusToString(result.status_) << " " << result.host_;
- } else {
- os << "(incomplete)";
- }
- os << '}';
- return os;
-}
-
-HostEnt::HostEnt(const struct hostent *hostent) : addrtype_(-1) {
- if (!hostent)
- return;
- if (hostent->h_name)
- name_ = hostent->h_name;
- if (hostent->h_aliases) {
- char** palias = hostent->h_aliases;
- while (*palias != nullptr) {
- aliases_.push_back(*palias);
- palias++;
- }
- }
- addrtype_ = hostent->h_addrtype;
- if (hostent->h_addr_list) {
- char** paddr = hostent->h_addr_list;
- while (*paddr != nullptr) {
- std::string addr = AddressToString(*paddr, hostent->h_length);
- addrs_.push_back(addr);
- paddr++;
- }
- }
-}
-
-std::ostream& operator<<(std::ostream& os, const HostEnt& host) {
- os << '{';
- os << "'" << host.name_ << "' "
- << "aliases=[";
- for (size_t ii = 0; ii < host.aliases_.size(); ii++) {
- if (ii > 0) os << ", ";
- os << host.aliases_[ii];
- }
- os << "] ";
- os << "addrs=[";
- for (size_t ii = 0; ii < host.addrs_.size(); ii++) {
- if (ii > 0) os << ", ";
- os << host.addrs_[ii];
- }
- os << "]";
- os << '}';
- return os;
-}
-
-void HostCallback(void *data, int status, int timeouts,
- struct hostent *hostent) {
- EXPECT_NE(nullptr, data);
- HostResult* result = reinterpret_cast<HostResult*>(data);
- result->done_ = true;
- result->status_ = status;
- result->timeouts_ = timeouts;
- result->host_ = HostEnt(hostent);
- if (verbose) std::cerr << "HostCallback(" << *result << ")" << std::endl;
-}
-
-std::ostream& operator<<(std::ostream& os, const SearchResult& result) {
- os << '{';
- if (result.done_) {
- os << StatusToString(result.status_) << " " << PacketToString(result.data_);
- } else {
- os << "(incomplete)";
- }
- os << '}';
- return os;
-}
-
-void SearchCallback(void *data, int status, int timeouts,
- unsigned char *abuf, int alen) {
- EXPECT_NE(nullptr, data);
- SearchResult* result = reinterpret_cast<SearchResult*>(data);
- result->done_ = true;
- result->status_ = status;
- result->timeouts_ = timeouts;
- result->data_.assign(abuf, abuf + alen);
- if (verbose) std::cerr << "SearchCallback(" << *result << ")" << std::endl;
-}
-
-std::ostream& operator<<(std::ostream& os, const NameInfoResult& result) {
- os << '{';
- if (result.done_) {
- os << StatusToString(result.status_) << " " << result.node_ << " " << result.service_;
- } else {
- os << "(incomplete)";
- }
- os << '}';
- return os;
-}
-
-void NameInfoCallback(void *data, int status, int timeouts,
- char *node, char *service) {
- EXPECT_NE(nullptr, data);
- NameInfoResult* result = reinterpret_cast<NameInfoResult*>(data);
- result->done_ = true;
- result->status_ = status;
- result->timeouts_ = timeouts;
- result->node_ = std::string(node ? node : "");
- result->service_ = std::string(service ? service : "");
- if (verbose) std::cerr << "NameInfoCallback(" << *result << ")" << std::endl;
-}
-
-std::vector<std::string> GetNameServers(ares_channel channel) {
- struct ares_addr_port_node* servers = nullptr;
- EXPECT_EQ(ARES_SUCCESS, ares_get_servers_ports(channel, &servers));
- struct ares_addr_port_node* server = servers;
- std::vector<std::string> results;
- while (server) {
- std::stringstream ss;
- switch (server->family) {
- case AF_INET:
- ss << AddressToString((char*)&server->addr.addr4, 4);
- break;
- case AF_INET6:
- if (server->udp_port != 0) {
- ss << '[';
- }
- ss << AddressToString((char*)&server->addr.addr6, 16);
- if (server->udp_port != 0) {
- ss << ']';
- }
- break;
- default:
- results.push_back("<unknown family>");
- break;
- }
- if (server->udp_port != 0) {
- ss << ":" << server->udp_port;
- }
- results.push_back(ss.str());
- server = server->next;
- }
- if (servers) ares_free_data(servers);
- return results;
-}
-
-TransientDir::TransientDir(const std::string& dirname) : dirname_(dirname) {
- if (mkdir_(dirname_.c_str(), 0755) != 0) {
- std::cerr << "Failed to create subdirectory '" << dirname_ << "'" << std::endl;
- }
-}
-
-TransientDir::~TransientDir() {
- rmdir(dirname_.c_str());
-}
-
-TransientFile::TransientFile(const std::string& filename,
- const std::string& contents)
- : filename_(filename) {
- FILE *f = fopen(filename.c_str(), "w");
- if (f == nullptr) {
- std::cerr << "Error: failed to create '" << filename << "'" << std::endl;
- return;
- }
- int rc = fwrite(contents.data(), 1, contents.size(), f);
- if (rc != (int)contents.size()) {
- std::cerr << "Error: failed to write contents of '" << filename << "'" << std::endl;
- }
- fclose(f);
-}
-
-TransientFile::~TransientFile() {
- unlink(filename_.c_str());
-}
-
-std::string TempNam(const char *dir, const char *prefix) {
- char *p = tempnam(dir, prefix);
- std::string result(p);
- free(p);
- return result;
-}
-
-TempFile::TempFile(const std::string& contents)
- : TransientFile(TempNam(nullptr, "ares"), contents) {
-
-}
-
-} // namespace test
-} // namespace ares