diff options
Diffstat (limited to 'src/core/nm-connectivity.c')
| -rw-r--r-- | src/core/nm-connectivity.c | 209 |
1 files changed, 189 insertions, 20 deletions
diff --git a/src/core/nm-connectivity.c b/src/core/nm-connectivity.c index c84d8a56..eea8adb2 100644 --- a/src/core/nm-connectivity.c +++ b/src/core/nm-connectivity.c @@ -622,6 +622,75 @@ _timeout_cb(gpointer user_data) cb_data_complete(cb_data, NM_CONNECTIVITY_LIMITED, "timeout"); 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; + gs_free char *data_escaped = NULL; + const char *msg; + gboolean print_data = FALSE; + + switch (type) { + case CURLINFO_TEXT: + print_data = TRUE; + msg = "== Info: "; + break; + case CURLINFO_DATA_OUT: + msg = "=> Send data"; + break; + case CURLINFO_SSL_DATA_OUT: + msg = "=> Send SSL data"; + break; + case CURLINFO_HEADER_IN: + msg = "<= Recv header"; + break; + case CURLINFO_DATA_IN: + msg = "<= Recv data"; + break; + case CURLINFO_SSL_DATA_IN: + msg = "<= Recv SSL data"; + break; + case CURLINFO_HEADER_OUT: + 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 static gboolean @@ -641,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"); @@ -660,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"); @@ -696,12 +769,103 @@ 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() && 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); + } curl_multi_add_handle(mhandle, ehandle); } 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; @@ -715,17 +879,24 @@ 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; - g_clear_object(&cb_data->concheck.resolve_cancellable); - if (!result) { - /* Never mind. Just let do curl do its own resolving. */ + gs_free char *dbus_error = NULL; + _LOG2D("can't resolve a name via systemd-resolved: %s", error->message); - do_curl_request(cb_data); + + dbus_error = g_dbus_error_get_remote_error(error); + if (nm_streq0(dbus_error, "org.freedesktop.resolve1.NoNameServers")) { + cb_data_complete(cb_data, NM_CONNECTIVITY_LIMITED, "resolve-error"); + return; + } + + /* Never mind. Fallback to the system resolver. */ + system_resolver_resolve(cb_data); return; } @@ -759,14 +930,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 @@ -892,6 +1063,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. @@ -927,8 +1100,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", @@ -943,17 +1114,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 |