diff options
Diffstat (limited to 'src/charon/control/interfaces/dbus_interface.c')
-rw-r--r-- | src/charon/control/interfaces/dbus_interface.c | 479 |
1 files changed, 479 insertions, 0 deletions
diff --git a/src/charon/control/interfaces/dbus_interface.c b/src/charon/control/interfaces/dbus_interface.c new file mode 100644 index 000000000..443df635c --- /dev/null +++ b/src/charon/control/interfaces/dbus_interface.c @@ -0,0 +1,479 @@ +/** + * @file dbus_interface.c + * + * @brief Implementation of dbus_interface_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + */ + +#define DBUS_API_SUBJECT_TO_CHANGE +#include <dbus/dbus.h> +#include <NetworkManager/NetworkManager.h> +#include <NetworkManager/NetworkManagerVPN.h> +#include <stdlib.h> + +#include "dbus_interface.h" + +#include <library.h> +#include <daemon.h> + + +#define NM_DBUS_SERVICE_STRONG "org.freedesktop.NetworkManager.strongswan" +#define NM_DBUS_INTERFACE_STRONG "org.freedesktop.NetworkManager.strongswan" +#define NM_DBUS_PATH_STRONG "/org/freedesktop/NetworkManager/strongswan" + +typedef struct private_dbus_interface_t private_dbus_interface_t; + +/** + * Private data of an dbus_interface_t object. + */ +struct private_dbus_interface_t { + + /** + * Public part of dbus_t object. + */ + dbus_interface_t public; + + /** + * DBUS connection + */ + DBusConnection* conn; + + /** + * error value used here and there + */ + DBusError err; + + /** + * state of the daemon + */ + NMVPNState state; + + /** + * dispatcher thread for DBUS messages + */ + pthread_t thread; + + /** + * name of the currently active connection + */ + char *name; +}; + +/** + * set daemon state and send StateChange signal to the bus + */ +static void set_state(private_dbus_interface_t *this, NMVPNState state) +{ + DBusMessage* msg; + + msg = dbus_message_new_signal(NM_DBUS_PATH_STRONG, NM_DBUS_INTERFACE_STRONG, NM_DBUS_VPN_SIGNAL_STATE_CHANGE); + + if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &this->state, + DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID) || + !dbus_connection_send(this->conn, msg, NULL)) + { + DBG1(DBG_CFG, "unable to send DBUS StateChange signal"); + } + dbus_connection_flush(this->conn); + dbus_message_unref(msg); + this->state = state; +} + + +/** + * get the child_cfg with the same name as the peer cfg + */ +static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name) +{ + child_cfg_t *current, *found = NULL; + iterator_t *iterator; + + iterator = peer_cfg->create_child_cfg_iterator(peer_cfg); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (streq(current->get_name(current), name)) + { + found = current; + found->get_ref(found); + break; + } + } + iterator->destroy(iterator); + return found; +} + +/** + * get a peer configuration by its name, or a name of its children + */ +static peer_cfg_t *get_peer_cfg_by_name(char *name) +{ + iterator_t *i1, *i2; + peer_cfg_t *current, *found = NULL; + child_cfg_t *child; + + i1 = charon->backends->create_iterator(charon->backends); + while (i1->iterate(i1, (void**)¤t)) + { + /* compare peer_cfgs name first */ + if (streq(current->get_name(current), name)) + { + found = current; + found->get_ref(found); + break; + } + /* compare all child_cfg names otherwise */ + i2 = current->create_child_cfg_iterator(current); + while (i2->iterate(i2, (void**)&child)) + { + if (streq(child->get_name(child), name)) + { + found = current; + found->get_ref(found); + break; + } + } + i2->destroy(i2); + if (found) + { + break; + } + } + i1->destroy(i1); + return found; +} + +/** + * logging dummy + */ +static bool dbus_log(void *param, signal_t signal, level_t level, + ike_sa_t *ike_sa, char *format, va_list args) +{ + return TRUE; +} + + +/** + * process NetworkManagers startConnection method call + */ +static bool start_connection(private_dbus_interface_t *this, DBusMessage* msg) +{ + DBusMessage *reply, *signal; + char *name, *user, **data, **passwords, **routes; + int data_count, passwords_count, routes_count; + u_int32_t me, other, p2p, netmask, mss; + char *dev, *domain, *banner; + const dbus_int32_t array[] = {}; + const dbus_int32_t *varray = array; + peer_cfg_t *peer_cfg; + child_cfg_t *child_cfg; + status_t status = FAILED; + + dbus_error_free(&this->err); + + if (!dbus_message_get_args(msg, &this->err, + DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &user, + DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &passwords, &passwords_count, + DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &data, &data_count, + DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &routes, &routes_count, + DBUS_TYPE_INVALID)) + { + return FALSE; + } + set_state(this, NM_VPN_STATE_STARTING); + + peer_cfg = get_peer_cfg_by_name(name); + if (peer_cfg) + { + free(this->name); + this->name = strdup(peer_cfg->get_name(peer_cfg)); + child_cfg = get_child_from_peer(peer_cfg, name); + if (child_cfg) + { + status = charon->interfaces->initiate(charon->interfaces, peer_cfg, + child_cfg, dbus_log, NULL); + } + else + { + peer_cfg->destroy(peer_cfg); + } + } + reply = dbus_message_new_method_return(msg); + dbus_connection_send(this->conn, reply, NULL); + dbus_message_unref(reply); + + if (status == SUCCESS) + { + + set_state(this, NM_VPN_STATE_STARTED); + signal = dbus_message_new_signal(NM_DBUS_PATH_STRONG, + NM_DBUS_INTERFACE_STRONG, + NM_DBUS_VPN_SIGNAL_IP4_CONFIG); + me = other = p2p = mss = netmask = 0; + dev = domain = banner = ""; + if (dbus_message_append_args(signal, + DBUS_TYPE_UINT32, &other, + DBUS_TYPE_STRING, &dev, + DBUS_TYPE_UINT32, &me, + DBUS_TYPE_UINT32, &p2p, + DBUS_TYPE_UINT32, &netmask, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &varray, 0, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &varray, 0, + DBUS_TYPE_UINT32, &mss, + DBUS_TYPE_STRING, &domain, + DBUS_TYPE_STRING, &banner, DBUS_TYPE_INVALID)) + { + dbus_connection_send(this->conn, signal, NULL); + } + dbus_message_unref(signal); + } + else + { + set_state(this, NM_VPN_STATE_STOPPED); + } + + dbus_connection_flush(this->conn); + return TRUE; +} + +/** + * process NetworkManagers stopConnection method call + */ +static bool stop_connection(private_dbus_interface_t *this, DBusMessage* msg) +{ + u_int32_t id; + iterator_t *iterator; + ike_sa_t *ike_sa; + + if (this->name == NULL) + { + return FALSE; + } + + dbus_error_free(&this->err); + + set_state(this, NM_VPN_STATE_STOPPING); + + iterator = charon->interfaces->create_ike_sa_iterator(charon->interfaces); + while (iterator->iterate(iterator, (void**)&ike_sa)) + { + child_sa_t *child_sa; + iterator_t *children; + + if (this->name && streq(this->name, ike_sa->get_name(ike_sa))) + { + id = ike_sa->get_unique_id(ike_sa); + iterator->destroy(iterator); + charon->interfaces->terminate_ike(charon->interfaces, id, NULL, NULL); + set_state(this, NM_VPN_STATE_STOPPED); + return TRUE;; + } + children = ike_sa->create_child_sa_iterator(ike_sa); + while (children->iterate(children, (void**)&child_sa)) + { + if (this->name && streq(this->name, child_sa->get_name(child_sa))) + { + id = child_sa->get_reqid(child_sa); + children->destroy(children); + iterator->destroy(iterator); + charon->interfaces->terminate_child(charon->interfaces, id, NULL, NULL); + set_state(this, NM_VPN_STATE_STOPPED); + return TRUE; + } + } + children->destroy(children); + } + iterator->destroy(iterator); + set_state(this, NM_VPN_STATE_STOPPED); + return TRUE; +} + +/** + * process NetworkManagers getState method call + */ +static bool get_state(private_dbus_interface_t *this, DBusMessage* msg) +{ + DBusMessage* reply; + reply = dbus_message_new_method_return(msg); + if (!reply || !dbus_message_append_args(reply, + DBUS_TYPE_UINT32, &this->state, + DBUS_TYPE_INVALID)) + { + return FALSE; + } + dbus_connection_send(this->conn, reply, NULL); + return TRUE; +} + +/** + * Handle incoming messages + */ +static DBusHandlerResult message_handler(DBusConnection *con, DBusMessage *msg, + private_dbus_interface_t *this) +{ + bool handled; + + if (dbus_message_is_method_call(msg, NM_DBUS_INTERFACE_STRONG, + "startConnection")) + { + handled = start_connection(this, msg); + } + else if (dbus_message_is_method_call(msg, NM_DBUS_INTERFACE_STRONG, + "stopConnection")) + { + handled = stop_connection(this, msg); + } + else if (dbus_message_is_method_call(msg, NM_DBUS_INTERFACE_STRONG, + "getState")) + { + handled = get_state(this, msg); + } + else + { + DBG1(DBG_CFG, "ignoring DBUS message %s.%s", + dbus_message_get_interface(msg), dbus_message_get_member(msg)); + handled = FALSE; + } + + if (handled) + { + return DBUS_HANDLER_RESULT_HANDLED; + } + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +/** + * Handle received signals + +static DBusHandlerResult signal_handler(DBusConnection *con, DBusMessage *msg, + private_dbus_interface_t *this) +{ + bool handled; + + if (dbus_message_is_signal(msg, NM_DBUS_INTERFACE, "VPNConnectionStateChange")) + { + NMVPNState state; + char *name; + + if (dbus_message_get_args(msg, &this->err, DBUS_TYPE_STRING, &name, + DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID)) + { + DBG1(DBG_CFG, "got state %d for %s", state, name); + } + handled = TRUE; + } + else + { + DBG1(DBG_CFG, "ignoring DBUS signal %s.%s", + dbus_message_get_interface(msg), dbus_message_get_member(msg)); + handled = FALSE; + } + if (handled) + { + return DBUS_HANDLER_RESULT_HANDLED; + } + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} */ + +/** + * dispatcher function processed by a seperate thread + */ +static void dispatch(private_dbus_interface_t *this) +{ + charon->drop_capabilities(charon, TRUE); + + while (dbus_connection_read_write_dispatch(this->conn, -1)) + { + /* nothing */ + } +} + +/** + * Implementation of interface_t.destroy. + */ +static void destroy(private_dbus_interface_t *this) +{ + pthread_cancel(this->thread); + pthread_join(this->thread, NULL); + dbus_connection_close(this->conn); + dbus_error_free(&this->err); + dbus_shutdown(); + free(this->name); + free(this); +} + +/* + * Described in header file + */ +interface_t *interface_create() +{ + int ret; + DBusObjectPathVTable v = {NULL, (void*)&message_handler, NULL, NULL, NULL, NULL}; + private_dbus_interface_t *this = malloc_thing(private_dbus_interface_t); + + this->public.interface.destroy = (void (*)(interface_t*))destroy; + + dbus_error_init(&this->err); + this->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &this->err); + if (dbus_error_is_set(&this->err)) + { + DBG1(DBG_CFG, "unable to open DBUS connection: %s", this->err.message); + charon->kill(charon, "DBUS initialization failed"); + } + dbus_connection_set_exit_on_disconnect(this->conn, FALSE); + + ret = dbus_bus_request_name(this->conn, NM_DBUS_SERVICE_STRONG, + DBUS_NAME_FLAG_REPLACE_EXISTING , &this->err); + if (dbus_error_is_set(&this->err)) + { + DBG1(DBG_CFG, "unable to set DBUS name: %s", this->err.message); + charon->kill(charon, "unable to set DBUS name"); + } + if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) + { + charon->kill(charon, "DBUS name already owned"); + } + if (!dbus_connection_register_object_path(this->conn, NM_DBUS_PATH_STRONG, &v, this)) + { + charon->kill(charon, "unable to register DBUS message handler"); + } + /* + if (!dbus_connection_add_filter(this->conn, (void*)signal_handler, this, NULL)) + { + charon->kill(charon, "unable to register DBUS signal handler"); + } + + dbus_bus_add_match(this->conn, "type='signal', " + "interface='" NM_DBUS_INTERFACE_VPN "'," + "path='" NM_DBUS_PATH_VPN "'", &this->err); + if (dbus_error_is_set (&this->err)) + { + charon->kill(charon, "unable to add DBUS signal match"); + }*/ + + this->name = NULL; + this->state = NM_VPN_STATE_INIT; + set_state(this, NM_VPN_STATE_STOPPED); + + if (pthread_create(&this->thread, NULL, (void*(*)(void*))dispatch, this) != 0) + { + charon->kill(charon, "unable to create stroke thread"); + } + + return &this->public.interface; +} + |