Fix EVDNS_BASE_DISABLE_WHEN_INACTIVE (#1493)

I faced with strange problem: event loop doesn't exit after dns resolving with
`EVDNS_BASE_DISABLE_WHEN_INACTIVE`.

Stand:
- Ubuntu 22;
- libevent release-2.1.12-stable
- `resolve.conf` contains 2 nameservers;
- I use `evdns_base_new` with `EVDNS_BASE_DISABLE_WHEN_INACTIVE | EVDNS_BASE_INITIALIZE_NAMESERVERS` to avoid OS specific code.

After small investigation, look like events related with dns sockets added to
event_base before `evdns->disable_when_inactive` was initialized. `libevent`
did epoll_ctl(DEL) after resolving completed on the first socket, but the
second socket remained in the `epoll` interest list.
This commit is contained in:
Vladislav Gusev 2023-07-13 22:20:33 +03:00 committed by GitHub
parent 36ad1006c9
commit 227510d577
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 106 additions and 4 deletions

View File

@ -4880,6 +4880,10 @@ evdns_base_new(struct event_base *event_base, int flags)
} }
#undef EVDNS_BASE_ALL_FLAGS #undef EVDNS_BASE_ALL_FLAGS
if (flags & EVDNS_BASE_DISABLE_WHEN_INACTIVE) {
base->disable_when_inactive = 1;
}
if (flags & EVDNS_BASE_INITIALIZE_NAMESERVERS) { if (flags & EVDNS_BASE_INITIALIZE_NAMESERVERS) {
int r; int r;
int opts = DNS_OPTIONS_ALL; int opts = DNS_OPTIONS_ALL;
@ -4890,16 +4894,13 @@ evdns_base_new(struct event_base *event_base, int flags)
#ifdef _WIN32 #ifdef _WIN32
r = evdns_base_config_windows_nameservers(base); r = evdns_base_config_windows_nameservers(base);
#else #else
r = evdns_base_resolv_conf_parse(base, opts, "/etc/resolv.conf"); r = evdns_base_resolv_conf_parse(base, opts, evutil_resolvconf_filename_());
#endif #endif
if (r && (EVDNS_ERROR_NO_NAMESERVERS_CONFIGURED != r)) { if (r && (EVDNS_ERROR_NO_NAMESERVERS_CONFIGURED != r)) {
evdns_base_free_and_unlock(base, 0); evdns_base_free_and_unlock(base, 0);
return NULL; return NULL;
} }
} }
if (flags & EVDNS_BASE_DISABLE_WHEN_INACTIVE) {
base->disable_when_inactive = 1;
}
EVDNS_UNLOCK(base); EVDNS_UNLOCK(base);
return base; return base;

View File

