evdns: Implement dns requests via tcp

This commit is contained in:
ayuseleznev 2020-05-21 12:46:20 +03:00
parent 083c6d54d5
commit 0f6ee89a39
6 changed files with 1290 additions and 121 deletions

825
evdns.c

File diff suppressed because it is too large Load Diff

View File

@ -183,7 +183,12 @@ extern "C" {
#define DNS_PTR 2
#define DNS_IPv6_AAAA 3
#define DNS_QUERY_NO_SEARCH 1
/** Disable searching for the query. */
#define DNS_QUERY_NO_SEARCH 0x01
/** Use TCP connections ("virtual circuits") for queries rather than UDP datagrams. */
#define DNS_QUERY_USEVC 0x02
/** Ignore trancation flag in responses (don't fallback to TCP connections). */
#define DNS_QUERY_IGNTC 0x04
/* Allow searching */
#define DNS_OPTION_SEARCH 1
@ -197,6 +202,9 @@ extern "C" {
* - attempts:
* - randomize-case:
* - initial-probe-timeout:
* - tcp-idle-timeout:
* - use-vc
* - ignore-tc
*/
#define DNS_OPTION_MISC 4
/* Load hosts file (i.e. "/etc/hosts") */
@ -390,7 +398,7 @@ struct evdns_request;
@param base the evdns_base to which to apply this operation
@param name a DNS hostname
@param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query.
@param flags either 0, or combination of DNS_QUERY_* flags.
@param callback a callback function to invoke when the request is completed
@param ptr an argument to pass to the callback function
@return an evdns_request object if successful, or NULL if an error occurred.
@ -404,7 +412,7 @@ struct evdns_request *evdns_base_resolve_ipv4(struct evdns_base *base, const cha
@param base the evdns_base to which to apply this operation
@param name a DNS hostname
@param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query.
@param flags either 0, or combination of DNS_QUERY_* flags.
@param callback a callback function to invoke when the request is completed
@param ptr an argument to pass to the callback function
@return an evdns_request object if successful, or NULL if an error occurred.
@ -421,7 +429,7 @@ struct in6_addr;
@param base the evdns_base to which to apply this operation
@param in an IPv4 address
@param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query.
@param flags either 0, or combination of DNS_QUERY_* flags.
@param callback a callback function to invoke when the request is completed
@param ptr an argument to pass to the callback function
@return an evdns_request object if successful, or NULL if an error occurred.
@ -436,7 +444,7 @@ struct evdns_request *evdns_base_resolve_reverse(struct evdns_base *base, const
@param base the evdns_base to which to apply this operation
@param in an IPv6 address
@param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query.
@param flags either 0, or combination of DNS_QUERY_* flags.
@param callback a callback function to invoke when the request is completed
@param ptr an argument to pass to the callback function
@return an evdns_request object if successful, or NULL if an error occurred.
@ -462,11 +470,14 @@ void evdns_cancel_request(struct evdns_base *base, struct evdns_request *req);
ndots, timeout, max-timeouts, max-inflight, attempts, randomize-case,
bind-to, initial-probe-timeout, getaddrinfo-allow-skew,
so-rcvbuf, so-sndbuf.
so-rcvbuf, so-sndbuf, tcp-idle-timeout, use-vc, ignore-tc.
In versions before Libevent 2.0.3-alpha, the option name needed to end with
a colon.
In case of options without values (use-vc, ingore-tc) val should be an empty
string or NULL.
@param base the evdns_base to which to apply this operation
@param option the name of the configuration option to be modified
@param val the value to be set
@ -646,7 +657,7 @@ typedef void (*evdns_request_callback_fn_type)(struct evdns_server_request *, vo
#define EVDNS_FLAGS_AA 0x400
#define EVDNS_FLAGS_RD 0x080
/** Create a new DNS server port.
/** Create a new UDP DNS server port.
@param base The event base to handle events for the server port.
@param socket A UDP socket to accept DNS requests.
@ -659,10 +670,60 @@ typedef void (*evdns_request_callback_fn_type)(struct evdns_server_request *, vo
*/
EVENT2_EXPORT_SYMBOL
struct evdns_server_port *evdns_add_server_port_with_base(struct event_base *base, evutil_socket_t socket, int flags, evdns_request_callback_fn_type callback, void *user_data);
struct evconnlistener;
/** Create a new TCP DNS server port.
@param base The event base to handle events for the server port.
@param listener A TCP listener to accept DNS requests.
@param flags Always 0 for now.
@param callback A function to invoke whenever we get a DNS request
on the socket.
@param user_data Data to pass to the callback.
@return an evdns_server_port structure for this server port or NULL if
an error occurred.
*/
EVENT2_EXPORT_SYMBOL
struct evdns_server_port *evdns_add_server_port_with_listener(
struct event_base *base, struct evconnlistener *listener, int flags,
evdns_request_callback_fn_type callback, void *user_data);
/** Close down a DNS server port, and free associated structures. */
EVENT2_EXPORT_SYMBOL
void evdns_close_server_port(struct evdns_server_port *port);
/**
* List of configurable evdns_server_port options.
*
* @see evdns_server_port_set_option()
*/
enum evdns_server_option {
/**
* Maximum number of simultaneous tcp connections (clients)
* that server can hold. Can be set only for TCP DNS servers.
*/
EVDNS_SOPT_TCP_MAX_CLIENTS,
/**
* Idle timeout (in seconds) of incoming TCP connections.
* If client doesn't send any requests via the connection
* during this period connection is closed by the server.
* Can be set only for TCP DNS servers.
*/
EVDNS_SOPT_TCP_IDLE_TIMEOUT,
};
/**
Configure DNS server.
@param port the evdns_server_port to which to apply this operation
@param option @see evdns_server_option for the list of possible options
@param val value of the option
@return 0 if successful, or -1 if an error occurred
*/
EVENT2_EXPORT_SYMBOL
int evdns_server_port_set_option(struct evdns_server_port *port, enum evdns_server_option option, size_t value);
/** Sets some flags in a reply we're building.
Allows setting of the AA or RD flags
*/

View File

@ -81,6 +81,23 @@
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define REPEAT_2(address) \
address "," address
#define REPEAT_4(address) \
REPEAT_2(address) "," REPEAT_2(address)
#define REPEAT_8(address) \
REPEAT_4(address) "," REPEAT_4(address)
#define REPEAT_16(address) \
REPEAT_8(address) "," REPEAT_8(address)
#define REPEAT_32(address) \
REPEAT_16(address) "," REPEAT_16(address)
#define REPEAT_64(address) \
REPEAT_32(address) "," REPEAT_32(address)
#define REPEAT_128(address) \
REPEAT_64(address) "," REPEAT_64(address)
#define REPEAT_256(address) \
REPEAT_128(address) "," REPEAT_128(address)
static int dns_ok = 0;
static int dns_got_cancel = 0;
static int dns_err = 0;
@ -477,7 +494,7 @@ struct generic_dns_callback_result {
int ttl;
size_t addrs_len;
void *addrs;
char addrs_buf[256];
char addrs_buf[4096];
};
static void
@ -503,8 +520,8 @@ generic_dns_callback(int result, char type, int count, int ttl, void *addresses,
}
if (len) {
res->addrs_len = len;
if (len > 256)
len = 256;
if (len > ARRAY_SIZE(res->addrs_buf))
len = ARRAY_SIZE(res->addrs_buf);
memcpy(res->addrs_buf, addresses, len);
res->addrs = res->addrs_buf;
}
@ -520,6 +537,11 @@ generic_dns_callback(int result, char type, int count, int ttl, void *addresses,
}
static struct regress_dns_server_table search_table[] = {
{ "small.a.example.com", "A", REPEAT_64("11.22.33.45"), 0, 0},
{ "medium.b.example.com", "A", REPEAT_64("11.22.33.45") "," REPEAT_64("12.22.33.45"), 0, 0},
{ "large.c.example.com", "A",
REPEAT_256("11.22.33.45") "," REPEAT_256("12.22.33.45") "," REPEAT_256("13.22.33.45") "," REPEAT_256("14.22.33.45"), 0, 0},
{ "lost.request.com", "err", "67", 0, 0},
{ "host.a.example.com", "err", "3", 0, 0 },
{ "host.b.example.com", "err", "3", 0, 0 },
{ "host.c.example.com", "A", "11.22.33.44", 0, 0 },
@ -529,12 +551,31 @@ static struct regress_dns_server_table search_table[] = {
{ "hostn.a.example.com", "errsoa", "0", 0, 0 },
{ "hostn.b.example.com", "errsoa", "3", 0, 0 },
{ "hostn.c.example.com", "err", "0", 0, 0 },
{ "host", "err", "3", 0, 0 },
{ "host2", "err", "3", 0, 0 },
{ "*", "err", "3", 0, 0 },
{ NULL, NULL, NULL, 0, 0 }
};
static struct regress_dns_server_table tcp_search_table[] = {
{ "small.a.example.com", "A", REPEAT_64("11.22.33.45"), 0, 0},
{ "medium.b.example.com", "A", REPEAT_64("11.22.33.45") "," REPEAT_64("12.22.33.45"), 0, 0},
{ "large.c.example.com", "A",
REPEAT_256("11.22.33.45") "," REPEAT_256("12.22.33.45") "," REPEAT_256("13.22.33.45") "," REPEAT_256("14.22.33.45"), 0, 0},
{ "lost.request.com", "err", "67", 0, 0},
{ NULL, NULL, NULL, 0, 0 }
};
#define assert_request_results(r, exp_result, exp_addresses) \
do { \
k_ = parse_csv_address_list(exp_addresses, AF_INET, addrs, ARRAY_SIZE(addrs)); \
tt_assert(r.result == exp_result); \
tt_assert(r.type == DNS_IPv4_A); \
tt_assert(r.count == k_); \
for (k_ = 0; k_ < r.count; ++k_) \
tt_int_op(((ev_uint32_t *)r.addrs)[k_], ==, addrs[k_].s_addr); \
} while (0)
static void
dns_search_test_impl(void *arg, int lower)
{
@ -553,7 +594,7 @@ dns_search_test_impl(void *arg, int lower)
table[i].lower = lower;
}
tt_assert(regress_dnsserver(base, &portnum, table));
tt_assert(regress_dnsserver(base, &portnum, table, NULL));
evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)portnum);
dns = evdns_base_new(base, 0);
@ -659,7 +700,7 @@ dns_search_cancel_test(void *arg)
struct generic_dns_callback_result r1;
char buf[64];
port = regress_get_dnsserver(base, &portnum, NULL,
port = regress_get_udp_dnsserver(base, &portnum, NULL,
search_cancel_server_cb, NULL);
tt_assert(port);
evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)portnum);
@ -736,7 +777,7 @@ dns_retry_test_impl(void *arg, int flags)
struct generic_dns_callback_result r1;
port = regress_get_dnsserver(base, &portnum, NULL,
port = regress_get_udp_dnsserver(base, &portnum, NULL,
fail_server_cb, &drop_count);
tt_assert(port);
evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)portnum);
@ -833,10 +874,10 @@ dns_reissue_test_impl(void *arg, int flags)
ev_uint16_t portnum1 = 0, portnum2=0;
char buf1[64], buf2[64];
port1 = regress_get_dnsserver(base, &portnum1, NULL,
port1 = regress_get_udp_dnsserver(base, &portnum1, NULL,
regress_dns_server_cb, internal_error_table);
tt_assert(port1);
port2 = regress_get_dnsserver(base, &portnum2, NULL,
port2 = regress_get_udp_dnsserver(base, &portnum2, NULL,
regress_dns_server_cb, reissue_table);
tt_assert(port2);
evutil_snprintf(buf1, sizeof(buf1), "127.0.0.1:%d", (int)portnum1);
@ -912,7 +953,7 @@ dns_inflight_test_impl(void *arg, int flags)
struct generic_dns_callback_result r[20];
int i;
dns_port = regress_get_dnsserver(base, &portnum, NULL,
dns_port = regress_get_udp_dnsserver(base, &portnum, NULL,
regress_dns_server_cb, reissue_table);
tt_assert(dns_port);
if (disable_when_inactive) {
@ -977,7 +1018,7 @@ dns_disable_when_inactive_no_ns_test(void *arg)
tt_assert(inactive_base);
/** Create dns server with inactive base, to avoid replying to clients */
tt_assert(regress_dnsserver(inactive_base, &portnum, search_table));
tt_assert(regress_dnsserver(inactive_base, &portnum, search_table, NULL));
evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)portnum);
dns = evdns_base_new(base, EVDNS_BASE_DISABLE_WHEN_INACTIVE);
@ -1301,7 +1342,7 @@ test_bufferevent_connect_hostname(void *arg)
listener_port = regress_get_socket_port(
evconnlistener_get_fd(listener));
port = regress_get_dnsserver(data->base, &dns_port, NULL,
port = regress_get_udp_dnsserver(data->base, &dns_port, NULL,
be_getaddrinfo_server_cb, &n_dns);
tt_assert(port);
tt_int_op(dns_port, >=, 0);
@ -1612,7 +1653,7 @@ test_getaddrinfo_async(void *arg)
/* 2. Okay, now we can actually test the asynchronous resolver. */
/* Start a dummy local dns server... */
port = regress_get_dnsserver(data->base, &dns_port, NULL,
port = regress_get_udp_dnsserver(data->base, &dns_port, NULL,
be_getaddrinfo_server_cb, &n_dns_questions);
tt_assert(port);
tt_int_op(dns_port, >=, 0);
@ -2136,7 +2177,7 @@ dns_client_fail_requests_test(void *arg)
struct generic_dns_callback_result r[20];
unsigned i;
dns_port = regress_get_dnsserver(base, &portnum, NULL,
dns_port = regress_get_udp_dnsserver(base, &portnum, NULL,
regress_dns_server_cb, reissue_table);
tt_assert(dns_port);
@ -2184,7 +2225,7 @@ dns_client_fail_requests_getaddrinfo_test(void *arg)
struct generic_dns_callback_result r[20];
int i;
dns_port = regress_get_dnsserver(base, &portnum, NULL,
dns_port = regress_get_udp_dnsserver(base, &portnum, NULL,
regress_dns_server_cb, reissue_table);
tt_assert(dns_port);
@ -2286,7 +2327,7 @@ getaddrinfo_race_gotresolve_test(void *arg)
if (evthread_make_base_notifiable(rp.base) < 0)
tt_abort_msg("Couldn't make base notifiable!");
dns_port = regress_get_dnsserver(rp.base, &portnum, NULL,
dns_port = regress_get_udp_dnsserver(rp.base, &portnum, NULL,
regress_dns_server_cb, reissue_table);
tt_assert(dns_port);
@ -2359,6 +2400,213 @@ end:
}
#endif
static void
test_tcp_resolve(void *arg)
{
struct basic_test_data *data = arg;
struct event_base *base = data->base;
struct evdns_base *dns = evdns_base_new(base, 0);
ev_uint16_t portnum = 0;
struct evdns_request *req = NULL;
struct generic_dns_callback_result r;
struct in_addr addrs[2048];
char buf[64];
int k_;
exit_base = base;
tt_assert(base);
tt_assert(regress_dnsserver(base, &portnum, search_table, tcp_search_table));
evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)portnum);
tt_assert(!evdns_base_nameserver_ip_add(dns, buf));
// small table
req = evdns_base_resolve_ipv4(
dns, "small.a.example.com", 0, generic_dns_callback, &r);
tt_assert(req);
n_replies_left = 1;
event_base_dispatch(base);
assert_request_results(r, DNS_ERR_NONE, REPEAT_64("11.22.33.45"));
tt_assert(search_table[0].seen == 1);
tt_assert(tcp_search_table[0].seen == 0);
// medium table
req = evdns_base_resolve_ipv4(
dns, "medium.b.example.com", DNS_QUERY_IGNTC, generic_dns_callback, &r);
tt_assert(req);
n_replies_left = 1;
event_base_dispatch(base);
tt_assert(r.type != DNS_IPv4_A);
tt_assert(r.result == DNS_ERR_TRUNCATED);
tt_assert(search_table[1].seen == 1);
tt_assert(tcp_search_table[1].seen == 0);
req = evdns_base_resolve_ipv4(
dns, "medium.b.example.com", DNS_QUERY_USEVC, generic_dns_callback, &r);
tt_assert(req);
n_replies_left = 1;
event_base_dispatch(base);
assert_request_results(r, DNS_ERR_NONE, REPEAT_64("11.22.33.45") "," REPEAT_64("12.22.33.45"));
tt_assert(search_table[1].seen == 1);
tt_assert(tcp_search_table[1].seen == 1);
// big table
req = evdns_base_resolve_ipv4(
dns, "large.c.example.com", DNS_QUERY_IGNTC, generic_dns_callback, &r);
tt_assert(req);
n_replies_left = 1;
event_base_dispatch(base);
tt_assert(r.type != DNS_IPv4_A);
tt_assert(r.result == DNS_ERR_TRUNCATED);
tt_assert(search_table[2].seen == 1);
tt_assert(tcp_search_table[2].seen == 0);
req = evdns_base_resolve_ipv4(
dns, "large.c.example.com", 0, generic_dns_callback, &r);
tt_assert(req);
n_replies_left = 1;
event_base_dispatch(base);
assert_request_results(r, DNS_ERR_NONE,
REPEAT_256("11.22.33.45") "," REPEAT_256("12.22.33.45") "," REPEAT_256("13.22.33.45") "," REPEAT_256("14.22.33.45"));
tt_assert(search_table[2].seen == 2);
tt_assert(tcp_search_table[2].seen == 1);
req = evdns_base_resolve_ipv4(
dns, "large.c.example.com", DNS_QUERY_USEVC, generic_dns_callback, &r);
tt_assert(req);
n_replies_left = 1;
event_base_dispatch(base);
assert_request_results(r, DNS_ERR_NONE,
REPEAT_256("11.22.33.45") "," REPEAT_256("12.22.33.45") "," REPEAT_256("13.22.33.45") "," REPEAT_256("14.22.33.45"));
tt_assert(search_table[2].seen == 2);
tt_assert(tcp_search_table[2].seen == 2);
end:
if (dns)
evdns_base_free(dns, 0);
regress_clean_dnsserver();
}
static void
test_tcp_resolve_pipeline(void *arg)
{
struct basic_test_data *data = arg;
struct event_base *base = data->base;
struct evdns_base *dns = evdns_base_new(base, 0);
ev_uint16_t portnum = 0;
struct evdns_request *reqs[3] = {NULL, NULL, NULL};
struct generic_dns_callback_result results[3];
char buf[64];
struct in_addr addrs[2048];
int i, k_;
exit_base = base;
tt_assert(base);
tt_assert(regress_dnsserver(base, &portnum, search_table, tcp_search_table));
evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)portnum);
tt_assert(!evdns_base_nameserver_ip_add(dns, buf));
tt_assert(!evdns_base_set_option(dns, "use-vc", NULL));
for (i = 0; i < 3; ++i) {
reqs[i] = evdns_base_resolve_ipv4(
dns, "large.c.example.com", 0, generic_dns_callback, &results[i]);
tt_assert(reqs[i]);
}
n_replies_left = 3;
event_base_dispatch(base);
for (i = 0; i < 3; ++i) {
assert_request_results(results[i], DNS_ERR_NONE,
REPEAT_256("11.22.33.45") "," REPEAT_256("12.22.33.45") "," REPEAT_256("13.22.33.45") "," REPEAT_256("14.22.33.45"));
}
tt_assert(search_table[2].seen == 0);
tt_assert(tcp_search_table[2].seen == 3);
end:
if (dns)
evdns_base_free(dns, 0);
regress_clean_dnsserver();
}
static void
test_tcp_resolve_many_clients(void *arg)
{
struct basic_test_data *data = arg;
struct event_base *base = data->base;
struct evdns_base *dns[3] = {evdns_base_new(base, 0), evdns_base_new(base, 0), evdns_base_new(base, 0)};
struct evdns_request *req[3] = {NULL, NULL, NULL};
struct generic_dns_callback_result r[3];
int k_, i;
ev_uint16_t portnum = 0;
char buf[64];
struct in_addr addrs[2048];
exit_base = base;
tt_assert(base);
tt_assert(regress_dnsserver(base, &portnum, search_table, tcp_search_table));
evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)portnum);
for (i = 0; i < 3; ++i) {
tt_assert(!evdns_base_nameserver_ip_add(dns[i], buf));
req[i] = evdns_base_resolve_ipv4(
dns[i], "small.a.example.com", DNS_QUERY_USEVC, generic_dns_callback, &r[i]);
tt_assert(req[i]);
}
n_replies_left = 3;
event_base_dispatch(base);
for (i = 0; i < 3; ++i) {
assert_request_results(r[i], DNS_ERR_NONE, REPEAT_64("11.22.33.45"));
}
tt_assert(search_table[0].seen == 0);
tt_assert(tcp_search_table[0].seen == 3);
end:
for (i = 0; i < 3; ++i) {
if (dns[i])
evdns_base_free(dns[i], 0);
}
regress_clean_dnsserver();
}
static void
test_tcp_timeout(void *arg)
{
struct generic_dns_callback_result r;
struct basic_test_data *data = arg;
struct event_base *base = data->base;
struct evdns_base *dns = evdns_base_new(base, 0);
ev_uint16_t portnum = 0;
struct evdns_request *req = NULL;
char buf[64];
exit_base = base;
tt_assert(base);
tt_assert(!evdns_base_set_option(dns, "timeout:", "1"));
tt_assert(regress_dnsserver(base, &portnum, search_table, tcp_search_table));
evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)portnum);
tt_assert(!evdns_base_nameserver_ip_add(dns, buf));
req = evdns_base_resolve_ipv4(
dns, "lost.request.com", DNS_QUERY_USEVC, generic_dns_callback, &r);
tt_assert(req);
n_replies_left = 1;
event_base_dispatch(base);
tt_assert(DNS_ERR_TIMEOUT == r.result);
end:
if (dns)
evdns_base_free(dns, 0);
regress_clean_dnsserver();
}
static void
test_set_so_rcvbuf_so_sndbuf(void *arg)
{
@ -2407,6 +2655,10 @@ test_set_option(void *arg)
const char *addr_port_options[] = {
"bind-to", "bind-to:",
};
const char *options_without_values[] = {
"use-vc", "use-vc:",
"ignore-tc", "ignore-tc:",
};
dns_base = evdns_base_new(data->base, 0);
tt_assert(dns_base);
@ -2437,6 +2689,13 @@ test_set_option(void *arg)
tt_assert(FAIL == evdns_base_set_option(dns_base, addr_port_options[i], "foo"));
}
for (i = 0; i < ARRAY_SIZE(options_without_values); ++i) {
tt_assert(SUCCESS == evdns_base_set_option(dns_base, options_without_values[i], NULL));
tt_assert(SUCCESS == evdns_base_set_option(dns_base, options_without_values[i], ""));
tt_assert(FAIL == evdns_base_set_option(dns_base, options_without_values[i], "1"));
tt_assert(FAIL == evdns_base_set_option(dns_base, options_without_values[i], "foo"));
}
#undef SUCCESS
#undef FAIL
end:
@ -2444,6 +2703,48 @@ end:
evdns_base_free(dns_base, 0);
}
static void
test_set_server_option(void *arg)
{
#define SUCCESS 0
#define FAIL -1
struct basic_test_data *data = arg;
struct evdns_server_port *tcp_port = NULL;
struct evdns_server_port *udp_port = NULL;
evutil_socket_t udp_sock = -1;
evutil_socket_t tcp_sock = -1;
ev_uint16_t portnum;
size_t i;
enum evdns_server_option tcp_options[] = {EVDNS_SOPT_TCP_MAX_CLIENTS, EVDNS_SOPT_TCP_IDLE_TIMEOUT};
portnum = 0;
tcp_port = regress_get_tcp_dnsserver(data->base, &portnum, &tcp_sock, NULL, NULL);
tt_assert(tcp_port);
portnum = 0;
udp_port = regress_get_udp_dnsserver(data->base, &portnum, &udp_sock, NULL, NULL);
tt_assert(udp_port);
for (i = 0; i < ARRAY_SIZE(tcp_options); ++i) {
tt_assert(SUCCESS == evdns_server_port_set_option(tcp_port, tcp_options[i], 0));
tt_assert(SUCCESS == evdns_server_port_set_option(tcp_port, tcp_options[i], 1));
tt_assert(SUCCESS == evdns_server_port_set_option(tcp_port, tcp_options[i], 100));
tt_assert(FAIL == evdns_server_port_set_option(udp_port, tcp_options[i], 0));
tt_assert(FAIL == evdns_server_port_set_option(udp_port, tcp_options[i], 100));
}
#undef SUCCESS
#undef FAIL
end:
if (tcp_port)
evdns_close_server_port(tcp_port);
if (tcp_sock >= 0)
evutil_closesocket(tcp_sock);
if (udp_port)
evdns_close_server_port(udp_port);
if (udp_sock >= 0)
evutil_closesocket(udp_sock);
}
#define DNS_LEGACY(name, flags) \
{ #name, run_legacy_test_fn, flags|TT_LEGACY, &legacy_setup, \
dns_##name }
@ -2516,11 +2817,21 @@ struct testcase_t dns_testcases[] = {
getaddrinfo_race_gotresolve_test,
TT_FORK|TT_OFF_BY_DEFAULT, NULL, NULL },
#endif
{ "tcp_resolve", test_tcp_resolve,
TT_FORK | TT_NEED_BASE, &basic_setup, NULL },
{ "tcp_resolve_pipeline", test_tcp_resolve_pipeline,
TT_FORK | TT_NEED_BASE, &basic_setup, NULL },
{ "tcp_resolve_many_clients", test_tcp_resolve_many_clients,
TT_FORK | TT_NEED_BASE, &basic_setup, NULL },
{ "tcp_timeout", test_tcp_timeout,
TT_FORK | TT_NEED_BASE, &basic_setup, NULL },
{ "set_SO_RCVBUF_SO_SNDBUF", test_set_so_rcvbuf_so_sndbuf,
TT_FORK|TT_NEED_BASE, &basic_setup, NULL },
{ "set_options", test_set_option,
TT_FORK|TT_NEED_BASE, &basic_setup, NULL },
{ "set_server_options", test_set_server_option,
TT_FORK|TT_NEED_BASE, &basic_setup, NULL },
END_OF_TESTCASES
};

