summaryrefslogtreecommitdiff
path: root/bus/driver-afbus.c
diff options
context:
space:
mode:
Diffstat (limited to 'bus/driver-afbus.c')
-rw-r--r--bus/driver-afbus.c345
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);
+}