A C library for asynchronous DNS requests (grpc依赖)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

885 lines
34 KiB

/*
* Copyright (C) The c-ares project
*
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting
* documentation, and that the name of M.I.T. not be used in
* advertising or publicity pertaining to distribution of the
* software without specific, written prior permission.
* M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is"
* without express or implied warranty.
*
* SPDX-License-Identifier: MIT
*/
#include "ares-test.h"
#include "dns-proto.h"
#include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <fcntl.h>
extern "C" {
// Remove command-line defines of package variables for the test project...
#undef PACKAGE_NAME
#undef PACKAGE_BUGREPORT
#undef PACKAGE_STRING
#undef PACKAGE_TARNAME
// ... so we can include the library's config without symbol redefinitions.
#include "ares_setup.h"
#include "ares_inet_net_pton.h"
#include "ares_data.h"
#include "ares_strsplit.h"
#include "ares_private.h"
#include "ares__htable.h"
#include "bitncmp.h"
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_SYS_UIO_H
# include <sys/uio.h>
#endif
}
#include <string>
#include <vector>
namespace ares {
namespace test {
#ifndef CARES_SYMBOL_HIDING
void CheckPtoN4(int size, unsigned int value, const char *input) {
struct in_addr a4;
a4.s_addr = 0;
uint32_t expected = htonl(value);
EXPECT_EQ(size, ares_inet_net_pton(AF_INET, input, &a4, sizeof(a4)))
<< " for input " << input;
EXPECT_EQ(expected, a4.s_addr) << " for input " << input;
}
#endif
bug fix: new ares_strsplit (#492) * add ares_strsplit unit test The test reveals a bug in the implementation of ares_strsplit when the make_set parameter is set to 1, as distinct domains are confused for equal: out = ares_strsplit("example.com, example.co", ", ", 1, &n); evaluates to n = 1 with out = { "example.com" }. * bugfix and cleanup of ares_strsplit The purpose of ares_strsplit in c-ares is to split a comma-delimited string of unique (up to letter case) domains. However, because the terminating NUL byte was not checked in the substrings when comparing for uniqueness, the function would sometimes drop domains it should not. For example, ares_strsplit("example.com, example.co", ",") would only result in a single domain "example.com". Aside from this bugfix, the following cleanup is performed: 1. The tokenization now happens with the help of strcspn instead of the custom function is_delim. 2. The function list_contains has been inlined. 3. The interface of ares_strsplit has been simplified by removing the parameter make_set since in practice it was always 1. 4. There are fewer passes over the input string. 5. We resize the table using realloc() down to its minimum size. 6. The docstring of ares_strsplit is updated and also a couple typos are fixed. There occurs a single use of ares_strsplit and since the make_set parameter has been removed, the call in ares_init.c is modified accordingly. The unit test for ares_strsplit is also updated. Fix By: Nikolaos Chatzikonstantinou (@createyourpersonalaccount)
2 years ago
#ifndef CARES_SYMBOL_HIDING
TEST_F(LibraryTest, Strsplit) {
using std::vector;
using std::string;
size_t n;
struct {
vector<string> inputs;
vector<string> delimiters;
vector<vector<string>> expected;
} data = {
{
"",
" ",
" ",
"example.com, example.co",
" a, b, A,c, d, e,,,D,e,e,E",
},
{ ", ", ", ", ", ", ", ", ", " },
{
{}, {}, {},
{ "example.com", "example.co" },
{ "a", "b", "c", "d", "e" },
},
};
for(size_t i = 0; i < data.inputs.size(); i++) {
char **out = ares__strsplit(data.inputs.at(i).c_str(),
bug fix: new ares_strsplit (#492) * add ares_strsplit unit test The test reveals a bug in the implementation of ares_strsplit when the make_set parameter is set to 1, as distinct domains are confused for equal: out = ares_strsplit("example.com, example.co", ", ", 1, &n); evaluates to n = 1 with out = { "example.com" }. * bugfix and cleanup of ares_strsplit The purpose of ares_strsplit in c-ares is to split a comma-delimited string of unique (up to letter case) domains. However, because the terminating NUL byte was not checked in the substrings when comparing for uniqueness, the function would sometimes drop domains it should not. For example, ares_strsplit("example.com, example.co", ",") would only result in a single domain "example.com". Aside from this bugfix, the following cleanup is performed: 1. The tokenization now happens with the help of strcspn instead of the custom function is_delim. 2. The function list_contains has been inlined. 3. The interface of ares_strsplit has been simplified by removing the parameter make_set since in practice it was always 1. 4. There are fewer passes over the input string. 5. We resize the table using realloc() down to its minimum size. 6. The docstring of ares_strsplit is updated and also a couple typos are fixed. There occurs a single use of ares_strsplit and since the make_set parameter has been removed, the call in ares_init.c is modified accordingly. The unit test for ares_strsplit is also updated. Fix By: Nikolaos Chatzikonstantinou (@createyourpersonalaccount)
2 years ago
data.delimiters.at(i).c_str(), &n);
if(data.expected.at(i).size() == 0) {
EXPECT_EQ(out, nullptr);
}
else {
EXPECT_EQ(n, data.expected.at(i).size());
for(size_t j = 0; j < n && j < data.expected.at(i).size(); j++) {
EXPECT_STREQ(out[j], data.expected.at(i).at(j).c_str());
}
}
ares__strsplit_free(out, n);
bug fix: new ares_strsplit (#492) * add ares_strsplit unit test The test reveals a bug in the implementation of ares_strsplit when the make_set parameter is set to 1, as distinct domains are confused for equal: out = ares_strsplit("example.com, example.co", ", ", 1, &n); evaluates to n = 1 with out = { "example.com" }. * bugfix and cleanup of ares_strsplit The purpose of ares_strsplit in c-ares is to split a comma-delimited string of unique (up to letter case) domains. However, because the terminating NUL byte was not checked in the substrings when comparing for uniqueness, the function would sometimes drop domains it should not. For example, ares_strsplit("example.com, example.co", ",") would only result in a single domain "example.com". Aside from this bugfix, the following cleanup is performed: 1. The tokenization now happens with the help of strcspn instead of the custom function is_delim. 2. The function list_contains has been inlined. 3. The interface of ares_strsplit has been simplified by removing the parameter make_set since in practice it was always 1. 4. There are fewer passes over the input string. 5. We resize the table using realloc() down to its minimum size. 6. The docstring of ares_strsplit is updated and also a couple typos are fixed. There occurs a single use of ares_strsplit and since the make_set parameter has been removed, the call in ares_init.c is modified accordingly. The unit test for ares_strsplit is also updated. Fix By: Nikolaos Chatzikonstantinou (@createyourpersonalaccount)
2 years ago
}
}
#endif
TEST_F(LibraryTest, InetPtoN) {
struct in_addr a4;
struct in6_addr a6;
#ifndef CARES_SYMBOL_HIDING
uint32_t expected;
CheckPtoN4(4 * 8, 0x01020304, "1.2.3.4");
CheckPtoN4(4 * 8, 0x81010101, "129.1.1.1");
CheckPtoN4(4 * 8, 0xC0010101, "192.1.1.1");
CheckPtoN4(4 * 8, 0xE0010101, "224.1.1.1");
CheckPtoN4(4 * 8, 0xE1010101, "225.1.1.1");
CheckPtoN4(4, 0xE0000000, "224");
CheckPtoN4(4 * 8, 0xFD000000, "253");
CheckPtoN4(4 * 8, 0xF0010101, "240.1.1.1");
CheckPtoN4(4 * 8, 0x02030405, "02.3.4.5");
CheckPtoN4(3 * 8, 0x01020304, "1.2.3.4/24");
CheckPtoN4(3 * 8, 0x01020300, "1.2.3/24");
CheckPtoN4(2 * 8, 0xa0000000, "0xa");
CheckPtoN4(0, 0x02030405, "2.3.4.5/000");
CheckPtoN4(1 * 8, 0x01020000, "1.2/8");
CheckPtoN4(2 * 8, 0x01020000, "0x0102/16");
CheckPtoN4(4 * 8, 0x02030405, "02.3.4.5");
EXPECT_EQ(16 * 8, ares_inet_net_pton(AF_INET6, "::", &a6, sizeof(a6)));
EXPECT_EQ(16 * 8, ares_inet_net_pton(AF_INET6, "::1", &a6, sizeof(a6)));
EXPECT_EQ(16 * 8, ares_inet_net_pton(AF_INET6, "1234:5678::", &a6, sizeof(a6)));
EXPECT_EQ(16 * 8, ares_inet_net_pton(AF_INET6, "12:34::ff", &a6, sizeof(a6)));
EXPECT_EQ(16 * 8, ares_inet_net_pton(AF_INET6, "12:34::ffff:1.2.3.4", &a6, sizeof(a6)));
EXPECT_EQ(23, ares_inet_net_pton(AF_INET6, "12:34::ffff:1.2.3.4/23", &a6, sizeof(a6)));
EXPECT_EQ(3 * 8, ares_inet_net_pton(AF_INET6, "12:34::ff/24", &a6, sizeof(a6)));
EXPECT_EQ(0, ares_inet_net_pton(AF_INET6, "12:34::ff/0", &a6, sizeof(a6)));
EXPECT_EQ(16 * 8, ares_inet_net_pton(AF_INET6, "12:34::ffff:0.2", &a6, sizeof(a6)));
EXPECT_EQ(16 * 8, ares_inet_net_pton(AF_INET6, "1234:1234:1234:1234:1234:1234:1234:1234", &a6, sizeof(a6)));
EXPECT_EQ(2, ares_inet_net_pton(AF_INET6, "0::00:00:00/2", &a6, sizeof(a6)));
// Various malformed versions
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "", &a4, sizeof(a4)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, " ", &a4, sizeof(a4)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "0x", &a4, sizeof(a4)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "0x ", &a4, sizeof(a4)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "x0", &a4, sizeof(a4)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "0xXYZZY", &a4, sizeof(a4)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "xyzzy", &a4, sizeof(a4)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET+AF_INET6, "1.2.3.4", &a4, sizeof(a4)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "257.2.3.4", &a4, sizeof(a4)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "002.3.4.x", &a4, sizeof(a4)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "00.3.4.x", &a4, sizeof(a4)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "2.3.4.x", &a4, sizeof(a4)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "2.3.4.5.6", &a4, sizeof(a4)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "2.3.4.5.6/12", &a4, sizeof(a4)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "2.3.4:5", &a4, sizeof(a4)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "2.3.4.5/120", &a4, sizeof(a4)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "2.3.4.5/1x", &a4, sizeof(a4)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "2.3.4.5/x", &a4, sizeof(a4)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ff/240", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ff/02", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ff/2y", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ff/y", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ff/", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, ":x", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, ":", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, ": :1234", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "::12345", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "1234::2345:3456::0011", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "1234:1234:1234:1234:1234:1234:1234:1234:", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "1234:1234:1234:1234:1234:1234:1234:1234::", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "1234:1234:1234:1234:1234:1234:1234:1.2.3.4", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, ":1234:1234:1234:1234:1234:1234:1234:1234", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, ":1234:1234:1234:1234:1234:1234:1234:1234:", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "1234:1234:1234:1234:1234:1234:1234:1234:5678", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "1234:1234:1234:1234:1234:1234:1234:1234:5678:5678", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "1234:1234:1234:1234:1234:1234:1234:1234:5678:5678:5678", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ffff:257.2.3.4", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ffff:1.2.3.4.5.6", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ffff:1.2.3.4.5", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ffff:1.2.3.z", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ffff:1.2.3001.4", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ffff:1.2.3..4", &a6, sizeof(a6)));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ffff:1.2.3.", &a6, sizeof(a6)));
// Hex constants are allowed.
EXPECT_EQ(4 * 8, ares_inet_net_pton(AF_INET, "0x01020304", &a4, sizeof(a4)));
expected = htonl(0x01020304);
EXPECT_EQ(expected, a4.s_addr);
EXPECT_EQ(4 * 8, ares_inet_net_pton(AF_INET, "0x0a0b0c0d", &a4, sizeof(a4)));
expected = htonl(0x0a0b0c0d);
EXPECT_EQ(expected, a4.s_addr);
EXPECT_EQ(4 * 8, ares_inet_net_pton(AF_INET, "0x0A0B0C0D", &a4, sizeof(a4)));
expected = htonl(0x0a0b0c0d);
EXPECT_EQ(expected, a4.s_addr);
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "0x0xyz", &a4, sizeof(a4)));
EXPECT_EQ(4 * 8, ares_inet_net_pton(AF_INET, "0x1122334", &a4, sizeof(a4)));
expected = htonl(0x11223340);
EXPECT_EQ(expected, a4.s_addr); // huh?
// No room, no room.
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "1.2.3.4", &a4, sizeof(a4) - 1));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET6, "12:34::ff", &a6, sizeof(a6) - 1));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "0x01020304", &a4, 2));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "0x01020304", &a4, 0));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "0x0a0b0c0d", &a4, 0));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "0x0xyz", &a4, 0));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "0x1122334", &a4, sizeof(a4) - 1));
EXPECT_EQ(-1, ares_inet_net_pton(AF_INET, "253", &a4, sizeof(a4) - 1));
#endif
EXPECT_EQ(1, ares_inet_pton(AF_INET, "1.2.3.4", &a4));
EXPECT_EQ(1, ares_inet_pton(AF_INET6, "12:34::ff", &a6));
EXPECT_EQ(1, ares_inet_pton(AF_INET6, "12:34::ffff:1.2.3.4", &a6));
EXPECT_EQ(0, ares_inet_pton(AF_INET, "xyzzy", &a4));
EXPECT_EQ(-1, ares_inet_pton(AF_INET+AF_INET6, "1.2.3.4", &a4));
}
TEST_F(LibraryTest, FreeCorruptData) {
// ares_free_data(p) expects that there is a type field and a marker
// field in the memory before p. Feed it incorrect versions of each.
struct ares_data *data = (struct ares_data *)malloc(sizeof(struct ares_data));
void* p = &(data->data);
// Invalid type
data->type = (ares_datatype)99;
data->mark = ARES_DATATYPE_MARK;
ares_free_data(p);
// Invalid marker
data->type = (ares_datatype)ARES_DATATYPE_MX_REPLY;
data->mark = ARES_DATATYPE_MARK + 1;
ares_free_data(p);
// Null pointer
ares_free_data(nullptr);
free(data);
}
#ifndef CARES_SYMBOL_HIDING
TEST_F(LibraryTest, FreeLongChain) {
struct ares_addr_node *data = nullptr;
for (int ii = 0; ii < 100000; ii++) {
struct ares_addr_node *prev = (struct ares_addr_node*)ares_malloc_data(ARES_DATATYPE_ADDR_NODE);
prev->next = data;
data = prev;
}
ares_free_data(data);
}
TEST(LibraryInit, StrdupFailures) {
EXPECT_EQ(ARES_SUCCESS, ares_library_init(ARES_LIB_INIT_ALL));
char* copy = ares_strdup("string");
EXPECT_NE(nullptr, copy);
ares_free(copy);
ares_library_cleanup();
}
TEST_F(LibraryTest, StrdupFailures) {
SetAllocFail(1);
char* copy = ares_strdup("string");
EXPECT_EQ(nullptr, copy);
}
TEST_F(LibraryTest, MallocDataFail) {
EXPECT_EQ(nullptr, ares_malloc_data((ares_datatype)99));
SetAllocSizeFail(sizeof(struct ares_data));
EXPECT_EQ(nullptr, ares_malloc_data(ARES_DATATYPE_MX_REPLY));
}
TEST(Misc, Bitncmp) {
byte a[4] = {0x80, 0x01, 0x02, 0x03};
byte b[4] = {0x80, 0x01, 0x02, 0x04};
byte c[4] = {0x01, 0xFF, 0x80, 0x02};
EXPECT_GT(0, ares__bitncmp(a, b, sizeof(a)*8));
EXPECT_LT(0, ares__bitncmp(b, a, sizeof(a)*8));
EXPECT_EQ(0, ares__bitncmp(a, a, sizeof(a)*8));
for (int ii = 1; ii < (3*8+5); ii++) {
EXPECT_EQ(0, ares__bitncmp(a, b, ii));
EXPECT_EQ(0, ares__bitncmp(b, a, ii));
EXPECT_LT(0, ares__bitncmp(a, c, ii));
EXPECT_GT(0, ares__bitncmp(c, a, ii));
}
// Last byte differs at 5th bit
EXPECT_EQ(0, ares__bitncmp(a, b, 3*8 + 3));
EXPECT_EQ(0, ares__bitncmp(a, b, 3*8 + 4));
EXPECT_EQ(0, ares__bitncmp(a, b, 3*8 + 5));
EXPECT_GT(0, ares__bitncmp(a, b, 3*8 + 6));
EXPECT_GT(0, ares__bitncmp(a, b, 3*8 + 7));
}
TEST_F(LibraryTest, ReadLine) {
TempFile temp("abcde\n0123456789\nXYZ\n012345678901234567890\n\n");
FILE *fp = fopen(temp.filename(), "r");
size_t bufsize = 4;
char *buf = (char *)ares_malloc(bufsize);
EXPECT_EQ(ARES_SUCCESS, ares__read_line(fp, &buf, &bufsize));
EXPECT_EQ("abcde", std::string(buf));
EXPECT_EQ(ARES_SUCCESS, ares__read_line(fp, &buf, &bufsize));
EXPECT_EQ("0123456789", std::string(buf));
EXPECT_EQ(ARES_SUCCESS, ares__read_line(fp, &buf, &bufsize));
EXPECT_EQ("XYZ", std::string(buf));
SetAllocFail(1);
EXPECT_EQ(ARES_ENOMEM, ares__read_line(fp, &buf, &bufsize));
EXPECT_EQ(nullptr, buf);
fclose(fp);
ares_free(buf);
}
TEST_F(LibraryTest, ReadLineNoBuf) {
TempFile temp("abcde\n0123456789\nXYZ\n012345678901234567890");
FILE *fp = fopen(temp.filename(), "r");
size_t bufsize = 0;
char *buf = nullptr;
SetAllocFail(1);
EXPECT_EQ(ARES_ENOMEM, ares__read_line(fp, &buf, &bufsize));
EXPECT_EQ(ARES_SUCCESS, ares__read_line(fp, &buf, &bufsize));
EXPECT_EQ("abcde", std::string(buf));
EXPECT_EQ(ARES_SUCCESS, ares__read_line(fp, &buf, &bufsize));
EXPECT_EQ("0123456789", std::string(buf));
EXPECT_EQ(ARES_SUCCESS, ares__read_line(fp, &buf, &bufsize));
EXPECT_EQ("XYZ", std::string(buf));
EXPECT_EQ(ARES_SUCCESS, ares__read_line(fp, &buf, &bufsize));
EXPECT_EQ("012345678901234567890", std::string(buf));
fclose(fp);
ares_free(buf);
}
Replace hosts parser, add caching capabilities (#591) HOSTS FILE PROCESSING OVERVIEW ============================== The hosts file on the system contains static entries to be processed locally rather than querying the nameserver. Each row is an IP address followed by a list of space delimited hostnames that match the ip address. This is used for both forward and reverse lookups. We are caching the entire parsed hosts file for performance reasons. Some files may be quite sizable and as per Issue #458 can approach 1/2MB in size, and the parse overhead on a rapid succession of queries can be quite large. The entries are stored in forwards and backwards hashtables so we can get O(1) performance on lookup. The file is cached until the file modification timestamp changes (or 60s if there is no implemented stat() capability). The hosts file processing is quite unique. It has to merge all related hosts and ips into a single entry due to file formatting requirements. For instance take the below: ``` 127.0.0.1 localhost.localdomain localhost ::1 localhost.localdomain localhost 192.168.1.1 host.example.com host 192.168.1.5 host.example.com host 2620:1234::1 host.example.com host6.example.com host6 host ``` This will yield 2 entries. 1) ips: `127.0.0.1,::1` hosts: `localhost.localdomain,localhost` 2) ips: `192.168.1.1,192.168.1.5,2620:1234::1` hosts: `host.example.com,host,host6.example.com,host6` It could be argued that if searching for `192.168.1.1` that the `host6` hostnames should not be returned, but this implementation will return them since they are related (both ips have the fqdn of host.example.com). It is unlikely this will matter in the real world. Fix By: Brad House (@bradh352)
1 year ago
TEST_F(FileChannelTest, GetAddrInfoHostsPositive) {
TempFile hostsfile("1.2.3.4 example.com \n"
" 2.3.4.5\tgoogle.com www.google.com\twww2.google.com\n"
"#comment\n"
"4.5.6.7\n"
"1.3.5.7 \n"
"::1 ipv6.com");
EnvValue with_env("CARES_HOSTS", hostsfile.filename());
struct ares_addrinfo_hints hints = {};
AddrInfoResult result = {};
hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_CANONNAME | ARES_AI_ENVHOSTS | ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "example.com", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done_);
std::stringstream ss;
ss << result.ai_;
EXPECT_EQ("{example.com addr=[1.2.3.4]}", ss.str());
}
Replace hosts parser, add caching capabilities (#591) HOSTS FILE PROCESSING OVERVIEW ============================== The hosts file on the system contains static entries to be processed locally rather than querying the nameserver. Each row is an IP address followed by a list of space delimited hostnames that match the ip address. This is used for both forward and reverse lookups. We are caching the entire parsed hosts file for performance reasons. Some files may be quite sizable and as per Issue #458 can approach 1/2MB in size, and the parse overhead on a rapid succession of queries can be quite large. The entries are stored in forwards and backwards hashtables so we can get O(1) performance on lookup. The file is cached until the file modification timestamp changes (or 60s if there is no implemented stat() capability). The hosts file processing is quite unique. It has to merge all related hosts and ips into a single entry due to file formatting requirements. For instance take the below: ``` 127.0.0.1 localhost.localdomain localhost ::1 localhost.localdomain localhost 192.168.1.1 host.example.com host 192.168.1.5 host.example.com host 2620:1234::1 host.example.com host6.example.com host6 host ``` This will yield 2 entries. 1) ips: `127.0.0.1,::1` hosts: `localhost.localdomain,localhost` 2) ips: `192.168.1.1,192.168.1.5,2620:1234::1` hosts: `host.example.com,host,host6.example.com,host6` It could be argued that if searching for `192.168.1.1` that the `host6` hostnames should not be returned, but this implementation will return them since they are related (both ips have the fqdn of host.example.com). It is unlikely this will matter in the real world. Fix By: Brad House (@bradh352)
1 year ago
TEST_F(FileChannelTest, GetAddrInfoHostsSpaces) {
TempFile hostsfile("1.2.3.4 example.com \n"
" 2.3.4.5\tgoogle.com www.google.com\twww2.google.com\n"
"#comment\n"
"4.5.6.7\n"
"1.3.5.7 \n"
"::1 ipv6.com");
EnvValue with_env("CARES_HOSTS", hostsfile.filename());
struct ares_addrinfo_hints hints = {};
AddrInfoResult result = {};
hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_CANONNAME | ARES_AI_ENVHOSTS | ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "google.com", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done_);
std::stringstream ss;
ss << result.ai_;
EXPECT_EQ("{www.google.com->google.com, www2.google.com->google.com addr=[2.3.4.5]}", ss.str());
}
Replace hosts parser, add caching capabilities (#591) HOSTS FILE PROCESSING OVERVIEW ============================== The hosts file on the system contains static entries to be processed locally rather than querying the nameserver. Each row is an IP address followed by a list of space delimited hostnames that match the ip address. This is used for both forward and reverse lookups. We are caching the entire parsed hosts file for performance reasons. Some files may be quite sizable and as per Issue #458 can approach 1/2MB in size, and the parse overhead on a rapid succession of queries can be quite large. The entries are stored in forwards and backwards hashtables so we can get O(1) performance on lookup. The file is cached until the file modification timestamp changes (or 60s if there is no implemented stat() capability). The hosts file processing is quite unique. It has to merge all related hosts and ips into a single entry due to file formatting requirements. For instance take the below: ``` 127.0.0.1 localhost.localdomain localhost ::1 localhost.localdomain localhost 192.168.1.1 host.example.com host 192.168.1.5 host.example.com host 2620:1234::1 host.example.com host6.example.com host6 host ``` This will yield 2 entries. 1) ips: `127.0.0.1,::1` hosts: `localhost.localdomain,localhost` 2) ips: `192.168.1.1,192.168.1.5,2620:1234::1` hosts: `host.example.com,host,host6.example.com,host6` It could be argued that if searching for `192.168.1.1` that the `host6` hostnames should not be returned, but this implementation will return them since they are related (both ips have the fqdn of host.example.com). It is unlikely this will matter in the real world. Fix By: Brad House (@bradh352)
1 year ago
TEST_F(FileChannelTest, GetAddrInfoHostsByALias) {
TempFile hostsfile("1.2.3.4 example.com \n"
" 2.3.4.5\tgoogle.com www.google.com\twww2.google.com\n"
"#comment\n"
"4.5.6.7\n"
"1.3.5.7 \n"
"::1 ipv6.com");
EnvValue with_env("CARES_HOSTS", hostsfile.filename());
struct ares_addrinfo_hints hints = {};
AddrInfoResult result = {};
hints.ai_family = AF_INET;
hints.ai_flags = ARES_AI_CANONNAME | ARES_AI_ENVHOSTS | ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "www2.google.com", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done_);
std::stringstream ss;
ss << result.ai_;
EXPECT_EQ("{www.google.com->google.com, www2.google.com->google.com addr=[2.3.4.5]}", ss.str());
}
Replace hosts parser, add caching capabilities (#591) HOSTS FILE PROCESSING OVERVIEW ============================== The hosts file on the system contains static entries to be processed locally rather than querying the nameserver. Each row is an IP address followed by a list of space delimited hostnames that match the ip address. This is used for both forward and reverse lookups. We are caching the entire parsed hosts file for performance reasons. Some files may be quite sizable and as per Issue #458 can approach 1/2MB in size, and the parse overhead on a rapid succession of queries can be quite large. The entries are stored in forwards and backwards hashtables so we can get O(1) performance on lookup. The file is cached until the file modification timestamp changes (or 60s if there is no implemented stat() capability). The hosts file processing is quite unique. It has to merge all related hosts and ips into a single entry due to file formatting requirements. For instance take the below: ``` 127.0.0.1 localhost.localdomain localhost ::1 localhost.localdomain localhost 192.168.1.1 host.example.com host 192.168.1.5 host.example.com host 2620:1234::1 host.example.com host6.example.com host6 host ``` This will yield 2 entries. 1) ips: `127.0.0.1,::1` hosts: `localhost.localdomain,localhost` 2) ips: `192.168.1.1,192.168.1.5,2620:1234::1` hosts: `host.example.com,host,host6.example.com,host6` It could be argued that if searching for `192.168.1.1` that the `host6` hostnames should not be returned, but this implementation will return them since they are related (both ips have the fqdn of host.example.com). It is unlikely this will matter in the real world. Fix By: Brad House (@bradh352)
1 year ago
TEST_F(FileChannelTest, GetAddrInfoHostsIPV6) {
TempFile hostsfile("1.2.3.4 example.com \n"
" 2.3.4.5\tgoogle.com www.google.com\twww2.google.com\n"
"#comment\n"
"4.5.6.7\n"
"1.3.5.7 \n"
"::1 ipv6.com");
EnvValue with_env("CARES_HOSTS", hostsfile.filename());
struct ares_addrinfo_hints hints = {};
AddrInfoResult result = {};
hints.ai_family = AF_INET6;
hints.ai_flags = ARES_AI_CANONNAME | ARES_AI_ENVHOSTS | ARES_AI_NOSORT;
ares_getaddrinfo(channel_, "ipv6.com", NULL, &hints, AddrInfoCallback, &result);
Process();
EXPECT_TRUE(result.done_);
std::stringstream ss;
ss << result.ai_;
EXPECT_EQ("{ipv6.com addr=[[0000:0000:0000:0000:0000:0000:0000:0001]]}", ss.str());
}
Replace hosts parser, add caching capabilities (#591) HOSTS FILE PROCESSING OVERVIEW ============================== The hosts file on the system contains static entries to be processed locally rather than querying the nameserver. Each row is an IP address followed by a list of space delimited hostnames that match the ip address. This is used for both forward and reverse lookups. We are caching the entire parsed hosts file for performance reasons. Some files may be quite sizable and as per Issue #458 can approach 1/2MB in size, and the parse overhead on a rapid succession of queries can be quite large. The entries are stored in forwards and backwards hashtables so we can get O(1) performance on lookup. The file is cached until the file modification timestamp changes (or 60s if there is no implemented stat() capability). The hosts file processing is quite unique. It has to merge all related hosts and ips into a single entry due to file formatting requirements. For instance take the below: ``` 127.0.0.1 localhost.localdomain localhost ::1 localhost.localdomain localhost 192.168.1.1 host.example.com host 192.168.1.5 host.example.com host 2620:1234::1 host.example.com host6.example.com host6 host ``` This will yield 2 entries. 1) ips: `127.0.0.1,::1` hosts: `localhost.localdomain,localhost` 2) ips: `192.168.1.1,192.168.1.5,2620:1234::1` hosts: `host.example.com,host,host6.example.com,host6` It could be argued that if searching for `192.168.1.1` that the `host6` hostnames should not be returned, but this implementation will return them since they are related (both ips have the fqdn of host.example.com). It is unlikely this will matter in the real world. Fix By: Brad House (@bradh352)
1 year ago
TEST_F(FileChannelTest, GetAddrInfoAllocFail) {
TempFile hostsfile("1.2.3.4 example.com alias1 alias2\n");
Replace hosts parser, add caching capabilities (#591) HOSTS FILE PROCESSING OVERVIEW ============================== The hosts file on the system contains static entries to be processed locally rather than querying the nameserver. Each row is an IP address followed by a list of space delimited hostnames that match the ip address. This is used for both forward and reverse lookups. We are caching the entire parsed hosts file for performance reasons. Some files may be quite sizable and as per Issue #458 can approach 1/2MB in size, and the parse overhead on a rapid succession of queries can be quite large. The entries are stored in forwards and backwards hashtables so we can get O(1) performance on lookup. The file is cached until the file modification timestamp changes (or 60s if there is no implemented stat() capability). The hosts file processing is quite unique. It has to merge all related hosts and ips into a single entry due to file formatting requirements. For instance take the below: ``` 127.0.0.1 localhost.localdomain localhost ::1 localhost.localdomain localhost 192.168.1.1 host.example.com host 192.168.1.5 host.example.com host 2620:1234::1 host.example.com host6.example.com host6 host ``` This will yield 2 entries. 1) ips: `127.0.0.1,::1` hosts: `localhost.localdomain,localhost` 2) ips: `192.168.1.1,192.168.1.5,2620:1234::1` hosts: `host.example.com,host,host6.example.com,host6` It could be argued that if searching for `192.168.1.1` that the `host6` hostnames should not be returned, but this implementation will return them since they are related (both ips have the fqdn of host.example.com). It is unlikely this will matter in the real world. Fix By: Brad House (@bradh352)
1 year ago
EnvValue with_env("CARES_HOSTS", hostsfile.filename());
struct ares_addrinfo_hints hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
Replace hosts parser, add caching capabilities (#591) HOSTS FILE PROCESSING OVERVIEW ============================== The hosts file on the system contains static entries to be processed locally rather than querying the nameserver. Each row is an IP address followed by a list of space delimited hostnames that match the ip address. This is used for both forward and reverse lookups. We are caching the entire parsed hosts file for performance reasons. Some files may be quite sizable and as per Issue #458 can approach 1/2MB in size, and the parse overhead on a rapid succession of queries can be quite large. The entries are stored in forwards and backwards hashtables so we can get O(1) performance on lookup. The file is cached until the file modification timestamp changes (or 60s if there is no implemented stat() capability). The hosts file processing is quite unique. It has to merge all related hosts and ips into a single entry due to file formatting requirements. For instance take the below: ``` 127.0.0.1 localhost.localdomain localhost ::1 localhost.localdomain localhost 192.168.1.1 host.example.com host 192.168.1.5 host.example.com host 2620:1234::1 host.example.com host6.example.com host6 host ``` This will yield 2 entries. 1) ips: `127.0.0.1,::1` hosts: `localhost.localdomain,localhost` 2) ips: `192.168.1.1,192.168.1.5,2620:1234::1` hosts: `host.example.com,host,host6.example.com,host6` It could be argued that if searching for `192.168.1.1` that the `host6` hostnames should not be returned, but this implementation will return them since they are related (both ips have the fqdn of host.example.com). It is unlikely this will matter in the real world. Fix By: Brad House (@bradh352)
1 year ago
// Fail a variety of different memory allocations, and confirm
// that the operation either fails with ENOMEM or succeeds
// with the expected result.
const int kCount = 34;
AddrInfoResult results[kCount];
for (int ii = 1; ii <= kCount; ii++) {
AddrInfoResult* result = &(results[ii - 1]);
ClearFails();
SetAllocFail(ii);
Replace hosts parser, add caching capabilities (#591) HOSTS FILE PROCESSING OVERVIEW ============================== The hosts file on the system contains static entries to be processed locally rather than querying the nameserver. Each row is an IP address followed by a list of space delimited hostnames that match the ip address. This is used for both forward and reverse lookups. We are caching the entire parsed hosts file for performance reasons. Some files may be quite sizable and as per Issue #458 can approach 1/2MB in size, and the parse overhead on a rapid succession of queries can be quite large. The entries are stored in forwards and backwards hashtables so we can get O(1) performance on lookup. The file is cached until the file modification timestamp changes (or 60s if there is no implemented stat() capability). The hosts file processing is quite unique. It has to merge all related hosts and ips into a single entry due to file formatting requirements. For instance take the below: ``` 127.0.0.1 localhost.localdomain localhost ::1 localhost.localdomain localhost 192.168.1.1 host.example.com host 192.168.1.5 host.example.com host 2620:1234::1 host.example.com host6.example.com host6 host ``` This will yield 2 entries. 1) ips: `127.0.0.1,::1` hosts: `localhost.localdomain,localhost` 2) ips: `192.168.1.1,192.168.1.5,2620:1234::1` hosts: `host.example.com,host,host6.example.com,host6` It could be argued that if searching for `192.168.1.1` that the `host6` hostnames should not be returned, but this implementation will return them since they are related (both ips have the fqdn of host.example.com). It is unlikely this will matter in the real world. Fix By: Brad House (@bradh352)
1 year ago
ares_getaddrinfo(channel_, "example.com", NULL, &hints, AddrInfoCallback, result);
Process();
EXPECT_TRUE(result->done_);
if (result->status_ == ARES_SUCCESS) {
std::stringstream ss;
ss << result->ai_;
EXPECT_EQ("{alias1->example.com, alias2->example.com addr=[1.2.3.4]}", ss.str()) << " failed alloc #" << ii;
if (verbose) std::cerr << "Succeeded despite failure of alloc #" << ii << std::endl;
}
}
}
TEST(Misc, OnionDomain) {
EXPECT_EQ(0, ares__is_onion_domain("onion.no"));
EXPECT_EQ(0, ares__is_onion_domain(".onion.no"));
EXPECT_EQ(1, ares__is_onion_domain(".onion"));
EXPECT_EQ(1, ares__is_onion_domain(".onion."));
EXPECT_EQ(1, ares__is_onion_domain("yes.onion"));
EXPECT_EQ(1, ares__is_onion_domain("yes.onion."));
EXPECT_EQ(1, ares__is_onion_domain("YES.ONION"));
EXPECT_EQ(1, ares__is_onion_domain("YES.ONION."));
}
TEST_F(LibraryTest, DNSRecord) {
ares_dns_record_t *dnsrec = NULL;
ares_dns_rr_t *rr = NULL;
struct in_addr addr;
struct ares_in6_addr addr6;
unsigned char *msg = NULL;
size_t msglen = 0;
size_t qdcount = 0;
size_t ancount = 0;
size_t nscount = 0;
size_t arcount = 0;
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_create(&dnsrec, 0x1234,
ARES_FLAG_QR|ARES_FLAG_AA|ARES_FLAG_RD|ARES_FLAG_RA,
ARES_OPCODE_QUERY, ARES_RCODE_NOERROR));
/* == Question == */
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_query_add(dnsrec, "example.com",
ARES_REC_TYPE_ANY,
ARES_CLASS_IN));
/* == Answer == */
/* A */
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ANSWER, "example.com",
ARES_REC_TYPE_A, ARES_CLASS_IN, 300));
EXPECT_LT(0, ares_inet_net_pton(AF_INET, "1.1.1.1", &addr, sizeof(addr)));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_addr(rr, ARES_RR_A_ADDR, &addr));
/* AAAA */
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ANSWER, "example.com",
ARES_REC_TYPE_AAAA, ARES_CLASS_IN, 300));
EXPECT_LT(0, ares_inet_net_pton(AF_INET6, "2600::4", &addr6, sizeof(addr6)));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_addr6(rr, ARES_RR_AAAA_ADDR, &addr6));
/* MX */
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ANSWER, "example.com",
ARES_REC_TYPE_MX, ARES_CLASS_IN, 3600));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_u16(rr, ARES_RR_MX_PREFERENCE, 10));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_str(rr, ARES_RR_MX_EXCHANGE, "mail.example.com"));
/* CNAME */
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ANSWER, "example.com",
ARES_REC_TYPE_CNAME, ARES_CLASS_IN, 3600));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_str(rr, ARES_RR_CNAME_CNAME, "b.example.com"));
/* TXT */
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ANSWER, "example.com",
ARES_REC_TYPE_TXT, ARES_CLASS_IN, 3600));
const char txt[] = "blah=here blah=there anywhere";
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_bin(rr, ARES_RR_TXT_DATA, (unsigned char *)txt,
sizeof(txt)));
/* == Authority == */
/* NS */
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_AUTHORITY, "example.com",
ARES_REC_TYPE_NS, ARES_CLASS_IN, 38400));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_str(rr, ARES_RR_NS_NSDNAME, "ns1.example.com"));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_AUTHORITY, "example.com",
ARES_REC_TYPE_NS, ARES_CLASS_IN, 38400));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_str(rr, ARES_RR_NS_NSDNAME, "ns2.example.com"));
/* SOA */
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_AUTHORITY, "example.com",
ARES_REC_TYPE_SOA, ARES_CLASS_IN, 86400));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_str(rr, ARES_RR_SOA_MNAME, "ns1.example.com"));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_str(rr, ARES_RR_SOA_RNAME, "tech\\.support.example.com"));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_u32(rr, ARES_RR_SOA_SERIAL, 2023110701));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_u32(rr, ARES_RR_SOA_REFRESH, 28800));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_u32(rr, ARES_RR_SOA_RETRY, 7200));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_u32(rr, ARES_RR_SOA_EXPIRE, 604800));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_u32(rr, ARES_RR_SOA_MINIMUM, 86400));
/* == Additional */
/* OPT */
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ADDITIONAL, "",
ARES_REC_TYPE_OPT, ARES_CLASS_IN, 0));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_u16(rr, ARES_RR_OPT_UDP_SIZE, 1280));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_u8(rr, ARES_RR_OPT_EXT_RCODE, 0));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_u8(rr, ARES_RR_OPT_VERSION, 0));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_u16(rr, ARES_RR_OPT_FLAGS, 0));
/* PTR -- doesn't make sense, but ok */
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ADDITIONAL, "example.com",
ARES_REC_TYPE_PTR, ARES_CLASS_IN, 300));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_str(rr, ARES_RR_PTR_DNAME, "b.example.com"));
/* HINFO */
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ADDITIONAL, "example.com",
ARES_REC_TYPE_HINFO, ARES_CLASS_IN, 300));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_str(rr, ARES_RR_HINFO_CPU, "Virtual"));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_str(rr, ARES_RR_HINFO_OS, "Linux"));
/* SRV */
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ADDITIONAL,
"_ldap.example.com", ARES_REC_TYPE_SRV, ARES_CLASS_IN, 300));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_u16(rr, ARES_RR_SRV_PRIORITY, 100));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_u16(rr, ARES_RR_SRV_WEIGHT, 1));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_u16(rr, ARES_RR_SRV_PORT, 389));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_str(rr, ARES_RR_SRV_TARGET, "ldap.example.com"));
/* URI */
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ADDITIONAL,
"_ftp._tcp.example.com", ARES_REC_TYPE_URI, ARES_CLASS_IN, 3600));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_u16(rr, ARES_RR_URI_PRIORITY, 10));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_u16(rr, ARES_RR_URI_WEIGHT, 1));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_str(rr, ARES_RR_URI_TARGET, "ftp://ftp.example.com/public"));
/* CAA */
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ADDITIONAL,
"example.com", ARES_REC_TYPE_CAA, ARES_CLASS_IN, 86400));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_u8(rr, ARES_RR_CAA_CRITICAL, 0));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_str(rr, ARES_RR_CAA_TAG, "issue"));
unsigned char caa[] = "letsencrypt.org";
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_bin(rr, ARES_RR_CAA_VALUE, caa, sizeof(caa)));
/* NAPTR */
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ADDITIONAL,
"example.com", ARES_REC_TYPE_NAPTR, ARES_CLASS_IN, 86400));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_u16(rr, ARES_RR_NAPTR_ORDER, 100));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_u16(rr, ARES_RR_NAPTR_PREFERENCE, 10));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_str(rr, ARES_RR_NAPTR_FLAGS, "S"));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_str(rr, ARES_RR_NAPTR_SERVICES, "SIP+D2U"));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_str(rr, ARES_RR_NAPTR_REGEXP,
"!^.*$!sip:customer-service@example.com!"));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_str(rr, ARES_RR_NAPTR_REPLACEMENT,
"_sip._udp.example.com."));
/* RAW_RR */
EXPECT_EQ(ARES_SUCCESS,
ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ADDITIONAL, "",
ARES_REC_TYPE_RAW_RR, ARES_CLASS_IN, 0));
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_u16(rr, ARES_RR_RAW_RR_TYPE, 65432));
unsigned char data[] = { 0x00 };
EXPECT_EQ(ARES_SUCCESS,
ares_dns_rr_set_bin(rr, ARES_RR_RAW_RR_DATA, data, sizeof(data)));
qdcount = ares_dns_record_query_cnt(dnsrec);
ancount = ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER);
nscount = ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_AUTHORITY);
arcount = ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ADDITIONAL);
/* Write */
EXPECT_EQ(ARES_SUCCESS, ares_dns_write(dnsrec, &msg, &msglen));
ares__buf_t *hexdump = ares__buf_create();
EXPECT_EQ(ARES_SUCCESS, ares__buf_hexdump(hexdump, msg, msglen));
char *hexdata = ares__buf_finish_str(hexdump, NULL);
//printf("HEXDUMP\n%s", hexdata);
ares_free(hexdata);
ares_dns_record_destroy(dnsrec); dnsrec = NULL;
/* Parse */
EXPECT_EQ(ARES_SUCCESS, ares_dns_parse(msg, msglen, 0, &dnsrec));
ares_free(msg); msg = NULL;
/* Re-write */
EXPECT_EQ(ARES_SUCCESS, ares_dns_write(dnsrec, &msg, &msglen));
EXPECT_EQ(qdcount, ares_dns_record_query_cnt(dnsrec));
EXPECT_EQ(ancount, ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER));
EXPECT_EQ(nscount, ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_AUTHORITY));
EXPECT_EQ(arcount, ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ADDITIONAL));
ares_dns_record_destroy(dnsrec);
ares_free(msg);
}
TEST_F(LibraryTest, CatDomain) {
char *s;
ares__cat_domain("foo", "example.net", &s);
EXPECT_STREQ("foo.example.net", s);
ares_free(s);
ares__cat_domain("foo", ".", &s);
EXPECT_STREQ("foo.", s);
ares_free(s);
ares__cat_domain("foo", "example.net.", &s);
EXPECT_STREQ("foo.example.net.", s);
ares_free(s);
}
TEST_F(LibraryTest, BufMisuse) {
EXPECT_EQ(NULL, ares__buf_create_const(NULL, 0));
ares__buf_reclaim(NULL);
EXPECT_NE(ARES_SUCCESS, ares__buf_append(NULL, NULL, 0));
size_t len = 10;
EXPECT_EQ(NULL, ares__buf_append_start(NULL, &len));
EXPECT_EQ(NULL, ares__buf_append_start(NULL, NULL));
ares__buf_append_finish(NULL, 0);
EXPECT_EQ(NULL, ares__buf_finish_bin(NULL, NULL));
EXPECT_EQ(NULL, ares__buf_finish_str(NULL, NULL));
ares__buf_tag(NULL);
EXPECT_NE(ARES_SUCCESS, ares__buf_tag_rollback(NULL));
EXPECT_NE(ARES_SUCCESS, ares__buf_tag_clear(NULL));
EXPECT_EQ(NULL, ares__buf_tag_fetch(NULL, NULL));
EXPECT_EQ(0, ares__buf_tag_length(NULL));
EXPECT_NE(ARES_SUCCESS, ares__buf_tag_fetch_bytes(NULL, NULL, NULL));
EXPECT_NE(ARES_SUCCESS, ares__buf_tag_fetch_string(NULL, NULL, 0));
EXPECT_NE(ARES_SUCCESS, ares__buf_fetch_bytes_dup(NULL, 0, NULL));
EXPECT_NE(ARES_SUCCESS, ares__buf_fetch_str_dup(NULL, 0, NULL));
EXPECT_EQ(0, ares__buf_consume_whitespace(NULL, ARES_FALSE));
EXPECT_EQ(0, ares__buf_consume_nonwhitespace(NULL));
EXPECT_EQ(0, ares__buf_consume_line(NULL, ARES_FALSE));
EXPECT_NE(ARES_SUCCESS, ares__buf_begins_with(NULL, NULL, 0));
EXPECT_EQ(0, ares__buf_get_position(NULL));
EXPECT_NE(ARES_SUCCESS, ares__buf_set_position(NULL, 0));
EXPECT_NE(ARES_SUCCESS, ares__dns_name_parse(NULL, NULL, ARES_FALSE));
EXPECT_NE(ARES_SUCCESS, ares__buf_parse_dns_binstr(NULL, 0, NULL, NULL, ARES_FALSE));
}
TEST_F(LibraryTest, HtableMisuse) {
EXPECT_EQ(NULL, ares__htable_create(NULL, NULL, NULL, NULL));
EXPECT_EQ(ARES_FALSE, ares__htable_insert(NULL, NULL));
EXPECT_EQ(NULL, ares__htable_get(NULL, NULL));
EXPECT_EQ(ARES_FALSE, ares__htable_remove(NULL, NULL));
EXPECT_EQ(0, ares__htable_num_keys(NULL));
}
TEST_F(LibraryTest, HtableAsvpMisuse) {
EXPECT_EQ(ARES_FALSE, ares__htable_asvp_insert(NULL, ARES_SOCKET_BAD, NULL));
EXPECT_EQ(ARES_FALSE, ares__htable_asvp_get(NULL, ARES_SOCKET_BAD, NULL));
EXPECT_EQ(ARES_FALSE, ares__htable_asvp_remove(NULL, ARES_SOCKET_BAD));
EXPECT_EQ(0, ares__htable_asvp_num_keys(NULL));
}
TEST_F(LibraryTest, HtableStrvpMisuse) {
EXPECT_EQ(ARES_FALSE, ares__htable_strvp_insert(NULL, NULL, NULL));
EXPECT_EQ(ARES_FALSE, ares__htable_strvp_get(NULL, NULL, NULL));
EXPECT_EQ(ARES_FALSE, ares__htable_strvp_remove(NULL, NULL));
EXPECT_EQ(0, ares__htable_strvp_num_keys(NULL));
}
TEST_F(LibraryTest, HtableSzvpMisuse) {
EXPECT_EQ(ARES_FALSE, ares__htable_szvp_insert(NULL, 0, NULL));
EXPECT_EQ(ARES_FALSE, ares__htable_szvp_get(NULL, 0, NULL));
EXPECT_EQ(ARES_FALSE, ares__htable_szvp_remove(NULL, 0));
EXPECT_EQ(0, ares__htable_szvp_num_keys(NULL));
}
TEST_F(LibraryTest, LlistMisuse) {
ares__llist_replace_destructor(NULL, NULL);
EXPECT_EQ(NULL, ares__llist_insert_before(NULL, NULL));
EXPECT_EQ(NULL, ares__llist_insert_after(NULL, NULL));
EXPECT_EQ(NULL, ares__llist_node_last(NULL));
EXPECT_EQ(NULL, ares__llist_node_next(NULL));
EXPECT_EQ(NULL, ares__llist_node_prev(NULL));
EXPECT_EQ(0, ares__llist_len(NULL));
EXPECT_EQ(NULL, ares__llist_node_parent(NULL));
EXPECT_EQ(NULL, ares__llist_node_claim(NULL));
ares__llist_node_replace(NULL, NULL);
}
TEST_F(LibraryTest, SlistMisuse) {
EXPECT_EQ(NULL, ares__slist_create(NULL, NULL, NULL));
ares__slist_replace_destructor(NULL, NULL);
EXPECT_EQ(NULL, ares__slist_insert(NULL, NULL));
EXPECT_EQ(NULL, ares__slist_node_find(NULL, NULL));
EXPECT_EQ(NULL, ares__slist_node_first(NULL));
EXPECT_EQ(NULL, ares__slist_node_last(NULL));
EXPECT_EQ(NULL, ares__slist_node_next(NULL));
EXPECT_EQ(NULL, ares__slist_node_prev(NULL));
EXPECT_EQ(NULL, ares__slist_node_val(NULL));
EXPECT_EQ(0, ares__slist_len(NULL));
EXPECT_EQ(NULL, ares__slist_node_parent(NULL));
EXPECT_EQ(NULL, ares__slist_first_val(NULL));
EXPECT_EQ(NULL, ares__slist_last_val(NULL));
EXPECT_EQ(NULL, ares__slist_node_claim(NULL));
}
#endif
#ifdef CARES_EXPOSE_STATICS
// These tests access internal static functions from the library, which
// are only exposed when CARES_EXPOSE_STATICS has been configured. As such
// they are tightly couple to the internal library implementation details.
extern "C" char *ares_striendstr(const char*, const char*);
TEST_F(LibraryTest, Striendstr) {
EXPECT_EQ(nullptr, ares_striendstr("abc", "12345"));
EXPECT_NE(nullptr, ares_striendstr("abc12345", "12345"));
EXPECT_NE(nullptr, ares_striendstr("abcxyzzy", "XYZZY"));
EXPECT_NE(nullptr, ares_striendstr("xyzzy", "XYZZY"));
EXPECT_EQ(nullptr, ares_striendstr("xyxzy", "XYZZY"));
EXPECT_NE(nullptr, ares_striendstr("", ""));
const char *str = "plugh";
EXPECT_NE(nullptr, ares_striendstr(str, str));
}
1 year ago
TEST_F(DefaultChannelTest, SingleDomain) {
TempFile aliases("www www.google.com\n");
EnvValue with_env("HOSTALIASES", aliases.filename());
SetAllocSizeFail(128);
char *ptr = nullptr;
EXPECT_EQ(ARES_ENOMEM, ares__single_domain(channel_, "www", &ptr));
channel_->flags |= ARES_FLAG_NOSEARCH|ARES_FLAG_NOALIASES;
EXPECT_EQ(ARES_SUCCESS, ares__single_domain(channel_, "www", &ptr));
EXPECT_EQ("www", std::string(ptr));
ares_free(ptr);
ptr = nullptr;
SetAllocFail(1);
EXPECT_EQ(ARES_ENOMEM, ares__single_domain(channel_, "www", &ptr));
EXPECT_EQ(nullptr, ptr);
}
#endif
TEST_F(DefaultChannelTest, SaveInvalidChannel) {
Dynamic Server List (#594) This PR makes the server list a dynamic sorted list of servers. The sort order is [ consecutive failures, system config index ]. The server list can be updated via ares_set_servers_*(). Any queries currently directed to servers that are no longer in the list will be automatically re-queued to a different server. Also, any time a failure occurs on the server, the sort order of the servers will be updated so that the one with the fewest consecutive failures is chosen for the next query that goes on the wire, this way bad or non-responsive servers are automatically isolated. Since the server list is now dynamic, the tracking of query failures per server has been removed and instead is relying on the server sort order as previously described. This simplifies the logic while also reducing the amount of memory required per query. However, because of this dynamic nature, it may not be easy to determine the server attempt order for enqueued queries if there have been any failures. If using the ARES_OPT_ROTATE, this is now implemented to be a random selection of the configured servers. Since the server list is dynamic, its not possible to go to the next server as configuration could have changed between queries or attempts for the same query. Finally, this PR moved some existing functions into new files to logically separate them. This should address issues #550 and #440, while also setting the framework to implement #301. #301 needs a little more effort since it configures things other than the servers themselves (domains, search, sortlist, lookups), which need to make sure they can be safely updated. Fix By: Brad House (@bradh352)
1 year ago
ares__slist_t *saved = channel_->servers;
channel_->servers = NULL;
struct ares_options opts;
int optmask = 0;
EXPECT_EQ(ARES_ENODATA, ares_save_options(channel_, &opts, &optmask));
Dynamic Server List (#594) This PR makes the server list a dynamic sorted list of servers. The sort order is [ consecutive failures, system config index ]. The server list can be updated via ares_set_servers_*(). Any queries currently directed to servers that are no longer in the list will be automatically re-queued to a different server. Also, any time a failure occurs on the server, the sort order of the servers will be updated so that the one with the fewest consecutive failures is chosen for the next query that goes on the wire, this way bad or non-responsive servers are automatically isolated. Since the server list is now dynamic, the tracking of query failures per server has been removed and instead is relying on the server sort order as previously described. This simplifies the logic while also reducing the amount of memory required per query. However, because of this dynamic nature, it may not be easy to determine the server attempt order for enqueued queries if there have been any failures. If using the ARES_OPT_ROTATE, this is now implemented to be a random selection of the configured servers. Since the server list is dynamic, its not possible to go to the next server as configuration could have changed between queries or attempts for the same query. Finally, this PR moved some existing functions into new files to logically separate them. This should address issues #550 and #440, while also setting the framework to implement #301. #301 needs a little more effort since it configures things other than the servers themselves (domains, search, sortlist, lookups), which need to make sure they can be safely updated. Fix By: Brad House (@bradh352)
1 year ago
channel_->servers = saved;
}
// Need to put this in own function due to nested lambda bug
// in VS2013. (C2888)
static int configure_socket(ares_socket_t s) {
// transposed from ares-process, simplified non-block setter.
#if defined(USE_BLOCKING_SOCKETS)
return 0; /* returns success */
#elif defined(HAVE_FCNTL_O_NONBLOCK)
/* most recent unix versions */
int flags;
flags = fcntl(s, F_GETFL, 0);
return fcntl(s, F_SETFL, flags | O_NONBLOCK);
#elif defined(HAVE_IOCTL_FIONBIO)
/* older unix versions */
int flags = 1;
return ioctl(s, FIONBIO, &flags);
#elif defined(HAVE_IOCTLSOCKET_FIONBIO)
#ifdef WATT32
char flags = 1;
#else
/* Windows */
unsigned long flags = 1UL;
#endif
return ioctlsocket(s, FIONBIO, &flags);
#elif defined(HAVE_IOCTLSOCKET_CAMEL_FIONBIO)
/* Amiga */
long flags = 1L;
return IoctlSocket(s, FIONBIO, flags);
#elif defined(HAVE_SETSOCKOPT_SO_NONBLOCK)
/* BeOS */
long b = 1L;
return setsockopt(s, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b));
#else
# error "no non-blocking method was found/used/set"
#endif
}
// TODO: This should not really be in this file, but we need ares config
// flags, and here they are available.
const struct ares_socket_functions VirtualizeIO::default_functions = {
[](int af, int type, int protocol, void *) -> ares_socket_t {
auto s = ::socket(af, type, protocol);
if (s == ARES_SOCKET_BAD) {
return s;
}
if (configure_socket(s) != 0) {
sclose(s);
return ares_socket_t(-1);
}
return s;
},
NULL,
NULL,
NULL,
NULL
};
} // namespace test
} // namespace ares