return TRUE;
}
+DBusMessage *
+_dbus_decode_kmsg (DBusString *data,
+ uint64_t sender_id,
+ int *fds,
+ unsigned n_fds)
+{
+ if (_dbus_string_get_length (data) >= DBUS_MINIMUM_HEADER_SIZE)
+ {
+
+ DBusValidity validity;
+ int byte_order, fields_array_len, header_len, body_len;
+ dbus_bool_t is_gvariant;
+
+ if (_dbus_header_have_message_untrusted (DBUS_MAXIMUM_MESSAGE_LENGTH,
+ &validity,
+ &byte_order,
+ &fields_array_len,
+ &header_len,
+ &body_len,
+ data, 0,
+ _dbus_string_get_length (data),
+ &is_gvariant))
+ {
+ DBusMessage *message;
+ dbus_uint32_t n_unix_fds = 0;
+ const DBusString *type_str = NULL;
+ int type_pos = 0;
+
+ _dbus_assert (validity == DBUS_VALID);
+
+ message = dbus_message_new_empty_header (is_gvariant);
+ if (message == NULL)
+ return NULL;
+
+ /*
+ * Validate and copy over header
+ */
+ _dbus_assert (_dbus_string_get_length (&message->header.data) == 0);
+ _dbus_assert ((header_len + body_len) <= _dbus_string_get_length (data));
+
+ if (!_dbus_header_load (&message->header,
+ DBUS_VALIDATION_MODE_DATA_IS_UNTRUSTED,
+ &validity,
+ byte_order,
+ fields_array_len,
+ header_len,
+ body_len,
+ data, 0,
+ _dbus_string_get_length (data)))
+ {
+ _dbus_verbose ("Failed to load header for new message code %d\n", validity);
+ dbus_message_unref (message);
+ return NULL;
+ }
+
+ _dbus_assert (validity == DBUS_VALID);
+
+ /*
+ * Validate body
+ */
+ if (_dbus_message_is_gvariant (message))
+ {
+ validity = _dbus_validate_gvariant_body_with_reason (type_str,
+ type_pos,
+ byte_order,
+ NULL,
+ data,
+ header_len,
+ body_len);
+ }
+ else
+ {
+ _dbus_verbose ("Not valid GVariant dbus message\n");
+ dbus_message_unref (message);
+ return NULL;
+ }
+
+ if (validity != DBUS_VALID)
+ {
+ _dbus_verbose ("Failed to validate message body code %d\n", validity);
+ dbus_message_unref (message);
+ return NULL;
+ }
+
+ /*
+ * Copy over Unix FDS
+ */
+ _dbus_header_get_field_basic(&message->header,
+ DBUS_HEADER_FIELD_UNIX_FDS,
+ DBUS_TYPE_UINT32,
+ &n_unix_fds);
+
+#ifdef HAVE_UNIX_FD_PASSING
+
+ if (n_unix_fds > n_fds)
+ {
+ _dbus_verbose("Message contains references to more unix fds than were sent %u != %u\n",
+ n_unix_fds, loader->n_unix_fds);
+ dbus_message_unref (message);
+ return NULL;
+ }
+
+ /* If this was a recycled message there might still be
+ some memory allocated for the fds */
+ dbus_free(message->unix_fds);
+
+ if (n_unix_fds > 0)
+ {
+ message->unix_fds = _dbus_memdup(fds, n_unix_fds * sizeof(message->unix_fds[0]));
+ if (message->unix_fds == NULL)
+ {
+ _dbus_verbose ("Failed to allocate file descriptor array\n");
+ dbus_message_unref (message);
+ return NULL;
+ }
+
+ message->n_unix_fds_allocated = message->n_unix_fds = n_unix_fds;
+ }
+ else
+ message->unix_fds = NULL;
+#else
+ if (n_unix_fds > 0)
+ {
+ _dbus_verbose ("Hmm, message claims to come with file descriptors "
+ "but that's not supported on our platform, disconnecting.\n");
+ dbus_message_unref (message);
+ return NULL;
+ }
+#endif
+
+ /*
+ * Copy over message body
+ */
+ _dbus_assert (_dbus_string_get_length (&message->body) == 0);
+ _dbus_assert (_dbus_string_get_length (data) >= (header_len + body_len));
+
+ if (!_dbus_string_copy_len (data, header_len, body_len, &message->body, 0))
+ {
+ _dbus_verbose ("Failed to move body into new message\n");
+ dbus_message_unref (message);
+ return NULL;
+ }
+
+ _dbus_assert (_dbus_string_get_length (&message->header.data) == header_len);
+ _dbus_assert (_dbus_string_get_length (&message->body) == body_len);
+
+ set_unique_sender (message, sender_id);
+ message->locked = TRUE;
+
+ /* Yupi, we have DBusMessage* */
+ return message;
+ }
+ else
+ {
+ _dbus_verbose ("Data broken with invalid code %d\n", validity);
+ return NULL;
+ } /* if _dbus_header_have_message_untrusted() */
+
+ }
+ else
+ {
+ _dbus_verbose ("message size < DBUS_MINIMUM_HEADER_SIZE\n");
+ return NULL;
+ } /* if DBUS_MINIMUM_HEADER_SIZE */
+}
+
/**
* Peeks at first loaded message, returns #NULL if no messages have
* been queued.
case EMLINK:
dbus_set_error (error,
DBUS_ERROR_LIMITS_EXCEEDED,
- NULL,
"The maximum number of pending replies per connection has been reached");
break;
case EXFULL:
dbus_set_error (error,
DBUS_ERROR_LIMITS_EXCEEDED,
- "No space in receiver's buffer",
- destination);
+ "No space in receiver's buffer");
+ break;
+
+ case ETIMEDOUT:
+ dbus_set_error (error,
+ DBUS_ERROR_TIMEOUT,
+ "Connection does not receive a reply");
break;
default:
+ dbus_set_error (error,
+ DBUS_ERROR_FAILED,
+ "Something went wrong: %s",
+ _dbus_strerror (ret));
break;
}
return result;
}
+/* function prototypes */
+static int kdbus_message_size (const struct kdbus_msg *msg,
+ int *n_fds);
+static int kdbus_decode_dbus_message (const struct kdbus_msg *msg,
+ char *data,
+ DBusTransportKdbus *kdbus_transport,
+ int *fds,
+ int *n_fds);
+
static int
kdbus_write_msg_internal (DBusTransportKdbus *transport,
DBusMessage *message,
const char *destination,
- dbus_bool_t check_sync_reply,
+ DBusMessage **sync_reply,
dbus_bool_t check_privileges)
{
struct kdbus_msg *msg = NULL;
dbus_uint64_t items_size;
dbus_uint64_t flags = 0;
dbus_uint64_t timeout_ns_or_cookie_reply = 0;
-
+ dbus_bool_t check_sync_reply = FALSE;
dbus_error_init (&error);
+ if (sync_reply != NULL)
+ check_sync_reply = TRUE;
+
// determine destination and destination id
if (destination)
{
}
else
{
- /* puts locally generated reply into received messages queue */
- if (!add_message_to_received (reply, transport->base.connection))
- ret_size = -1;
+ if (check_sync_reply)
+ {
+ *sync_reply = reply;
+ }
+ else
+ {
+ /* puts locally generated reply into received messages queue */
+ if (!add_message_to_received (reply, transport->base.connection))
+ ret_size = -1;
+ }
}
-
goto out;
}
}
else
{
- /* puts locally generated reply into received messages queue */
- if (!add_message_to_received (reply, transport->base.connection))
- ret_size = -1;
+ if (check_sync_reply)
+ {
+ *sync_reply = reply;
+ }
+ else
+ {
+ /* puts locally generated reply into received messages queue */
+ if (!add_message_to_received (reply, transport->base.connection))
+ ret_size = -1;
+ }
}
+ goto out;
}
- if (-1 != ret_size && check_sync_reply)
- kdbus_close_message (transport, msg_reply);
+ if (check_sync_reply)
+ {
+ DBusString str;
+ int size, ret, *fds;
+ unsigned n_fds;
+ char *data;
+
+ /* check message size */
+ size = kdbus_message_size (msg_reply, &n_fds);
+ if (size <= 0)
+ {
+ ret_size = -1;
+ kdbus_close_message (transport, msg_reply);
+ goto out;
+ }
+
+ /* alloc buffer for decoded message */
+ data = dbus_malloc (sizeof (char) * size);
+ if (data == NULL)
+ {
+ ret_size = -1;
+ kdbus_close_message (transport, msg_reply);
+ goto out;
+ }
+
+ /* alloc palce for fds */
+ fds = dbus_malloc (sizeof (int) * (n_fds + 1));
+
+ /* decode dbus message */
+ ret = kdbus_decode_dbus_message (msg_reply, data,
+ transport, fds, &n_fds);
+ if (ret <= 0)
+ {
+ ret_size = -1;
+ kdbus_close_message (transport, msg_reply);
+ dbus_free (data);
+ goto out;
+ }
+ _dbus_string_init_const_len (&str, data, ret);
+
+ /* create DBusMessage from kmsg */
+ *sync_reply = _dbus_decode_kmsg (&str, msg_reply->src_id, fds, n_fds);
+ if (*sync_reply == NULL)
+ ret_size = -1;
- out:
+ kdbus_close_message (transport, msg_reply);
+ dbus_free (fds);
+ dbus_free (data);
+ }
+
+out:
if (msg)
_kdbus_free_msg (msg);
if (memfd >= 0)
DBusMessage *message,
const char *destination)
{
- return kdbus_write_msg_internal (transport, message, destination, FALSE, TRUE);
+ return kdbus_write_msg_internal (transport, message, destination, NULL, TRUE);
}
static dbus_uint64_t
dbus_message_lock (sub_message);
- if (kdbus_write_msg_internal (transport, sub_message, name, FALSE, FALSE) == -1)
+ if (kdbus_write_msg_internal (transport, sub_message, name, NULL, FALSE) == -1)
return NULL;
else
{
* @return the length of the kdbus message's payload.
*/
static int
-kdbus_message_size (const struct kdbus_msg *msg)
+kdbus_message_size (const struct kdbus_msg *msg,
+ int *n_fds)
{
const struct kdbus_item *item;
int ret_size = 0;
case KDBUS_ITEM_PAYLOAD_MEMFD:
ret_size += item->memfd.size;
break;
+ case KDBUS_ITEM_FDS:
+ *n_fds = (item->size - KDBUS_ITEM_HEADER_SIZE) / sizeof (int);
+ break;
default:
break;
}
return -1;
}
- buf_size = kdbus_message_size (msg);
+ buf_size = kdbus_message_size (msg, n_fds);
if (buf_size == -1)
{
_dbus_verbose ("kdbus error - too short message: %d (%m)\n", errno);
return ret_size;
}
+static DBusMessage *
+kdbus_send_sync_call (DBusTransportKdbus *transport,
+ DBusMessage *message)
+{
+ DBusMessage *reply = NULL;
+ DBusError error;
+ const char *destination;
+
+ dbus_message_lock (message);
+
+ destination = dbus_message_get_destination (message);
+ if (destination)
+ {
+ int ret;
+
+ ret = capture_org_freedesktop_DBus ((DBusTransportKdbus*)transport, destination, message, &reply);
+ if (ret < 0)
+ goto error;
+ else if (ret == 0)
+ goto out;
+ }
+
+ kdbus_write_msg_internal (transport, message, destination, &reply, TRUE);
+ if (reply == NULL)
+ goto error;
+ else
+ goto out;
+
+error:
+
+ dbus_error_init (&error);
+
+ dbus_set_error (&error, DBUS_ERROR_FAILED,
+ "Something went wrong");
+
+ reply = reply_with_error ((char*)error.name,
+ NULL,
+ error.message,
+ message);
+
+ dbus_error_free (&error);
+
+out:
+
+ _dbus_assert (reply != NULL);
+ dbus_message_lock (reply);
+
+ return reply;
+}
+
/**
* Copy-paste from socket transport. Only renames done.
*/
_dbus_transport_kdbus_get_unix_user);
_dbus_transport_set_get_unix_process_id_function (&kdbus_transport->base,
_dbus_transport_kdbus_get_unix_process_id);
+ _dbus_transport_set_send_sync_call_function (&kdbus_transport->base,
+ (DBusTransportSendSyncCallFunction) kdbus_send_sync_call);
_dbus_transport_set_assure_protocol_function (&kdbus_transport->base,
_dbus_message_assure_gvariant,
DBUS_PROTOCOL_VERSION_GVARIANT);