diff --git a/bus/Makefile.am b/bus/Makefile.am index 6cbc09a..cf3a962 100644 --- a/bus/Makefile.am +++ b/bus/Makefile.am @@ -63,6 +63,15 @@ endif endif endif +if HAVE_AFBUS +AFBUS_SOURCE = \ + driver-afbus.c \ + driver-afbus.h \ + $(NULL) +else +AFBUS_SOURCE = +endif + BUS_SOURCES= \ activation.c \ activation.h \ @@ -83,6 +92,7 @@ BUS_SOURCES= \ dispatch.h \ driver.c \ driver.h \ + $(AFBUS_SOURCE) \ expirelist.c \ expirelist.h \ policy.c \ diff --git a/bus/bus.c b/bus/bus.c index e80e708..5038958 100644 --- a/bus/bus.c +++ b/bus/bus.c @@ -52,11 +52,20 @@ struct BusContext char *type; char *servicehelper; char *address; + + /* if we are in AF_BUS compat mode, contains the AF_BUS address */ + char *main_address; + char *pidfile; char *user; char *log_prefix; DBusLoop *loop; DBusList *servers; + + /* One of the servers may be a AF_BUS server. We can have at most only one + * AF_BUS server. */ + DBusServer *main_afbus_server; + BusConnections *connections; BusActivation *activation; BusRegistry *registry; @@ -207,6 +216,29 @@ free_server_data (void *data) dbus_free (bd); } +static void +shutdown_server (BusContext *context, + DBusServer *server) +{ + if (server == NULL || + !dbus_server_get_is_connected (server)) + return; + + if (!dbus_server_set_watch_functions (server, + NULL, NULL, NULL, + context, + NULL)) + _dbus_assert_not_reached ("setting watch functions to NULL failed"); + + if (!dbus_server_set_timeout_functions (server, + NULL, NULL, NULL, + context, + NULL)) + _dbus_assert_not_reached ("setting timeout functions to NULL failed"); + + dbus_server_disconnect (server); +} + static dbus_bool_t setup_server (BusContext *context, DBusServer *server, @@ -275,6 +307,7 @@ process_config_first_time_only (BusContext *context, DBusString log_prefix; DBusList *link; DBusList **addresses; + DBusList **addresses_if_possible; const char *user, *pidfile; char **auth_mechanisms; DBusList **auth_mechanisms_list; @@ -448,6 +481,7 @@ process_config_first_time_only (BusContext *context, while (link != NULL) { DBusServer *server; + dbus_bool_t is_afbus; server = dbus_server_listen (link->data, error); if (server == NULL) @@ -455,17 +489,78 @@ process_config_first_time_only (BusContext *context, _DBUS_ASSERT_ERROR_IS_SET (error); goto failed; } - else if (!setup_server (context, server, auth_mechanisms, error)) + is_afbus = dbus_server_is_afbus (server); + + /* Only accept one AF_BUS server */ + if (is_afbus && context->main_afbus_server != NULL) { + dbus_server_disconnect (server); + dbus_server_unref (server); + dbus_set_error (error, + DBUS_ERROR_MULTIPLE_AFBUS, + "Cannot listen on multiple AF_BUS address"); + goto failed; + } + + if (!setup_server (context, server, auth_mechanisms, error)) + { + shutdown_server (context, server); + dbus_server_unref (server); _DBUS_ASSERT_ERROR_IS_SET (error); goto failed; } + if (is_afbus) + context->main_afbus_server = server; if (!_dbus_list_append (&context->servers, server)) goto oom; link = _dbus_list_get_next_link (addresses, link); } + + addresses_if_possible = + bus_config_parser_get_addresses_if_possible (parser); + + link = _dbus_list_get_first_link (addresses_if_possible); + while (link != NULL) + { + DBusServer *server; + dbus_bool_t is_afbus; + + server = dbus_server_listen (link->data, NULL); + if (server == NULL) + { + link = _dbus_list_get_next_link (addresses_if_possible, link); + continue; + } + is_afbus = dbus_server_is_afbus (server); + + /* Only accept one AF_BUS server */ + if (is_afbus && context->main_afbus_server != NULL) + { + dbus_server_disconnect (server); + dbus_server_unref (server); + + link = _dbus_list_get_next_link (addresses_if_possible, link); + continue; + } + + if (!setup_server (context, server, auth_mechanisms, NULL)) + { + shutdown_server (context, server); + dbus_server_unref (server); + + link = _dbus_list_get_next_link (addresses_if_possible, link); + continue; + } + if (is_afbus) + context->main_afbus_server = server; + + if (!_dbus_list_append (&context->servers, server)) + goto oom; + + link = _dbus_list_get_next_link (addresses_if_possible, link); + } } context->fork = bus_config_parser_get_fork (parser); @@ -499,6 +594,7 @@ process_config_every_time (BusContext *context, DBusError *error) { DBusString full_address; + DBusString main_address; DBusList *link; DBusList **dirs; char *addr; @@ -518,6 +614,12 @@ process_config_every_time (BusContext *context, return FALSE; } + if (!_dbus_string_init (&main_address)) + { + BUS_SET_OOM (error); + return FALSE; + } + /* get our limits and timeout lengths */ bus_config_parser_get_limits (parser, &context->limits); @@ -554,6 +656,18 @@ process_config_every_time (BusContext *context, goto failed; } + if (dbus_server_is_afbus (link->data)) + { + /* There must be at most one AF_BUS server */ + _dbus_assert (_dbus_string_get_length (&main_address) == 0); + + if (!_dbus_string_append (&main_address, addr)) + { + BUS_SET_OOM (error); + goto failed; + } + } + dbus_free (addr); addr = NULL; @@ -561,7 +675,10 @@ process_config_every_time (BusContext *context, } if (is_reload) - dbus_free (context->address); + { + dbus_free (context->address); + dbus_free (context->main_address); + } if (!_dbus_string_copy_data (&full_address, &context->address)) { @@ -569,6 +686,12 @@ process_config_every_time (BusContext *context, goto failed; } + if (!_dbus_string_copy_data (&main_address, &context->main_address)) + { + BUS_SET_OOM (error); + goto failed; + } + /* get the service directories */ dirs = bus_config_parser_get_service_dirs (parser); @@ -609,6 +732,7 @@ process_config_every_time (BusContext *context, failed: _dbus_string_free (&full_address); + _dbus_string_free (&main_address); if (addr) dbus_free (addr); @@ -990,29 +1114,6 @@ bus_context_reload_config (BusContext *context, return ret; } -static void -shutdown_server (BusContext *context, - DBusServer *server) -{ - if (server == NULL || - !dbus_server_get_is_connected (server)) - return; - - if (!dbus_server_set_watch_functions (server, - NULL, NULL, NULL, - context, - NULL)) - _dbus_assert_not_reached ("setting watch functions to NULL failed"); - - if (!dbus_server_set_timeout_functions (server, - NULL, NULL, NULL, - context, - NULL)) - _dbus_assert_not_reached ("setting timeout functions to NULL failed"); - - dbus_server_disconnect (server); -} - void bus_context_shutdown (BusContext *context) { @@ -1099,6 +1200,7 @@ bus_context_unref (BusContext *context) dbus_free (context->log_prefix); dbus_free (context->type); dbus_free (context->address); + dbus_free (context->main_address); dbus_free (context->user); dbus_free (context->servicehelper); @@ -1134,6 +1236,12 @@ bus_context_get_address (BusContext *context) } const char* +bus_context_get_main_address (BusContext *context) +{ + return context->main_address; +} + +const char* bus_context_get_servicehelper (BusContext *context) { return context->servicehelper; diff --git a/bus/bus.h b/bus/bus.h index 3597884..f2a55f2 100644 --- a/bus/bus.h +++ b/bus/bus.h @@ -88,6 +88,7 @@ dbus_bool_t bus_context_get_id (BusContext DBusString *uuid); const char* bus_context_get_type (BusContext *context); const char* bus_context_get_address (BusContext *context); +const char* bus_context_get_main_address (BusContext *context); const char* bus_context_get_servicehelper (BusContext *context); dbus_bool_t bus_context_get_systemd_activation (BusContext *context); BusRegistry* bus_context_get_registry (BusContext *context); diff --git a/bus/config-parser-common.c b/bus/config-parser-common.c index c522ff4..8747835 100644 --- a/bus/config-parser-common.c +++ b/bus/config-parser-common.c @@ -63,6 +63,10 @@ bus_config_parser_element_name_to_type (const char *name) { return ELEMENT_LISTEN; } + else if (strcmp (name, "listen_if_possible") == 0) + { + return ELEMENT_LISTEN_IF_POSSIBLE; + } else if (strcmp (name, "auth") == 0) { return ELEMENT_AUTH; @@ -145,6 +149,8 @@ bus_config_parser_element_type_to_name (ElementType type) return "user"; case ELEMENT_LISTEN: return "listen"; + case ELEMENT_LISTEN_IF_POSSIBLE: + return "listen_if_possible"; case ELEMENT_AUTH: return "auth"; case ELEMENT_POLICY: diff --git a/bus/config-parser-common.h b/bus/config-parser-common.h index 186bf4c..6ea509c 100644 --- a/bus/config-parser-common.h +++ b/bus/config-parser-common.h @@ -31,6 +31,7 @@ typedef enum ELEMENT_INCLUDE, ELEMENT_USER, ELEMENT_LISTEN, + ELEMENT_LISTEN_IF_POSSIBLE, ELEMENT_AUTH, ELEMENT_POLICY, ELEMENT_LIMIT, diff --git a/bus/config-parser.c b/bus/config-parser.c index 07e8fbb..fffc0e1 100644 --- a/bus/config-parser.c +++ b/bus/config-parser.c @@ -96,6 +96,8 @@ struct BusConfigParser DBusList *listen_on; /**< List of addresses to listen to */ + DBusList *listen_on_if_possible; /**< List of addresses to listen to, but continue on failure */ + DBusList *mechanisms; /**< Auth mechanisms */ DBusList *service_dirs; /**< Directories to look for session services in */ @@ -327,6 +329,9 @@ merge_included (BusConfigParser *parser, while ((link = _dbus_list_pop_first_link (&included->listen_on))) _dbus_list_append_link (&parser->listen_on, link); + while ((link = _dbus_list_pop_first_link (&included->listen_on_if_possible))) + _dbus_list_append_link (&parser->listen_on_if_possible, link); + while ((link = _dbus_list_pop_first_link (&included->mechanisms))) _dbus_list_append_link (&parser->mechanisms, link); @@ -497,6 +502,12 @@ bus_config_parser_unref (BusConfigParser *parser) _dbus_list_clear (&parser->listen_on); + _dbus_list_foreach (&parser->listen_on_if_possible, + (DBusForeachFunction) dbus_free, + NULL); + + _dbus_list_clear (&parser->listen_on_if_possible); + _dbus_list_foreach (&parser->service_dirs, (DBusForeachFunction) dbus_free, NULL); @@ -774,6 +785,19 @@ start_busconfig_child (BusConfigParser *parser, return TRUE; } + else if (element_type == ELEMENT_LISTEN_IF_POSSIBLE) + { + if (!check_no_attributes (parser, "listen_if_possible", attribute_names, attribute_values, error)) + return FALSE; + + if (push_element (parser, ELEMENT_LISTEN_IF_POSSIBLE) == NULL) + { + BUS_SET_OOM (error); + return FALSE; + } + + return TRUE; + } else if (element_type == ELEMENT_AUTH) { if (!check_no_attributes (parser, "auth", attribute_names, attribute_values, error)) @@ -2018,6 +2042,7 @@ bus_config_parser_end_element (BusConfigParser *parser, case ELEMENT_USER: case ELEMENT_CONFIGTYPE: case ELEMENT_LISTEN: + case ELEMENT_LISTEN_IF_POSSIBLE: case ELEMENT_PIDFILE: case ELEMENT_AUTH: case ELEMENT_SERVICEDIR: @@ -2518,6 +2543,24 @@ bus_config_parser_content (BusConfigParser *parser, } break; + case ELEMENT_LISTEN_IF_POSSIBLE: + { + char *s; + + e->had_content = TRUE; + + if (!_dbus_string_copy_data (content, &s)) + goto nomem; + + if (!_dbus_list_append (&parser->listen_on_if_possible, + s)) + { + dbus_free (s); + goto nomem; + } + } + break; + case ELEMENT_AUTH: { char *s; @@ -2646,6 +2689,12 @@ bus_config_parser_get_addresses (BusConfigParser *parser) } DBusList** +bus_config_parser_get_addresses_if_possible (BusConfigParser *parser) +{ + return &parser->listen_on_if_possible; +} + +DBusList** bus_config_parser_get_mechanisms (BusConfigParser *parser) { return &parser->mechanisms; @@ -3123,6 +3172,9 @@ config_parsers_equal (const BusConfigParser *a, if (!lists_of_c_strings_equal (a->listen_on, b->listen_on)) return FALSE; + if (!lists_of_c_strings_equal (a->listen_on_if_possible, b->listen_on_if_possible)) + return FALSE; + if (!lists_of_c_strings_equal (a->mechanisms, b->mechanisms)) return FALSE; diff --git a/bus/config-parser.h b/bus/config-parser.h index ba5bf74..f8a3c07 100644 --- a/bus/config-parser.h +++ b/bus/config-parser.h @@ -61,6 +61,7 @@ dbus_bool_t bus_config_parser_finished (BusConfigParser *parser, const char* bus_config_parser_get_user (BusConfigParser *parser); const char* bus_config_parser_get_type (BusConfigParser *parser); DBusList** bus_config_parser_get_addresses (BusConfigParser *parser); +DBusList** bus_config_parser_get_addresses_if_possible (BusConfigParser *parser); DBusList** bus_config_parser_get_mechanisms (BusConfigParser *parser); dbus_bool_t bus_config_parser_get_fork (BusConfigParser *parser); dbus_bool_t bus_config_parser_get_allow_anonymous (BusConfigParser *parser); diff --git a/bus/connection.c b/bus/connection.c index d69758c..2d68c84 100644 --- a/bus/connection.c +++ b/bus/connection.c @@ -30,9 +30,16 @@ #include "signals.h" #include "expirelist.h" #include "selinux.h" +#include "driver-afbus.h" #include #include #include +#ifdef HAVE_AFBUS +#include +#endif + +#include /* See NOTES */ +#include /* Trim executed commands to this length; we want to keep logs readable */ #define MAX_LOG_COMMAND_LEN 50 @@ -94,6 +101,13 @@ typedef struct char *cached_loginfo_string; BusSELinuxID *selinux_id; + dbus_bool_t peer_address_set; + struct sockaddr_storage peer_address; + socklen_t peer_addrlen; + DBusConnection *proxy_connection; /**< If this is a compat connection, + proxy_connection is the proxy + connection to AF_BUS */ + long connection_tv_sec; /**< Time when we connected (seconds component) */ long connection_tv_usec; /**< Time when we connected (microsec component) */ int stamp; /**< connections->stamp last time we were traversed */ @@ -184,12 +198,33 @@ adjust_connections_for_uid (BusConnections *connections, } } +static DBusHandlerResult +proxy_connection_message_filter (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + DBusConnection *source_connection = user_data; + + if (dbus_message_get_destination (message) == NULL && + dbus_message_is_signal (message, + DBUS_INTERFACE_LOCAL, + "Disconnected")) + { + dbus_connection_close (source_connection); + return DBUS_HANDLER_RESULT_HANDLED; + } + + dbus_connection_send (source_connection, message, NULL); + return DBUS_HANDLER_RESULT_HANDLED; +} + void bus_connection_disconnected (DBusConnection *connection) { BusConnectionData *d; BusService *service; BusMatchmaker *matchmaker; + DBusError error; d = BUS_CONNECTION_DATA (connection); _dbus_assert (d != NULL); @@ -197,6 +232,20 @@ bus_connection_disconnected (DBusConnection *connection) _dbus_verbose ("%s disconnected, dropping all service ownership and releasing\n", d->name ? d->name : "(inactive)"); + if (d->proxy_connection) + { + dbus_connection_remove_filter (d->proxy_connection, + proxy_connection_message_filter, connection); + dbus_connection_close (d->proxy_connection); + dbus_connection_unref (d->proxy_connection); + d->proxy_connection = NULL; + } + + dbus_error_init (&error); + + if (!bus_driver_afbus_disconnected(connection, &error)) + dbus_error_free (&error); + /* Delete our match rules */ if (d->n_match_rules > 0) { @@ -309,6 +358,18 @@ bus_connection_disconnected (DBusConnection *connection) dbus_connection_unref (connection); } +DBusConnection * +bus_connection_get_proxy_connection (DBusConnection *connection) +{ + BusConnectionData *d; + + d = BUS_CONNECTION_DATA (connection); + + _dbus_assert (d != NULL); + + return d->proxy_connection; +} + static dbus_bool_t add_connection_watch (DBusWatch *watch, void *data) @@ -588,6 +649,52 @@ oom: return FALSE; } +#ifdef HAVE_AFBUS +static dbus_bool_t +bus_connections_setup_proxy_connection (BusConnections *connections, + DBusConnection *connection, + const char *main_address) +{ + BusConnectionData *d = BUS_CONNECTION_DATA (connection); + DBusError error; + dbus_bool_t retval = FALSE; + + dbus_error_init (&error); + + d->proxy_connection + = dbus_connection_open_private (main_address, &error); + if (dbus_error_is_set (&error)) + { + dbus_error_free (&error); + goto out; + } + + dbus_connection_set_route_peer_messages (d->proxy_connection, TRUE); + + dbus_connection_set_dispatch_status_function (d->proxy_connection, + dispatch_status_function, + bus_context_get_loop (connections->context), NULL); + + if (!dbus_connection_add_filter(d->proxy_connection, + proxy_connection_message_filter, connection, NULL)) + goto out; + + + if (!dbus_connection_set_watch_functions (d->proxy_connection, + add_connection_watch, + remove_connection_watch, + toggle_connection_watch, + connection, + NULL)) + goto out; + + return TRUE; + +out: + return retval; +} +#endif + dbus_bool_t bus_connections_setup_connection (BusConnections *connections, DBusConnection *connection) @@ -596,7 +703,6 @@ bus_connections_setup_connection (BusConnections *connections, BusConnectionData *d; dbus_bool_t retval; DBusError error; - d = dbus_new0 (BusConnectionData, 1); @@ -622,8 +728,32 @@ bus_connections_setup_connection (BusConnections *connections, dbus_connection_set_route_peer_messages (connection, TRUE); retval = FALSE; - dbus_error_init (&error); + +#ifdef HAVE_AFBUS + d->peer_addrlen = sizeof(d->peer_address); + d->peer_address_set = dbus_connection_get_peer_address (connection, + &d->peer_address, (long int *)&d->peer_addrlen); + + /* If this connection is not AF_BUS, and we have a AF_BUS + * server, we are in compat mode and we need to setup a + * new connection */ + if (d->peer_address_set && + ((struct sockaddr *)&d->peer_address)->sa_family != AF_BUS) + { + const char *main_address; + main_address = bus_context_get_main_address (connections->context); + if (main_address && main_address[0] != '\0') + { + if (!bus_connections_setup_proxy_connection (connections, connection, + main_address)) + { + goto out; + } + } + } +#endif + d->selinux_id = bus_selinux_init_connection_id (connection, &error); if (dbus_error_is_set (&error)) @@ -2383,3 +2513,21 @@ bus_connection_get_peak_bus_names (DBusConnection *connection) return d->peak_bus_names; } #endif /* DBUS_ENABLE_STATS */ + +int +bus_connection_get_peer_address (DBusConnection *connection, + struct sockaddr **peer_address, + socklen_t *peer_addrlen) +{ + BusConnectionData *d; + + d = BUS_CONNECTION_DATA (connection); + + if (!d->peer_address_set) + return FALSE; + + *peer_address = (struct sockaddr *) &d->peer_address; + *peer_addrlen = d->peer_addrlen; + + return TRUE; +} diff --git a/bus/connection.h b/bus/connection.h index c936021..95856f3 100644 --- a/bus/connection.h +++ b/bus/connection.h @@ -24,6 +24,8 @@ #ifndef BUS_CONNECTION_H #define BUS_CONNECTION_H +#include +#include #include #include #include "bus.h" @@ -103,8 +105,9 @@ dbus_bool_t bus_connection_complete (DBusConnection *connection, const DBusString *name, DBusError *error); -/* called by dispatch.c when the connection is dropped */ -void bus_connection_disconnected (DBusConnection *connection); +/* called by dispatch.c */ +void bus_connection_disconnected (DBusConnection *connection); +DBusConnection *bus_connection_get_proxy_connection (DBusConnection *connection); dbus_bool_t bus_connection_is_in_unix_group (DBusConnection *connection, unsigned long gid); @@ -151,4 +154,9 @@ int bus_connections_get_peak_bus_names_per_conn (BusConnections *connections); int bus_connection_get_peak_match_rules (DBusConnection *connection); int bus_connection_get_peak_bus_names (DBusConnection *connection); +/* called by driver-afbus.c */ +int bus_connection_get_peer_address (DBusConnection *connection, + struct sockaddr **peer_address, + socklen_t *peer_addrlen); + #endif /* BUS_CONNECTION_H */ diff --git a/bus/dispatch.c b/bus/dispatch.c index 7a96f9d..4feae05 100644 --- a/bus/dispatch.c +++ b/bus/dispatch.c @@ -27,6 +27,7 @@ #include "dispatch.h" #include "connection.h" #include "driver.h" +#include "driver-afbus.h" #include "services.h" #include "activation.h" #include "utils.h" @@ -129,6 +130,18 @@ bus_dispatch_matches (BusTransaction *transaction, BUS_SET_OOM (error); return FALSE; } + + /* If using AF_BUS and the message came to the daemon, the client sending + * the message doesn't yet know the address for this service, so notify + * it of the address associated with the service. */ + if (!bus_driver_afbus_emit_forwarded (transaction, + sender, + addressed_recipient, + dbus_message_get_destination (message))) + { + _dbus_verbose ("bus_driver_afbus_emit_forwarded() failed\n"); + return FALSE; + } } /* Now dispatch to others who look interested in this message */ @@ -180,6 +193,7 @@ bus_dispatch (DBusConnection *connection, BusContext *context; DBusHandlerResult result; DBusConnection *addressed_recipient; + DBusConnection *proxy_connection; result = DBUS_HANDLER_RESULT_HANDLED; @@ -241,6 +255,15 @@ bus_dispatch (DBusConnection *connection, } } + /* Directly send the message to the proxy without analysing it */ + proxy_connection = bus_connection_get_proxy_connection (connection); + if (proxy_connection) + { + if (!dbus_connection_send (proxy_connection, message, NULL)) + BUS_SET_OOM (&error); + goto out; + } + /* Create our transaction */ transaction = bus_transaction_new (context); if (transaction == NULL) diff --git a/bus/driver-afbus.c b/bus/driver-afbus.c new file mode 100644 index 0000000..70edafb --- /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 + +#include "driver-afbus.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/bus/driver-afbus.h b/bus/driver-afbus.h new file mode 100644 index 0000000..9c40111 --- /dev/null +++ b/bus/driver-afbus.h @@ -0,0 +1,96 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* driver-afbus.h 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 + * + */ + +#ifndef BUS_DRIVER_AFBUS_H +#define BUS_DRIVER_AFBUS_H + +#include +#include +#include +#include "connection.h" + +#if HAVE_AFBUS +dbus_bool_t bus_driver_afbus_add_match_rule (DBusConnection *connection, + const char *rule, + DBusError *error); +dbus_bool_t bus_driver_afbus_remove_match_rule (DBusConnection *connection, + const char *rule, + DBusError *error); +dbus_bool_t bus_driver_afbus_append_unique_name (DBusConnection *connection, + DBusString *str); +dbus_bool_t bus_driver_afbus_emit_forwarded (BusTransaction *transaction, + DBusConnection *connection, + DBusConnection *addressed_recipient, + const char *service_name); +dbus_bool_t bus_driver_afbus_assign_address (DBusConnection *connection); +dbus_bool_t bus_driver_afbus_disconnected (DBusConnection *connection, + DBusError *error); +#else +static inline +dbus_bool_t bus_driver_afbus_add_match_rule (DBusConnection *connection, + const char *rule, + DBusError *error) +{ + return TRUE; +} + +static inline +dbus_bool_t bus_driver_afbus_remove_match_rule (DBusConnection *connection, + const char *rule, + DBusError *error) +{ + return TRUE; +} + +static inline +dbus_bool_t bus_driver_afbus_append_unique_name (DBusConnection *connection, + DBusString *str) +{ + return TRUE; +} + +static inline +dbus_bool_t bus_driver_afbus_emit_forwarded (BusTransaction *transaction, + DBusConnection *connection, + DBusConnection *addressed_recipient, + const char *service_name) +{ + return TRUE; +} + +static inline +dbus_bool_t bus_driver_afbus_assign_address (DBusConnection *connection) +{ + return TRUE; +} + +static inline +dbus_bool_t bus_driver_afbus_disconnected (DBusConnection *connection, + DBusError *error) +{ + return TRUE; +} + +#endif + +#endif /* BUS_DRIVER_AFBUS_H */ diff --git a/bus/driver.c b/bus/driver.c index 574e0f3..11767c8 100644 --- a/bus/driver.c +++ b/bus/driver.c @@ -26,6 +26,7 @@ #include "activation.h" #include "connection.h" #include "driver.h" +#include "driver-afbus.h" #include "dispatch.h" #include "services.h" #include "selinux.h" @@ -182,8 +183,9 @@ bus_driver_send_service_acquired (DBusConnection *connection, } static dbus_bool_t -create_unique_client_name (BusRegistry *registry, - DBusString *str) +create_unique_client_name (DBusConnection *connection, + BusRegistry *registry, + DBusString *str) { /* We never want to use the same unique client name twice, because * we want to guarantee that if you send a message to a given unique @@ -214,19 +216,23 @@ create_unique_client_name (BusRegistry *registry, _dbus_assert (next_major_number > 0); _dbus_assert (next_minor_number >= 0); - /* appname:MAJOR-MINOR */ + /* unique name: + * :[AF-BUS.BUS-ADDRESS.]MAJOR.MINOR */ if (!_dbus_string_append (str, ":")) return FALSE; - if (!_dbus_string_append_int (str, next_major_number)) - return FALSE; + if (!bus_driver_afbus_append_unique_name (connection, str)) + { + if (!_dbus_string_append_int (str, next_major_number)) + return FALSE; - if (!_dbus_string_append (str, ".")) - return FALSE; + if (!_dbus_string_append (str, ".")) + return FALSE; - if (!_dbus_string_append_int (str, next_minor_number)) - return FALSE; + if (!_dbus_string_append_int (str, next_minor_number)) + return FALSE; + } next_minor_number += 1; @@ -287,7 +293,7 @@ bus_driver_handle_hello (DBusConnection *connection, registry = bus_connection_get_registry (connection); - if (!create_unique_client_name (registry, &unique_name)) + if (!create_unique_client_name (connection, registry, &unique_name)) { BUS_SET_OOM (error); goto out_0; @@ -610,6 +616,13 @@ bus_driver_handle_acquire_service (DBusConnection *connection, goto out; } + /* If using AF_BUS, assign an address to this peer */ + if (!bus_driver_afbus_assign_address (connection)) + { + BUS_SET_OOM (error); + goto out; + } + retval = TRUE; out: @@ -988,6 +1001,9 @@ bus_driver_handle_add_match (DBusConnection *connection, if (rule == NULL) goto failed; + if (!bus_driver_afbus_add_match_rule (connection, text, error)) + goto failed; + matchmaker = bus_connection_get_matchmaker (connection); if (!bus_matchmaker_add_rule (matchmaker, rule)) @@ -1051,6 +1067,9 @@ bus_driver_handle_remove_match (DBusConnection *connection, message, error)) goto failed; + if (!bus_driver_afbus_remove_match_rule (connection, text, error)) + goto failed; + matchmaker = bus_connection_get_matchmaker (connection); if (!bus_matchmaker_remove_rule_by_value (matchmaker, rule, error)) @@ -1779,6 +1798,13 @@ static InterfaceHandler interface_handlers[] = { { DBUS_INTERFACE_INTROSPECTABLE, introspectable_message_handlers, NULL }, #ifdef DBUS_ENABLE_STATS { BUS_INTERFACE_STATS, stats_message_handlers, NULL }, +#ifdef HAVE_AFBUS + { DBUS_INTERFACE_AFBUS, NULL, + " \n" + " \n" + " \n" + " \n" }, +#endif #endif { NULL, NULL, NULL } }; diff --git a/bus/session.conf.in b/bus/session.conf.in index e121ff9..09a7ecf 100644 --- a/bus/session.conf.in +++ b/bus/session.conf.in @@ -12,7 +12,8 @@ the behavior of child processes. --> - @DBUS_SESSION_BUS_DEFAULT_ADDRESS@ + afbus:tmpdir=/tmp + unix:tmpdir=/tmp diff --git a/bus/system.conf.in b/bus/system.conf.in index 92f4cc4..515509f 100644 --- a/bus/system.conf.in +++ b/bus/system.conf.in @@ -39,6 +39,7 @@ means use abstract namespace, don't really create filesystem file; only Linux supports this. Use path=/whatever on other systems.) --> + afbus:@DBUS_SYSTEM_SOCKET@.afbus @DBUS_SYSTEM_BUS_DEFAULT_ADDRESS@ diff --git a/configure.ac b/configure.ac index 4f86e9d..51a48fd 100644 --- a/configure.ac +++ b/configure.ac @@ -1509,6 +1509,20 @@ if test "x$with_systemdsystemunitdir" != xno; then fi AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != xno ]) +## Check for AF_DBUS +AC_MSG_CHECKING([for AF_BUS socket family]) +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [[return socket (PF_BUS, SOCK_STREAM, 0);]])], + [have_afbus=yes], + [have_afbus=no]) +AC_MSG_RESULT([$have_afbus]) +if test "x$have_afbus" = "xyes"; then + AC_DEFINE(HAVE_AFBUS, 1, [Define if AF_BUS is available]) +fi +AM_CONDITIONAL(HAVE_AFBUS, test "x$have_afbus" = "xyes") + ##### Set up location for system bus socket if ! test -z "$with_system_socket"; then DBUS_SYSTEM_SOCKET=$with_system_socket @@ -1648,7 +1662,11 @@ AC_SUBST(TEST_SOCKET_DIR) AC_DEFINE_UNQUOTED(DBUS_TEST_SOCKET_DIR, "$TEST_SOCKET_DIR", [Where to put test sockets]) if test "x$dbus_unix" = xyes; then - TEST_LISTEN="unix:tmpdir=$TEST_SOCKET_DIR" + if test "x$have_afbus" = "xyes"; then + TEST_LISTEN="afbus:tmpdir=$TEST_SOCKET_DIR" + else + TEST_LISTEN="unix:tmpdir=$TEST_SOCKET_DIR" + fi else TEST_LISTEN="tcp:host=localhost" fi @@ -1668,6 +1686,8 @@ if test x$dbus_win = xyes; then DBUS_SESSION_BUS_DEFAULT_ADDRESS="$with_dbus_session_bus_default_address" elif test x$have_launchd = xyes; then DBUS_SESSION_BUS_DEFAULT_ADDRESS="launchd:env=DBUS_LAUNCHD_SESSION_BUS_SOCKET" +elif test x$have_afbus = xyes; then + DBUS_SESSION_BUS_DEFAULT_ADDRESS="afbus:tmpdir=$DBUS_SESSION_SOCKET_DIR" else DBUS_SESSION_BUS_DEFAULT_ADDRESS="unix:tmpdir=$DBUS_SESSION_SOCKET_DIR" fi diff --git a/dbus/Makefile.am b/dbus/Makefile.am index bb5ccca..b925419 100644 --- a/dbus/Makefile.am +++ b/dbus/Makefile.am @@ -87,6 +87,16 @@ else launchd_source = endif +if HAVE_AFBUS +afbus_source = \ + dbus-server-afbus.c \ + dbus-server-afbus.h \ + dbus-transport-afbus.c \ + dbus-transport-afbus.h +else +afbus_source = +endif + DBUS_LIB_arch_sources = \ dbus-uuidgen.c \ dbus-uuidgen.h \ @@ -95,6 +105,7 @@ DBUS_LIB_arch_sources = \ DBUS_SHARED_arch_sources = \ $(launchd_source) \ + $(afbus_source) \ dbus-file-unix.c \ dbus-pipe-unix.c \ dbus-sysdeps-unix.c \ diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index ee33b6c..5f3a459 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -45,6 +45,8 @@ #include "dbus-bus.h" #include "dbus-marshal-basic.h" +#include + #ifdef DBUS_DISABLE_CHECKS #define TOOK_LOCK_CHECK(connection) #define RELEASING_LOCK_CHECK(connection) @@ -2054,9 +2056,15 @@ _dbus_connection_send_preallocated_and_unlock (DBusConnection *connection, dbus_uint32_t *client_serial) { DBusDispatchStatus status; + const char *sender; HAVE_LOCK_CHECK (connection); - + + /* Set the sender field */ + sender = dbus_bus_get_unique_name (connection); + if (sender) + dbus_message_set_sender (message, sender); + _dbus_connection_send_preallocated_unlocked_no_update (connection, preallocated, message, client_serial); @@ -3359,6 +3367,7 @@ dbus_connection_send_with_reply (DBusConnection *connection, DBusPendingCall *pending; dbus_int32_t serial = -1; DBusDispatchStatus status; + const char *sender; _dbus_return_val_if_fail (connection != NULL, FALSE); _dbus_return_val_if_fail (message != NULL, FALSE); @@ -3401,6 +3410,11 @@ dbus_connection_send_with_reply (DBusConnection *connection, return FALSE; } + /* Set the sender field */ + sender = dbus_bus_get_unique_name (connection); + if (sender) + dbus_message_set_sender (message, sender); + /* Assign a serial to the message */ serial = dbus_message_get_serial (message); if (serial == 0) @@ -5439,6 +5453,27 @@ dbus_connection_set_route_peer_messages (DBusConnection *connection, CONNECTION_UNLOCK (connection); } +dbus_bool_t +dbus_connection_get_peer_address (DBusConnection *connection, + void *addr, + long *len) +{ + dbus_bool_t res; + int fd; + socklen_t _len = *len; + + _dbus_return_val_if_fail (connection != NULL, FALSE); + + res = _dbus_transport_get_socket_fd (connection->transport, &fd); + if (!res) + return res; + + res = getpeername(fd, (struct sockaddr *) addr, &_len) == 0; + if (res) + *len = _len; + return res; +} + /** * Adds a message filter. Filters are handlers that are run on all * incoming messages, prior to the objects registered with diff --git a/dbus/dbus-connection.h b/dbus/dbus-connection.h index fe4d04e..81fe9f1 100644 --- a/dbus/dbus-connection.h +++ b/dbus/dbus-connection.h @@ -287,6 +287,10 @@ void dbus_connection_set_allow_anonymous (DBusConnection DBUS_EXPORT void dbus_connection_set_route_peer_messages (DBusConnection *connection, dbus_bool_t value); +DBUS_EXPORT +dbus_bool_t dbus_connection_get_peer_address (DBusConnection *connection, + void *addr, + long *len); /* Filters */ diff --git a/dbus/dbus-protocol.h b/dbus/dbus-protocol.h index 60605ab..71c2870 100644 --- a/dbus/dbus-protocol.h +++ b/dbus/dbus-protocol.h @@ -458,6 +458,12 @@ extern "C" { /** XML document type declaration of the introspection format version 1.0 */ #define DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "\n" +/* AF_BUS errors */ + +/** Tried to use a netlink socket and it failed. */ +#define DBUS_ERROR_NETLINK "org.freedesktop.DBus.Error.NetLink" +#define DBUS_ERROR_MULTIPLE_AFBUS "org.freedesktop.DBus.Error.MultipleAFBUS" + /** @} */ #ifdef __cplusplus diff --git a/dbus/dbus-server-afbus.c b/dbus/dbus-server-afbus.c new file mode 100644 index 0000000..1d8e9bd --- /dev/null +++ b/dbus/dbus-server-afbus.c @@ -0,0 +1,397 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-server-unix.c Server implementation for Unix network protocols. + * + * 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 +#include +#include +#include "dbus-connection-internal.h" +#include "dbus-server-afbus.h" +#include "dbus-server-protected.h" +#include "dbus-string.h" +#include "dbus-sysdeps.h" +#include "dbus-transport.h" +#include "dbus-transport-afbus.h" + +/** + * @defgroup DBusServerAfbus DBusServer implementation for AF_BUS sockets + * @ingroup DBusInternals + * @brief Implementation details of DBusServer for AF_BUS sockets + * + * @{ + */ +/** + * Opaque object representing a AF_BUS-based server implementation. + */ +typedef struct DBusServerAfbus DBusServerAfbus; + +/** + * Implementation details of DBusServerAfbus. All members + * are private. + */ +struct DBusServerAfbus +{ + DBusServer base; /**< Parent class members. */ + int fd; /**< File descriptor. */ + DBusWatch *watch; /**< File descriptor watch. */ + DBusString address; /**< The server address */ +}; + +static void +afbus_finalize (DBusServer *server) +{ + DBusServerAfbus *afbus_server = (DBusServerAfbus *) server; + + _dbus_server_finalize_base (server); + + if (afbus_server->watch != NULL) + { + _dbus_watch_unref (afbus_server->watch); + afbus_server->watch = NULL; + } + + dbus_free (server); +} + +static void +afbus_disconnect (DBusServer *server) +{ + DBusServerAfbus *afbus_server = (DBusServerAfbus *) server; + + HAVE_LOCK_CHECK (server); + + if (afbus_server->watch) + { + _dbus_server_remove_watch (server, afbus_server->watch); + _dbus_watch_invalidate (afbus_server->watch); + _dbus_watch_unref (afbus_server->watch); + afbus_server->watch = NULL; + } + + _dbus_close_socket (afbus_server->fd, NULL); + afbus_server->fd = -1; + + HAVE_LOCK_CHECK (server); +} + +static const DBusServerVTable afbus_vtable = { + afbus_finalize, + afbus_disconnect +}; + +static dbus_bool_t +handle_new_client (DBusServer *server, + int client_fd) +{ + DBusTransport *transport; + DBusConnection *connection; + DBusNewConnectionFunction new_connection_function; + void *new_connection_data; + + _dbus_verbose ("Creating new client connection with fd %d\n", client_fd); + + if (!_dbus_set_fd_nonblocking (client_fd, NULL)) + { + SERVER_UNLOCK (server); + return TRUE; + } + + transport = _dbus_transport_new_for_afbus (client_fd, &server->guid_hex, NULL); + if (transport == NULL) + { + _dbus_close_socket (client_fd, NULL); + SERVER_UNLOCK (server); + return FALSE; + } + + if (!_dbus_transport_set_auth_mechanisms (transport, + (const char **) server->auth_mechanisms)) + { + _dbus_transport_unref (transport); + SERVER_UNLOCK (server); + return FALSE; + } + + /* note that client_fd is now owned by the transport, and will be + * closed on transport disconnection/finalization + */ + connection = _dbus_connection_new_for_transport (transport); + _dbus_transport_unref (transport); + transport = NULL; /* now under the connection lock */ + + if (connection == NULL) + { + SERVER_UNLOCK (server); + return FALSE; + } + + /* See if someone wants to handle this new connection, self-referencing + * for paranoia. + */ + new_connection_function = server->new_connection_function; + new_connection_data = server->new_connection_data; + + _dbus_server_ref_unlocked (server); + SERVER_UNLOCK (server); + + if (new_connection_function) + { + (* new_connection_function) (server, connection, + new_connection_data); + } + dbus_server_unref (server); + + /* If no one grabbed a reference, the connection will die. */ + _dbus_connection_close_if_only_one_ref (connection); + dbus_connection_unref (connection); + + return TRUE; +} + +static dbus_bool_t +afbus_handle_watch (DBusWatch *watch, + unsigned int flags, + void *data) +{ + DBusServerAfbus *afbus_server = (DBusServerAfbus *) data; + DBusServer *server = (DBusServer *) data; + + SERVER_LOCK (server); + +#ifndef DBUS_DISABLE_ASSERT + _dbus_assert (afbus_server->watch == watch); +#endif + + _dbus_verbose ("Handling client connection, flags 0x%x\n", flags); + + if (flags & DBUS_WATCH_READABLE) + { + int client_fd; + + client_fd = _dbus_accept (afbus_server->fd); + if (client_fd < 0) + { + /* EINTR handled for us */ + + if (_dbus_get_is_errno_eagain_or_ewouldblock ()) + _dbus_verbose ("No client available to accept after all\n"); + else + _dbus_verbose ("Failed to accept a client connection: %s\n", + _dbus_strerror_from_errno ()); + + SERVER_UNLOCK (server); + } + else + { + if (!handle_new_client (server, client_fd)) + _dbus_verbose ("Rejected client connection due to lack of memory\n"); + } + } + + if (flags & DBUS_WATCH_ERROR) + _dbus_verbose ("Error on server listening socket\n"); + + if (flags & DBUS_WATCH_HANGUP) + _dbus_verbose ("Hangup on server listening socket\n"); + + return TRUE; +} + +static DBusServer * +_dbus_server_new_for_afbus (int fd, const char *path, DBusError *error) +{ + DBusServerAfbus *afbus_server; + DBusServer *server; + struct sockaddr_bus sock_address; + + afbus_server = dbus_new0 (DBusServerAfbus, 1); + if (afbus_server == NULL) + return NULL; + + if (!_dbus_string_init (&afbus_server->address) || + !_dbus_string_append (&afbus_server->address, "afbus:path=") || + !_dbus_string_append (&afbus_server->address, path)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed_1; + } + + afbus_server->fd = fd; + + sock_address.sbus_family = AF_BUS; + strcpy (sock_address.sbus_path, path); + if (bind (afbus_server->fd, (struct sockaddr *) &sock_address, sizeof (sock_address)) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to bind socket \"%s\": %s", + path, _dbus_strerror (errno)); + goto failed_1; + } + + if (listen (afbus_server->fd, 30 /* backlog */) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to listen on AF_BUS socket: %s", + _dbus_strerror (errno)); + goto failed_1; + } + + afbus_server->watch = _dbus_watch_new (fd, + DBUS_WATCH_READABLE, + TRUE, + afbus_handle_watch, afbus_server, + NULL); + if (afbus_server->watch == NULL) + goto failed_1; + + if (!_dbus_server_init_base (&afbus_server->base, + &afbus_vtable, &afbus_server->address)) + goto failed_2; + + server = (DBusServer *) afbus_server; + server->is_afbus = TRUE; + + + SERVER_LOCK (server); + + if (!_dbus_server_add_watch (&afbus_server->base, + afbus_server->watch)) + { + SERVER_UNLOCK (server); + _dbus_server_finalize_base (&afbus_server->base); + + goto failed_2; + } + + SERVER_UNLOCK (server); + + _dbus_server_trace_ref (&afbus_server->base, 0, 1, "new_for_afbus"); + return (DBusServer *) afbus_server; + + failed_2: + _dbus_watch_unref (afbus_server->watch); + + failed_1: + _dbus_close_socket (afbus_server->fd, NULL); + dbus_free (afbus_server); + + return NULL; +} + +/** + * Starts a DBusServer listening on AF_BUS socket. + * Sets error if the result is not OK. + * + * @param entry an address entry + * @param server_p location to store a new DBusServer, or #NULL on failure. + * @param error location to store rationale for failure on bad address + * @returns the outcome + * + */ +DBusServerListenResult +_dbus_server_listen_afbus (DBusAddressEntry *entry, + DBusServer **server_p, + DBusError *error) +{ + const char *method, *path, *tmpdir; + int listen_fd; + + *server_p = NULL; + + method = dbus_address_entry_get_method (entry); + if (strcmp (method, "afbus") != 0) + { + /* If we don't handle the method, we return NULL with the + * error unset + */ + _DBUS_ASSERT_ERROR_IS_CLEAR(error); + return DBUS_SERVER_LISTEN_NOT_HANDLED; + } + + path = dbus_address_entry_get_value (entry, "path"); + tmpdir = dbus_address_entry_get_value (entry, "tmpdir"); + + if (path == NULL && tmpdir == NULL) + { + _dbus_set_bad_address(error, "unix", + "path or tmpdir", + NULL); + return DBUS_SERVER_LISTEN_BAD_ADDRESS; + } + + if (path && tmpdir) + { + _dbus_set_bad_address(error, NULL, NULL, + "cannot specify both \"path\" and \"tmpdir\" at the same time"); + return DBUS_SERVER_LISTEN_BAD_ADDRESS; + } + + if (!_dbus_open_socket (&listen_fd, PF_BUS, SOCK_SEQPACKET, BUS_PROTO_DBUS, error)) + return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; + + if (!_dbus_set_fd_nonblocking (listen_fd, error)) + goto failed_1; + + if (tmpdir != NULL) + { + DBusString full_path; + DBusString filename; + + if (!_dbus_string_init (&full_path)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; + } + + if (!_dbus_string_init (&filename)) + { + _dbus_string_free (&full_path); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; + } + + if (!_dbus_string_append (&filename, + "dbus-") || + !_dbus_generate_random_ascii (&filename, 10) || + !_dbus_string_append (&full_path, tmpdir) || + !_dbus_concat_dir_and_file (&full_path, &filename)) + { + _dbus_string_free (&full_path); + _dbus_string_free (&filename); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; + } + + path = _dbus_string_get_const_data (&full_path); + } + + *server_p = _dbus_server_new_for_afbus (listen_fd, path, error); + if (*server_p == NULL) + goto failed_1; + + return DBUS_SERVER_LISTEN_OK; + + failed_1: + _dbus_close_socket (listen_fd, NULL); + + return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; +} diff --git a/dbus/dbus-server-afbus.h b/dbus/dbus-server-afbus.h new file mode 100644 index 0000000..8e5ad31 --- /dev/null +++ b/dbus/dbus-server-afbus.h @@ -0,0 +1,36 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-server-unix.h Server implementation for Unix network protocols. + * + * 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 + * + */ +#ifndef DBUS_SERVER_AFBUS_H +#define DBUS_SERVER_AFBUS_H + +#include "dbus-server-protected.h" + +DBUS_BEGIN_DECLS + +DBusServerListenResult _dbus_server_listen_afbus (DBusAddressEntry *entry, + DBusServer **server_p, + DBusError *error); + +DBUS_END_DECLS + +#endif diff --git a/dbus/dbus-server-protected.h b/dbus/dbus-server-protected.h index dd5234b..b423cb9 100644 --- a/dbus/dbus-server-protected.h +++ b/dbus/dbus-server-protected.h @@ -89,6 +89,9 @@ struct DBusServer #ifndef DBUS_DISABLE_CHECKS unsigned int have_server_lock : 1; /**< Does someone have the server mutex locked */ #endif +#ifdef HAVE_AFBUS + unsigned int is_afbus : 1; /**< TRUE if this server listen on a AF_BUS socket */ +#endif }; dbus_bool_t _dbus_server_init_base (DBusServer *server, diff --git a/dbus/dbus-server.c b/dbus/dbus-server.c index b62c2b4..2a23252 100644 --- a/dbus/dbus-server.c +++ b/dbus/dbus-server.c @@ -25,6 +25,9 @@ #include "dbus-server.h" #include "dbus-server-unix.h" #include "dbus-server-socket.h" +#ifdef HAVE_AFBUS +#include "dbus-server-afbus.h" +#endif #include "dbus-string.h" #ifdef DBUS_BUILD_TESTS #include "dbus-server-debug-pipe.h" @@ -529,6 +532,9 @@ static const struct { } listen_funcs[] = { { _dbus_server_listen_socket } , { _dbus_server_listen_platform_specific } +#ifdef HAVE_AFBUS + , { _dbus_server_listen_afbus } +#endif #ifdef DBUS_BUILD_TESTS , { _dbus_server_listen_debug_pipe } #endif @@ -844,6 +850,30 @@ dbus_server_get_address (DBusServer *server) } /** + * Returns TRUE if the server listens on an AF_BUS address + * + * @param server the server + * @returns TRUE if the server listens on an AF_BUS address + */ +dbus_bool_t +dbus_server_is_afbus (DBusServer *server) +{ + dbus_bool_t retval; + + _dbus_return_val_if_fail (server != NULL, FALSE); + +#ifdef HAVE_AFBUS + SERVER_LOCK (server); + retval = server->is_afbus; + SERVER_UNLOCK (server); +#else + retval = FALSE; +#endif + + return retval; +} + +/** * Returns the unique ID of the server, as a newly-allocated * string which must be freed by the caller. This ID is * normally used by clients to tell when two #DBusConnection diff --git a/dbus/dbus-server.h b/dbus/dbus-server.h index bdbefa0..49cb9cf 100644 --- a/dbus/dbus-server.h +++ b/dbus/dbus-server.h @@ -62,6 +62,8 @@ dbus_bool_t dbus_server_get_is_connected (DBusServer *server); DBUS_EXPORT char* dbus_server_get_address (DBusServer *server); DBUS_EXPORT +dbus_bool_t dbus_server_is_afbus (DBusServer *server); +DBUS_EXPORT char* dbus_server_get_id (DBusServer *server); DBUS_EXPORT void dbus_server_set_new_connection_function (DBusServer *server, diff --git a/dbus/dbus-shared.h b/dbus/dbus-shared.h index 6a57670..b6b29b3 100644 --- a/dbus/dbus-shared.h +++ b/dbus/dbus-shared.h @@ -80,6 +80,10 @@ typedef enum #define DBUS_PATH_DBUS "/org/freedesktop/DBus" /** The object path used in local/in-process-generated messages. */ #define DBUS_PATH_LOCAL "/org/freedesktop/DBus/Local" +#ifdef HAVE_AFBUS +/** The object path used for AF_BUS sockets. */ +#define DBUS_PATH_AFBUS "/org/freedesktop/DBus/AF_BUS" +#endif /* Interfaces, these #define don't do much other than * catch typos at compile time @@ -92,6 +96,10 @@ typedef enum #define DBUS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties" /** The interface supported by most dbus peers */ #define DBUS_INTERFACE_PEER "org.freedesktop.DBus.Peer" +#ifdef HAVE_AFBUS +/** The interface supported by AF_BUS transport */ +#define DBUS_INTERFACE_AFBUS "org.freedesktop.DBus.AF_BUS" +#endif /** This is a special interface whose methods can only be invoked * by the local implementation (messages from remote apps aren't diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index cef8bd3..26e3d00 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -122,7 +122,7 @@ #endif /* Solaris */ -static dbus_bool_t +dbus_bool_t _dbus_open_socket (int *fd_p, int domain, int type, diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h index 4052cda..a589486 100644 --- a/dbus/dbus-sysdeps.h +++ b/dbus/dbus-sysdeps.h @@ -125,6 +125,11 @@ typedef unsigned long dbus_gid_t; * */ +dbus_bool_t _dbus_open_socket (int *fd_p, + int domain, + int type, + int protocol, + DBusError *error); dbus_bool_t _dbus_close_socket (int fd, DBusError *error); int _dbus_read_socket (int fd, diff --git a/dbus/dbus-transport-afbus.c b/dbus/dbus-transport-afbus.c new file mode 100644 index 0000000..9574bca --- /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 +#include +#include +#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; +} diff --git a/dbus/dbus-transport-afbus.h b/dbus/dbus-transport-afbus.h new file mode 100644 index 0000000..c94e32d --- /dev/null +++ b/dbus/dbus-transport-afbus.h @@ -0,0 +1,41 @@ +/* -*- 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 + * + */ +#ifndef DBUS_TRANSPORT_AFBUS_H +#define DBUS_TRANSPORT_AFBUS_H + +#include +#include +#include + +DBUS_BEGIN_DECLS + +DBusTransport *_dbus_transport_new_for_afbus (int fd, + const DBusString *server_guid, + const DBusString *address); +DBusTransportOpenResult _dbus_transport_open_afbus (DBusAddressEntry *entry, + DBusTransport **transport_p, + DBusError *error); + +DBUS_END_DECLS + +#endif diff --git a/dbus/dbus-transport-protected.h b/dbus/dbus-transport-protected.h index 44b9d78..9becbdb 100644 --- a/dbus/dbus-transport-protected.h +++ b/dbus/dbus-transport-protected.h @@ -69,6 +69,10 @@ struct DBusTransportVTable dbus_bool_t (* get_socket_fd) (DBusTransport *transport, int *fd_p); /**< Get socket file descriptor */ + + void (* process_incoming_message) (DBusTransport *transport, + DBusMessage *message); + /**> Method to allow transport to filter messages */ }; /** diff --git a/dbus/dbus-transport-socket.c b/dbus/dbus-transport-socket.c index 544d00a..a3951ef 100644 --- a/dbus/dbus-transport-socket.c +++ b/dbus/dbus-transport-socket.c @@ -22,6 +22,7 @@ */ #include +#include #include "dbus-internals.h" #include "dbus-connection-internal.h" #include "dbus-nonce.h" @@ -38,43 +39,13 @@ * @{ */ -/** - * Opaque object representing a socket file descriptor transport. - */ -typedef struct DBusTransportSocket DBusTransportSocket; - -/** - * Implementation details of DBusTransportSocket. All members are private. - */ -struct DBusTransportSocket -{ - DBusTransport base; /**< Parent instance */ - int fd; /**< File descriptor. */ - DBusWatch *read_watch; /**< Watch for readability. */ - DBusWatch *write_watch; /**< Watch for writability. */ - - int max_bytes_read_per_iteration; /**< To avoid blocking too long. */ - int max_bytes_written_per_iteration; /**< To avoid blocking too long. */ - - int message_bytes_written; /**< Number of bytes of current - * outgoing message that have - * been written. - */ - DBusString encoded_outgoing; /**< Encoded version of current - * outgoing message. - */ - DBusString encoded_incoming; /**< Encoded version of current - * incoming data. - */ -}; - static void free_watches (DBusTransport *transport) { DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; _dbus_verbose ("start\n"); - + if (socket_transport->read_watch) { if (transport->connection) @@ -104,7 +75,7 @@ socket_finalize (DBusTransport *transport) DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; _dbus_verbose ("\n"); - + free_watches (transport); _dbus_string_free (&socket_transport->encoded_outgoing); @@ -136,7 +107,16 @@ check_write_watch (DBusTransport *transport) _dbus_transport_ref (transport); if (_dbus_transport_get_is_authenticated (transport)) - needed = _dbus_connection_has_messages_to_send_unlocked (transport->connection); + { + if (!socket_transport->auth_notified && + socket_transport->vtable->authenticated != NULL) + { + socket_transport->auth_notified = TRUE; + socket_transport->vtable->authenticated (socket_transport); + } + + needed = _dbus_connection_has_messages_to_send_unlocked (transport->connection); + } else { if (transport->send_credentials_pending) @@ -146,7 +126,7 @@ check_write_watch (DBusTransport *transport) DBusAuthState auth_state; auth_state = _dbus_auth_do_work (transport->auth); - + /* If we need memory we install the write watch just in case, * if there's no need for it, it will get de-installed * next time we try reading. @@ -191,9 +171,18 @@ check_read_watch (DBusTransport *transport) _dbus_transport_ref (transport); if (_dbus_transport_get_is_authenticated (transport)) - need_read_watch = - (_dbus_counter_get_size_value (transport->live_messages) < transport->max_live_messages_size) && - (_dbus_counter_get_unix_fd_value (transport->live_messages) < transport->max_live_messages_unix_fds); + { + if (!socket_transport->auth_notified && + socket_transport->vtable->authenticated != NULL) + { + socket_transport->auth_notified = TRUE; + socket_transport->vtable->authenticated (socket_transport); + } + + need_read_watch = + (_dbus_counter_get_size_value (transport->live_messages) < transport->max_live_messages_size) && + (_dbus_counter_get_unix_fd_value (transport->live_messages) < transport->max_live_messages_unix_fds); + } else { if (transport->receive_credentials_pending) @@ -489,6 +478,59 @@ do_authentication (DBusTransport *transport, return TRUE; } +static int +_write_socket (DBusTransportSocket *socket_transport, + DBusMessage *message, + const DBusString *buffer, + int start, + int len) +{ + int bytes_written; + + if (socket_transport->vtable->write_socket != NULL) + { + bytes_written = socket_transport->vtable->write_socket (socket_transport, + message, + buffer, start, len); + } + else + { + bytes_written = _dbus_write_socket (socket_transport->fd, + buffer, + start, len); + } + + return bytes_written; +} + +static int +_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 bytes_written; + + if (socket_transport->vtable->write_socket_two != NULL) + { + bytes_written = socket_transport->vtable->write_socket_two (socket_transport, message, + header, header_start, header_len, + body, body_start, body_len); + } + else + { + bytes_written = _dbus_write_socket_two (socket_transport->fd, + header, header_start, header_len, + body, body_start, body_len); + } + + return bytes_written; +} + /* returns false on oom */ static dbus_bool_t do_writing (DBusTransport *transport) @@ -579,12 +621,13 @@ do_writing (DBusTransport *transport) _dbus_verbose ("encoded message is %d bytes\n", total_bytes_to_write); #endif - + bytes_written = - _dbus_write_socket (socket_transport->fd, - &socket_transport->encoded_outgoing, - socket_transport->message_bytes_written, - total_bytes_to_write - socket_transport->message_bytes_written); + _write_socket (socket_transport, + message, + &socket_transport->encoded_outgoing, + socket_transport->message_bytes_written, + total_bytes_to_write - socket_transport->message_bytes_written); } else { @@ -623,21 +666,23 @@ do_writing (DBusTransport *transport) if (socket_transport->message_bytes_written < header_len) { bytes_written = - _dbus_write_socket_two (socket_transport->fd, - header, - socket_transport->message_bytes_written, - header_len - socket_transport->message_bytes_written, - body, - 0, body_len); + _write_socket_two (socket_transport, + message, + header, + socket_transport->message_bytes_written, + header_len - socket_transport->message_bytes_written, + body, + 0, body_len); } else { bytes_written = - _dbus_write_socket (socket_transport->fd, - body, - (socket_transport->message_bytes_written - header_len), - body_len - - (socket_transport->message_bytes_written - header_len)); + _write_socket (socket_transport, + message, + body, + (socket_transport->message_bytes_written - header_len), + body_len - + (socket_transport->message_bytes_written - header_len)); } } } @@ -696,7 +741,7 @@ do_reading (DBusTransport *transport) { DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; DBusString *buffer; - int bytes_read; + int bytes_read, bytes_available; int total; dbus_bool_t oom; @@ -730,7 +775,12 @@ do_reading (DBusTransport *transport) if (!dbus_watch_get_enabled (socket_transport->read_watch)) return TRUE; - + + if (ioctl (socket_transport->fd, FIONREAD, &bytes_available) < 0) + bytes_available = socket_transport->max_bytes_read_per_iteration; + else if (bytes_available < socket_transport->max_bytes_read_per_iteration) + bytes_available = socket_transport->max_bytes_read_per_iteration; + if (_dbus_auth_needs_decoding (transport->auth)) { /* Does fd passing even make sense with encoded data? */ @@ -741,7 +791,7 @@ do_reading (DBusTransport *transport) else bytes_read = _dbus_read_socket (socket_transport->fd, &socket_transport->encoded_incoming, - socket_transport->max_bytes_read_per_iteration); + bytes_available); _dbus_assert (_dbus_string_get_length (&socket_transport->encoded_incoming) == bytes_read); @@ -796,7 +846,7 @@ do_reading (DBusTransport *transport) bytes_read = _dbus_read_socket_with_unix_fds(socket_transport->fd, buffer, - socket_transport->max_bytes_read_per_iteration, + bytes_available, fds, &n_fds); if (bytes_read >= 0 && n_fds > 0) @@ -808,7 +858,7 @@ do_reading (DBusTransport *transport) #endif { bytes_read = _dbus_read_socket (socket_transport->fd, - buffer, socket_transport->max_bytes_read_per_iteration); + buffer, bytes_available); } _dbus_message_loader_return_buffer (transport->loader, @@ -1207,6 +1257,19 @@ socket_get_socket_fd (DBusTransport *transport, return TRUE; } +static void +socket_process_incoming_message (DBusTransport *transport, + DBusMessage *message) +{ + DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; + + if (socket_transport->vtable->message_received != NULL) + { + (* socket_transport->vtable->message_received) (socket_transport, + message); + } +} + static const DBusTransportVTable socket_vtable = { socket_finalize, socket_handle_watch, @@ -1214,37 +1277,40 @@ static const DBusTransportVTable socket_vtable = { socket_connection_set, socket_do_iteration, socket_live_messages_changed, - socket_get_socket_fd + socket_get_socket_fd, + socket_process_incoming_message }; /** - * Creates a new transport for the given socket file descriptor. The file - * descriptor must be nonblocking (use _dbus_set_fd_nonblocking() to - * make it so). This function is shared by various transports that - * boil down to a full duplex file descriptor. + * Initializes the base class members of DBusTransportSocket. Chained up to + * by subclasses in their constructor. The server GUID is the + * globally unique ID for the server creating this connection + * and will be #NULL for the client side of a connection. The GUID + * is in hex format. * - * @param fd the file descriptor. + * @param socket_transport the transport being created. + * @param fd The socket for this transport + * @param vtable the subclass vtable. * @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. + * @param address the address of the transport + * @returns #TRUE on success. */ -DBusTransport* -_dbus_transport_new_for_socket (int fd, - const DBusString *server_guid, - const DBusString *address) +dbus_bool_t +_dbus_transport_socket_init_base (DBusTransportSocket *socket_transport, + int fd, + const DBusTransportSocketVTable *vtable, + const DBusString *server_guid, + const DBusString *address) { - DBusTransportSocket *socket_transport; - - socket_transport = dbus_new0 (DBusTransportSocket, 1); - if (socket_transport == NULL) - return NULL; - if (!_dbus_string_init (&socket_transport->encoded_outgoing)) goto failed_0; if (!_dbus_string_init (&socket_transport->encoded_incoming)) goto failed_1; - + + socket_transport->auth_notified = FALSE; + socket_transport->vtable = vtable; + socket_transport->write_watch = _dbus_watch_new (fd, DBUS_WATCH_WRITABLE, FALSE, @@ -1275,7 +1341,7 @@ _dbus_transport_new_for_socket (int fd, socket_transport->max_bytes_read_per_iteration = 2048; socket_transport->max_bytes_written_per_iteration = 2048; - return (DBusTransport*) socket_transport; + return TRUE; failed_4: _dbus_watch_invalidate (socket_transport->read_watch); @@ -1288,8 +1354,46 @@ _dbus_transport_new_for_socket (int fd, failed_1: _dbus_string_free (&socket_transport->encoded_outgoing); failed_0: - dbus_free (socket_transport); - return NULL; + + return FALSE; +} + +/** + * Creates a new transport for the given socket file descriptor. The file + * descriptor must be nonblocking (use _dbus_set_fd_nonblocking() to + * make it so). This function is shared by various transports that + * boil down to a full duplex file 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_socket (int fd, + const DBusString *server_guid, + const DBusString *address) +{ + DBusTransportSocket *socket_transport; + static const DBusTransportSocketVTable vtable = { + NULL + }; + + socket_transport = dbus_new0 (DBusTransportSocket, 1); + if (socket_transport == NULL) + return NULL; + + if (!_dbus_transport_socket_init_base (socket_transport, + fd, + &vtable, + server_guid, + address)) + { + dbus_free (socket_transport); + return NULL; + } + + return (DBusTransport *) socket_transport; } /** diff --git a/dbus/dbus-transport-socket.h b/dbus/dbus-transport-socket.h index 8aefae3..ed7fdcc 100644 --- a/dbus/dbus-transport-socket.h +++ b/dbus/dbus-transport-socket.h @@ -27,6 +27,80 @@ DBUS_BEGIN_DECLS +/** + * Opaque object representing a socket file descriptor transport. + */ +typedef struct DBusTransportSocket DBusTransportSocket; + +/** + * The virtual table that must be implemented to + * create a socket-based transport. + */ +typedef struct DBusTransportSocketVTable DBusTransportSocketVTable; + +struct DBusTransportSocketVTable +{ + int (* write_socket) (DBusTransportSocket *socket_transport, + DBusMessage *message, + const DBusString *buffer, + int start, + int len); + /**< Write part of a message to the transport */ + + int (* 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); + /**< Write header and body to the transport */ + + void (* authenticated) (DBusTransportSocket *socket_transport); + /**< Notification of authentication successful */ + + void (* message_received) (DBusTransportSocket *socket_transport, + DBusMessage *message); + /**< Notification of a message received */ +}; + +/** + * Implementation details of DBusTransportSocket. All members are private. + */ +struct DBusTransportSocket +{ + DBusTransport base; /**< Parent instance */ + int fd; /**< File descriptor. */ + DBusWatch *read_watch; /**< Watch for readability. */ + DBusWatch *write_watch; /**< Watch for writability. */ + + int max_bytes_read_per_iteration; /**< To avoid blocking too long. */ + int max_bytes_written_per_iteration; /**< To avoid blocking too long. */ + + int message_bytes_written; /**< Number of bytes of current + * outgoing message that have + * been written. + */ + DBusString encoded_outgoing; /**< Encoded version of current + * outgoing message. + */ + DBusString encoded_incoming; /**< Encoded version of current + * incoming data. + */ + dbus_bool_t auth_notified; /**< Have we notified subclasses + * we are authenticated? + */ + const DBusTransportSocketVTable *vtable; /**< Virtual table of transport methods. */ +}; + +dbus_bool_t _dbus_transport_socket_init_base (DBusTransportSocket *socket_transport, + int fd, + const DBusTransportSocketVTable *vtable, + const DBusString *server_guid, + const DBusString *address); +void _dbus_transport_socket_finalize_base (DBusTransportSocket *socket_transport); + DBusTransport* _dbus_transport_new_for_socket (int fd, const DBusString *server_guid, const DBusString *address); diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c index 6b58fda..44923da 100644 --- a/dbus/dbus-transport.c +++ b/dbus/dbus-transport.c @@ -25,6 +25,9 @@ #include "dbus-transport-protected.h" #include "dbus-transport-unix.h" #include "dbus-transport-socket.h" +#ifdef HAVE_AFBUS +#include "dbus-transport-afbus.h" +#endif #include "dbus-connection-internal.h" #include "dbus-watch.h" #include "dbus-auth.h" @@ -348,6 +351,9 @@ static const struct { { _dbus_transport_open_socket }, { _dbus_transport_open_platform_specific }, { _dbus_transport_open_autolaunch } +#ifdef HAVE_AFBUS + , { _dbus_transport_open_afbus } +#endif #ifdef DBUS_BUILD_TESTS , { _dbus_transport_open_debug_pipe } #endif @@ -980,6 +986,28 @@ _dbus_transport_do_iteration (DBusTransport *transport, _dbus_verbose ("end\n"); } +/** + * Allows transports to process incoming messages + * + * @param transport the transport. + * @param message the message to be filtered + */ +void +_dbus_transport_process_incoming_message (DBusTransport *transport, + DBusMessage *message) +{ + _dbus_verbose ("Transport message filtering\n"); + + if (transport->vtable->process_incoming_message != NULL) + { + _dbus_transport_ref (transport); + (* transport->vtable->process_incoming_message) (transport, message); + _dbus_transport_unref (transport); + } + + _dbus_verbose ("end\n"); +} + static dbus_bool_t recover_unused_bytes (DBusTransport *transport) { @@ -1134,6 +1162,7 @@ _dbus_transport_queue_messages (DBusTransport *transport) _dbus_assert (link != NULL); message = link->data; + _dbus_transport_process_incoming_message (transport, message); _dbus_verbose ("queueing received message %p\n", message); diff --git a/dbus/dbus-transport.h b/dbus/dbus-transport.h index 4b82151..194b2c6 100644 --- a/dbus/dbus-transport.h +++ b/dbus/dbus-transport.h @@ -52,6 +52,8 @@ dbus_bool_t _dbus_transport_set_connection (DBusTransport void _dbus_transport_do_iteration (DBusTransport *transport, unsigned int flags, int timeout_milliseconds); +void _dbus_transport_process_incoming_message (DBusTransport *transport, + DBusMessage *message); DBusDispatchStatus _dbus_transport_get_dispatch_status (DBusTransport *transport); dbus_bool_t _dbus_transport_queue_messages (DBusTransport *transport); diff --git a/test/data/valid-config-files/basic.conf b/test/data/valid-config-files/basic.conf index 5297097..af46d8b 100644 --- a/test/data/valid-config-files/basic.conf +++ b/test/data/valid-config-files/basic.conf @@ -4,6 +4,8 @@ mybususer unix:path=/foo/bar tcp:port=1234 + tcp:port=1235 + pigeon:port=carrier,speed=110mph basic.d /usr/share/foo nonexistent.conf