/*
* Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu>
- * Copyright (c) 2007-2010 Niels Provos and Nick Mathewson
+ * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#include "regress.h"
#include "regress_testutils.h"
+#include "../util-internal.h"
+
static int dns_ok = 0;
static int dns_got_cancel = 0;
static int dns_err = 0;
{ "host2.a.example.com", "err", "3", 0 },
{ "host2.b.example.com", "A", "200.100.0.100", 0 },
{ "host2.c.example.com", "err", "3", 0 },
+ { "hostn.a.example.com", "errsoa", "0", 0 },
+ { "hostn.b.example.com", "errsoa", "3", 0 },
+ { "hostn.c.example.com", "err", "0", 0 },
{ "host", "err", "3", 0 },
{ "host2", "err", "3", 0 },
ev_uint16_t portnum = 0;
char buf[64];
- struct generic_dns_callback_result r1, r2, r3, r4, r5;
+ struct generic_dns_callback_result r[8];
tt_assert(regress_dnsserver(base, &portnum, search_table));
evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)portnum);
evdns_base_search_add(dns, "b.example.com");
evdns_base_search_add(dns, "c.example.com");
- n_replies_left = 5;
+ n_replies_left = sizeof(r)/sizeof(r[0]);
exit_base = base;
- evdns_base_resolve_ipv4(dns, "host", 0, generic_dns_callback, &r1);
- evdns_base_resolve_ipv4(dns, "host2", 0, generic_dns_callback, &r2);
- evdns_base_resolve_ipv4(dns, "host", DNS_NO_SEARCH, generic_dns_callback, &r3);
- evdns_base_resolve_ipv4(dns, "host2", DNS_NO_SEARCH, generic_dns_callback, &r4);
- evdns_base_resolve_ipv4(dns, "host3", 0, generic_dns_callback, &r5);
+ evdns_base_resolve_ipv4(dns, "host", 0, generic_dns_callback, &r[0]);
+ evdns_base_resolve_ipv4(dns, "host2", 0, generic_dns_callback, &r[1]);
+ evdns_base_resolve_ipv4(dns, "host", DNS_NO_SEARCH, generic_dns_callback, &r[2]);
+ evdns_base_resolve_ipv4(dns, "host2", DNS_NO_SEARCH, generic_dns_callback, &r[3]);
+ evdns_base_resolve_ipv4(dns, "host3", 0, generic_dns_callback, &r[4]);
+ evdns_base_resolve_ipv4(dns, "hostn.a.example.com", DNS_NO_SEARCH, generic_dns_callback, &r[5]);
+ evdns_base_resolve_ipv4(dns, "hostn.b.example.com", DNS_NO_SEARCH, generic_dns_callback, &r[6]);
+ evdns_base_resolve_ipv4(dns, "hostn.c.example.com", DNS_NO_SEARCH, generic_dns_callback, &r[7]);
event_base_dispatch(base);
- tt_int_op(r1.type, ==, DNS_IPv4_A);
- tt_int_op(r1.count, ==, 1);
- tt_int_op(((ev_uint32_t*)r1.addrs)[0], ==, htonl(0x0b16212c));
- tt_int_op(r2.type, ==, DNS_IPv4_A);
- tt_int_op(r2.count, ==, 1);
- tt_int_op(((ev_uint32_t*)r2.addrs)[0], ==, htonl(0xc8640064));
- tt_int_op(r3.result, ==, DNS_ERR_NOTEXIST);
- tt_int_op(r4.result, ==, DNS_ERR_NOTEXIST);
- tt_int_op(r5.result, ==, DNS_ERR_NOTEXIST);
+ tt_int_op(r[0].type, ==, DNS_IPv4_A);
+ tt_int_op(r[0].count, ==, 1);
+ tt_int_op(((ev_uint32_t*)r[0].addrs)[0], ==, htonl(0x0b16212c));
+ tt_int_op(r[1].type, ==, DNS_IPv4_A);
+ tt_int_op(r[1].count, ==, 1);
+ tt_int_op(((ev_uint32_t*)r[1].addrs)[0], ==, htonl(0xc8640064));
+ tt_int_op(r[2].result, ==, DNS_ERR_NOTEXIST);
+ tt_int_op(r[3].result, ==, DNS_ERR_NOTEXIST);
+ tt_int_op(r[4].result, ==, DNS_ERR_NOTEXIST);
+ tt_int_op(r[5].result, ==, DNS_ERR_NODATA);
+ tt_int_op(r[5].ttl, ==, 42);
+ tt_int_op(r[6].result, ==, DNS_ERR_NOTEXIST);
+ tt_int_op(r[6].ttl, ==, 42);
+ tt_int_op(r[7].result, ==, DNS_ERR_NODATA);
+ tt_int_op(r[7].ttl, ==, 0);
end:
if (dns)
/* === Test for bufferevent_socket_connect_hostname */
static int total_connected_or_failed = 0;
+static int total_n_accepted = 0;
static struct event_base *be_connect_hostname_base = NULL;
/* Implements a DNS server for the connect_hostname test and the
{
int *p = arg;
(*p)++;
+ ++total_n_accepted;
/* don't do anything with the socket; let it close when we exit() */
+ if (total_n_accepted >= 3 && total_connected_or_failed >= 5)
+ event_base_loopexit(be_connect_hostname_base,
+ NULL);
}
struct be_conn_hostname_result {
if ((what & BEV_EVENT_CONNECTED) || (what & BEV_EVENT_ERROR)) {
int r;
- ++total_connected_or_failed;
- TT_BLATHER(("Got %d connections or errors.", total_connected_or_failed));
if ((r = bufferevent_socket_get_dns_error(bev))) {
got->dnserr = r;
TT_BLATHER(("DNS error %d: %s", r,
evutil_gai_strerror(r)));
- }
- if (total_connected_or_failed >= 5)
+ } ++total_connected_or_failed;
+ TT_BLATHER(("Got %d connections or errors.", total_connected_or_failed));
+
+ if (total_n_accepted >= 3 && total_connected_or_failed >= 5)
event_base_loopexit(be_connect_hostname_base,
NULL);
}
++pending;
}
+#ifdef EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED
+/* FIXME: We should move this to regress_main.c if anything else needs it.*/
+
+/* Trivial replacements for malloc/free/realloc to check for memory leaks.
+ * Not threadsafe. */
+static int allocated_chunks = 0;
+
+static void *
+cnt_malloc(size_t sz)
+{
+ allocated_chunks += 1;
+ return malloc(sz);
+}
+
+static void *
+cnt_realloc(void *old, size_t sz)
+{
+ if (!old)
+ allocated_chunks += 1;
+ if (!sz)
+ allocated_chunks -= 1;
+ return realloc(old, sz);
+}
+
static void
-test_getaddrinfo_async_cancel_stress(void *arg)
+cnt_free(void *ptr)
{
- struct basic_test_data *data = arg;
- struct event_base *base = data->base;
+ allocated_chunks -= 1;
+ free(ptr);
+}
+
+struct testleak_env_t {
+ struct event_base *base;
+ struct evdns_base *dns_base;
+ struct evdns_request *req;
+ struct generic_dns_callback_result r;
+};
+
+static void *
+testleak_setup(const struct testcase_t *testcase)
+{
+ struct testleak_env_t *env;
+
+ allocated_chunks = 0;
+ event_set_mem_functions(cnt_malloc, cnt_realloc, cnt_free);
+ event_enable_debug_mode();
+
+ /* not mm_calloc: we don't want to mess with the count. */
+ env = calloc(1, sizeof(struct testleak_env_t));
+ env->base = event_base_new();
+ env->dns_base = evdns_base_new(env->base, 0);
+ env->req = evdns_base_resolve_ipv4(
+ env->dns_base, "example.com", DNS_QUERY_NO_SEARCH,
+ generic_dns_callback, &env->r);
+ return env;
+}
+
+static int
+testleak_cleanup(const struct testcase_t *testcase, void *env_)
+{
+ int ok = 0;
+ struct testleak_env_t *env = env_;
+#ifdef _EVENT_DISABLE_DEBUG_MODE
+ tt_int_op(allocated_chunks, ==, 0);
+#else
+ /* FIXME: that's `1' because of event_debug_map_HT_GROW */
+ tt_int_op(allocated_chunks, ==, 1);
+#endif
+ ok = 1;
+end:
+ if (env->dns_base)
+ evdns_base_free(env->dns_base, 0);
+ if (env->base)
+ event_base_free(env->base);
+ if (env)
+ free(env);
+ return ok;
+}
+
+static struct testcase_setup_t testleak_funcs = {
+ testleak_setup, testleak_cleanup
+};
+
+static void
+test_dbg_leak_cancel(void *env_)
+{
+ /* cancel, loop, free/dns, free/base */
+ struct testleak_env_t *env = env_;
+ int send_err_shutdown = 1;
+ evdns_cancel_request(env->dns_base, env->req);
+ env->req = 0;
+
+ /* `req` is freed in callback, that's why one loop is required. */
+ event_base_loop(env->base, EVLOOP_NONBLOCK);
+
+ /* send_err_shutdown means nothing as soon as our request is
+ * already canceled */
+ evdns_base_free(env->dns_base, send_err_shutdown);
+ env->dns_base = 0;
+ event_base_free(env->base);
+ env->base = 0;
+}
+
+static void
+test_dbg_leak_shutdown(void *env_)
+{
+ /* free/dns, loop, free/base */
+ struct testleak_env_t *env = env_;
+ int send_err_shutdown = 1;
+
+ /* `req` is freed both with `send_err_shutdown` and without it,
+ * the only difference is `evdns_callback` call */
+ env->req = 0;
+
+ evdns_base_free(env->dns_base, send_err_shutdown);
+ env->dns_base = 0;
+
+ /* `req` is freed in callback, that's why one loop is required */
+ event_base_loop(env->base, EVLOOP_NONBLOCK);
+ event_base_free(env->base);
+ env->base = 0;
+}
+#endif
+
+static void
+test_getaddrinfo_async_cancel_stress(void *ptr)
+{
+ struct event_base *base;
struct evdns_base *dns_base = NULL;
struct evdns_server_port *server = NULL;
evutil_socket_t fd = -1;
{ "getaddrinfo_async", test_getaddrinfo_async,
TT_FORK|TT_NEED_BASE, &basic_setup, (char*)"" },
{ "getaddrinfo_cancel_stress", test_getaddrinfo_async_cancel_stress,
- TT_FORK|TT_NEED_BASE, &basic_setup, (char*)"" },
+ TT_FORK, NULL, NULL },
+
+#ifdef EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED
+ { "leak_shutdown", test_dbg_leak_shutdown, TT_FORK, &testleak_funcs, NULL },
+ { "leak_cancel", test_dbg_leak_cancel, TT_FORK, &testleak_funcs, NULL },
+#endif
END_OF_TESTCASES
};