summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrancesco Giudici <fgiudici@redhat.com>2016-04-04 18:23:13 +0200
committerFrancesco Giudici <fgiudici@redhat.com>2016-04-10 14:41:44 +0200
commit4f2c25492d4369fb5916cc114f048bfba4d9dd5a (patch)
tree7ca57b799ebd32b90439a47177253b0dd6927fa3
parente77cda0cbf8eacac23a4b833856fa192a2b2bc63 (diff)
downloadNetworkManager-4f2c25492d4369fb5916cc114f048bfba4d9dd5a.tar.gz
connectivity: add libcurl code for connectivity checking
- should be enabled at compile time - is an alternative to libsoup code
-rw-r--r--src/Makefile.am6
-rw-r--r--src/connectivity/nm-connectivity-curl.c317
-rw-r--r--src/connectivity/nm-connectivity-curl.h44
-rw-r--r--src/nm-connectivity.c4
4 files changed, 371 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 0c67c08e16..72f9be905b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -459,10 +459,16 @@ libNetworkManager_la_SOURCES += \
endif
if WITH_CONCHECK
+if WITH_LIBCURL
+libNetworkManager_la_SOURCES += \
+ connectivity/nm-connectivity-curl.c \
+ connectivity/nm-connectivity-curl.h
+else
libNetworkManager_la_SOURCES += \
connectivity/nm-connectivity-soup.c \
connectivity/nm-connectivity-soup.h
endif
+endif
GLIB_GENERATED = nm-enum-types.h nm-enum-types.c
GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM --fhead '\#include <nm-core-enum-types.h>\n'
diff --git a/src/connectivity/nm-connectivity-curl.c b/src/connectivity/nm-connectivity-curl.c
new file mode 100644
index 0000000000..57578e3e6c
--- /dev/null
+++ b/src/connectivity/nm-connectivity-curl.c
@@ -0,0 +1,317 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ */
+
+#include "nm-connectivity-curl.h"
+#include "../nm-connectivity.h"
+#include "../nm-connectivity-priv.h"
+#include "../nm-config.h"
+
+typedef struct {
+ GSimpleAsyncResult *simple;
+ char *uri;
+ char *response;
+ guint check_id_when_scheduled;
+ CURL *curl_ehandle;
+ size_t msg_size;
+ char *msg;
+ curl_socket_t sock;
+ GIOChannel *ch;
+ guint ev;
+} ConCheckCbData;
+
+typedef struct {
+ curl_socket_t sockfd;
+ CURL *easy;
+ int action;
+ long timeout;
+ GIOChannel *ch;
+ guint ev;
+} CurlSockData;
+
+static size_t
+easy_write_callback (void *buffer, size_t size, size_t nmemb, void *userp)
+{
+ ConCheckCbData *cb_data = userp;
+
+ cb_data->msg = g_realloc (cb_data->msg, cb_data->msg_size + size*nmemb);
+ memcpy (cb_data->msg + cb_data->msg_size, buffer, nmemb*size);
+ cb_data->msg_size += size * nmemb;
+
+ _LOGT ("Received %lu bytes from CURL\n", size * nmemb);
+
+ return size * nmemb;
+}
+
+static NMConnectivityState
+curl_check_connectivity (CURLM *mhandle, CURLMcode ret)
+{
+ NMConnectivity *self;
+ NMConnectivityPrivate *priv;
+ NMConnectivityState new_state = NM_CONNECTIVITY_UNKNOWN;
+ ConCheckCbData *cb_data;
+ CURLMsg *msg;
+ CURLcode eret;
+ gint m_left;
+
+ _LOGT ("curl_multi check for easy messages");
+ if (ret != CURLM_OK) {
+ _LOGE ("Connectivity check failed");
+ return NM_CONNECTIVITY_UNKNOWN;
+ }
+
+ while ((msg = curl_multi_info_read(mhandle, &m_left))) {
+ _LOGT ("curl MSG received - ehandle:%p, type:%d", msg->easy_handle, msg->msg);
+ if (msg->msg != CURLMSG_DONE)
+ continue;
+
+ /* Here we have completed a session. Check easy session result. */
+ eret = curl_easy_getinfo (msg->easy_handle, CURLINFO_PRIVATE, &cb_data);
+ if (eret != CURLE_OK) {
+ _LOGE ("curl cannot extract cb_data for easy handle %p, skipping msg", msg->easy_handle);
+ continue;
+ }
+ self = NM_CONNECTIVITY (g_async_result_get_source_object (G_ASYNC_RESULT (cb_data->simple)));
+ priv = NM_CONNECTIVITY_GET_PRIVATE (self);
+
+ if (msg->data.result != CURLE_OK) {
+ _LOGD ("Check for uri '%s' failed", cb_data->uri);
+ new_state = NM_CONNECTIVITY_LIMITED;
+ goto cleanup;
+ }
+ /* TODO --> Check NM specific HTML headers */
+
+ /* Check response */
+ if (cb_data->msg && g_str_has_prefix (cb_data->msg, cb_data->response)) {
+ _LOGD ("Check for uri '%s' successful.", cb_data->uri);
+ new_state = NM_CONNECTIVITY_FULL;
+ goto cleanup;
+ }
+
+ _LOGI ("Check for uri '%s' did not match expected response '%s'; assuming captive portal.",
+ cb_data->uri, cb_data->response);
+ new_state = NM_CONNECTIVITY_PORTAL;
+cleanup:
+ /* Only update the state, if the call was done from external, or if the periodic check
+ * is still the one that called this async check. */
+ if (!cb_data->check_id_when_scheduled || cb_data->check_id_when_scheduled == priv->concheck.check_id) {
+ /* Only update the state, if the URI and response parameters did not change
+ * since invocation.
+ * The interval does not matter for exernal calls, and for internal calls
+ * we don't reach this line if the interval changed. */
+ if ( !g_strcmp0 (cb_data->uri, priv->uri)
+ && !g_strcmp0 (cb_data->response, priv->response)) {
+ _LOGT ("Update to connectivity state %s",
+ nm_connectivity_state_to_string (new_state));
+ update_state (self, new_state);
+ }
+ }
+ g_simple_async_result_set_op_res_gssize (cb_data->simple, new_state);
+ g_simple_async_result_complete (cb_data->simple);
+ g_object_unref (cb_data->simple);
+
+ curl_multi_remove_handle (mhandle, cb_data->curl_ehandle);
+ curl_easy_cleanup (cb_data->curl_ehandle);
+ g_free (cb_data->uri);
+ g_free (cb_data->response);
+ g_slice_free (ConCheckCbData, cb_data);
+ }
+
+ return new_state;
+}
+
+static gboolean
+curl_timeout_cb (gpointer user_data)
+{
+ NMConnectivityConcheck *concheck = user_data;
+ NMConnectivityState new_state;
+ CURLMcode ret;
+ int pending_conn;
+
+ ret = curl_multi_socket_action (concheck->curl_mhandle, CURL_SOCKET_TIMEOUT, 0, &pending_conn);
+ _LOGT ("timeout elapsed - multi_socket_action (%d conn remaining)", pending_conn);
+
+ new_state = curl_check_connectivity (concheck->curl_mhandle, ret);
+
+ return FALSE;
+}
+
+static int
+curl_timer_cb (CURLM *multi, long timeout_ms, void *userp)
+{
+ NMConnectivityConcheck *concheck = userp;
+
+ _LOGT ("curl_multi timer invocation --> timeout ms: %ld", timeout_ms);
+ switch (timeout_ms) {
+ case -1:
+ /* TODO?: should we cancel current timer ? */
+ break;
+ case 0:
+ /*
+ * Do we really need special management of this case?
+ */
+ default:
+ concheck->curl_timer = g_timeout_add (timeout_ms * 1000, curl_timeout_cb, concheck);
+ break;
+ }
+ return 0;
+}
+
+static gboolean
+curl_socketevent_cb (GIOChannel *ch, GIOCondition condition, gpointer data)
+{
+ NMConnectivityConcheck *concheck = data;
+ NMConnectivityState new_state;
+ CURLMcode ret;
+ int pending_conn = 0;
+ gboolean bret = TRUE;
+ int fd = g_io_channel_unix_get_fd (ch);
+
+ ret = curl_multi_socket_action (concheck->curl_mhandle, fd, 0, &pending_conn);
+ _LOGT ("activity on monitored fd %d - multi_socket_action (%d conn remaining)", fd, pending_conn);
+
+ new_state = curl_check_connectivity (concheck->curl_mhandle, ret);
+
+ if (pending_conn == 0) {
+ if (concheck->curl_timer)
+ g_source_remove (concheck->curl_timer);
+ bret = FALSE;
+ }
+ return bret;
+}
+
+static int
+curl_socket_cb (CURL *e_handle, curl_socket_t s, int what, void *userp, void *socketp)
+{
+ NMConnectivityConcheck *concheck = (NMConnectivityConcheck*) userp;
+ CurlSockData *fdp = (CurlSockData*) socketp;
+ _LOGT ("curl_multi socket callback --> socket %d", s);
+
+ switch (what) {
+ case CURL_POLL_NONE:
+ case CURL_POLL_IN:
+ case CURL_POLL_OUT:
+ case CURL_POLL_INOUT:
+ if (!fdp) {
+ _LOGT ("register new socket s=%d", s);
+ fdp = g_malloc0 (sizeof (CurlSockData));
+ fdp->ch = g_io_channel_unix_new (s);
+ fdp->sockfd = s;
+ fdp->action = what;
+ fdp->easy = e_handle;
+ fdp->ev = g_io_add_watch (fdp->ch, G_IO_IN|G_IO_OUT, curl_socketevent_cb, concheck);
+ curl_multi_assign (concheck->curl_mhandle, s, fdp);
+ }
+ break;
+ case CURL_POLL_REMOVE:
+ _LOGT ("remove socket s=%d", s);
+ if ((fdp) && (fdp->ev)) {
+ g_source_remove (fdp->ev);
+ g_free (fdp);
+ }
+ break;
+ }
+ return 0;
+}
+
+gboolean
+nm_connectivity_do_check (char *uri,
+ char *response,
+ guint interval,
+ GSimpleAsyncResult *simple,
+ NMConnectivityConcheck *concheck,
+ gboolean periodic_check)
+{
+ if (uri && interval) {
+ ConCheckCbData *cb_data = g_slice_new (ConCheckCbData);
+
+ cb_data->curl_ehandle = curl_easy_init ();
+ curl_easy_setopt (cb_data->curl_ehandle, CURLOPT_URL, uri);
+ curl_easy_setopt (cb_data->curl_ehandle, CURLOPT_WRITEFUNCTION, easy_write_callback);
+ curl_easy_setopt (cb_data->curl_ehandle, CURLOPT_WRITEDATA, cb_data);
+ curl_easy_setopt (cb_data->curl_ehandle, CURLOPT_PRIVATE, cb_data);
+ /*
+ * TODO --> disable keepalive
+ * curl http redirection is disabled by default but not connection presistence
+ */
+
+ cb_data->simple = simple;
+ cb_data->uri = g_strdup (uri);
+ if (response)
+ cb_data->response = g_strdup (response);
+ else
+ cb_data->response = g_strdup (NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE);
+ cb_data->msg_size = 0;
+ cb_data->msg = NULL;
+
+ /* For internal calls (periodic), remember the check-id at time of scheduling. */
+ cb_data->check_id_when_scheduled = periodic_check ? concheck->check_id : 0;
+
+ curl_multi_add_handle (concheck->curl_mhandle, cb_data->curl_ehandle);
+
+ concheck->initial_check_obsoleted = TRUE;
+
+ _LOGD ("check: send %srequest to '%s'", periodic_check ? "periodic " : "", uri);
+ return TRUE;
+ } else {
+ g_warn_if_fail (!periodic_check);
+ _LOGD ("check: faking request. Connectivity check disabled");
+ return FALSE;
+ }
+}
+
+gboolean
+nm_connectivity_lib_is_uri_valid (const char *uri, gboolean changed)
+{
+ gboolean is_valid = TRUE;
+
+ /* Here we should check if url is valid (and return false)
+ * or if the url is an https one (and issue a warning).
+ */
+ return is_valid;
+}
+
+void
+nm_connectivity_lib_init (NMConnectivityConcheck *concheck, guint timeout)
+{
+ CURLcode retv;
+
+ retv = curl_global_init (CURL_GLOBAL_ALL);
+ if (retv != CURLE_OK)
+ _LOGI ("Unable to init CURL, connectivity check will be affected");
+
+ concheck->curl_mhandle = curl_multi_init ();
+ curl_multi_setopt (concheck->curl_mhandle, CURLMOPT_SOCKETFUNCTION, curl_socket_cb);
+ curl_multi_setopt (concheck->curl_mhandle, CURLMOPT_SOCKETDATA, concheck);
+ curl_multi_setopt (concheck->curl_mhandle, CURLMOPT_TIMERFUNCTION, curl_timer_cb);
+ curl_multi_setopt (concheck->curl_mhandle, CURLMOPT_TIMERDATA, concheck);
+
+ /* Manage timeout here */
+}
+
+void
+nm_connectivity_lib_dispose (NMConnectivityConcheck *concheck)
+{
+ /* TODO?: should we check here if there is some pending easy handle? */
+ curl_multi_cleanup (concheck->curl_mhandle);
+ curl_global_cleanup (); // not thread safe
+
+ nm_clear_g_source (&concheck->check_id);
+}
+
diff --git a/src/connectivity/nm-connectivity-curl.h b/src/connectivity/nm-connectivity-curl.h
new file mode 100644
index 0000000000..8a81c91036
--- /dev/null
+++ b/src/connectivity/nm-connectivity-curl.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ */
+
+#include <curl/curl.h>
+#include <glib.h>
+#include <gio/gio.h>
+
+typedef struct {
+ CURLM *curl_mhandle;
+ guint curl_timer;
+ gboolean initial_check_obsoleted;
+ guint check_id;
+} NMConnectivityConcheck;
+
+gboolean
+nm_connectivity_do_check (char *uri, char *response, guint interval, GSimpleAsyncResult *simple,
+ NMConnectivityConcheck *concheck, gboolean periodic_check);
+
+gboolean
+nm_connectivity_lib_is_uri_valid (const char *uri, gboolean changed);
+
+void
+nm_connectivity_lib_init (NMConnectivityConcheck *concheck, guint timeout);
+
+void
+nm_connectivity_lib_dispose (NMConnectivityConcheck *concheck);
+
diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c
index db539ca47d..ced7cbaa21 100644
--- a/src/nm-connectivity.c
+++ b/src/nm-connectivity.c
@@ -24,8 +24,12 @@
#include <string.h>
#if WITH_CONCHECK
+#if WITH_LIBCURL
+#include "connectivity/nm-connectivity-curl.h"
+#else
#include "connectivity/nm-connectivity-soup.h"
#endif
+#endif
#include "nm-connectivity.h"
#include "nm-connectivity-priv.h"