@ -1849,6 +1849,23 @@ evutil_set_evdns_getaddrinfo_cancel_fn_(evdns_getaddrinfo_cancel_fn fn)
evdns_getaddrinfo_cancel_impl = fn; evdns_getaddrinfo_cancel_impl = fn;
} }
static const char *evutil_custom_resolvconf_filename = NULL;
void
evutil_set_resolvconf_filename_(const char *filename)
{
evutil_custom_resolvconf_filename = filename;
}
const char *
evutil_resolvconf_filename_(void)
{
if (evutil_custom_resolvconf_filename)
return evutil_custom_resolvconf_filename;
return "/etc/resolv.conf";
}
/* Internal helper function: act like evdns_getaddrinfo if dns_base is set; /* Internal helper function: act like evdns_getaddrinfo if dns_base is set;
* otherwise do a blocking resolve and pass the result to the callback in the * otherwise do a blocking resolve and pass the result to the callback in the
* way that evdns_getaddrinfo would. * way that evdns_getaddrinfo would.

View File

@ -1185,6 +1185,80 @@ end:
if (dns) if (dns)
evdns_base_free(dns, 0); evdns_base_free(dns, 0);
} }
static const char *dns_resolvconf_with_one_nameserver =
"nameserver 127.0.0.53\n";
static void
dns_initialize_inactive_one_nameserver_test(void *arg)
{
struct basic_test_data *data = arg;
struct event_base *base = data->base;
struct evdns_base *dns = NULL;
char *filename = NULL;
tt_int_op(regress_make_tmpfile(dns_resolvconf_with_one_nameserver,
strlen(dns_resolvconf_with_one_nameserver), &filename),
!=, -1);
tt_assert(filename);
evutil_set_resolvconf_filename_(filename);
dns = evdns_base_new(base,
EVDNS_BASE_INITIALIZE_NAMESERVERS | EVDNS_BASE_DISABLE_WHEN_INACTIVE);
tt_assert(dns);
tt_int_op(event_base_loop(base, EVLOOP_NONBLOCK), ==, 1);
end:
if (dns)
evdns_base_free(dns, 0);
if (filename) {
unlink(filename);
free(filename);
}
evutil_set_resolvconf_filename_(NULL);
}
static const char *dns_resolvconf_with_two_nameservers =
"nameserver 127.0.0.53\n"
"nameserver 127.0.0.53\n";
static void
dns_initialize_inactive_two_nameservers_test(void *arg)
{
struct basic_test_data *data = arg;
struct event_base *base = data->base;
struct evdns_base *dns = NULL;
char *filename = NULL;
tt_int_op(regress_make_tmpfile(dns_resolvconf_with_two_nameservers,
strlen(dns_resolvconf_with_two_nameservers), &filename),
!=, -1);
tt_assert(filename);
evutil_set_resolvconf_filename_(filename);
dns = evdns_base_new(base,
EVDNS_BASE_INITIALIZE_NAMESERVERS | EVDNS_BASE_DISABLE_WHEN_INACTIVE);
tt_assert(dns);
tt_int_op(event_base_loop(base, EVLOOP_NONBLOCK), ==, 1);
end:
if (dns)
evdns_base_free(dns, 0);
if (filename) {
unlink(filename);
free(filename);
}
evutil_set_resolvconf_filename_(NULL);
}
#ifndef _WIN32 #ifndef _WIN32
#define RESOLV_FILE "empty-resolv.conf" #define RESOLV_FILE "empty-resolv.conf"
static void static void
@ -3036,6 +3110,10 @@ struct testcase_t dns_testcases[] = {
{ "initialize_nameservers", dns_initialize_nameservers_test, { "initialize_nameservers", dns_initialize_nameservers_test,
TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, TT_FORK|TT_NEED_BASE, &basic_setup, NULL },
#ifndef _WIN32 #ifndef _WIN32
{"initialize_with_one_inactive_nameserver", dns_initialize_inactive_one_nameserver_test,
TT_FORK | TT_NEED_BASE, &basic_setup, NULL},
{"initialize_with_two_inactive_nameservers", dns_initialize_inactive_two_nameservers_test,
TT_FORK | TT_NEED_BASE, &basic_setup, NULL},
{ "nameservers_no_default", dns_nameservers_no_default_test, { "nameservers_no_default", dns_nameservers_no_default_test,
TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, TT_FORK|TT_NEED_BASE, &basic_setup, NULL },
{ "no_nameservers_configured", dns_nameservers_no_nameservers_configured_test, { "no_nameservers_configured", dns_nameservers_no_nameservers_configured_test,

View File

@ -417,6 +417,12 @@ typedef void (*evdns_getaddrinfo_cancel_fn)(
EVENT2_EXPORT_SYMBOL EVENT2_EXPORT_SYMBOL
void evutil_set_evdns_getaddrinfo_cancel_fn_(evdns_getaddrinfo_cancel_fn fn); void evutil_set_evdns_getaddrinfo_cancel_fn_(evdns_getaddrinfo_cancel_fn fn);
/* Customization point to override "/etc/resolv.conf" */
EVENT2_EXPORT_SYMBOL
void evutil_set_resolvconf_filename_(const char *filename);
EVENT2_EXPORT_SYMBOL
const char *evutil_resolvconf_filename_(void);
EVENT2_EXPORT_SYMBOL EVENT2_EXPORT_SYMBOL
struct evutil_addrinfo *evutil_new_addrinfo_(struct sockaddr *sa, struct evutil_addrinfo *evutil_new_addrinfo_(struct sockaddr *sa,
ev_socklen_t socklen, const struct evutil_addrinfo *hints); ev_socklen_t socklen, const struct evutil_addrinfo *hints);