NetworkManager/src/core/nm-pacrunner-manager.c
Thomas Haller 08eff4c46e
glib-aux: rename IP address related helpers from "nm-inet-utils.h"
- name things related to `in_addr_t`, `struct in6_addr`, `NMIPAddr` as
  `nm_ip4_addr_*()`, `nm_ip6_addr_*()`, `nm_ip_addr_*()`, respectively.

- we have a wrapper `nm_inet_ntop()` for `inet_ntop()`. This name
  of our wrapper is chosen to be familiar with the libc underlying
  function. With this, also name functions that are about string
  representations of addresses `nm_inet_*()`, `nm_inet4_*()`,
  `nm_inet6_*()`. For example, `nm_inet_parse_str()`,
  `nm_inet_is_normalized()`.

<<<<

  R() {
     git grep -l "$1" | xargs sed -i "s/\<$1\>/$2/g"
  }

  R NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX          NM_CMP_DIRECT_IP4_ADDR_SAME_PREFIX
  R NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX          NM_CMP_DIRECT_IP6_ADDR_SAME_PREFIX
  R NM_UTILS_INET_ADDRSTRLEN                   NM_INET_ADDRSTRLEN
  R _nm_utils_inet4_ntop                       nm_inet4_ntop
  R _nm_utils_inet6_ntop                       nm_inet6_ntop
  R _nm_utils_ip4_get_default_prefix           nm_ip4_addr_get_default_prefix
  R _nm_utils_ip4_get_default_prefix0          nm_ip4_addr_get_default_prefix0
  R _nm_utils_ip4_netmask_to_prefix            nm_ip4_addr_netmask_to_prefix
  R _nm_utils_ip4_prefix_to_netmask            nm_ip4_addr_netmask_from_prefix
  R nm_utils_inet4_ntop_dup                    nm_inet4_ntop_dup
  R nm_utils_inet6_ntop_dup                    nm_inet6_ntop_dup
  R nm_utils_inet_ntop                         nm_inet_ntop
  R nm_utils_inet_ntop_dup                     nm_inet_ntop_dup
  R nm_utils_ip4_address_clear_host_address    nm_ip4_addr_clear_host_address
  R nm_utils_ip4_address_is_link_local         nm_ip4_addr_is_link_local
  R nm_utils_ip4_address_is_loopback           nm_ip4_addr_is_loopback
  R nm_utils_ip4_address_is_zeronet            nm_ip4_addr_is_zeronet
  R nm_utils_ip4_address_same_prefix           nm_ip4_addr_same_prefix
  R nm_utils_ip4_address_same_prefix_cmp       nm_ip4_addr_same_prefix_cmp
  R nm_utils_ip6_address_clear_host_address    nm_ip6_addr_clear_host_address
  R nm_utils_ip6_address_same_prefix           nm_ip6_addr_same_prefix
  R nm_utils_ip6_address_same_prefix_cmp       nm_ip6_addr_same_prefix_cmp
  R nm_utils_ip6_is_ula                        nm_ip6_addr_is_ula
  R nm_utils_ip_address_same_prefix            nm_ip_addr_same_prefix
  R nm_utils_ip_address_same_prefix_cmp        nm_ip_addr_same_prefix_cmp
  R nm_utils_ip_is_site_local                  nm_ip_addr_is_site_local
  R nm_utils_ipaddr_is_normalized              nm_inet_is_normalized
  R nm_utils_ipaddr_is_valid                   nm_inet_is_valid
  R nm_utils_ipx_address_clear_host_address    nm_ip_addr_clear_host_address
  R nm_utils_parse_inaddr                      nm_inet_parse_str
  R nm_utils_parse_inaddr_bin                  nm_inet_parse_bin
  R nm_utils_parse_inaddr_bin_full             nm_inet_parse_bin_full
  R nm_utils_parse_inaddr_prefix               nm_inet_parse_with_prefix_str
  R nm_utils_parse_inaddr_prefix_bin           nm_inet_parse_with_prefix_bin
  R test_nm_utils_ip6_address_same_prefix      test_nm_ip_addr_same_prefix

  ./contrib/scripts/nm-code-format.sh -F
2022-08-25 19:05:51 +02:00

