diff options
Diffstat (limited to 'bus/driver-afbus.c')
-rw-r--r-- | bus/driver-afbus.c | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/bus/driver-afbus.c b/bus/driver-afbus.c new file mode 100644 index 00000000..70edafb8 --- /dev/null +++ b/bus/driver-afbus.c @@ -0,0 +1,345 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* driver.c Bus client, AF_BUS bits (driver) + * + * Copyright (C) 2012 Collabora Ltd + * + * Licensed under the Academic Free License version 2.1 + * + * 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 + * + */ + +#include <config.h> + +#include "driver-afbus.h" + +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <asm/types.h> +#include <errno.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <linux/connector.h> +#include <dbus/dbus-transport-afbus.h> + +#define CN_IDX_NFDBUS 0xA /* netfilter D-Bus */ +#define CN_VAL_NFDBUS 0x1 + +#define NFDBUS_CMD_ADDMATCH 0x01 +#define NFDBUS_CMD_REMOVEMATCH 0x02 +#define NFDBUS_CMD_REMOVEALLMATCH 0x03 + +struct nfdbus_nl_cfg_req { + __u32 cmd; + __u32 len; + struct sockaddr_bus addr; + __u64 pad; + unsigned char data[0]; +}; + +struct nfdbus_nl_cfg_reply { + __u32 ret_code; +}; + +static int +ensure_nl_sock(DBusError *error) +{ + static int nlsock = 0; + + struct sockaddr_nl l_local; + int fd; + int ret; + + if (nlsock > 0) + return nlsock; + + fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); + if (fd == -1) { + dbus_set_error (error, DBUS_ERROR_NETLINK, + "Couldn't use the netlink socket: %s", + strerror(errno)); + return -1; + } + + if (!_dbus_set_fd_nonblocking (fd, error)) + { + _dbus_close_socket (fd, NULL); + return -1; + } + + l_local.nl_family = AF_NETLINK; + l_local.nl_groups = 0; + l_local.nl_pid = 0; + ret = bind(fd, (struct sockaddr *)&l_local, sizeof(struct sockaddr_nl)); + if (ret == -1) { + close(fd); + dbus_set_error (error, DBUS_ERROR_NETLINK, + "Couldn't bind the netlink socket: %s", + strerror(errno)); + return -1; + } + + nlsock = fd; + return nlsock; +} + +static int netlink_send(int nlsock, struct cn_msg *msg, int seq) +{ + struct nlmsghdr *nlh; + unsigned int size; + char buf[4096]; + struct cn_msg *m; + + size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len); + + nlh = (struct nlmsghdr *)buf; + nlh->nlmsg_seq = seq; + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_type = NLMSG_DONE; + nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh)); + nlh->nlmsg_flags = 0; + + m = NLMSG_DATA(nlh); + memcpy(m, msg, sizeof(*m) + msg->len); + + return send(nlsock, nlh, size, 0); +} + +static dbus_bool_t +bus_driver_afbus_upload_match_rule (DBusConnection *connection, + const char *rule, + int cmd, + DBusError *error) +{ + static int seq; + + int nlsock; + int ret; + + char buf[sizeof(struct cn_msg) + sizeof(struct nfdbus_nl_cfg_req) + 1024]; + + struct cn_msg *data; + struct nlmsghdr *reply; + struct nfdbus_nl_cfg_req *req; + //struct nfdbus_nl_cfg_reply *req_reply; + + struct sockaddr_storage *address; + socklen_t addrlen = sizeof(address); + + if (!bus_connection_get_peer_address(connection, + (struct sockaddr **) &address, &addrlen)) + return TRUE; + + if (((struct sockaddr*)address)->sa_family != AF_BUS) + return TRUE; + + nlsock = ensure_nl_sock (error); + if (nlsock == -1) + return FALSE; + + memset(buf, 0, sizeof(buf)); + + data = (struct cn_msg *)buf; + + data->id.idx = CN_IDX_NFDBUS; + data->id.val = CN_VAL_NFDBUS; + data->seq = seq++; + data->ack = 0; + data->len = sizeof(struct nfdbus_nl_cfg_req) + strlen(rule) + 1; + req = (struct nfdbus_nl_cfg_req *) data->data; + + req->cmd = cmd; + req->len = strlen(rule) + 1; + req->addr = *(struct sockaddr_bus *)address; + strcpy((char *)req->data, rule); + + ret = netlink_send(nlsock, data, seq++); + if (ret <= 0) + { + } + + memset(buf, 0, sizeof(buf)); + ret = recv(nlsock, buf, sizeof(buf), 0); + if (ret <= 0) + { + } + + reply = (struct nlmsghdr *)buf; + if (reply->nlmsg_type != NLMSG_DONE) + { + } + + return TRUE; +} + +dbus_bool_t +bus_driver_afbus_add_match_rule (DBusConnection *connection, + const char *rule, + DBusError *error) +{ + if (bus_driver_afbus_upload_match_rule (connection, rule, NFDBUS_CMD_ADDMATCH, error)) + { + /* Check if the match rule is for eavesdropping, and set the socket + * to allow receiving all messages if so */ + if (strstr (rule, "eavesdrop=true")) + { + int fd; + + if (dbus_connection_get_socket (connection, &fd)) + { + if (setsockopt (fd, SOL_BUS, BUS_SET_EAVESDROP, NULL, 0) == 0) + return TRUE; + else + { + dbus_set_error (error, + _dbus_error_from_errno (errno), + "Failed to setsockopt on socket %d: %s", + fd, _dbus_strerror (errno)); + } + } + else + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + } + else + return TRUE; + } + + return FALSE; +} + +dbus_bool_t +bus_driver_afbus_remove_match_rule (DBusConnection *connection, + const char *rule, + DBusError *error) +{ + return bus_driver_afbus_upload_match_rule (connection, rule, NFDBUS_CMD_REMOVEMATCH, error); +} + +dbus_bool_t +bus_driver_afbus_append_unique_name (DBusConnection *connection, + DBusString *str) +{ + struct sockaddr_bus address; + long len = sizeof(address); + + memset (&address, 0, sizeof (address)); + if (!dbus_connection_get_peer_address(connection, &address, &len) || + address.sbus_family != AF_BUS) + return FALSE; + + if (!_dbus_string_append (str, "AF-BUS.")) + return FALSE; + + if (!_dbus_string_append_uint (str, address.sbus_addr.s_addr)) + return FALSE; + + return TRUE; +} + +dbus_bool_t +bus_driver_afbus_emit_forwarded (BusTransaction *transaction, + DBusConnection *connection, + DBusConnection *addressed_recipient, + const char *service_name) +{ + struct sockaddr_bus address; + long len = sizeof (address); + DBusMessage *message; + dbus_bool_t result = FALSE; + + memset (&address, 0, sizeof (address)); + if (!dbus_connection_get_peer_address (addressed_recipient, &address, &len) || + address.sbus_family != AF_BUS) + { + /* Don't return an error if it is not a AF_BUS socket */ + return TRUE; + } + + /* Prepare the message to be sent */ + message = dbus_message_new_signal (DBUS_PATH_AFBUS, + DBUS_INTERFACE_AFBUS, + "Forwarded"); + if (message == NULL) + { + _dbus_verbose ("Could not allocate AF_BUS.Forwarded signal message\n"); + return FALSE; + } + + if (!dbus_message_set_sender (message, DBUS_SERVICE_DBUS)) + { + _dbus_verbose ("Could not set sender for AF_BUS.Forwarded signal message\n"); + goto out; + } + + if (!dbus_message_append_args (message, + DBUS_TYPE_STRING, &service_name, + DBUS_TYPE_UINT64, &address.sbus_addr.s_addr, + DBUS_TYPE_INVALID)) + { + _dbus_verbose ("Could not append arguments for AF_BUS.Forwarded signal message\n"); + goto out; + } + + if (bus_transaction_send (transaction, connection, message)) + result = TRUE; + else + _dbus_verbose ("Could not send AF_BUS.Forwarded signal message\n"); + + out: + dbus_message_unref (message); + + return result; +} + +dbus_bool_t +bus_driver_afbus_assign_address (DBusConnection *connection) +{ + struct sockaddr_bus address; + long len = sizeof (address); + int fd; + static dbus_uint64_t next_address = 0x1111000000000001ULL; + + memset (&address, 0, sizeof (address)); + if (!dbus_connection_get_peer_address (connection, &address, &len) || + ((struct sockaddr *) &address)->sa_family != AF_BUS) + { + /* Don't return an error if it is not a AF_BUS socket */ + return TRUE; + } + + if (!dbus_connection_get_unix_fd (connection, &fd)) + { + return FALSE; + } + + address.sbus_addr.s_addr = next_address; + if (setsockopt (fd, SOL_BUS, BUS_ADD_ADDR, &address, sizeof (address)) != 0) + { + return FALSE; + } + + next_address++; + + return TRUE; +} + +dbus_bool_t +bus_driver_afbus_disconnected (DBusConnection *connection, + DBusError *error) +{ + return bus_driver_afbus_upload_match_rule (connection, "", + NFDBUS_CMD_REMOVEALLMATCH, error); +} |