diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c index ecef6cd3c57b16e104bf297e2ae3d4473b199633..f03881945318ff67d17f1ceef69170662f2368da 100644 --- a/gio/gdbusmessage.c +++ b/gio/gdbusmessage.c @@ -2091,6 +2091,8 @@ g_dbus_message_bytes_needed (guchar *blob, GError **error) { gssize ret; + guint32 headers_array_size; + guint32 body_size; ret = -1; @@ -2098,23 +2100,28 @@ g_dbus_message_bytes_needed (guchar *blob, g_return_val_if_fail (error == NULL || *error == NULL, -1); g_return_val_if_fail (blob_len >= 16, -1); - if (blob[0] == 'l') - { - /* core header (12 bytes) + ARRAY of STRUCT of (BYTE,VARIANT) */ - ret = 12 + 4 + GUINT32_FROM_LE (((guint32 *) blob)[3]); - /* round up so it's a multiple of 8 */ - ret = 8 * ((ret + 7)/8); - /* finally add the body size */ - ret += GUINT32_FROM_LE (((guint32 *) blob)[1]); - } - else if (blob[0] == 'B') + if (blob[0] == 'l' || blob[0] == 'B') { + memcpy (&headers_array_size, blob + 3*sizeof (guint32), sizeof (headers_array_size)); + memcpy (&body_size, blob + 1 * sizeof (guint32), sizeof (body_size)); + + if (blob[0] == 'l') + { + headers_array_size = GUINT32_FROM_LE (headers_array_size); + body_size = GUINT32_FROM_LE (body_size); + } + else + { + headers_array_size = GUINT32_FROM_BE (headers_array_size); + body_size = GUINT32_FROM_BE (body_size); + } + /* core header (12 bytes) + ARRAY of STRUCT of (BYTE,VARIANT) */ - ret = 12 + 4 + GUINT32_FROM_BE (((guint32 *) blob)[3]); + ret = 12 + 4 + headers_array_size; /* round up so it's a multiple of 8 */ ret = 8 * ((ret + 7)/8); /* finally add the body size */ - ret += GUINT32_FROM_BE (((guint32 *) blob)[1]); + ret += body_size; } else { diff --git a/gio/gdbusprivate.c b/gio/gdbusprivate.c index 0b8630ab2da3df6de60a9cb2d5254b771a7e851c..0c88cf9ae541dafd26118bb7e9c428944468e5d5 100644 --- a/gio/gdbusprivate.c +++ b/gio/gdbusprivate.c @@ -61,6 +61,11 @@ #include "glibintl.h" +#define READ_BUFFER_MIN_READ_SIZE_FOR_HEADER 16 +/* Buffer size of 4096 seems to be big enough to contain most of messages + * on a standard desktop. */ +#define READ_BUFFER_MIN_BUFFER_SIZE 4096 + static gboolean _g_dbus_worker_do_initial_read (gpointer data); static void schedule_pending_close (GDBusWorker *worker); @@ -373,6 +378,7 @@ struct GDBusWorker gchar *read_buffer; gsize read_buffer_allocated_size; gsize read_buffer_cur_size; + gsize read_buffer_cur_pos; gsize read_buffer_bytes_wanted; GUnixFDList *read_fd_list; GSocketControlMessage **read_ancillary_messages; @@ -565,6 +571,43 @@ _g_dbus_worker_unfreeze (GDBusWorker *worker) static void _g_dbus_worker_do_read_unlocked (GDBusWorker *worker); +static gssize _g_dbus_worker_buffer_get_next_full_message_length (GDBusWorker *worker) +{ + GError *error; + gssize remaining_data_in_buffer; + + if (worker->read_buffer == NULL) + return 0; + + remaining_data_in_buffer = worker->read_buffer_cur_size - worker->read_buffer_cur_pos; + + if (remaining_data_in_buffer >= READ_BUFFER_MIN_READ_SIZE_FOR_HEADER) + { + gssize message_len; + /* OK, got the header - determine how many more bytes are needed */ + error = NULL; + message_len = g_dbus_message_bytes_needed ((guchar *) worker->read_buffer + worker->read_buffer_cur_pos, + READ_BUFFER_MIN_READ_SIZE_FOR_HEADER, + &error); + if (message_len == -1) + { + g_warning ("_g_dbus_worker_do_read_cb: error determining bytes needed: %s", error->message); + _g_dbus_worker_emit_disconnected (worker, FALSE, error); + g_error_free (error); + return -1; + } + if (message_len <= remaining_data_in_buffer) + return message_len; + + worker->read_buffer_bytes_wanted = message_len; + } + else + { + worker->read_buffer_bytes_wanted = 0; + } + return 0; +} + /* called in private thread shared by all GDBusConnection instances (without read-lock held) */ static void _g_dbus_worker_do_read_cb (GInputStream *input_stream, @@ -574,6 +617,7 @@ _g_dbus_worker_do_read_cb (GInputStream *input_stream, GDBusWorker *worker = user_data; GError *error; gssize bytes_read; + gssize message_len; g_mutex_lock (&worker->read_lock); @@ -720,97 +764,111 @@ _g_dbus_worker_do_read_cb (GInputStream *input_stream, read_message_print_transport_debug (bytes_read, worker); worker->read_buffer_cur_size += bytes_read; - if (worker->read_buffer_bytes_wanted == worker->read_buffer_cur_size) + + while ((message_len = _g_dbus_worker_buffer_get_next_full_message_length (worker) ) > 0) { /* OK, got what we asked for! */ - if (worker->read_buffer_bytes_wanted == 16) - { - gssize message_len; - /* OK, got the header - determine how many more bytes are needed */ - error = NULL; - message_len = g_dbus_message_bytes_needed ((guchar *) worker->read_buffer, - 16, - &error); - if (message_len == -1) - { - g_warning ("_g_dbus_worker_do_read_cb: error determining bytes needed: %s", error->message); - _g_dbus_worker_emit_disconnected (worker, FALSE, error); - g_error_free (error); - goto out; - } + GDBusMessage *message; + guchar *read_buffer = (guchar *)worker->read_buffer + worker->read_buffer_cur_pos; + error = NULL; + + /* TODO: use connection->priv->auth to decode the message */ - worker->read_buffer_bytes_wanted = message_len; - _g_dbus_worker_do_read_unlocked (worker); + message = g_dbus_message_new_from_blob (read_buffer, + message_len, + worker->capabilities, + &error); + if (message == NULL) + { + gchar *s; + s = _g_dbus_hexdump (read_buffer, message_len, 2); + g_warning ("Error decoding D-Bus message of %" G_GSIZE_FORMAT " bytes\n" + "The error is: %s\n" + "The payload is as follows:\n" + "%s", + message_len, + error->message, + s); + g_free (s); + _g_dbus_worker_emit_disconnected (worker, FALSE, error); + g_error_free (error); + goto out; } - else + +#ifdef G_OS_UNIX + if (worker->read_fd_list != NULL) { - GDBusMessage *message; - error = NULL; + guint fds_needed; + GUnixFDList *fd_list_for_message; - /* TODO: use connection->priv->auth to decode the message */ + fds_needed = g_dbus_message_get_num_unix_fds (message); - message = g_dbus_message_new_from_blob ((guchar *) worker->read_buffer, - worker->read_buffer_cur_size, - worker->capabilities, - &error); - if (message == NULL) + if ((guint)g_unix_fd_list_get_length (worker->read_fd_list) > fds_needed) { - gchar *s; - s = _g_dbus_hexdump (worker->read_buffer, worker->read_buffer_cur_size, 2); - g_warning ("Error decoding D-Bus message of %" G_GSIZE_FORMAT " bytes\n" - "The error is: %s\n" - "The payload is as follows:\n" - "%s", - worker->read_buffer_cur_size, - error->message, - s); - g_free (s); - _g_dbus_worker_emit_disconnected (worker, FALSE, error); - g_error_free (error); - goto out; - } + gint num_fds; + GUnixFDList *old_worker_read_fd_list; + gint *fds; -#ifdef G_OS_UNIX - if (worker->read_fd_list != NULL) + old_worker_read_fd_list = worker->read_fd_list; + fds = g_unix_fd_list_steal_fds (old_worker_read_fd_list, &num_fds); + fd_list_for_message = g_unix_fd_list_new_from_array (fds, fds_needed); + worker->read_fd_list = g_unix_fd_list_new_from_array (fds + fds_needed, num_fds - fds_needed); + g_object_unref (old_worker_read_fd_list); + g_free (fds); + } + else { - g_dbus_message_set_unix_fd_list (message, worker->read_fd_list); - g_object_unref (worker->read_fd_list); + fd_list_for_message = worker->read_fd_list; worker->read_fd_list = NULL; } + + if (fds_needed > 0) + g_dbus_message_set_unix_fd_list (message, fd_list_for_message); + g_object_unref (fd_list_for_message); + } #endif - if (G_UNLIKELY (_g_dbus_debug_message ())) + if (G_UNLIKELY (_g_dbus_debug_message ())) + { + gchar *s; + _g_dbus_debug_print_lock (); + g_print ("========================================================================\n" + "GDBus-debug:Message:\n" + " <<<< RECEIVED D-Bus message (%" G_GSIZE_FORMAT " bytes)\n", + message_len); + s = g_dbus_message_print (message, 2); + g_print ("%s", s); + g_free (s); + if (G_UNLIKELY (_g_dbus_debug_payload ())) { - gchar *s; - _g_dbus_debug_print_lock (); - g_print ("========================================================================\n" - "GDBus-debug:Message:\n" - " <<<< RECEIVED D-Bus message (%" G_GSIZE_FORMAT " bytes)\n", - worker->read_buffer_cur_size); - s = g_dbus_message_print (message, 2); - g_print ("%s", s); + s = _g_dbus_hexdump (read_buffer, message_len, 2); + g_print ("%s\n", s); g_free (s); - if (G_UNLIKELY (_g_dbus_debug_payload ())) - { - s = _g_dbus_hexdump (worker->read_buffer, worker->read_buffer_cur_size, 2); - g_print ("%s\n", s); - g_free (s); - } - _g_dbus_debug_print_unlock (); } + _g_dbus_debug_print_unlock (); + } - /* yay, got a message, go deliver it */ - _g_dbus_worker_queue_or_deliver_received_message (worker, g_steal_pointer (&message)); + /* yay, got a message, go deliver it */ + _g_dbus_worker_queue_or_deliver_received_message (worker, g_steal_pointer (&message)); - /* start reading another message! */ - worker->read_buffer_bytes_wanted = 0; - worker->read_buffer_cur_size = 0; - _g_dbus_worker_do_read_unlocked (worker); - } + worker->read_buffer_cur_pos += message_len; } - else + + if (message_len == 0) { - /* didn't get all the bytes we requested - so repeat the request... */ + /* set up reading another data package */ + if (worker->read_buffer_cur_pos > 0) + { + if (worker->read_buffer_cur_pos < worker->read_buffer_cur_size) + { + memmove (worker->read_buffer, + worker->read_buffer + worker->read_buffer_cur_pos, + worker->read_buffer_cur_size - worker->read_buffer_cur_pos); + } + worker->read_buffer_cur_size -= worker->read_buffer_cur_pos; + worker->read_buffer_cur_pos = 0; + } + /* start reading another message or repeat the request to get all the bytes */ _g_dbus_worker_do_read_unlocked (worker); } @@ -835,15 +893,21 @@ _g_dbus_worker_do_read_unlocked (GDBusWorker *worker) /* if bytes_wanted is zero, it means start reading a message */ if (worker->read_buffer_bytes_wanted == 0) { - worker->read_buffer_cur_size = 0; - worker->read_buffer_bytes_wanted = 16; + if (worker->socket != NULL) + { + worker->read_buffer_bytes_wanted = READ_BUFFER_MIN_BUFFER_SIZE; + } + else + { + worker->read_buffer_cur_size = 0; + worker->read_buffer_bytes_wanted = READ_BUFFER_MIN_READ_SIZE_FOR_HEADER; + } } /* ensure we have a (big enough) buffer */ if (worker->read_buffer == NULL || worker->read_buffer_bytes_wanted > worker->read_buffer_allocated_size) { - /* TODO: 4096 is randomly chosen; might want a better chosen default minimum */ - worker->read_buffer_allocated_size = MAX (worker->read_buffer_bytes_wanted, 4096); + worker->read_buffer_allocated_size = MAX (worker->read_buffer_bytes_wanted, READ_BUFFER_MIN_BUFFER_SIZE); worker->read_buffer = g_realloc (worker->read_buffer, worker->read_buffer_allocated_size); } @@ -2533,26 +2597,30 @@ read_message_print_transport_debug (gssize bytes_read, gsize size; gint32 serial; gint32 message_length; + guchar *read_buffer; if (G_LIKELY (!_g_dbus_debug_transport ())) goto out; - size = bytes_read + worker->read_buffer_cur_size; + read_buffer = (guchar *)worker->read_buffer + worker->read_buffer_cur_pos; + + size = bytes_read + worker->read_buffer_cur_size - worker->read_buffer_cur_pos; serial = 0; message_length = 0; if (size >= 16) - message_length = g_dbus_message_bytes_needed ((guchar *) worker->read_buffer, size, NULL); + message_length = g_dbus_message_bytes_needed ((guchar *) read_buffer, size, NULL); if (size >= 1) { - switch (worker->read_buffer[0]) + if (size >= 12) + memcpy (&serial, read_buffer + 2 * (sizeof (guint32)), sizeof (serial)); + + switch (read_buffer[0]) { case 'l': - if (size >= 12) - serial = GUINT32_FROM_LE (((guint32 *) worker->read_buffer)[2]); + serial = GUINT32_FROM_LE (serial); break; case 'B': - if (size >= 12) - serial = GUINT32_FROM_BE (((guint32 *) worker->read_buffer)[2]); + serial = GUINT32_FROM_BE (serial); break; default: /* an error will be set elsewhere if this happens */