summaryrefslogtreecommitdiff
path: root/dbus/dbus-transport-afbus.c
diff options
context:
space:
mode:
Diffstat (limited to 'dbus/dbus-transport-afbus.c')
-rw-r--r--dbus/dbus-transport-afbus.c394
1 files changed, 394 insertions, 0 deletions
diff --git a/dbus/dbus-transport-afbus.c b/dbus/dbus-transport-afbus.c
new file mode 100644
index 00000000..9574bcaf
--- /dev/null
+++ b/dbus/dbus-transport-afbus.c
@@ -0,0 +1,394 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-transport-socket.h Socket subclasses of DBusTransport
+ *
+ * Copyright (C) 212 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 <errno.h>
+#include <sys/socket.h>
+#include "dbus-connection-internal.h"
+#include "dbus-hash.h"
+#include "dbus-transport-afbus.h"
+#include "dbus-transport-socket.h"
+#include "dbus-watch.h"
+
+static DBusHashTable *wkn_addresses_cache = NULL;
+
+/**
+ * @defgroup DBusTransportAfbus AF_BUS-based DBusTransport implementation
+ * @ingroup DBusInternals
+ * @brief Implementation details of DBusTransport on AF_BUS sockets
+ *
+ * @{
+ */
+
+/**
+ * Opaque object representing a AF_BUS-based transport.
+ */
+typedef struct DBusTransportAfbus DBusTransportAfbus;
+
+/**
+ * Implementation details of DBusTransportAfbus. All members are private.
+ */
+struct DBusTransportAfbus
+{
+ DBusTransportSocket base; /**< Parent instance */
+};
+
+static dbus_bool_t
+get_write_destination (int fd,
+ DBusMessage *message,
+ struct sockaddr_bus *sock)
+{
+ socklen_t addrlen;
+ const char *destination;
+ const char *sender;
+
+ /* if the sender is the bus driver, don't set the sockaddr, just let the
+ * message be delivered to the peer socket
+ */
+ sender = dbus_message_get_sender (message);
+ if (sender && strcmp (sender, DBUS_SERVICE_DBUS) == 0)
+ {
+ return FALSE;
+ }
+
+ addrlen = sizeof(struct sockaddr_bus);
+ if (getsockname (fd, (struct sockaddr *) sock, &addrlen) != 0)
+ return FALSE;
+ if (addrlen != sizeof(struct sockaddr_bus))
+ return FALSE;
+ if (sock->sbus_family != AF_BUS)
+ return FALSE;
+
+ destination = dbus_message_get_destination (message);
+ if (destination && strcmp (destination, DBUS_SERVICE_DBUS) == 0)
+ {
+ /* a message for the bus driver */
+ sock->sbus_addr.s_addr = 0;
+ return TRUE;
+ }
+
+ if (destination != NULL && strlen (destination) > 0)
+ {
+ dbus_uint64_t peer = 0x0000000000000000ULL;
+
+ /* If destination is a unique name, just retrieve the peer address from it */
+ if (strncmp (destination, ":AF-BUS.", 8) == 0)
+ {
+ DBusString tmp;
+
+ _dbus_string_init_const (&tmp, &destination[8]);
+ if (!_dbus_string_parse_uint (&tmp, 0, (unsigned long *) &peer, NULL))
+ peer = 0x0000000000000000ULL;
+ _dbus_string_free (&tmp);
+ }
+ else
+ {
+ /* a message for a well known name */
+ if (wkn_addresses_cache != NULL)
+ {
+ dbus_uint64_t *peer_pointer;
+
+ peer_pointer = (dbus_uint64_t *) _dbus_hash_table_lookup_string (wkn_addresses_cache, destination);
+ if (peer_pointer != NULL)
+ peer = *peer_pointer;
+ }
+ }
+
+ sock->sbus_addr.s_addr = peer;
+
+ return TRUE;
+ }
+ else
+ {
+ /* a multicast message */
+ sock->sbus_addr.s_addr = 0x0000ffffffffffffULL;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int
+afbus_write_socket (DBusTransportSocket *socket_transport,
+ DBusMessage *message,
+ const DBusString *buffer,
+ int start,
+ int len)
+{
+ int fd, bytes_written;
+ struct sockaddr_bus sock;
+
+ if (!_dbus_transport_get_socket_fd ((DBusTransport *) socket_transport, &fd))
+ {
+ _dbus_verbose ("Couldn't get socket's file descriptor\n");
+ return -1;
+ }
+
+ if (get_write_destination (fd, message, &sock))
+ {
+ const char *data;
+
+ /* Send the data to specific address */
+ data = _dbus_string_get_const_data_len (buffer, start, len);
+
+ bytes_written = sendto (fd, data, len, MSG_NOSIGNAL,
+ (struct sockaddr *) &sock, sizeof (struct sockaddr_bus));
+ }
+ else
+ bytes_written = _dbus_write_socket (fd, buffer, start, len);
+
+ return bytes_written;
+}
+
+static int
+afbus_write_socket_two (DBusTransportSocket *socket_transport,
+ DBusMessage *message,
+ const DBusString *header,
+ int header_start,
+ int header_len,
+ const DBusString *body,
+ int body_start,
+ int body_len)
+{
+ int fd, bytes_written;
+ struct sockaddr_bus sock;
+
+ if (!_dbus_transport_get_socket_fd ((DBusTransport *) socket_transport, &fd))
+ {
+ _dbus_verbose ("Couldn't get socket's file descriptor\n");
+ return -1;
+ }
+
+ if (get_write_destination (fd, message, &sock))
+ {
+ struct iovec vectors[2];
+ const char *data1, *data2;
+ struct msghdr m;
+
+ data1 = _dbus_string_get_const_data_len (header, header_start, header_len);
+ if (body != NULL)
+ data2 = _dbus_string_get_const_data_len (body, body_start, body_len);
+ else
+ {
+ data2 = NULL;
+ body_start = body_len = 0;
+ }
+
+ vectors[0].iov_base = (char *) data1;
+ vectors[0].iov_len = header_len;
+ vectors[1].iov_base = (char *) data2;
+ vectors[1].iov_len = body_len;
+
+ _DBUS_ZERO(m);
+ m.msg_iov = vectors;
+ m.msg_iovlen = data2 ? 2 : 1;
+ m.msg_name = &sock;
+ m.msg_namelen = sizeof (sock);
+
+ again:
+ bytes_written = sendmsg (fd, &m, MSG_NOSIGNAL);
+ if (bytes_written < 0 && errno == EINTR)
+ goto again;
+ }
+ else
+ bytes_written = _dbus_write_socket_two (fd, header, header_start, header_len, body, body_start, body_len);
+
+ return bytes_written;
+}
+
+static void
+afbus_authenticated (DBusTransportSocket *socket_transport)
+{
+ if (socket_transport->base.is_server)
+ {
+ /* Make the client join the bus when authenticated, so that it can send
+ * unicast messages to peers other than the daemon */
+ if (setsockopt (socket_transport->fd, SOL_BUS, BUS_JOIN_BUS, NULL, 0) != 0)
+ {
+ _dbus_verbose ("Could not join client to the bus\n");
+ }
+ }
+}
+
+static void
+afbus_message_received (DBusTransportSocket *socket_transport,
+ DBusMessage *message)
+{
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_SIGNAL)
+ {
+ const char *path, *interface, *member;
+
+ path = dbus_message_get_path (message);
+ interface = dbus_message_get_interface (message);
+ member = dbus_message_get_member (message);
+
+ if (strcmp (path, DBUS_PATH_AFBUS) == 0
+ && strcmp (interface, DBUS_INTERFACE_AFBUS) == 0
+ && strcmp (member, "Forwarded") == 0)
+ {
+ char *wkn;
+ dbus_uint64_t peer;
+ dbus_uint64_t *peer_pointer;
+
+ /* Update the cache */
+ if (wkn_addresses_cache == NULL)
+ {
+ wkn_addresses_cache = _dbus_hash_table_new (DBUS_HASH_STRING,
+ dbus_free,
+ dbus_free);
+ if (wkn_addresses_cache == NULL)
+ return;
+ }
+
+ dbus_message_get_args (message, NULL,
+ DBUS_TYPE_STRING, &wkn,
+ DBUS_TYPE_UINT64, &peer,
+ DBUS_TYPE_INVALID);
+
+ peer_pointer = dbus_new (dbus_uint64_t, 1);
+ *peer_pointer = peer;
+ _dbus_hash_table_insert_string (wkn_addresses_cache,
+ _dbus_strdup (wkn),
+ (void *) peer_pointer);
+ }
+ }
+}
+
+static const DBusTransportSocketVTable afbus_vtable = {
+ afbus_write_socket,
+ afbus_write_socket_two,
+ afbus_authenticated,
+ afbus_message_received
+};
+
+/**
+ * Creates a new AF_BUS-based transport for the given socket descriptor.
+ *
+ * @param fd the file descriptor.
+ * @param server_guid non-#NULL if this transport is on the server side of a connection
+ * @param address the transport's address
+ * @returns the new transport, or #NULL if no memory.
+ */
+DBusTransport*
+_dbus_transport_new_for_afbus (int fd,
+ const DBusString *server_guid,
+ const DBusString *address)
+{
+ DBusTransportAfbus *afbus_transport;
+
+ afbus_transport = dbus_new0 (DBusTransportAfbus, 1);
+ if (afbus_transport == NULL)
+ return NULL;
+
+ if (!_dbus_transport_socket_init_base (&afbus_transport->base,
+ fd,
+ &afbus_vtable,
+ server_guid, address))
+ goto failed_1;
+
+ return (DBusTransport *) afbus_transport;
+
+ failed_1:
+ dbus_free (afbus_transport);
+
+ return NULL;
+}
+
+/**
+ * Opens a AF_BUS-based transport.
+ *
+ * @param entry the address entry to try opening as a tcp transport.
+ * @param transport_p return location for the opened transport
+ * @param error error to be set
+ * @returns result of the attempt
+ */
+DBusTransportOpenResult
+_dbus_transport_open_afbus (DBusAddressEntry *entry,
+ DBusTransport **transport_p,
+ DBusError *error)
+{
+ const char *method, *path;
+ DBusString address;
+ int fd;
+ struct sockaddr_bus sock_address;
+
+ method = dbus_address_entry_get_method (entry);
+ if (strcmp (method, "afbus") != 0)
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return DBUS_TRANSPORT_OPEN_NOT_HANDLED;
+ }
+
+ path = dbus_address_entry_get_value (entry, "path");
+ if (path == NULL)
+ {
+ _dbus_set_bad_address (error, "afbus",
+ "path or tmpdir",
+ NULL);
+ return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
+ }
+
+ if (!_dbus_string_init (&address) ||
+ !_dbus_string_append (&address, "afbus:path=") ||
+ !_dbus_string_append (&address, path))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
+ }
+
+ if (!_dbus_open_socket (&fd, PF_BUS, SOCK_SEQPACKET, BUS_PROTO_DBUS, error))
+ goto failed_1;
+
+ if (!_dbus_set_fd_nonblocking (fd, error))
+ goto failed_2;
+
+ sock_address.sbus_family = AF_BUS;
+ strcpy (sock_address.sbus_path, path);
+ if (connect (fd, (struct sockaddr *) &sock_address, sizeof (sock_address)) < 0)
+ {
+ dbus_set_error (error,
+ _dbus_error_from_errno (errno),
+ "Failed to connect to socket %s: %s",
+ path, _dbus_strerror (errno));
+ goto failed_2;
+ }
+
+ *transport_p = _dbus_transport_new_for_afbus (fd, NULL, &address);
+ if (*transport_p == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed_2;
+ }
+
+ _dbus_string_free (&address);
+
+ return DBUS_TRANSPORT_OPEN_OK;
+
+ failed_2:
+ _dbus_close_socket (fd, NULL);
+
+ failed_1:
+ _dbus_string_free (&address);
+
+ return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
+}