|
Packit Service |
87a54e |
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
Packit |
5756e2 |
/*
|
|
Packit |
5756e2 |
* Copyright (C) 2011 Thomas Bechtold <thomasbechtold@jpberlin.de>
|
|
Packit |
5756e2 |
* Copyright (C) 2011 Dan Williams <dcbw@redhat.com>
|
|
Packit |
5756e2 |
* Copyright (C) 2016 - 2018 Red Hat, Inc.
|
|
Packit |
5756e2 |
*/
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
#include "nm-default.h"
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
#include "nm-connectivity.h"
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
#if WITH_CONCHECK
|
|
Packit Service |
a1bd4f |
#include <curl/curl.h>
|
|
Packit |
5756e2 |
#endif
|
|
Packit |
5756e2 |
#include <linux/rtnetlink.h>
|
|
Packit |
5756e2 |
#include <glib-unix.h>
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
#include "c-list/src/c-list.h"
|
|
Packit |
5756e2 |
#include "nm-core-internal.h"
|
|
Packit |
5756e2 |
#include "nm-config.h"
|
|
Packit |
5756e2 |
#include "NetworkManagerUtils.h"
|
|
Packit |
5756e2 |
#include "nm-dbus-manager.h"
|
|
Packit |
5756e2 |
#include "dns/nm-dns-manager.h"
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
#define HEADER_STATUS_ONLINE "X-NetworkManager-Status: online\r\n"
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
/*****************************************************************************/
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
static NM_UTILS_LOOKUP_STR_DEFINE(_state_to_string,
|
|
Packit Service |
a1bd4f |
int /*NMConnectivityState*/,
|
|
Packit Service |
a1bd4f |
NM_UTILS_LOOKUP_DEFAULT_WARN("???"),
|
|
Packit Service |
a1bd4f |
NM_UTILS_LOOKUP_STR_ITEM(NM_CONNECTIVITY_UNKNOWN, "UNKNOWN"),
|
|
Packit Service |
a1bd4f |
NM_UTILS_LOOKUP_STR_ITEM(NM_CONNECTIVITY_NONE, "NONE"),
|
|
Packit Service |
a1bd4f |
NM_UTILS_LOOKUP_STR_ITEM(NM_CONNECTIVITY_LIMITED, "LIMITED"),
|
|
Packit Service |
a1bd4f |
NM_UTILS_LOOKUP_STR_ITEM(NM_CONNECTIVITY_PORTAL, "PORTAL"),
|
|
Packit Service |
a1bd4f |
NM_UTILS_LOOKUP_STR_ITEM(NM_CONNECTIVITY_FULL, "FULL"),
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
NM_UTILS_LOOKUP_STR_ITEM(NM_CONNECTIVITY_ERROR, "ERROR"),
|
|
Packit Service |
a1bd4f |
NM_UTILS_LOOKUP_STR_ITEM(NM_CONNECTIVITY_FAKE, "FAKE"),
|
|
Packit Service |
a1bd4f |
NM_UTILS_LOOKUP_STR_ITEM(NM_CONNECTIVITY_CANCELLED, "CANCELLED"),
|
|
Packit Service |
a1bd4f |
NM_UTILS_LOOKUP_STR_ITEM(NM_CONNECTIVITY_DISPOSING,
|
|
Packit Service |
a1bd4f |
"DISPOSING"), );
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
const char *
|
|
Packit Service |
a1bd4f |
nm_connectivity_state_to_string(NMConnectivityState state)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
return _state_to_string(state);
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
/*****************************************************************************/
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
typedef struct {
|
|
Packit Service |
a1bd4f |
guint ref_count;
|
|
Packit Service |
a1bd4f |
char *uri;
|
|
Packit Service |
a1bd4f |
char *host;
|
|
Packit Service |
a1bd4f |
char *port;
|
|
Packit Service |
a1bd4f |
char *response;
|
|
Packit |
5756e2 |
} ConConfig;
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
struct _NMConnectivityCheckHandle {
|
|
Packit Service |
a1bd4f |
CList handles_lst;
|
|
Packit Service |
a1bd4f |
NMConnectivity * self;
|
|
Packit Service |
a1bd4f |
NMConnectivityCheckCallback callback;
|
|
Packit Service |
a1bd4f |
gpointer user_data;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
char *ifspec;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
const char *completed_log_message;
|
|
Packit Service |
a1bd4f |
char * completed_log_message_free;
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
#if WITH_CONCHECK
|
|
Packit Service |
a1bd4f |
struct {
|
|
Packit Service |
a1bd4f |
ConConfig *con_config;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
GCancellable * resolve_cancellable;
|
|
Packit Service |
a1bd4f |
CURLM * curl_mhandle;
|
|
Packit Service |
a1bd4f |
CURL * curl_ehandle;
|
|
Packit Service |
a1bd4f |
struct curl_slist *request_headers;
|
|
Packit Service |
a1bd4f |
struct curl_slist *hosts;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
gsize response_good_cnt;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
guint curl_timer;
|
|
Packit Service |
a1bd4f |
int ch_ifindex;
|
|
Packit Service |
a1bd4f |
} concheck;
|
|
Packit |
5756e2 |
#endif
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
guint64 request_counter;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
int addr_family;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
guint timeout_id;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
NMConnectivityState completed_state;
|
|
Packit Service |
a1bd4f |
const char * completed_reason;
|
|
Packit |
5756e2 |
};
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
enum {
|
|
Packit Service |
a1bd4f |
CONFIG_CHANGED,
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
LAST_SIGNAL
|
|
Packit |
5756e2 |
};
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
static guint signals[LAST_SIGNAL] = {0};
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
typedef struct {
|
|
Packit Service |
a1bd4f |
CList handles_lst_head;
|
|
Packit Service |
a1bd4f |
CList completed_handles_lst_head;
|
|
Packit Service |
a1bd4f |
NMConfig * config;
|
|
Packit Service |
a1bd4f |
ConConfig *con_config;
|
|
Packit Service |
a1bd4f |
guint interval;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
bool enabled : 1;
|
|
Packit Service |
a1bd4f |
bool uri_valid : 1;
|
|
Packit |
5756e2 |
} NMConnectivityPrivate;
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
struct _NMConnectivity {
|
|
Packit Service |
a1bd4f |
GObject parent;
|
|
Packit Service |
a1bd4f |
NMConnectivityPrivate _priv;
|
|
Packit |
5756e2 |
};
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
struct _NMConnectivityClass {
|
|
Packit Service |
a1bd4f |
GObjectClass parent;
|
|
Packit |
5756e2 |
};
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
G_DEFINE_TYPE(NMConnectivity, nm_connectivity, G_TYPE_OBJECT)
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
#define NM_CONNECTIVITY_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMConnectivity, NM_IS_CONNECTIVITY)
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
NM_DEFINE_SINGLETON_GETTER(NMConnectivity, nm_connectivity_get, NM_TYPE_CONNECTIVITY);
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
/*****************************************************************************/
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
#define _NMLOG_DOMAIN LOGD_CONCHECK
|
|
Packit Service |
a1bd4f |
#define _NMLOG(level, ...) __NMLOG_DEFAULT(level, _NMLOG_DOMAIN, "connectivity", __VA_ARGS__)
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
#define _NMLOG2_DOMAIN LOGD_CONCHECK
|
|
Packit Service |
a1bd4f |
#define _NMLOG2(level, ...) \
|
|
Packit Service |
a1bd4f |
G_STMT_START \
|
|
Packit Service |
a1bd4f |
{ \
|
|
Packit Service |
a1bd4f |
const NMLogLevel __level = (level); \
|
|
Packit Service |
a1bd4f |
\
|
|
Packit Service |
a1bd4f |
if (nm_logging_enabled(__level, _NMLOG2_DOMAIN)) { \
|
|
Packit Service |
a1bd4f |
_nm_log(__level, \
|
|
Packit Service |
a1bd4f |
_NMLOG2_DOMAIN, \
|
|
Packit Service |
a1bd4f |
0, \
|
|
Packit Service |
a1bd4f |
(cb_data->ifspec ? &cb_data->ifspec[3] : NULL), \
|
|
Packit Service |
a1bd4f |
NULL, \
|
|
Packit Service |
a1bd4f |
"connectivity: (%s,IPv%c,%" G_GUINT64_FORMAT \
|
|
Packit Service |
a1bd4f |
") " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
|
|
Packit Service |
a1bd4f |
(cb_data->ifspec ? &cb_data->ifspec[3] : ""), \
|
|
Packit Service |
a1bd4f |
nm_utils_addr_family_to_char(cb_data->addr_family), \
|
|
Packit Service |
a1bd4f |
cb_data->request_counter _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
|
|
Packit Service |
a1bd4f |
} \
|
|
Packit Service |
a1bd4f |
} \
|
|
Packit Service |
a1bd4f |
G_STMT_END
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
/*****************************************************************************/
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
#if WITH_CONCHECK
|
|
Packit |
5756e2 |
static ConConfig *
|
|
Packit Service |
a1bd4f |
_con_config_ref(ConConfig *con_config)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
if (con_config) {
|
|
Packit Service |
a1bd4f |
nm_assert(con_config->ref_count > 0);
|
|
Packit Service |
a1bd4f |
++con_config->ref_count;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
return con_config;
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
#endif
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static void
|
|
Packit Service |
a1bd4f |
_con_config_unref(ConConfig *con_config)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
if (!con_config)
|
|
Packit Service |
a1bd4f |
return;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
nm_assert(con_config->ref_count > 0);
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
if (--con_config->ref_count != 0)
|
|
Packit Service |
a1bd4f |
return;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
g_free(con_config->uri);
|
|
Packit Service |
a1bd4f |
g_free(con_config->host);
|
|
Packit Service |
a1bd4f |
g_free(con_config->port);
|
|
Packit Service |
a1bd4f |
g_free(con_config->response);
|
|
Packit Service |
a1bd4f |
g_slice_free(ConConfig, con_config);
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
#if WITH_CONCHECK
|
|
Packit |
5756e2 |
static const char *
|
|
Packit Service |
a1bd4f |
_con_config_get_response(const ConConfig *con_config)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
return con_config->response ?: NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE;
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
#endif
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
/*****************************************************************************/
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static void
|
|
Packit Service |
a1bd4f |
cb_data_complete(NMConnectivityCheckHandle *cb_data,
|
|
Packit Service |
a1bd4f |
NMConnectivityState state,
|
|
Packit Service |
a1bd4f |
const char * log_message)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
NMConnectivity *self;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
nm_assert(cb_data);
|
|
Packit Service |
a1bd4f |
nm_assert(NM_IS_CONNECTIVITY(cb_data->self));
|
|
Packit Service |
a1bd4f |
nm_assert(cb_data->callback);
|
|
Packit Service |
a1bd4f |
nm_assert(state != NM_CONNECTIVITY_UNKNOWN);
|
|
Packit Service |
a1bd4f |
nm_assert(log_message);
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
self = cb_data->self;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
/* mark the handle as completing. After this point, nm_connectivity_check_cancel()
|
|
Packit Service |
a1bd4f |
* is no longer possible. */
|
|
Packit Service |
a1bd4f |
cb_data->self = NULL;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
c_list_unlink_stale(&cb_data->handles_lst);
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
#if WITH_CONCHECK
|
|
Packit Service |
a1bd4f |
if (cb_data->concheck.curl_ehandle) {
|
|
Packit Service |
a1bd4f |
/* Contrary to what cURL manual claim it is *not* safe to remove
|
|
Packit Service |
a1bd4f |
* the easy handle "at any moment"; specifically it's not safe to
|
|
Packit Service |
a1bd4f |
* remove *any* handle from within a libcurl callback. That is
|
|
Packit Service |
a1bd4f |
* why we queue completed handles in this case.
|
|
Packit Service |
a1bd4f |
*
|
|
Packit Service |
a1bd4f |
* cb_data_complete() is however only called *not* from within a
|
|
Packit Service |
a1bd4f |
* libcurl callback. So, this is fine. */
|
|
Packit Service |
a1bd4f |
curl_easy_setopt(cb_data->concheck.curl_ehandle, CURLOPT_WRITEFUNCTION, NULL);
|
|
Packit Service |
a1bd4f |
curl_easy_setopt(cb_data->concheck.curl_ehandle, CURLOPT_WRITEDATA, NULL);
|
|
Packit Service |
a1bd4f |
curl_easy_setopt(cb_data->concheck.curl_ehandle, CURLOPT_HEADERFUNCTION, NULL);
|
|
Packit Service |
a1bd4f |
curl_easy_setopt(cb_data->concheck.curl_ehandle, CURLOPT_HEADERDATA, NULL);
|
|
Packit Service |
a1bd4f |
curl_easy_setopt(cb_data->concheck.curl_ehandle, CURLOPT_PRIVATE, NULL);
|
|
Packit Service |
a1bd4f |
curl_easy_setopt(cb_data->concheck.curl_ehandle, CURLOPT_HTTPHEADER, NULL);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
curl_multi_remove_handle(cb_data->concheck.curl_mhandle, cb_data->concheck.curl_ehandle);
|
|
Packit Service |
a1bd4f |
curl_easy_cleanup(cb_data->concheck.curl_ehandle);
|
|
Packit Service |
a1bd4f |
curl_multi_cleanup(cb_data->concheck.curl_mhandle);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
curl_slist_free_all(cb_data->concheck.request_headers);
|
|
Packit Service |
a1bd4f |
curl_slist_free_all(cb_data->concheck.hosts);
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
nm_clear_g_source(&cb_data->concheck.curl_timer);
|
|
Packit Service |
a1bd4f |
nm_clear_g_cancellable(&cb_data->concheck.resolve_cancellable);
|
|
Packit |
5756e2 |
#endif
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
nm_clear_g_source(&cb_data->timeout_id);
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
_LOG2D("check completed: %s; %s", nm_connectivity_state_to_string(state), log_message);
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
cb_data->callback(self, cb_data, state, cb_data->user_data);
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
/* Note: self might be a danling pointer at this point. It must not be used
|
|
Packit Service |
a1bd4f |
* after this point, and all callers must either take a reference first, or
|
|
Packit Service |
a1bd4f |
* not use the self pointer too. */
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
#if WITH_CONCHECK
|
|
Packit Service |
a1bd4f |
_con_config_unref(cb_data->concheck.con_config);
|
|
Packit |
5756e2 |
#endif
|
|
Packit Service |
a1bd4f |
g_free(cb_data->ifspec);
|
|
Packit Service |
a1bd4f |
if (cb_data->completed_log_message_free)
|
|
Packit Service |
a1bd4f |
g_free(cb_data->completed_log_message_free);
|
|
Packit Service |
a1bd4f |
g_slice_free(NMConnectivityCheckHandle, cb_data);
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
/*****************************************************************************/
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
#if WITH_CONCHECK
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static void
|
|
Packit Service |
a1bd4f |
cb_data_queue_completed(NMConnectivityCheckHandle *cb_data,
|
|
Packit Service |
a1bd4f |
NMConnectivityState state,
|
|
Packit Service |
a1bd4f |
const char * log_message_static,
|
|
Packit Service |
a1bd4f |
char * log_message_take /* take */)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
nm_assert(cb_data);
|
|
Packit Service |
a1bd4f |
nm_assert(NM_IS_CONNECTIVITY(cb_data->self));
|
|
Packit Service |
a1bd4f |
nm_assert(state != NM_CONNECTIVITY_UNKNOWN);
|
|
Packit Service |
a1bd4f |
nm_assert(log_message_static || log_message_take);
|
|
Packit Service |
a1bd4f |
nm_assert(cb_data->completed_state == NM_CONNECTIVITY_UNKNOWN);
|
|
Packit Service |
a1bd4f |
nm_assert(!cb_data->completed_log_message);
|
|
Packit Service |
a1bd4f |
nm_assert(c_list_contains(&NM_CONNECTIVITY_GET_PRIVATE(cb_data->self)->handles_lst_head,
|
|
Packit Service |
a1bd4f |
&cb_data->handles_lst));
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
cb_data->completed_state = state;
|
|
Packit Service |
a1bd4f |
cb_data->completed_log_message = log_message_static ?: log_message_take;
|
|
Packit Service |
a1bd4f |
cb_data->completed_log_message_free = log_message_take;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
c_list_unlink_stale(&cb_data->handles_lst);
|
|
Packit Service |
a1bd4f |
c_list_link_tail(&NM_CONNECTIVITY_GET_PRIVATE(cb_data->self)->completed_handles_lst_head,
|
|
Packit Service |
a1bd4f |
&cb_data->handles_lst);
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static void
|
|
Packit Service |
a1bd4f |
_complete_queued(NMConnectivity *self)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
NMConnectivity * self_keep_alive = NULL;
|
|
Packit Service |
a1bd4f |
NMConnectivityPrivate * priv = NM_CONNECTIVITY_GET_PRIVATE(self);
|
|
Packit Service |
a1bd4f |
NMConnectivityCheckHandle *cb_data;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
while ((cb_data = c_list_first_entry(&priv->completed_handles_lst_head,
|
|
Packit Service |
a1bd4f |
NMConnectivityCheckHandle,
|
|
Packit Service |
a1bd4f |
handles_lst))) {
|
|
Packit Service |
a1bd4f |
if (!self_keep_alive)
|
|
Packit Service |
a1bd4f |
self_keep_alive = g_object_ref(self);
|
|
Packit Service |
a1bd4f |
cb_data_complete(cb_data, cb_data->completed_state, cb_data->completed_log_message);
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
nm_g_object_unref(self_keep_alive);
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static gboolean
|
|
Packit Service |
a1bd4f |
_con_curl_check_connectivity(CURLM *mhandle, int sockfd, int ev_bitmask)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
NMConnectivityCheckHandle *cb_data;
|
|
Packit Service |
a1bd4f |
CURLMsg * msg;
|
|
Packit Service |
a1bd4f |
int m_left;
|
|
Packit Service |
a1bd4f |
long response_code;
|
|
Packit Service |
a1bd4f |
CURLMcode ret;
|
|
Packit Service |
a1bd4f |
int running_handles;
|
|
Packit Service |
a1bd4f |
gboolean success = TRUE;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
ret = curl_multi_socket_action(mhandle, sockfd, ev_bitmask, &running_handles);
|
|
Packit Service |
a1bd4f |
if (ret != CURLM_OK) {
|
|
Packit Service |
a1bd4f |
_LOGD("connectivity check failed: (%d) %s", ret, curl_multi_strerror(ret));
|
|
Packit Service |
a1bd4f |
success = FALSE;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
while ((msg = curl_multi_info_read(mhandle, &m_left))) {
|
|
Packit Service |
a1bd4f |
const char *response;
|
|
Packit Service |
a1bd4f |
CURLcode eret;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (msg->msg != CURLMSG_DONE)
|
|
Packit Service |
a1bd4f |
continue;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
/* Here we have completed a session. Check easy session result. */
|
|
Packit Service |
a1bd4f |
eret = curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char **) &cb_data);
|
|
Packit Service |
a1bd4f |
if (eret != CURLE_OK) {
|
|
Packit Service |
a1bd4f |
_LOGD("curl cannot extract cb_data for easy handle, skipping msg: (%d) %s",
|
|
Packit Service |
a1bd4f |
eret,
|
|
Packit Service |
a1bd4f |
curl_easy_strerror(eret));
|
|
Packit Service |
a1bd4f |
success = FALSE;
|
|
Packit Service |
a1bd4f |
continue;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
nm_assert(cb_data);
|
|
Packit Service |
a1bd4f |
nm_assert(NM_IS_CONNECTIVITY(cb_data->self));
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (cb_data->completed_state != NM_CONNECTIVITY_UNKNOWN) {
|
|
Packit Service |
a1bd4f |
/* callback was already invoked earlier. Nothing to do. */
|
|
Packit Service |
a1bd4f |
continue;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (msg->data.result != CURLE_OK) {
|
|
Packit Service |
a1bd4f |
cb_data_queue_completed(cb_data,
|
|
Packit Service |
a1bd4f |
NM_CONNECTIVITY_LIMITED,
|
|
Packit Service |
a1bd4f |
NULL,
|
|
Packit Service |
a1bd4f |
g_strdup_printf("check failed: (%d) %s",
|
|
Packit Service |
a1bd4f |
msg->data.result,
|
|
Packit Service |
a1bd4f |
curl_easy_strerror(msg->data.result)));
|
|
Packit Service |
a1bd4f |
continue;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
response = _con_config_get_response(cb_data->concheck.con_config);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (response[0] == '\0'
|
|
Packit Service |
a1bd4f |
&& (curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &response_code)
|
|
Packit Service |
a1bd4f |
== CURLE_OK)) {
|
|
Packit Service |
a1bd4f |
if (response_code == 204) {
|
|
Packit Service |
a1bd4f |
/* We expected an empty response, and we got a 204 response code (no content).
|
|
Packit Service |
a1bd4f |
* We may or may not have received any content (we would ignore it).
|
|
Packit Service |
a1bd4f |
* Anyway, the response_code 204 means we are good. */
|
|
Packit Service |
a1bd4f |
cb_data_queue_completed(cb_data,
|
|
Packit Service |
a1bd4f |
NM_CONNECTIVITY_FULL,
|
|
Packit Service |
a1bd4f |
"no content, as expected",
|
|
Packit Service |
a1bd4f |
NULL);
|
|
Packit Service |
a1bd4f |
continue;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (response_code == 200 && cb_data->concheck.response_good_cnt == 0) {
|
|
Packit Service |
a1bd4f |
/* we expected no response, and indeed we got an empty reply (with status code 200) */
|
|
Packit Service |
a1bd4f |
cb_data_queue_completed(cb_data,
|
|
Packit Service |
a1bd4f |
NM_CONNECTIVITY_FULL,
|
|
Packit Service |
a1bd4f |
"empty response, as expected",
|
|
Packit Service |
a1bd4f |
NULL);
|
|
Packit Service |
a1bd4f |
continue;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
/* If we get here, it means that easy_write_cb() didn't read enough
|
|
Packit Service |
a1bd4f |
* bytes to be able to do a match, or that we were asking for no content
|
|
Packit Service |
a1bd4f |
* (204 response code) and we actually got some. Either way, that is
|
|
Packit Service |
a1bd4f |
* an indication of a captive portal */
|
|
Packit Service |
a1bd4f |
cb_data_queue_completed(cb_data, NM_CONNECTIVITY_PORTAL, "unexpected short response", NULL);
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
/* if we return a failure, we don't know what went wrong. It's likely serious, because
|
|
Packit Service |
a1bd4f |
* a failure here is not expected. Return FALSE, so that we stop polling the file descriptor.
|
|
Packit Service |
a1bd4f |
* Worst case, this leaves the pending connectivity check unhandled, until our regular
|
|
Packit Service |
a1bd4f |
* time-out kicks in. */
|
|
Packit Service |
a1bd4f |
return success;
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static gboolean
|
|
Packit Service |
a1bd4f |
_con_curl_timeout_cb(gpointer user_data)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
NMConnectivityCheckHandle *cb_data = user_data;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
_con_curl_check_connectivity(cb_data->concheck.curl_mhandle, CURL_SOCKET_TIMEOUT, 0);
|
|
Packit Service |
a1bd4f |
_complete_queued(cb_data->self);
|
|
Packit Service |
a1bd4f |
return G_SOURCE_CONTINUE;
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static int
|
|
Packit Service |
a1bd4f |
multi_timer_cb(CURLM *multi, long timeout_msec, void *userdata)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
NMConnectivityCheckHandle *cb_data = userdata;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
nm_clear_g_source(&cb_data->concheck.curl_timer);
|
|
Packit Service |
a1bd4f |
if (timeout_msec != -1)
|
|
Packit Service |
a1bd4f |
cb_data->concheck.curl_timer = g_timeout_add(timeout_msec, _con_curl_timeout_cb, cb_data);
|
|
Packit Service |
a1bd4f |
return 0;
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
typedef struct {
|
|
Packit Service |
a1bd4f |
NMConnectivityCheckHandle *cb_data;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
GSource *source;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
/* this is a very simplistic weak-pointer. If ConCurlSockData gets
|
|
Packit Service |
a1bd4f |
* destroyed, it will set *destroy_notify to TRUE.
|
|
Packit Service |
a1bd4f |
*
|
|
Packit Service |
a1bd4f |
* _con_curl_socketevent_cb() uses this to detect whether it can
|
|
Packit Service |
a1bd4f |
* safely access @fdp after _con_curl_check_connectivity(). */
|
|
Packit Service |
a1bd4f |
gboolean *destroy_notify;
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
} ConCurlSockData;
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static gboolean
|
|
Packit Service |
a1bd4f |
_con_curl_socketevent_cb(int fd, GIOCondition condition, gpointer user_data)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
ConCurlSockData * fdp = user_data;
|
|
Packit Service |
a1bd4f |
NMConnectivityCheckHandle *cb_data = fdp->cb_data;
|
|
Packit Service |
a1bd4f |
int action = 0;
|
|
Packit Service |
a1bd4f |
gboolean fdp_destroyed = FALSE;
|
|
Packit Service |
a1bd4f |
gboolean success;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (condition & G_IO_IN)
|
|
Packit Service |
a1bd4f |
action |= CURL_CSELECT_IN;
|
|
Packit Service |
a1bd4f |
if (condition & G_IO_OUT)
|
|
Packit Service |
a1bd4f |
action |= CURL_CSELECT_OUT;
|
|
Packit Service |
a1bd4f |
if (condition & G_IO_ERR)
|
|
Packit Service |
a1bd4f |
action |= CURL_CSELECT_ERR;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
nm_assert(!fdp->destroy_notify);
|
|
Packit Service |
a1bd4f |
fdp->destroy_notify = &fdp_destroyed;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
success = _con_curl_check_connectivity(cb_data->concheck.curl_mhandle, fd, action);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (fdp_destroyed) {
|
|
Packit Service |
a1bd4f |
/* hups. fdp got invalidated during _con_curl_check_connectivity(). That's fine,
|
|
Packit Service |
a1bd4f |
* just don't touch it. */
|
|
Packit Service |
a1bd4f |
} else {
|
|
Packit Service |
a1bd4f |
nm_assert(fdp->destroy_notify == &fdp_destroyed);
|
|
Packit Service |
a1bd4f |
fdp->destroy_notify = NULL;
|
|
Packit Service |
a1bd4f |
if (!success)
|
|
Packit Service |
a1bd4f |
nm_clear_g_source_inst(&fdp->source);
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
_complete_queued(cb_data->self);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
return G_SOURCE_CONTINUE;
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static int
|
|
Packit Service |
a1bd4f |
multi_socket_cb(CURL *e_handle, curl_socket_t fd, int what, void *userdata, void *socketp)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
NMConnectivityCheckHandle *cb_data = userdata;
|
|
Packit Service |
a1bd4f |
ConCurlSockData * fdp = socketp;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
(void) _NM_ENSURE_TYPE(int, fd);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (what == CURL_POLL_REMOVE) {
|
|
Packit Service |
a1bd4f |
if (fdp) {
|
|
Packit Service |
a1bd4f |
if (fdp->destroy_notify)
|
|
Packit Service |
a1bd4f |
*fdp->destroy_notify = TRUE;
|
|
Packit Service |
a1bd4f |
nm_clear_g_source_inst(&fdp->source);
|
|
Packit Service |
a1bd4f |
curl_multi_assign(cb_data->concheck.curl_mhandle, fd, NULL);
|
|
Packit Service |
a1bd4f |
g_slice_free(ConCurlSockData, fdp);
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
} else {
|
|
Packit Service |
a1bd4f |
GIOCondition condition;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (!fdp) {
|
|
Packit Service |
a1bd4f |
fdp = g_slice_new(ConCurlSockData);
|
|
Packit Service |
a1bd4f |
*fdp = (ConCurlSockData){
|
|
Packit Service |
a1bd4f |
.cb_data = cb_data,
|
|
Packit Service |
a1bd4f |
};
|
|
Packit Service |
a1bd4f |
curl_multi_assign(cb_data->concheck.curl_mhandle, fd, fdp);
|
|
Packit Service |
a1bd4f |
} else
|
|
Packit Service |
a1bd4f |
nm_clear_g_source_inst(&fdp->source);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (what == CURL_POLL_IN)
|
|
Packit Service |
a1bd4f |
condition = G_IO_IN;
|
|
Packit Service |
a1bd4f |
else if (what == CURL_POLL_OUT)
|
|
Packit Service |
a1bd4f |
condition = G_IO_OUT;
|
|
Packit Service |
a1bd4f |
else if (what == CURL_POLL_INOUT)
|
|
Packit Service |
a1bd4f |
condition = G_IO_IN | G_IO_OUT;
|
|
Packit Service |
a1bd4f |
else
|
|
Packit Service |
a1bd4f |
condition = 0;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (condition) {
|
|
Packit Service |
a1bd4f |
fdp->source = nm_g_unix_fd_source_new(fd,
|
|
Packit Service |
a1bd4f |
condition,
|
|
Packit Service |
a1bd4f |
G_PRIORITY_DEFAULT,
|
|
Packit Service |
a1bd4f |
_con_curl_socketevent_cb,
|
|
Packit Service |
a1bd4f |
fdp,
|
|
Packit Service |
a1bd4f |
NULL);
|
|
Packit Service |
a1bd4f |
g_source_attach(fdp->source, NULL);
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
return CURLM_OK;
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static size_t
|
|
Packit Service |
a1bd4f |
easy_header_cb(char *buffer, size_t size, size_t nitems, void *userdata)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
NMConnectivityCheckHandle *cb_data = userdata;
|
|
Packit Service |
a1bd4f |
size_t len = size * nitems;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (cb_data->completed_state != NM_CONNECTIVITY_UNKNOWN) {
|
|
Packit Service |
a1bd4f |
/* already completed. */
|
|
Packit Service |
a1bd4f |
return 0;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (len >= sizeof(HEADER_STATUS_ONLINE) - 1
|
|
Packit Service |
a1bd4f |
&& !g_ascii_strncasecmp(buffer, HEADER_STATUS_ONLINE, sizeof(HEADER_STATUS_ONLINE) - 1)) {
|
|
Packit Service |
a1bd4f |
cb_data_queue_completed(cb_data, NM_CONNECTIVITY_FULL, "status header found", NULL);
|
|
Packit Service |
a1bd4f |
return 0;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
return len;
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static size_t
|
|
Packit Service |
a1bd4f |
easy_write_cb(void *buffer, size_t size, size_t nmemb, void *userdata)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
NMConnectivityCheckHandle *cb_data = userdata;
|
|
Packit Service |
a1bd4f |
size_t len = size * nmemb;
|
|
Packit Service |
a1bd4f |
size_t response_len;
|
|
Packit Service |
a1bd4f |
size_t check_len;
|
|
Packit Service |
a1bd4f |
const char * response;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (cb_data->completed_state != NM_CONNECTIVITY_UNKNOWN) {
|
|
Packit Service |
a1bd4f |
/* already completed. */
|
|
Packit Service |
a1bd4f |
return 0;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (len == 0) {
|
|
Packit Service |
a1bd4f |
/* no data. That can happen, it's fine. */
|
|
Packit Service |
a1bd4f |
return len;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
response = _con_config_get_response(cb_data->concheck.con_config);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (response[0] == '\0') {
|
|
Packit Service |
a1bd4f |
/* no response expected. We are however graceful and accept any
|
|
Packit Service |
a1bd4f |
* extra response that we might receive. We determine the empty
|
|
Packit Service |
a1bd4f |
* response based on the status code 204.
|
|
Packit Service |
a1bd4f |
*
|
|
Packit Service |
a1bd4f |
* Continue receiving... */
|
|
Packit Service |
a1bd4f |
cb_data->concheck.response_good_cnt += len;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (cb_data->concheck.response_good_cnt > (gsize)(100 * 1024)) {
|
|
Packit Service |
a1bd4f |
/* we expect an empty response. We accept either
|
|
Packit Service |
a1bd4f |
* 1) status code 204 and any response
|
|
Packit Service |
a1bd4f |
* 2) status code 200 and an empty response.
|
|
Packit Service |
a1bd4f |
*
|
|
Packit Service |
a1bd4f |
* Here, we want to continue receiving data, to see whether we have
|
|
Packit Service |
a1bd4f |
* case 1). Arguably, the server shouldn't send us 204 with a non-empty
|
|
Packit Service |
a1bd4f |
* response, but we accept that also with a non-empty response, so
|
|
Packit Service |
a1bd4f |
* keep receiving.
|
|
Packit Service |
a1bd4f |
*
|
|
Packit Service |
a1bd4f |
* However, if we get an excessive amount of data, we put a stop on it
|
|
Packit Service |
a1bd4f |
* and fail. */
|
|
Packit Service |
a1bd4f |
cb_data_queue_completed(cb_data,
|
|
Packit Service |
a1bd4f |
NM_CONNECTIVITY_PORTAL,
|
|
Packit Service |
a1bd4f |
"unexpected non-empty response",
|
|
Packit Service |
a1bd4f |
NULL);
|
|
Packit Service |
a1bd4f |
return 0;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
return len;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
nm_assert(cb_data->concheck.response_good_cnt < strlen(response));
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
response_len = strlen(response);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
check_len = NM_MIN(len, response_len - cb_data->concheck.response_good_cnt);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (strncmp(&response[cb_data->concheck.response_good_cnt], buffer, check_len) != 0) {
|
|
Packit Service |
a1bd4f |
cb_data_queue_completed(cb_data, NM_CONNECTIVITY_PORTAL, "unexpected response", NULL);
|
|
Packit Service |
a1bd4f |
return 0;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
cb_data->concheck.response_good_cnt += len;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (cb_data->concheck.response_good_cnt >= response_len) {
|
|
Packit Service |
a1bd4f |
/* We already have enough data, and it matched. */
|
|
Packit Service |
a1bd4f |
cb_data_queue_completed(cb_data, NM_CONNECTIVITY_FULL, "expected response", NULL);
|
|
Packit Service |
a1bd4f |
return 0;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
return len;
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static gboolean
|
|
Packit Service |
a1bd4f |
_timeout_cb(gpointer user_data)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
NMConnectivityCheckHandle *cb_data = user_data;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
nm_assert(NM_IS_CONNECTIVITY(cb_data->self));
|
|
Packit Service |
a1bd4f |
nm_assert(c_list_contains(&NM_CONNECTIVITY_GET_PRIVATE(cb_data->self)->handles_lst_head,
|
|
Packit Service |
a1bd4f |
&cb_data->handles_lst));
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
cb_data_complete(cb_data, NM_CONNECTIVITY_LIMITED, "timeout");
|
|
Packit Service |
a1bd4f |
return G_SOURCE_REMOVE;
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
#endif
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static gboolean
|
|
Packit Service |
a1bd4f |
_idle_cb(gpointer user_data)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
NMConnectivityCheckHandle *cb_data = user_data;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
nm_assert(NM_IS_CONNECTIVITY(cb_data->self));
|
|
Packit Service |
a1bd4f |
nm_assert(c_list_contains(&NM_CONNECTIVITY_GET_PRIVATE(cb_data->self)->handles_lst_head,
|
|
Packit Service |
a1bd4f |
&cb_data->handles_lst));
|
|
Packit Service |
a1bd4f |
nm_assert(cb_data->completed_reason);
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
cb_data->timeout_id = 0;
|
|
Packit Service |
a1bd4f |
cb_data_complete(cb_data, cb_data->completed_state, cb_data->completed_reason);
|
|
Packit Service |
a1bd4f |
return G_SOURCE_REMOVE;
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
#if WITH_CONCHECK
|
|
Packit |
5756e2 |
static void
|
|
Packit Service |
a1bd4f |
do_curl_request(NMConnectivityCheckHandle *cb_data)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
CURLM *mhandle;
|
|
Packit Service |
a1bd4f |
CURL * ehandle;
|
|
Packit Service |
a1bd4f |
long resolve;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
mhandle = curl_multi_init();
|
|
Packit Service |
a1bd4f |
if (!mhandle) {
|
|
Packit Service |
a1bd4f |
cb_data_complete(cb_data, NM_CONNECTIVITY_ERROR, "curl error");
|
|
Packit Service |
a1bd4f |
return;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
ehandle = curl_easy_init();
|
|
Packit Service |
a1bd4f |
if (!ehandle) {
|
|
Packit Service |
a1bd4f |
curl_multi_cleanup(mhandle);
|
|
Packit Service |
a1bd4f |
cb_data_complete(cb_data, NM_CONNECTIVITY_ERROR, "curl error");
|
|
Packit Service |
a1bd4f |
return;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
cb_data->concheck.curl_mhandle = mhandle;
|
|
Packit Service |
a1bd4f |
cb_data->concheck.curl_ehandle = ehandle;
|
|
Packit Service |
a1bd4f |
cb_data->concheck.request_headers = curl_slist_append(NULL, "Connection: close");
|
|
Packit Service |
a1bd4f |
cb_data->timeout_id = g_timeout_add_seconds(20, _timeout_cb, cb_data);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
curl_multi_setopt(mhandle, CURLMOPT_SOCKETFUNCTION, multi_socket_cb);
|
|
Packit Service |
a1bd4f |
curl_multi_setopt(mhandle, CURLMOPT_SOCKETDATA, cb_data);
|
|
Packit Service |
a1bd4f |
curl_multi_setopt(mhandle, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
|
|
Packit Service |
a1bd4f |
curl_multi_setopt(mhandle, CURLMOPT_TIMERDATA, cb_data);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
switch (cb_data->addr_family) {
|
|
Packit Service |
a1bd4f |
case AF_INET:
|
|
Packit Service |
a1bd4f |
resolve = CURL_IPRESOLVE_V4;
|
|
Packit Service |
a1bd4f |
break;
|
|
Packit Service |
a1bd4f |
case AF_INET6:
|
|
Packit Service |
a1bd4f |
resolve = CURL_IPRESOLVE_V6;
|
|
Packit Service |
a1bd4f |
break;
|
|
Packit Service |
a1bd4f |
case AF_UNSPEC:
|
|
Packit Service |
a1bd4f |
resolve = CURL_IPRESOLVE_WHATEVER;
|
|
Packit Service |
a1bd4f |
break;
|
|
Packit Service |
a1bd4f |
default:
|
|
Packit Service |
a1bd4f |
resolve = CURL_IPRESOLVE_WHATEVER;
|
|
Packit Service |
a1bd4f |
g_warn_if_reached();
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
curl_easy_setopt(ehandle, CURLOPT_URL, cb_data->concheck.con_config->uri);
|
|
Packit Service |
a1bd4f |
curl_easy_setopt(ehandle, CURLOPT_WRITEFUNCTION, easy_write_cb);
|
|
Packit Service |
a1bd4f |
curl_easy_setopt(ehandle, CURLOPT_WRITEDATA, cb_data);
|
|
Packit Service |
a1bd4f |
curl_easy_setopt(ehandle, CURLOPT_HEADERFUNCTION, easy_header_cb);
|
|
Packit Service |
a1bd4f |
curl_easy_setopt(ehandle, CURLOPT_HEADERDATA, cb_data);
|
|
Packit Service |
a1bd4f |
curl_easy_setopt(ehandle, CURLOPT_PRIVATE, cb_data);
|
|
Packit Service |
a1bd4f |
curl_easy_setopt(ehandle, CURLOPT_HTTPHEADER, cb_data->concheck.request_headers);
|
|
Packit Service |
a1bd4f |
curl_easy_setopt(ehandle, CURLOPT_INTERFACE, cb_data->ifspec);
|
|
Packit Service |
a1bd4f |
curl_easy_setopt(ehandle, CURLOPT_RESOLVE, cb_data->concheck.hosts);
|
|
Packit Service |
a1bd4f |
curl_easy_setopt(ehandle, CURLOPT_IPRESOLVE, resolve);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
curl_multi_add_handle(mhandle, ehandle);
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static void
|
|
Packit Service |
a1bd4f |
resolve_cb(GObject *object, GAsyncResult *res, gpointer user_data)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
NMConnectivityCheckHandle *cb_data;
|
|
Packit Service |
a1bd4f |
gs_unref_variant GVariant *result = NULL;
|
|
Packit Service |
a1bd4f |
gs_unref_variant GVariant *addresses = NULL;
|
|
Packit Service |
a1bd4f |
gsize no_addresses;
|
|
Packit Service |
a1bd4f |
int ifindex;
|
|
Packit Service |
a1bd4f |
int addr_family;
|
|
Packit Service |
a1bd4f |
gsize len = 0;
|
|
Packit Service |
a1bd4f |
gsize i;
|
|
Packit Service |
a1bd4f |
gs_free_error GError *error = NULL;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
result = g_dbus_connection_call_finish(G_DBUS_CONNECTION(object), res, &error);
|
|
Packit Service |
a1bd4f |
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
Packit Service |
a1bd4f |
return;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
cb_data = user_data;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
g_clear_object(&cb_data->concheck.resolve_cancellable);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (!result) {
|
|
Packit Service |
a1bd4f |
/* Never mind. Just let do curl do its own resolving. */
|
|
Packit Service |
a1bd4f |
_LOG2D("can't resolve a name via systemd-resolved: %s", error->message);
|
|
Packit Service |
a1bd4f |
do_curl_request(cb_data);
|
|
Packit Service |
a1bd4f |
return;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
addresses = g_variant_get_child_value(result, 0);
|
|
Packit Service |
a1bd4f |
no_addresses = g_variant_n_children(addresses);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
for (i = 0; i < no_addresses; i++) {
|
|
Packit Service |
a1bd4f |
gs_unref_variant GVariant *address = NULL;
|
|
Packit Service |
a1bd4f |
char str_addr[NM_UTILS_INET_ADDRSTRLEN];
|
|
Packit Service |
a1bd4f |
gs_free char * host_entry = NULL;
|
|
Packit Service |
a1bd4f |
const guchar * address_buf;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
g_variant_get_child(addresses, i, "(ii@ay)", &ifindex, &addr_family, &address);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (cb_data->addr_family != AF_UNSPEC && cb_data->addr_family != addr_family)
|
|
Packit Service |
a1bd4f |
continue;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
address_buf = g_variant_get_fixed_array(address, &len, 1);
|
|
Packit Service |
a1bd4f |
if ((addr_family == AF_INET && len != sizeof(struct in_addr))
|
|
Packit Service |
a1bd4f |
|| (addr_family == AF_INET6 && len != sizeof(struct in6_addr)))
|
|
Packit Service |
a1bd4f |
continue;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
host_entry = g_strdup_printf("%s:%s:%s",
|
|
Packit Service |
a1bd4f |
cb_data->concheck.con_config->host,
|
|
Packit Service |
a1bd4f |
cb_data->concheck.con_config->port ?: "80",
|
|
Packit Service |
a1bd4f |
nm_utils_inet_ntop(addr_family, address_buf, str_addr));
|
|
Packit Service |
a1bd4f |
cb_data->concheck.hosts = curl_slist_append(cb_data->concheck.hosts, host_entry);
|
|
Packit Service |
a1bd4f |
_LOG2T("adding '%s' to curl resolve list", host_entry);
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
do_curl_request(cb_data);
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
#endif
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
#define SD_RESOLVED_DNS ((guint64)(1LL << 0))
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static NMConnectivityState
|
|
Packit Service |
a1bd4f |
check_platform_config(NMConnectivity *self,
|
|
Packit Service |
a1bd4f |
NMPlatform * platform,
|
|
Packit Service |
a1bd4f |
int ifindex,
|
|
Packit Service |
a1bd4f |
int addr_family,
|
|
Packit Service |
a1bd4f |
const char ** reason)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
const NMDedupMultiHeadEntry *addresses;
|
|
Packit Service |
a1bd4f |
const NMDedupMultiHeadEntry *routes;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (!nm_platform_link_is_connected(platform, ifindex)) {
|
|
Packit Service |
a1bd4f |
NM_SET_OUT(reason, "no carrier");
|
|
Packit Service |
a1bd4f |
return NM_CONNECTIVITY_NONE;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
addresses = nm_platform_lookup_object(platform,
|
|
Packit Service |
a1bd4f |
addr_family == AF_INET ? NMP_OBJECT_TYPE_IP4_ADDRESS
|
|
Packit Service |
a1bd4f |
: NMP_OBJECT_TYPE_IP6_ADDRESS,
|
|
Packit Service |
a1bd4f |
ifindex);
|
|
Packit Service |
a1bd4f |
if (!addresses || addresses->len == 0) {
|
|
Packit Service |
a1bd4f |
NM_SET_OUT(reason, "no IP address configured");
|
|
Packit Service |
a1bd4f |
return NM_CONNECTIVITY_NONE;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
routes = nm_platform_lookup_object(platform,
|
|
Packit Service |
a1bd4f |
addr_family == AF_INET ? NMP_OBJECT_TYPE_IP4_ROUTE
|
|
Packit Service |
a1bd4f |
: NMP_OBJECT_TYPE_IP6_ROUTE,
|
|
Packit Service |
a1bd4f |
ifindex);
|
|
Packit Service |
a1bd4f |
if (!routes || routes->len == 0) {
|
|
Packit Service |
a1bd4f |
NM_SET_OUT(reason, "no IP route configured");
|
|
Packit Service |
a1bd4f |
return NM_CONNECTIVITY_NONE;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
switch (addr_family) {
|
|
Packit Service |
a1bd4f |
case AF_INET:
|
|
Packit Service |
a1bd4f |
{
|
|
Packit Service |
a1bd4f |
const NMPlatformIP4Route *route;
|
|
Packit Service |
a1bd4f |
gboolean found_global = FALSE;
|
|
Packit Service |
a1bd4f |
NMDedupMultiIter iter;
|
|
Packit Service |
a1bd4f |
const NMPObject * plobj;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
/* For IPv4 also require a route with global scope. */
|
|
Packit Service |
a1bd4f |
nmp_cache_iter_for_each (&iter, routes, &plobj) {
|
|
Packit Service |
a1bd4f |
route = NMP_OBJECT_CAST_IP4_ROUTE(plobj);
|
|
Packit Service |
a1bd4f |
if (nm_platform_route_scope_inv(route->scope_inv) == RT_SCOPE_UNIVERSE) {
|
|
Packit Service |
a1bd4f |
found_global = TRUE;
|
|
Packit Service |
a1bd4f |
break;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (!found_global) {
|
|
Packit Service |
a1bd4f |
NM_SET_OUT(reason, "no global route configured");
|
|
Packit Service |
a1bd4f |
return NM_CONNECTIVITY_LIMITED;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
break;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
case AF_INET6:
|
|
Packit Service |
a1bd4f |
/* Route scopes aren't meaningful for IPv6 so any route is fine. */
|
|
Packit Service |
a1bd4f |
break;
|
|
Packit Service |
a1bd4f |
default:
|
|
Packit Service |
a1bd4f |
g_return_val_if_reached(FALSE);
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
NM_SET_OUT(reason, NULL);
|
|
Packit Service |
a1bd4f |
return NM_CONNECTIVITY_UNKNOWN;
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
NMConnectivityCheckHandle *
|
|
Packit Service |
a1bd4f |
nm_connectivity_check_start(NMConnectivity * self,
|
|
Packit Service |
a1bd4f |
int addr_family,
|
|
Packit Service |
a1bd4f |
NMPlatform * platform,
|
|
Packit Service |
a1bd4f |
int ifindex,
|
|
Packit Service |
a1bd4f |
const char * iface,
|
|
Packit Service |
a1bd4f |
NMConnectivityCheckCallback callback,
|
|
Packit Service |
a1bd4f |
gpointer user_data)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
NMConnectivityPrivate * priv;
|
|
Packit Service |
a1bd4f |
NMConnectivityCheckHandle *cb_data;
|
|
Packit Service |
a1bd4f |
static guint64 request_counter = 0;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
g_return_val_if_fail(NM_IS_CONNECTIVITY(self), NULL);
|
|
Packit Service |
a1bd4f |
g_return_val_if_fail(callback, NULL);
|
|
Packit Service |
a1bd4f |
nm_assert(!platform || NM_IS_PLATFORM(platform));
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
priv = NM_CONNECTIVITY_GET_PRIVATE(self);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
cb_data = g_slice_new0(NMConnectivityCheckHandle);
|
|
Packit Service |
a1bd4f |
cb_data->self = self;
|
|
Packit Service |
a1bd4f |
cb_data->request_counter = ++request_counter;
|
|
Packit Service |
a1bd4f |
c_list_link_tail(&priv->handles_lst_head, &cb_data->handles_lst);
|
|
Packit Service |
a1bd4f |
cb_data->callback = callback;
|
|
Packit Service |
a1bd4f |
cb_data->user_data = user_data;
|
|
Packit Service |
a1bd4f |
cb_data->completed_state = NM_CONNECTIVITY_UNKNOWN;
|
|
Packit Service |
a1bd4f |
cb_data->addr_family = addr_family;
|
|
Packit Service |
a1bd4f |
if (iface)
|
|
Packit Service |
a1bd4f |
cb_data->ifspec = g_strdup_printf("if!%s", iface);
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
#if WITH_CONCHECK
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
cb_data->concheck.con_config = _con_config_ref(priv->con_config);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (iface && ifindex > 0 && priv->enabled && priv->uri_valid) {
|
|
Packit Service |
a1bd4f |
gboolean has_systemd_resolved;
|
|
Packit Service |
a1bd4f |
NMConnectivityState state;
|
|
Packit Service |
a1bd4f |
const char * reason;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
cb_data->concheck.ch_ifindex = ifindex;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (platform) {
|
|
Packit Service |
a1bd4f |
state = check_platform_config(self, platform, ifindex, addr_family, &reason);
|
|
Packit Service |
a1bd4f |
nm_assert((state == NM_CONNECTIVITY_UNKNOWN) == !reason);
|
|
Packit Service |
a1bd4f |
if (state != NM_CONNECTIVITY_UNKNOWN) {
|
|
Packit Service |
a1bd4f |
_LOG2D("skip connectivity check due to %s", reason);
|
|
Packit Service |
a1bd4f |
cb_data->completed_state = state;
|
|
Packit Service |
a1bd4f |
cb_data->completed_reason = reason;
|
|
Packit Service |
a1bd4f |
cb_data->timeout_id = g_idle_add(_idle_cb, cb_data);
|
|
Packit Service |
a1bd4f |
return cb_data;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
/* note that we pick up support for systemd-resolved right away when we need it.
|
|
Packit Service |
a1bd4f |
* We don't need to remember the setting, because we can (cheaply) check anew
|
|
Packit Service |
a1bd4f |
* on each request.
|
|
Packit Service |
a1bd4f |
*
|
|
Packit Service |
a1bd4f |
* Yes, this makes NMConnectivity singleton dependent on NMDnsManager singleton.
|
|
Packit Service |
a1bd4f |
* Well, not really: it makes connectivity-check-start dependent on NMDnsManager
|
|
Packit Service |
a1bd4f |
* which merely means, not to start a connectivity check, late during shutdown.
|
|
Packit Service |
a1bd4f |
*
|
|
Packit Service |
a1bd4f |
* NMDnsSystemdResolved tries to D-Bus activate systemd-resolved only once,
|
|
Packit Service |
a1bd4f |
* to not spam syslog with failures messages from dbus-daemon.
|
|
Packit Service |
a1bd4f |
* Note that unless NMDnsSystemdResolved tried and failed to start systemd-resolved,
|
|
Packit Service |
a1bd4f |
* it guesses that systemd-resolved is activatable and returns %TRUE here. That
|
|
Packit Service |
a1bd4f |
* means, while NMDnsSystemdResolved would not try to D-Bus activate systemd-resolved
|
|
Packit Service |
a1bd4f |
* more than once, NMConnectivity might -- until NMDnsSystemdResolved tried itself
|
|
Packit Service |
a1bd4f |
* and noticed that systemd-resolved is not available.
|
|
Packit Service |
a1bd4f |
* This is relatively cumbersome to avoid, because we would have to go through
|
|
Packit Service |
a1bd4f |
* NMDnsSystemdResolved trying to asynchronously start the service, to ensure there
|
|
Packit Service |
a1bd4f |
* is only one attempt to start the service. */
|
|
Packit Service |
a1bd4f |
has_systemd_resolved = nm_dns_manager_has_systemd_resolved(nm_dns_manager_get());
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (has_systemd_resolved) {
|
|
Packit Service |
a1bd4f |
GDBusConnection *dbus_connection;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
dbus_connection = NM_MAIN_DBUS_CONNECTION_GET;
|
|
Packit Service |
a1bd4f |
if (!dbus_connection) {
|
|
Packit Service |
a1bd4f |
/* we have no D-Bus connection? That might happen in configure and quit mode.
|
|
Packit Service |
a1bd4f |
*
|
|
Packit Service |
a1bd4f |
* Anyway, something is very odd, just fail connectivity check. */
|
|
Packit Service |
a1bd4f |
_LOG2D("start fake request (fail due to no D-Bus connection)");
|
|
Packit Service |
a1bd4f |
cb_data->completed_state = NM_CONNECTIVITY_ERROR;
|
|
Packit Service |
a1bd4f |
cb_data->completed_reason = "no D-Bus connection";
|
|
Packit Service |
a1bd4f |
cb_data->timeout_id = g_idle_add(_idle_cb, cb_data);
|
|
Packit Service |
a1bd4f |
return cb_data;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
cb_data->concheck.resolve_cancellable = g_cancellable_new();
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
g_dbus_connection_call(dbus_connection,
|
|
Packit Service |
a1bd4f |
"org.freedesktop.resolve1",
|
|
Packit Service |
a1bd4f |
"/org/freedesktop/resolve1",
|
|
Packit Service |
a1bd4f |
"org.freedesktop.resolve1.Manager",
|
|
Packit Service |
a1bd4f |
"ResolveHostname",
|
|
Packit Service |
a1bd4f |
g_variant_new("(isit)",
|
|
Packit Service |
a1bd4f |
(gint32) cb_data->concheck.ch_ifindex,
|
|
Packit Service |
a1bd4f |
cb_data->concheck.con_config->host,
|
|
Packit Service |
a1bd4f |
(gint32) cb_data->addr_family,
|
|
Packit Service |
a1bd4f |
SD_RESOLVED_DNS),
|
|
Packit Service |
a1bd4f |
G_VARIANT_TYPE("(a(iiay)st)"),
|
|
Packit Service |
a1bd4f |
G_DBUS_CALL_FLAGS_NONE,
|
|
Packit Service |
a1bd4f |
-1,
|
|
Packit Service |
a1bd4f |
cb_data->concheck.resolve_cancellable,
|
|
Packit Service |
a1bd4f |
resolve_cb,
|
|
Packit Service |
a1bd4f |
cb_data);
|
|
Packit Service |
a1bd4f |
_LOG2D("start request to '%s' (try resolving '%s' using systemd-resolved)",
|
|
Packit Service |
a1bd4f |
cb_data->concheck.con_config->uri,
|
|
Packit Service |
a1bd4f |
cb_data->concheck.con_config->host);
|
|
Packit Service |
a1bd4f |
} else {
|
|
Packit Service |
a1bd4f |
_LOG2D("start request to '%s' (systemd-resolved not available)",
|
|
Packit Service |
a1bd4f |
cb_data->concheck.con_config->uri);
|
|
Packit Service |
a1bd4f |
do_curl_request(cb_data);
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
return cb_data;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit |
5756e2 |
#endif
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
if (!cb_data->ifspec) {
|
|
Packit Service |
a1bd4f |
cb_data->completed_state = NM_CONNECTIVITY_ERROR;
|
|
Packit Service |
a1bd4f |
cb_data->completed_reason = "missing interface";
|
|
Packit Service |
a1bd4f |
} else {
|
|
Packit Service |
a1bd4f |
cb_data->completed_state = NM_CONNECTIVITY_FAKE;
|
|
Packit Service |
a1bd4f |
cb_data->completed_reason = "fake result";
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
_LOG2D("start fake request (%s)", cb_data->completed_reason);
|
|
Packit Service |
a1bd4f |
cb_data->timeout_id = g_idle_add(_idle_cb, cb_data);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
return cb_data;
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
void
|
|
Packit Service |
a1bd4f |
nm_connectivity_check_cancel(NMConnectivityCheckHandle *cb_data)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
g_return_if_fail(cb_data);
|
|
Packit Service |
a1bd4f |
g_return_if_fail(NM_IS_CONNECTIVITY(cb_data->self));
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
nm_assert(
|
|
Packit Service |
a1bd4f |
c_list_contains(&NM_CONNECTIVITY_GET_PRIVATE(cb_data->self)->handles_lst_head,
|
|
Packit Service |
a1bd4f |
&cb_data->handles_lst)
|
|
Packit Service |
a1bd4f |
|| c_list_contains(&NM_CONNECTIVITY_GET_PRIVATE(cb_data->self)->completed_handles_lst_head,
|
|
Packit Service |
a1bd4f |
&cb_data->handles_lst));
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
cb_data_complete(cb_data, NM_CONNECTIVITY_CANCELLED, "cancelled");
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
/*****************************************************************************/
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
gboolean
|
|
Packit Service |
a1bd4f |
nm_connectivity_check_enabled(NMConnectivity *self)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
g_return_val_if_fail(NM_IS_CONNECTIVITY(self), FALSE);
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
return NM_CONNECTIVITY_GET_PRIVATE(self)->enabled;
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
/*****************************************************************************/
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
guint
|
|
Packit Service |
a1bd4f |
nm_connectivity_get_interval(NMConnectivity *self)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
return nm_connectivity_check_enabled(self) ? NM_CONNECTIVITY_GET_PRIVATE(self)->interval : 0;
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static gboolean
|
|
Packit Service |
a1bd4f |
host_and_port_from_uri(const char *uri, char **host, char **port)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
const char *p = uri;
|
|
Packit Service |
a1bd4f |
const char *host_begin = NULL;
|
|
Packit Service |
a1bd4f |
size_t host_len = 0;
|
|
Packit Service |
a1bd4f |
const char *port_begin = NULL;
|
|
Packit Service |
a1bd4f |
size_t port_len = 0;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
/* scheme */
|
|
Packit Service |
a1bd4f |
while (*p != ':' && *p != '/') {
|
|
Packit Service |
a1bd4f |
if (!*p++)
|
|
Packit Service |
a1bd4f |
return FALSE;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
/* :// */
|
|
Packit Service |
a1bd4f |
if (*p++ != ':')
|
|
Packit Service |
a1bd4f |
return FALSE;
|
|
Packit Service |
a1bd4f |
if (*p++ != '/')
|
|
Packit Service |
a1bd4f |
return FALSE;
|
|
Packit Service |
a1bd4f |
if (*p++ != '/')
|
|
Packit Service |
a1bd4f |
return FALSE;
|
|
Packit Service |
a1bd4f |
/* host */
|
|
Packit Service |
a1bd4f |
if (*p == '[')
|
|
Packit Service |
a1bd4f |
return FALSE;
|
|
Packit Service |
a1bd4f |
host_begin = p;
|
|
Packit Service |
a1bd4f |
while (*p && *p != ':' && *p != '/') {
|
|
Packit Service |
a1bd4f |
host_len++;
|
|
Packit Service |
a1bd4f |
p++;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
if (host_len == 0)
|
|
Packit Service |
a1bd4f |
return FALSE;
|
|
Packit Service |
a1bd4f |
*host = g_strndup(host_begin, host_len);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
/* port */
|
|
Packit Service |
a1bd4f |
if (*p++ == ':') {
|
|
Packit Service |
a1bd4f |
port_begin = p;
|
|
Packit Service |
a1bd4f |
while (*p && *p != '/') {
|
|
Packit Service |
a1bd4f |
port_len++;
|
|
Packit Service |
a1bd4f |
p++;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
if (port_len)
|
|
Packit Service |
a1bd4f |
*port = g_strndup(port_begin, port_len);
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
return TRUE;
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static void
|
|
Packit Service |
a1bd4f |
update_config(NMConnectivity *self, NMConfigData *config_data)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE(self);
|
|
Packit Service |
a1bd4f |
guint interval;
|
|
Packit Service |
a1bd4f |
gboolean enabled;
|
|
Packit Service |
a1bd4f |
gboolean changed = FALSE;
|
|
Packit Service |
a1bd4f |
const char * cur_uri = priv->con_config ? priv->con_config->uri : NULL;
|
|
Packit Service |
a1bd4f |
const char * cur_response = priv->con_config ? priv->con_config->response : NULL;
|
|
Packit Service |
a1bd4f |
const char * new_response;
|
|
Packit Service |
a1bd4f |
const char * new_uri;
|
|
Packit Service |
a1bd4f |
gboolean new_uri_valid = priv->uri_valid;
|
|
Packit Service |
a1bd4f |
gboolean new_host_port = FALSE;
|
|
Packit Service |
a1bd4f |
gs_free char * new_host = NULL;
|
|
Packit Service |
a1bd4f |
gs_free char * new_port = NULL;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
new_uri = nm_config_data_get_connectivity_uri(config_data);
|
|
Packit Service |
a1bd4f |
if (!nm_streq0(new_uri, cur_uri)) {
|
|
Packit Service |
a1bd4f |
new_uri_valid = (new_uri && *new_uri);
|
|
Packit Service |
a1bd4f |
if (new_uri_valid) {
|
|
Packit Service |
a1bd4f |
gs_free char *scheme = g_uri_parse_scheme(new_uri);
|
|
Packit Service |
a1bd4f |
gboolean is_https = FALSE;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (!scheme) {
|
|
Packit Service |
a1bd4f |
_LOGE("invalid URI '%s' for connectivity check.", new_uri);
|
|
Packit Service |
a1bd4f |
new_uri_valid = FALSE;
|
|
Packit Service |
a1bd4f |
} else if (g_ascii_strcasecmp(scheme, "https") == 0) {
|
|
Packit Service |
a1bd4f |
_LOGW("use of HTTPS for connectivity checking is not reliable and is discouraged "
|
|
Packit Service |
a1bd4f |
"(URI: %s)",
|
|
Packit Service |
a1bd4f |
new_uri);
|
|
Packit Service |
a1bd4f |
is_https = TRUE;
|
|
Packit Service |
a1bd4f |
} else if (g_ascii_strcasecmp(scheme, "http") != 0) {
|
|
Packit Service |
a1bd4f |
_LOGE("scheme of '%s' uri doesn't use a scheme that is allowed for connectivity "
|
|
Packit Service |
a1bd4f |
"check.",
|
|
Packit Service |
a1bd4f |
new_uri);
|
|
Packit Service |
a1bd4f |
new_uri_valid = FALSE;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
if (new_uri_valid) {
|
|
Packit Service |
a1bd4f |
new_host_port = TRUE;
|
|
Packit Service |
a1bd4f |
if (!host_and_port_from_uri(new_uri, &new_host, &new_port)) {
|
|
Packit Service |
a1bd4f |
_LOGE("cannot parse host and port from '%s'", new_uri);
|
|
Packit Service |
a1bd4f |
new_uri_valid = FALSE;
|
|
Packit Service |
a1bd4f |
} else if (!new_port && is_https)
|
|
Packit Service |
a1bd4f |
new_port = g_strdup("443");
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (new_uri_valid || priv->uri_valid != new_uri_valid)
|
|
Packit Service |
a1bd4f |
changed = TRUE;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
new_response = nm_config_data_get_connectivity_response(config_data);
|
|
Packit Service |
a1bd4f |
if (!nm_streq0(new_response, cur_response))
|
|
Packit Service |
a1bd4f |
changed = TRUE;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
if (!priv->con_config || !nm_streq0(new_uri, priv->con_config->uri)
|
|
Packit Service |
a1bd4f |
|| !nm_streq0(new_response, priv->con_config->response)) {
|
|
Packit Service |
a1bd4f |
if (!new_host_port) {
|
|
Packit Service |
a1bd4f |
new_host = priv->con_config ? g_strdup(priv->con_config->host) : NULL;
|
|
Packit Service |
a1bd4f |
new_port = priv->con_config ? g_strdup(priv->con_config->port) : NULL;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
_con_config_unref(priv->con_config);
|
|
Packit Service |
a1bd4f |
priv->con_config = g_slice_new(ConConfig);
|
|
Packit Service |
a1bd4f |
*priv->con_config = (ConConfig){
|
|
Packit Service |
a1bd4f |
.ref_count = 1,
|
|
Packit Service |
a1bd4f |
.uri = g_strdup(new_uri),
|
|
Packit Service |
a1bd4f |
.response = g_strdup(new_response),
|
|
Packit Service |
a1bd4f |
.host = g_steal_pointer(&new_host),
|
|
Packit Service |
a1bd4f |
.port = g_steal_pointer(&new_port),
|
|
Packit Service |
a1bd4f |
};
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
priv->uri_valid = new_uri_valid;
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
interval = nm_config_data_get_connectivity_interval(config_data);
|
|
Packit Service |
a1bd4f |
interval = MIN(interval, (7 * 24 * 3600));
|
|
Packit Service |
a1bd4f |
if (priv->interval != interval) {
|
|
Packit Service |
a1bd4f |
priv->interval = interval;
|
|
Packit Service |
a1bd4f |
changed = TRUE;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
enabled = FALSE;
|
|
Packit |
5756e2 |
#if WITH_CONCHECK
|
|
Packit Service |
a1bd4f |
if (priv->uri_valid && priv->interval)
|
|
Packit Service |
a1bd4f |
enabled = nm_config_data_get_connectivity_enabled(config_data);
|
|
Packit |
5756e2 |
#endif
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
if (priv->enabled != enabled) {
|
|
Packit Service |
a1bd4f |
priv->enabled = enabled;
|
|
Packit Service |
a1bd4f |
changed = TRUE;
|
|
Packit Service |
a1bd4f |
}
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
if (changed)
|
|
Packit Service |
a1bd4f |
g_signal_emit(self, signals[CONFIG_CHANGED], 0);
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static void
|
|
Packit Service |
a1bd4f |
config_changed_cb(NMConfig * config,
|
|
Packit Service |
a1bd4f |
NMConfigData * config_data,
|
|
Packit Service |
a1bd4f |
NMConfigChangeFlags changes,
|
|
Packit Service |
a1bd4f |
NMConfigData * old_data,
|
|
Packit Service |
a1bd4f |
NMConnectivity * self)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
update_config(self, config_data);
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
/*****************************************************************************/
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static void
|
|
Packit Service |
a1bd4f |
nm_connectivity_init(NMConnectivity *self)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE(self);
|
|
Packit |
5756e2 |
#if WITH_CONCHECK
|
|
Packit Service |
a1bd4f |
CURLcode ret;
|
|
Packit |
5756e2 |
#endif
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
c_list_init(&priv->handles_lst_head);
|
|
Packit Service |
a1bd4f |
c_list_init(&priv->completed_handles_lst_head);
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
priv->config = g_object_ref(nm_config_get());
|
|
Packit Service |
a1bd4f |
g_signal_connect(G_OBJECT(priv->config),
|
|
Packit Service |
a1bd4f |
NM_CONFIG_SIGNAL_CONFIG_CHANGED,
|
|
Packit Service |
a1bd4f |
G_CALLBACK(config_changed_cb),
|
|
Packit Service |
a1bd4f |
self);
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
#if WITH_CONCHECK
|
|
Packit Service |
a1bd4f |
ret = curl_global_init(CURL_GLOBAL_ALL);
|
|
Packit Service |
a1bd4f |
if (ret != CURLE_OK) {
|
|
Packit Service |
a1bd4f |
_LOGE("unable to init cURL, connectivity check will not work: (%d) %s",
|
|
Packit Service |
a1bd4f |
ret,
|
|
Packit Service |
a1bd4f |
curl_easy_strerror(ret));
|
|
Packit Service |
a1bd4f |
}
|
|
Packit |
5756e2 |
#endif
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
update_config(self, nm_config_get_data(priv->config));
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static void
|
|
Packit Service |
a1bd4f |
dispose(GObject *object)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
NMConnectivity * self = NM_CONNECTIVITY(object);
|
|
Packit Service |
a1bd4f |
NMConnectivityPrivate * priv = NM_CONNECTIVITY_GET_PRIVATE(self);
|
|
Packit Service |
a1bd4f |
NMConnectivityCheckHandle *cb_data;
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
nm_assert(c_list_is_empty(&priv->completed_handles_lst_head));
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
while (
|
|
Packit Service |
a1bd4f |
(cb_data =
|
|
Packit Service |
a1bd4f |
c_list_first_entry(&priv->handles_lst_head, NMConnectivityCheckHandle, handles_lst)))
|
|
Packit Service |
a1bd4f |
cb_data_complete(cb_data, NM_CONNECTIVITY_DISPOSING, "shutting down");
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
nm_clear_pointer(&priv->con_config, _con_config_unref);
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
#if WITH_CONCHECK
|
|
Packit Service |
a1bd4f |
curl_global_cleanup();
|
|
Packit |
5756e2 |
#endif
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
if (priv->config) {
|
|
Packit Service |
a1bd4f |
g_signal_handlers_disconnect_by_func(priv->config, config_changed_cb, self);
|
|
Packit Service |
a1bd4f |
g_clear_object(&priv->config);
|
|
Packit Service |
a1bd4f |
}
|
|
Packit |
5756e2 |
|
|
Packit Service |
a1bd4f |
G_OBJECT_CLASS(nm_connectivity_parent_class)->dispose(object);
|
|
Packit |
5756e2 |
}
|
|
Packit |
5756e2 |
|
|
Packit |
5756e2 |
static void
|
|
Packit Service |
a1bd4f |
nm_connectivity_class_init(NMConnectivityClass *klass)
|
|
Packit |
5756e2 |
{
|
|
Packit Service |
a1bd4f |
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
signals[CONFIG_CHANGED] = g_signal_new(NM_CONNECTIVITY_CONFIG_CHANGED,
|
|
Packit Service |
a1bd4f |
G_OBJECT_CLASS_TYPE(object_class),
|
|
Packit Service |
a1bd4f |
G_SIGNAL_RUN_FIRST,
|
|
Packit Service |
a1bd4f |
0,
|
|
Packit Service |
a1bd4f |
NULL,
|
|
Packit Service |
a1bd4f |
NULL,
|
|
Packit Service |
a1bd4f |
NULL,
|
|
Packit Service |
a1bd4f |
G_TYPE_NONE,
|
|
Packit Service |
a1bd4f |
0);
|
|
Packit Service |
a1bd4f |
|
|
Packit Service |
a1bd4f |
object_class->dispose = dispose;
|
|
Packit |
5756e2 |
}
|