View File

@ -1418,7 +1418,7 @@ http_connection_async_test(void *arg)
struct evhttp *http = http_setup(&port, data->base, 0);
exit_base = data->base;
tt_assert(regress_dnsserver(data->base, &portnum, search_table));
tt_assert(regress_dnsserver(data->base, &portnum, search_table, NULL));
dns_base = evdns_base_new(data->base, 0/* init name servers */);
tt_assert(dns_base);
@ -1699,7 +1699,7 @@ http_cancel_test(void *arg)
if (type & BY_HOST) {
const char *timeout = (type & NS_TIMEOUT) ? "6" : "3";
tt_assert(regress_dnsserver(data->base, &portnum, search_table));
tt_assert(regress_dnsserver(data->base, &portnum, search_table, NULL));
dns_base = evdns_base_new(data->base, 0/* init name servers */);
tt_assert(dns_base);
@ -4132,7 +4132,7 @@ http_connection_retry_conn_address_test_impl(void *arg, int ssl)
struct evdns_base *dns_base = NULL;
char address[64];
tt_assert(regress_dnsserver(data->base, &portnum, search_table));
tt_assert(regress_dnsserver(data->base, &portnum, search_table, NULL));
dns_base = evdns_base_new(data->base, 0/* init name servers */);
tt_assert(dns_base);
@ -4669,7 +4669,7 @@ http_ipv6_for_domain_test_impl(void *arg, int family)
ev_uint16_t portnum = 0;
char address[64];
tt_assert(regress_dnsserver(data->base, &portnum, ipv6_search_table));
tt_assert(regress_dnsserver(data->base, &portnum, ipv6_search_table, NULL));
dns_base = evdns_base_new(data->base, 0/* init name servers */);
tt_assert(dns_base);

View File

@ -69,9 +69,13 @@
#include "regress.h"
#include "regress_testutils.h"
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
/* globals */
static struct evdns_server_port *dns_port;
evutil_socket_t dns_sock = -1;
static struct evdns_server_port *udp_dns_port;
evutil_socket_t udp_dns_sock = -1;
static struct evdns_server_port *tcp_dns_port;
evutil_socket_t tcp_dns_sock = -1;
/* Helper: return the port that a socket is bound on, in host order. */
int
@ -90,7 +94,7 @@ regress_get_socket_port(evutil_socket_t fd)
}
struct evdns_server_port *
regress_get_dnsserver(struct event_base *base,
regress_get_udp_dnsserver(struct event_base *base,
ev_uint16_t *portnum,
evutil_socket_t *psock,
evdns_request_callback_fn_type cb,
@ -126,16 +130,64 @@ end:
return NULL;
}
struct evdns_server_port *
regress_get_tcp_dnsserver(struct event_base *base,
ev_uint16_t *portnum,
evutil_socket_t *psock,
evdns_request_callback_fn_type cb,
void *arg)
{
struct evdns_server_port *port = NULL;
evutil_socket_t sock;
struct sockaddr_in my_addr;
struct evconnlistener *listener;
memset(&my_addr, 0, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(*portnum);
my_addr.sin_addr.s_addr = htonl(0x7f000001UL);
listener = evconnlistener_new_bind(base, NULL, NULL,
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 128,
(struct sockaddr*)&my_addr, sizeof(my_addr));
if (!listener)
goto end;
port = evdns_add_server_port_with_listener(base, listener, 0, cb, arg);
if (!port)
goto end;
sock = evconnlistener_get_fd(listener);
if (!*portnum)
*portnum = regress_get_socket_port(sock);
if (psock)
*psock = sock;
return port;
end:
if (listener)
evconnlistener_free(listener);
return NULL;
}
void
regress_clean_dnsserver(void)
{
if (dns_port) {
evdns_close_server_port(dns_port);
dns_port = NULL;
if (udp_dns_port) {
evdns_close_server_port(udp_dns_port);
udp_dns_port = NULL;
}
if (dns_sock >= 0) {
evutil_closesocket(dns_sock);
dns_sock = -1;
if (udp_dns_sock >= 0) {
evutil_closesocket(udp_dns_sock);
udp_dns_sock = -1;
}
if (tcp_dns_port) {
evdns_close_server_port(tcp_dns_port);
tcp_dns_port = NULL;
}
if (tcp_dns_sock >= 0) {
evutil_closesocket(tcp_dns_sock);
tcp_dns_sock = -1;
}
}
@ -171,7 +223,11 @@ regress_dns_server_cb(struct evdns_server_request *req, void *data)
if (!strcmp(tab->anstype, "err")) {
int err = atoi(tab->ans);
tt_assert(! evdns_server_request_respond(req, err));
if (DNS_ERR_TIMEOUT == err) {
tt_assert(! evdns_server_request_drop(req));
} else {
tt_assert(! evdns_server_request_respond(req, err));
}
return;
} else if (!strcmp(tab->anstype, "errsoa")) {
int err = atoi(tab->ans);
@ -191,12 +247,9 @@ regress_dns_server_cb(struct evdns_server_request *req, void *data)
tt_assert(! evdns_server_request_respond(req, err));
return;
} else if (!strcmp(tab->anstype, "A")) {
struct in_addr in;
if (!evutil_inet_pton(AF_INET, tab->ans, &in)) {
TT_DIE(("Bad A value %s in table", tab->ans));
}
evdns_server_request_add_a_reply(req, question, 1, &in.s_addr,
100);
struct in_addr in[2048];
int count = parse_csv_address_list(tab->ans, AF_INET, in, ARRAY_SIZE(in));
evdns_server_request_add_a_reply(req, question, count, in, 100);
} else if (!strcmp(tab->anstype, "AAAA")) {
struct in6_addr in6;
if (!evutil_inet_pton(AF_INET6, tab->ans, &in6)) {
@ -215,11 +268,30 @@ end:
int
regress_dnsserver(struct event_base *base, ev_uint16_t *port,
struct regress_dns_server_table *search_table)
struct regress_dns_server_table *udp_seach_table,
struct regress_dns_server_table *tcp_seach_table)
{
dns_port = regress_get_dnsserver(base, port, &dns_sock,
regress_dns_server_cb, search_table);
return dns_port != NULL;
if (!udp_seach_table && !tcp_seach_table)
goto error;
if (tcp_seach_table) {
tcp_dns_port = regress_get_tcp_dnsserver(base, port, &tcp_dns_sock,
regress_dns_server_cb, tcp_seach_table);
if (!tcp_dns_port)
goto error;
}
if (udp_seach_table) {
udp_dns_port = regress_get_udp_dnsserver(base, port, &udp_dns_sock,
regress_dns_server_cb, udp_seach_table);
if (!udp_dns_port)
goto error;
}
return 1;
error:
regress_clean_dnsserver();
return 0;
}
int
@ -231,3 +303,29 @@ regress_get_listener_addr(struct evconnlistener *lev,
return -1;
return getsockname(s, sa, socklen);
}
int
parse_csv_address_list(const char *s, int family, void *addrs, size_t addrs_size)
{
int i = 0;
char *token;
char buf[16384];
void *next_addr;
tt_assert(family == AF_INET || family == AF_INET6);
tt_assert(strlen(s) < ARRAY_SIZE(buf));
strcpy(buf, s);
token = strtok(buf, ",");
do {
tt_assert((unsigned)i < addrs_size);
next_addr = (family == AF_INET) ? (void *)((struct in_addr*)addrs + i)
: (void *)((struct in6_addr*)addrs + i);
if (!evutil_inet_pton(AF_INET, token, next_addr)) {
TT_DIE(("Bad %s value %s in table", (family == AF_INET) ? "A" :"AAAA", token));
}
++i;
token = strtok (NULL, ",");
} while (token);
end:
return i;
}

View File

@ -32,13 +32,20 @@
struct regress_dns_server_table {
const char *q;
const char *anstype;
const char *ans;
const char *ans; /* Comma-separated list of IP numbers (e.g. "1.2.3.4", "1.2.3.4,5.6.7.8") */
int seen;
int lower;
};
struct evdns_server_port *
regress_get_dnsserver(struct event_base *base,
regress_get_udp_dnsserver(struct event_base *base,
ev_uint16_t *portnum,
evutil_socket_t *psock,
evdns_request_callback_fn_type cb,
void *arg);
struct evdns_server_port *
regress_get_tcp_dnsserver(struct event_base *base,
ev_uint16_t *portnum,
evutil_socket_t *psock,
evdns_request_callback_fn_type cb,
@ -51,9 +58,12 @@ int regress_get_socket_port(evutil_socket_t fd);
void regress_dns_server_cb(
struct evdns_server_request *req, void *data);
/* globally allocates a dns server that serves from a search table */
/* Globally allocates a dns server that serves from a search table.
TCP and UDP listeners are created on the same port number. If one of the
input search tables is NULL appropriate listener is not created. */
int regress_dnsserver(struct event_base *base, ev_uint16_t *port,
struct regress_dns_server_table *seach_table);
struct regress_dns_server_table *udp_seach_table,
struct regress_dns_server_table *tcp_seach_table);
/* clean up the global dns server resources */
void regress_clean_dnsserver(void);
@ -63,5 +73,9 @@ struct sockaddr;
int regress_get_listener_addr(struct evconnlistener *lev,
struct sockaddr *sa, ev_socklen_t *socklen);
/* Parse comma-separated list of IP addresses. */
int parse_csv_address_list(const char *s, int family,
void *addrs, size_t addrs_size);
#endif /* REGRESS_TESTUTILS_H_INCLUDED_ */