573 lines
20 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2016 Atul Anand <atulhjp@gmail.com>.
*/
#include "src/core/nm-default-daemon.h"
#include "nm-pacrunner-manager.h"
#include "c-list/src/c-list.h"
#include "libnm-glib-aux/nm-dbus-aux.h"
#include "libnm-platform/nm-platform.h"
#include "NetworkManagerUtils.h"
#include "nm-dbus-manager.h"
#include "nm-l3-config-data.h"
#include "nm-utils.h"
#define PACRUNNER_DBUS_SERVICE "org.pacrunner"
#define PACRUNNER_DBUS_INTERFACE "org.pacrunner.Manager"
#define PACRUNNER_DBUS_PATH "/org/pacrunner/manager"
/*****************************************************************************/
struct _NMPacrunnerConfId {
CList conf_id_lst;
NMPacrunnerManager *self;
GVariant *parameters;
char *path;
guint64 log_id;
guint refcount;
};
typedef struct {
GDBusConnection *dbus_connection;
GCancellable *cancellable;
CList conf_id_lst_head;
guint64 log_id_counter;
guint name_owner_changed_id;
bool dbus_initied : 1;
bool has_name_owner : 1;
bool try_start_blocked : 1;
} NMPacrunnerManagerPrivate;
struct _NMPacrunnerManager {
GObject parent;
NMPacrunnerManagerPrivate _priv;
};
struct _NMPacrunnerManagerClass {
GObjectClass parent;
};
G_DEFINE_TYPE(NMPacrunnerManager, nm_pacrunner_manager, G_TYPE_OBJECT)
#define NM_PACRUNNER_MANAGER_GET_PRIVATE(self) \
_NM_GET_PRIVATE(self, NMPacrunnerManager, NM_IS_PACRUNNER_MANAGER)
/*****************************************************************************/
NM_DEFINE_SINGLETON_GETTER(NMPacrunnerManager, nm_pacrunner_manager_get, NM_TYPE_PACRUNNER_MANAGER);
/*****************************************************************************/
#define _NMLOG_DOMAIN LOGD_PROXY
#define _NMLOG(level, ...) __NMLOG_DEFAULT(level, _NMLOG_DOMAIN, "pacrunner", __VA_ARGS__)
#define _NMLOG2_PREFIX_NAME "pacrunner"
#define _NMLOG2(level, conf_id, ...) \
G_STMT_START \
{ \
nm_log((level), \
_NMLOG_DOMAIN, \
NULL, \
NULL, \
"%s%" G_GUINT64_FORMAT "]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
_NMLOG2_PREFIX_NAME ": call[", \
(conf_id)->log_id _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
} \
G_STMT_END
/*****************************************************************************/
static void _call_destroy_proxy_configuration(NMPacrunnerManager *self,
NMPacrunnerConfId *conf_id,
const char *path,
gboolean verbose_log);
/*****************************************************************************/
static NMPacrunnerConfId *
conf_id_ref(NMPacrunnerConfId *conf_id)
{
nm_assert(conf_id);
nm_assert(conf_id->refcount > 0);
conf_id->refcount++;
return conf_id;
}
static void
conf_id_unref(NMPacrunnerConfId *conf_id)
{
nm_assert(conf_id);
nm_assert(conf_id->refcount > 0);
if (conf_id->refcount == 1) {
g_variant_unref(conf_id->parameters);
g_free(conf_id->path);
c_list_unlink_stale(&conf_id->conf_id_lst);
g_object_unref(conf_id->self);
g_slice_free(NMPacrunnerConfId, conf_id);
} else
conf_id->refcount--;
}
NM_AUTO_DEFINE_FCN0(NMPacrunnerConfId *, _nm_auto_unref_conf_id, conf_id_unref);
#define nm_auto_unref_conf_id nm_auto(_nm_auto_unref_conf_id)
/*****************************************************************************/
static void
get_ip_domains(GPtrArray *domains, const NML3ConfigData *l3cd, int addr_family)
{
NMDedupMultiIter ipconf_iter;
char *cidr;
guint num;
guint i;
char sbuf[NM_INET_ADDRSTRLEN];
const NMPlatformIPAddress *address;
const NMPlatformIPRoute *route;
const char *const *strv;
strv = nm_l3_config_data_get_searches(l3cd, addr_family, &num);
for (i = 0; i < num; i++)
g_ptr_array_add(domains, g_strdup(strv[i]));
strv = nm_l3_config_data_get_domains(l3cd, addr_family, &num);
for (i = 0; i < num; i++)
g_ptr_array_add(domains, g_strdup(strv[i]));
nm_l3_config_data_iter_ip_address_for_each (&ipconf_iter, l3cd, addr_family, &address) {
cidr = g_strdup_printf("%s/%u",
nm_inet_ntop(addr_family, address->address_ptr, sbuf),
address->plen);
g_ptr_array_add(domains, cidr);
}
nm_l3_config_data_iter_ip_route_for_each (&ipconf_iter, l3cd, addr_family, &route) {
if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route))
continue;
cidr = g_strdup_printf("%s/%u",
nm_inet_ntop(addr_family, route->network_ptr, sbuf),
route->plen);
g_ptr_array_add(domains, cidr);
}
}
static GVariant *
_make_request_create_proxy_configuration(const char *iface, const NML3ConfigData *l3cd)
{
GVariantBuilder builder;
NMProxyConfigMethod method;
const char *s;
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
if (iface) {
g_variant_builder_add(&builder, "{sv}", "Interface", g_variant_new_string(iface));
}
method = l3cd ? nm_l3_config_data_get_proxy_method(l3cd) : NM_PROXY_CONFIG_METHOD_UNKNOWN;
switch (method) {
case NM_PROXY_CONFIG_METHOD_AUTO:
g_variant_builder_add(&builder, "{sv}", "Method", g_variant_new_string("auto"));
s = nm_l3_config_data_get_proxy_pac_url(l3cd);
if (s)
g_variant_builder_add(&builder, "{sv}", "URL", g_variant_new_string(s));
s = nm_l3_config_data_get_proxy_pac_script(l3cd);
if (s)
g_variant_builder_add(&builder, "{sv}", "Script", g_variant_new_string(s));
break;
case NM_PROXY_CONFIG_METHOD_UNKNOWN:
case NM_PROXY_CONFIG_METHOD_NONE:
g_variant_builder_add(&builder, "{sv}", "Method", g_variant_new_string("direct"));
break;
}
g_variant_builder_add(
&builder,
"{sv}",
"BrowserOnly",
g_variant_new_boolean(l3cd ? !!nm_l3_config_data_get_proxy_browser_only(l3cd) : FALSE));
if (l3cd) {
gs_unref_ptrarray GPtrArray *domains = NULL;
domains = g_ptr_array_new_with_free_func(g_free);
get_ip_domains(domains, l3cd, AF_INET);
get_ip_domains(domains, l3cd, AF_INET6);
if (domains->len > 0) {
g_variant_builder_add(
&builder,
"{sv}",
"Domains",
g_variant_new_strv((const char *const *) domains->pdata, domains->len));
}
}
return g_variant_new("(a{sv})", &builder);
}
/*****************************************************************************/
static void
_call_destroy_proxy_configuration_cb(GObject *source, GAsyncResult *res, gpointer user_data)
{
nm_auto_unref_conf_id NMPacrunnerConfId *conf_id = user_data;
gs_free_error GError *error = NULL;
gs_unref_variant GVariant *ret = NULL;
ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), res, &error);
if (!ret) {
if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
_LOG2T(conf_id, "destroy proxy configuration: failed with %s", error->message);
else
_LOG2T(conf_id, "destroy proxy configuration: cancelled");
return;
}
_LOG2T(conf_id, "destroy proxy configuration: success");
}
static void
_call_create_proxy_configuration_cb(GObject *source, GAsyncResult *res, gpointer user_data)
{
nm_auto_unref_conf_id NMPacrunnerConfId *conf_id = user_data;
NMPacrunnerManager *self = NM_PACRUNNER_MANAGER(conf_id->self);
gs_free_error GError *error = NULL;
gs_unref_variant GVariant *variant = NULL;
const char *path = NULL;
nm_assert(!conf_id->path);
variant = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), res, &error);
if (!variant) {
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
_LOG2T(conf_id, "create proxy configuration failed: %s", error->message);
else
_LOG2T(conf_id, "create proxy configuration cancelled");
return;
}
g_variant_get(variant, "(&o)", &path);
if (c_list_is_empty(&conf_id->conf_id_lst)) {
_LOG2T(conf_id,
"create proxy configuration succeeded (%s), but destroy it right away",
path);
_call_destroy_proxy_configuration(self, conf_id, path, FALSE);
} else {
_LOG2T(conf_id, "create proxy configuration succeeded (%s)", path);
conf_id->path = g_strdup(path);
}
}
static void
_call_destroy_proxy_configuration(NMPacrunnerManager *self,
NMPacrunnerConfId *conf_id,
const char *path,
gboolean verbose_log)
{
NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE(self);
if (verbose_log)
_LOG2T(conf_id, "destroy proxy configuration %s...", path);
g_dbus_connection_call(priv->dbus_connection,
PACRUNNER_DBUS_SERVICE,
PACRUNNER_DBUS_PATH,
PACRUNNER_DBUS_INTERFACE,
"DestroyProxyConfiguration",
g_variant_new("(o)", path),
G_VARIANT_TYPE("()"),
G_DBUS_CALL_FLAGS_NO_AUTO_START,
NM_SHUTDOWN_TIMEOUT_1500_MSEC,
priv->cancellable,
_call_destroy_proxy_configuration_cb,
conf_id_ref(conf_id));
}
static void
_call_create_proxy_configuration(NMPacrunnerManager *self,
NMPacrunnerConfId *conf_id,
gboolean verbose_log)
{
NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE(self);
if (verbose_log)
_LOG2T(conf_id, "create proxy configuration...");
g_dbus_connection_call(priv->dbus_connection,
PACRUNNER_DBUS_SERVICE,
PACRUNNER_DBUS_PATH,
PACRUNNER_DBUS_INTERFACE,
"CreateProxyConfiguration",
conf_id->parameters,
G_VARIANT_TYPE("(o)"),
G_DBUS_CALL_FLAGS_NO_AUTO_START,
NM_SHUTDOWN_TIMEOUT_1500_MSEC,
priv->cancellable,
_call_create_proxy_configuration_cb,
conf_id_ref(conf_id));
}
static gboolean
_try_start_service_by_name(NMPacrunnerManager *self)
{
NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE(self);
if (priv->try_start_blocked || !priv->dbus_initied)
return FALSE;
_LOGD("try D-Bus activating pacrunner...");
priv->try_start_blocked = TRUE;
nm_dbus_connection_call_start_service_by_name(priv->dbus_connection,
PACRUNNER_DBUS_SERVICE,
-1,
NULL,
NULL,
NULL);
return TRUE;
}
/*****************************************************************************/
/**
* nm_pacrunner_manager_add:
* @self: the #NMPacrunnerManager
* @iface: the iface for the connection or %NULL
* @l3cd: the #NML3ConfigData of the connection to extract domain info from
*
* Returns: a #NMPacrunnerConfId id. The function cannot
* fail and always returns a non NULL pointer. The conf-id may
* be used to remove the configuration later via nm_pacrunner_manager_remove().
* Note that the conf-id keeps the @self instance alive.
*/
NMPacrunnerConfId *
nm_pacrunner_manager_add(NMPacrunnerManager *self, const char *iface, const NML3ConfigData *l3cd)
{
NMPacrunnerManagerPrivate *priv;
NMPacrunnerConfId *conf_id;
gs_free char *log_msg = NULL;
g_return_val_if_fail(NM_IS_PACRUNNER_MANAGER(self), NULL);
priv = NM_PACRUNNER_MANAGER_GET_PRIVATE(self);
conf_id = g_slice_new(NMPacrunnerConfId);
*conf_id = (NMPacrunnerConfId){
.log_id = ++priv->log_id_counter,
.refcount = 1,
.self = g_object_ref(self),
.parameters = g_variant_ref_sink(_make_request_create_proxy_configuration(iface, l3cd)),
};
c_list_link_tail(&priv->conf_id_lst_head, &conf_id->conf_id_lst);
if (!priv->has_name_owner) {
_LOG2T(conf_id,
"add config: %s (%s)",
(log_msg = g_variant_print(conf_id->parameters, FALSE)),
"pacrunner D-Bus service not running");
_try_start_service_by_name(self);
} else {
_LOG2T(conf_id,
"add config: %s (%s)",
(log_msg = g_variant_print(conf_id->parameters, FALSE)),
"create proxy configuration");
_call_create_proxy_configuration(self, conf_id, FALSE);
}
return conf_id;
}
/**
* nm_pacrunner_manager_remove:
* @conf_id: the conf id obtained from nm_pacrunner_manager_add()
*/
void
nm_pacrunner_manager_remove(NMPacrunnerConfId *conf_id)
{
_nm_unused nm_auto_unref_conf_id NMPacrunnerConfId *conf_id_free = conf_id;
NMPacrunnerManager *self;
NMPacrunnerManagerPrivate *priv;
g_return_if_fail(conf_id);
self = conf_id->self;
g_return_if_fail(NM_IS_PACRUNNER_MANAGER(self));
priv = NM_PACRUNNER_MANAGER_GET_PRIVATE(self);
_LOG2T(conf_id, "removing...");
nm_assert(c_list_contains(&priv->conf_id_lst_head, &conf_id->conf_id_lst));
c_list_unlink(&conf_id->conf_id_lst);
if (!conf_id->path) {
/* There is no ID to destroy the configuration.
*
* That can happen because:
*
* - pacrunner D-Bus service is not running (no name owner) and we didn't call CreateProxyConfiguration.
* - CreateProxyConfiguration failed.
* - CreateProxyConfiguration is in progress.
*
* In all cases there is nothing to do. Note that if CreateProxyConfiguration is in progress
* it has a reference on the conf-id and it will automatically destroy the configuration
* when it completes.
*/
return;
}
_call_destroy_proxy_configuration(self, conf_id, conf_id->path, TRUE);
}
gboolean
nm_pacrunner_manager_remove_clear(NMPacrunnerConfId **p_conf_id)
{
g_return_val_if_fail(p_conf_id, FALSE);
if (!*p_conf_id)
return FALSE;
nm_pacrunner_manager_remove(g_steal_pointer(p_conf_id));
return TRUE;
}
/*****************************************************************************/
static void
name_owner_changed(NMPacrunnerManager *self, const char *name_owner)
{
NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE(self);
NMPacrunnerConfId *conf_id;
gboolean has_name_owner;
has_name_owner = (name_owner && name_owner[0]);
if (priv->dbus_initied && priv->has_name_owner == has_name_owner)
return;
priv->has_name_owner = has_name_owner;
nm_clear_g_cancellable(&priv->cancellable);
if (has_name_owner) {
priv->dbus_initied = TRUE;
priv->try_start_blocked = FALSE;
_LOGD("pacrunner appeared on D-Bus (%s)", name_owner);
priv->cancellable = g_cancellable_new();
c_list_for_each_entry (conf_id, &priv->conf_id_lst_head, conf_id_lst)
_call_create_proxy_configuration(self, conf_id, TRUE);
} else {
if (!priv->dbus_initied) {
priv->dbus_initied = TRUE;
nm_assert(!priv->try_start_blocked);
_LOGD("pacrunner not on D-Bus");
} else
_LOGD("pacrunner disappeared from D-Bus");
if (!c_list_is_empty(&priv->conf_id_lst_head)) {
c_list_for_each_entry (conf_id, &priv->conf_id_lst_head, conf_id_lst)
nm_clear_g_free(&conf_id->path);
_try_start_service_by_name(self);
}
}
}
static void
name_owner_changed_cb(GDBusConnection *connection,
const char *sender_name,
const char *object_path,
const char *interface_name,
const char *signal_name,
GVariant *parameters,
gpointer user_data)
{
const char *new_owner;
if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)")))
return;
g_variant_get(parameters, "(&s&s&s)", NULL, NULL, &new_owner);
name_owner_changed(user_data, new_owner);
}
static void
get_name_owner_cb(const char *name_owner, GError *error, gpointer user_data)
{
if (!name_owner && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
name_owner_changed(user_data, name_owner);
}
/*****************************************************************************/
static void
nm_pacrunner_manager_init(NMPacrunnerManager *self)
{
NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE(self);
c_list_init(&priv->conf_id_lst_head);
priv->dbus_connection = nm_g_object_ref(NM_MAIN_DBUS_CONNECTION_GET);
if (!priv->dbus_connection) {
_LOGD("no D-Bus connection to talk to pacrunner");
return;
}
priv->name_owner_changed_id =
nm_dbus_connection_signal_subscribe_name_owner_changed(priv->dbus_connection,
PACRUNNER_DBUS_SERVICE,
name_owner_changed_cb,
self,
NULL);
priv->cancellable = g_cancellable_new();
nm_dbus_connection_call_get_name_owner(priv->dbus_connection,
PACRUNNER_DBUS_SERVICE,
-1,
priv->cancellable,
get_name_owner_cb,
self);
}
static void
dispose(GObject *object)
{
NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE(object);
nm_assert(c_list_is_empty(&priv->conf_id_lst_head));
/* we cancel all pending operations. Note that pacrunner automatically
* removes all configuration once NetworkManager disconnects from
* the bus -- which happens soon after we destroy the pacrunner manager.
*/
nm_clear_g_cancellable(&priv->cancellable);
nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id);
g_clear_object(&priv->dbus_connection);
G_OBJECT_CLASS(nm_pacrunner_manager_parent_class)->dispose(object);
}
static void
nm_pacrunner_manager_class_init(NMPacrunnerManagerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->dispose = dispose;
}