diff --git a/src/core/nm-connectivity.c b/src/core/nm-connectivity.c index 8a79e45d67..8852cb3e96 100644 --- a/src/core/nm-connectivity.c +++ b/src/core/nm-connectivity.c @@ -623,47 +623,73 @@ _timeout_cb(gpointer user_data) return G_SOURCE_REMOVE; } +static gboolean +easy_debug_enabled(void) +{ + static int enabled = 0; + int e; + + /* libcurl debug logging can be useful, but is very verbose. + * Only enable it when we have a certain environment variable set. */ + +again: + e = g_atomic_int_get(&enabled); + if (G_UNLIKELY(e == 0)) { + e = _nm_utils_ascii_str_to_bool(g_getenv("NM_LOG_CONCHECK"), FALSE) ? 1 : -1; + if (G_UNLIKELY(!g_atomic_int_compare_and_exchange(&enabled, 0, e))) + goto again; + } + return e >= 0; +} + static int easy_debug_cb(CURL *handle, curl_infotype type, char *data, size_t size, void *userptr) { - NMConnectivityCheckHandle *cb_data = userptr; - const char *escaped = NULL; - gs_free char *to_free = NULL; + NMConnectivityCheckHandle *cb_data = userptr; + gs_free char *data_escaped = NULL; + const char *msg; + gboolean print_data = FALSE; switch (type) { case CURLINFO_TEXT: - escaped = nm_utils_buf_utf8safe_escape((char *) data, - size, - NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, - &to_free); - _LOG2T("libcurl: == Info: %s", escaped ?: ""); - /* fall-through */ - default: /* in case a new one is introduced to shock us */ - return 0; - + print_data = TRUE; + msg = "== Info: "; + break; case CURLINFO_DATA_OUT: - _LOG2T("libcurl => Send data"); - return 0; + msg = "=> Send data"; + break; case CURLINFO_SSL_DATA_OUT: - _LOG2T("libcurl => Send SSL data"); - return 0; + msg = "=> Send SSL data"; + break; case CURLINFO_HEADER_IN: - _LOG2T("libcurl <= Recv header"); - return 0; + msg = "<= Recv header"; + break; case CURLINFO_DATA_IN: - _LOG2T("libcurl <= Recv data"); - return 0; + msg = "<= Recv data"; + break; case CURLINFO_SSL_DATA_IN: - _LOG2T("libcurl <= Recv SSL data"); - return 0; + msg = "<= Recv SSL data"; + break; case CURLINFO_HEADER_OUT: - escaped = nm_utils_buf_utf8safe_escape((char *) data, - size, - NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, - &to_free); - _LOG2T("libcurl => Send header: %s", escaped ?: ""); + print_data = TRUE; + msg = "=> Send header: "; + return 0; + default: return 0; } + + _LOG2T("libcurl %s%s%s%s", + msg, + NM_PRINT_FMT_QUOTED(print_data, + "[", + (data_escaped = nm_utils_buf_utf8safe_escape_cp( + data, + size, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL)) + ?: "", + "]", + "")); + return 0; } #endif @@ -684,12 +710,14 @@ _idle_cb(gpointer user_data) #if WITH_CONCHECK static void -do_curl_request(NMConnectivityCheckHandle *cb_data) +do_curl_request(NMConnectivityCheckHandle *cb_data, const char *hosts) { CURLM *mhandle; CURL *ehandle; long resolve; + _LOG2T("set curl resolve list to '%s'", hosts); + mhandle = curl_multi_init(); if (!mhandle) { cb_data_complete(cb_data, NM_CONNECTIVITY_ERROR, "curl error"); @@ -703,6 +731,8 @@ do_curl_request(NMConnectivityCheckHandle *cb_data) return; } + cb_data->concheck.hosts = curl_slist_append(NULL, hosts); + cb_data->concheck.curl_mhandle = mhandle; cb_data->concheck.curl_ehandle = ehandle; cb_data->concheck.request_headers = curl_slist_append(NULL, "Connection: close"); @@ -739,7 +769,7 @@ do_curl_request(NMConnectivityCheckHandle *cb_data) curl_easy_setopt(ehandle, CURLOPT_RESOLVE, cb_data->concheck.hosts); curl_easy_setopt(ehandle, CURLOPT_IPRESOLVE, resolve); curl_easy_setopt(ehandle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); - if (_LOGT_ENABLED()) { + if (_LOGT_ENABLED() && easy_debug_enabled()) { curl_easy_setopt(ehandle, CURLOPT_DEBUGFUNCTION, easy_debug_cb); curl_easy_setopt(ehandle, CURLOPT_DEBUGDATA, cb_data); curl_easy_setopt(ehandle, CURLOPT_VERBOSE, 1L); @@ -749,7 +779,93 @@ do_curl_request(NMConnectivityCheckHandle *cb_data) } static void -resolve_cb(GObject *object, GAsyncResult *res, gpointer user_data) +system_resolver_resolve_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + nm_auto_str_buf NMStrBuf strbuf_hosts = NM_STR_BUF_INIT(0, FALSE); + gs_free_error GError *resolv_error = NULL; + GList *list; + GList *iter; + NMConnectivityCheckHandle *cb_data; + + list = g_resolver_lookup_by_name_finish(G_RESOLVER(source_object), res, &resolv_error); + + if (nm_utils_error_is_cancelled(resolv_error)) + return; + + cb_data = user_data; + + nm_assert(cb_data); + nm_assert(NM_IS_CONNECTIVITY(cb_data->self)); + + if (resolv_error) { + _LOG2D("failure to resolve name: %s", resolv_error->message); + cb_data_complete(cb_data, NM_CONNECTIVITY_LIMITED, "resolve-error"); + return; + } + + for (iter = list; iter; iter = iter->next) { + GInetAddress *a = iter->data; + char str_addr[NM_UTILS_INET_ADDRSTRLEN]; + int addr_family; + + switch (g_inet_address_get_family(a)) { + case G_SOCKET_FAMILY_IPV4: + addr_family = AF_INET; + break; + case G_SOCKET_FAMILY_IPV6: + addr_family = AF_INET6; + break; + default: + addr_family = AF_UNSPEC; + break; + } + if (cb_data->addr_family != AF_UNSPEC && cb_data->addr_family != addr_family) + continue; + + if (strbuf_hosts.len == 0) { + nm_str_buf_append_printf(&strbuf_hosts, + "%s:%s:", + cb_data->concheck.con_config->host, + cb_data->concheck.con_config->port ?: "80"); + } else + nm_str_buf_append_c(&strbuf_hosts, ','); + + nm_str_buf_append(&strbuf_hosts, + nm_utils_inet_ntop(addr_family, g_inet_address_to_bytes(a), str_addr)); + } + + g_list_free_full(list, g_object_unref); + + if (strbuf_hosts.len == 0) { + _LOG2D("system resolver returned no usable IPv%c addresses", + nm_utils_addr_family_to_char(cb_data->addr_family)); + cb_data_complete(cb_data, NM_CONNECTIVITY_LIMITED, "resolve-error"); + return; + } + + do_curl_request(cb_data, nm_str_buf_get_str(&strbuf_hosts)); +} + +static void +system_resolver_resolve(NMConnectivityCheckHandle *cb_data) +{ + gs_unref_object GResolver *resolver = NULL; + + _LOG2D("start request to '%s' (try resolving '%s' using system resolver)", + cb_data->concheck.con_config->uri, + cb_data->concheck.con_config->host); + + resolver = g_resolver_get_default(); + + g_resolver_lookup_by_name_async(resolver, + cb_data->concheck.con_config->host, + cb_data->concheck.resolve_cancellable, + system_resolver_resolve_cb, + cb_data); +} + +static void +systemd_resolved_resolve_cb(GObject *object, GAsyncResult *res, gpointer user_data) { NMConnectivityCheckHandle *cb_data; gs_unref_variant GVariant *result = NULL; @@ -763,7 +879,7 @@ resolve_cb(GObject *object, GAsyncResult *res, gpointer user_data) nm_auto_str_buf NMStrBuf strbuf_hosts = NM_STR_BUF_INIT(0, FALSE); result = g_dbus_connection_call_finish(G_DBUS_CONNECTION(object), res, &error); - if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + if (nm_utils_error_is_cancelled(error)) return; cb_data = user_data; @@ -771,9 +887,9 @@ resolve_cb(GObject *object, GAsyncResult *res, gpointer user_data) g_clear_object(&cb_data->concheck.resolve_cancellable); if (!result) { - /* Never mind. Just let do curl do its own resolving. */ + /* Never mind. Fallback to the system resolver. */ _LOG2D("can't resolve a name via systemd-resolved: %s", error->message); - do_curl_request(cb_data); + system_resolver_resolve(cb_data); return; } @@ -807,14 +923,14 @@ resolve_cb(GObject *object, GAsyncResult *res, gpointer user_data) nm_str_buf_append(&strbuf_hosts, nm_utils_inet_ntop(addr_family, address_buf, str_addr)); } - if (strbuf_hosts.len > 0) { - const char *s = nm_str_buf_get_str(&strbuf_hosts); - - cb_data->concheck.hosts = curl_slist_append(NULL, s); - _LOG2T("set curl resolve list to '%s'", s); + if (strbuf_hosts.len == 0) { + _LOG2D("systemd-resolve returned no usable IPv%c addresses", + nm_utils_addr_family_to_char(cb_data->addr_family)); + cb_data_complete(cb_data, NM_CONNECTIVITY_LIMITED, "resolve-error"); + return; } - do_curl_request(cb_data); + do_curl_request(cb_data, nm_str_buf_get_str(&strbuf_hosts)); } #endif @@ -940,6 +1056,8 @@ nm_connectivity_check_start(NMConnectivity *self, } } + cb_data->concheck.resolve_cancellable = g_cancellable_new(); + /* note that we pick up support for systemd-resolved right away when we need it. * We don't need to remember the setting, because we can (cheaply) check anew * on each request. @@ -975,8 +1093,6 @@ nm_connectivity_check_start(NMConnectivity *self, return cb_data; } - cb_data->concheck.resolve_cancellable = g_cancellable_new(); - g_dbus_connection_call(dbus_connection, "org.freedesktop.resolve1", "/org/freedesktop/resolve1", @@ -991,17 +1107,15 @@ nm_connectivity_check_start(NMConnectivity *self, G_DBUS_CALL_FLAGS_NONE, -1, cb_data->concheck.resolve_cancellable, - resolve_cb, + systemd_resolved_resolve_cb, cb_data); _LOG2D("start request to '%s' (try resolving '%s' using systemd-resolved)", cb_data->concheck.con_config->uri, cb_data->concheck.con_config->host); - } else { - _LOG2D("start request to '%s' (systemd-resolved not available)", - cb_data->concheck.con_config->uri); - do_curl_request(cb_data); + return cb_data; } + system_resolver_resolve(cb_data); return cb_data; } #endif