mirror of
https://github.com/libevent/libevent.git
synced 2025-01-31 09:12:55 +08:00
evdns: Implement dns requests via tcp
This commit is contained in:
parent
083c6d54d5
commit
0f6ee89a39
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user