| /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ |
| /* dbus-marshal-header.c Managing marshaling/demarshaling of message headers |
| * |
| * Copyright (C) 2005 Red Hat, Inc. |
| * |
| * Licensed under the Academic Free License version 2.1 |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #include <config.h> |
| #include "dbus/dbus-shared.h" |
| #include "dbus-marshal-header.h" |
| #include "dbus-marshal-recursive.h" |
| #include "dbus-marshal-byteswap.h" |
| |
| /** |
| * @addtogroup DBusMarshal |
| * |
| * @{ |
| */ |
| |
| |
| /* Not thread locked, but strictly const/read-only so should be OK |
| */ |
| /** Static #DBusString containing the signature of a message header */ |
| _DBUS_STRING_DEFINE_STATIC(_dbus_header_signature_str, DBUS_HEADER_SIGNATURE); |
| /** Static #DBusString containing the local interface */ |
| _DBUS_STRING_DEFINE_STATIC(_dbus_local_interface_str, DBUS_INTERFACE_LOCAL); |
| /** Static #DBusString containing the local path */ |
| _DBUS_STRING_DEFINE_STATIC(_dbus_local_path_str, DBUS_PATH_LOCAL); |
| |
| /** Offset from start of _dbus_header_signature_str to the signature of the fields array */ |
| #define FIELDS_ARRAY_SIGNATURE_OFFSET 6 |
| /** Offset from start of _dbus_header_signature_str to the signature of an element of the fields array */ |
| #define FIELDS_ARRAY_ELEMENT_SIGNATURE_OFFSET 7 |
| |
| |
| /** Offset to byte order from start of header */ |
| #define BYTE_ORDER_OFFSET 0 |
| /** Offset to type from start of header */ |
| #define TYPE_OFFSET 1 |
| /** Offset to flags from start of header */ |
| #define FLAGS_OFFSET 2 |
| /** Offset to version from start of header */ |
| #define VERSION_OFFSET 3 |
| /** Offset to body length from start of header */ |
| #define BODY_LENGTH_OFFSET 4 |
| /** Offset to client serial from start of header */ |
| #define SERIAL_OFFSET 8 |
| /** Offset to fields array length from start of header */ |
| #define FIELDS_ARRAY_LENGTH_OFFSET 12 |
| /** Offset to first field in header */ |
| #define FIRST_FIELD_OFFSET 16 |
| |
| typedef struct |
| { |
| unsigned char code; /**< the field code */ |
| unsigned char type; /**< the value type */ |
| } HeaderFieldType; |
| |
| static const HeaderFieldType |
| _dbus_header_field_types[DBUS_HEADER_FIELD_LAST+1] = { |
| { DBUS_HEADER_FIELD_INVALID, DBUS_TYPE_INVALID }, |
| { DBUS_HEADER_FIELD_PATH, DBUS_TYPE_OBJECT_PATH }, |
| { DBUS_HEADER_FIELD_INTERFACE, DBUS_TYPE_STRING }, |
| { DBUS_HEADER_FIELD_MEMBER, DBUS_TYPE_STRING }, |
| { DBUS_HEADER_FIELD_ERROR_NAME, DBUS_TYPE_STRING }, |
| { DBUS_HEADER_FIELD_REPLY_SERIAL, DBUS_TYPE_UINT32 }, |
| { DBUS_HEADER_FIELD_DESTINATION, DBUS_TYPE_STRING }, |
| { DBUS_HEADER_FIELD_SENDER, DBUS_TYPE_STRING }, |
| { DBUS_HEADER_FIELD_SIGNATURE, DBUS_TYPE_SIGNATURE }, |
| { DBUS_HEADER_FIELD_UNIX_FDS, DBUS_TYPE_UINT32 } |
| }; |
| |
| /** Macro to look up the correct type for a field */ |
| #define EXPECTED_TYPE_OF_FIELD(field) (_dbus_header_field_types[field].type) |
| |
| /** The most padding we could ever need for a header */ |
| #define MAX_POSSIBLE_HEADER_PADDING 7 |
| static dbus_bool_t |
| reserve_header_padding (DBusHeader *header) |
| { |
| _dbus_assert (header->padding <= MAX_POSSIBLE_HEADER_PADDING); |
| |
| if (!_dbus_string_lengthen (&header->data, |
| MAX_POSSIBLE_HEADER_PADDING - header->padding)) |
| return FALSE; |
| header->padding = MAX_POSSIBLE_HEADER_PADDING; |
| return TRUE; |
| } |
| |
| static void |
| correct_header_padding (DBusHeader *header) |
| { |
| int unpadded_len; |
| |
| _dbus_assert (header->padding == 7); |
| |
| _dbus_string_shorten (&header->data, header->padding); |
| unpadded_len = _dbus_string_get_length (&header->data); |
| |
| if (!_dbus_string_align_length (&header->data, 8)) |
| _dbus_assert_not_reached ("couldn't pad header though enough padding was preallocated"); |
| |
| header->padding = _dbus_string_get_length (&header->data) - unpadded_len; |
| } |
| |
| /** Compute the end of the header, ignoring padding */ |
| #define HEADER_END_BEFORE_PADDING(header) \ |
| (_dbus_string_get_length (&(header)->data) - (header)->padding) |
| |
| /** |
| * Invalidates all fields in the cache. This may be used when the |
| * cache is totally uninitialized (contains junk) so should not |
| * look at what's in there now. |
| * |
| * @param header the header |
| */ |
| static void |
| _dbus_header_cache_invalidate_all (DBusHeader *header) |
| { |
| int i; |
| |
| i = 0; |
| while (i <= DBUS_HEADER_FIELD_LAST) |
| { |
| header->fields[i].value_pos = _DBUS_HEADER_FIELD_VALUE_UNKNOWN; |
| ++i; |
| } |
| } |
| |
| /** |
| * Caches one field |
| * |
| * @param header the header |
| * @param field_code the field |
| * @param variant_reader the reader for the variant in the field |
| */ |
| static void |
| _dbus_header_cache_one (DBusHeader *header, |
| int field_code, |
| DBusTypeReader *variant_reader) |
| { |
| header->fields[field_code].value_pos = |
| _dbus_type_reader_get_value_pos (variant_reader); |
| |
| #if 0 |
| _dbus_verbose ("cached value_pos %d for field %d\n", |
| header->fields[field_code].value_pos, field_code) |
| #endif |
| } |
| |
| /** |
| * Revalidates the fields cache |
| * |
| * @param header the header |
| */ |
| static void |
| _dbus_header_cache_revalidate (DBusHeader *header) |
| { |
| DBusTypeReader array; |
| DBusTypeReader reader; |
| int i; |
| |
| i = 0; |
| while (i <= DBUS_HEADER_FIELD_LAST) |
| { |
| header->fields[i].value_pos = _DBUS_HEADER_FIELD_VALUE_NONEXISTENT; |
| ++i; |
| } |
| |
| _dbus_type_reader_init (&reader, |
| header->byte_order, |
| &_dbus_header_signature_str, |
| FIELDS_ARRAY_SIGNATURE_OFFSET, |
| &header->data, |
| FIELDS_ARRAY_LENGTH_OFFSET); |
| |
| _dbus_type_reader_recurse (&reader, &array); |
| |
| while (_dbus_type_reader_get_current_type (&array) != DBUS_TYPE_INVALID) |
| { |
| DBusTypeReader sub; |
| DBusTypeReader variant; |
| unsigned char field_code; |
| |
| _dbus_type_reader_recurse (&array, &sub); |
| |
| _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_BYTE); |
| _dbus_type_reader_read_basic (&sub, &field_code); |
| |
| /* Unknown fields should be ignored */ |
| if (field_code > DBUS_HEADER_FIELD_LAST) |
| goto next_field; |
| |
| _dbus_type_reader_next (&sub); |
| |
| _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_VARIANT); |
| _dbus_type_reader_recurse (&sub, &variant); |
| |
| _dbus_header_cache_one (header, field_code, &variant); |
| |
| next_field: |
| _dbus_type_reader_next (&array); |
| } |
| } |
| |
| /** |
| * Checks for a field, updating the cache if required. |
| * |
| * @param header the header |
| * @param field the field to check |
| * @returns #FALSE if the field doesn't exist |
| */ |
| static dbus_bool_t |
| _dbus_header_cache_check (DBusHeader *header, |
| int field) |
| { |
| _dbus_assert (field <= DBUS_HEADER_FIELD_LAST); |
| |
| if (header->fields[field].value_pos == _DBUS_HEADER_FIELD_VALUE_UNKNOWN) |
| _dbus_header_cache_revalidate (header); |
| |
| if (header->fields[field].value_pos == _DBUS_HEADER_FIELD_VALUE_NONEXISTENT) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |
| /** |
| * Checks whether a field is known not to exist. It may exist |
| * even if it's not known to exist. |
| * |
| * @param header the header |
| * @param field the field to check |
| * @returns #FALSE if the field definitely doesn't exist |
| */ |
| static dbus_bool_t |
| _dbus_header_cache_known_nonexistent (DBusHeader *header, |
| int field) |
| { |
| _dbus_assert (field <= DBUS_HEADER_FIELD_LAST); |
| |
| return (header->fields[field].value_pos == _DBUS_HEADER_FIELD_VALUE_NONEXISTENT); |
| } |
| |
| /** |
| * Writes a struct of { byte, variant } with the given basic type. |
| * |
| * @param writer the writer (should be ready to write a struct) |
| * @param type the type of the value |
| * @param value the value as for _dbus_marshal_set_basic() |
| * @returns #FALSE if no memory |
| */ |
| static dbus_bool_t |
| write_basic_field (DBusTypeWriter *writer, |
| int field, |
| int type, |
| const void *value) |
| { |
| DBusTypeWriter sub; |
| DBusTypeWriter variant; |
| int start; |
| int padding; |
| unsigned char field_byte; |
| DBusString contained_type; |
| char buf[2]; |
| |
| start = writer->value_pos; |
| padding = _dbus_string_get_length (writer->value_str) - start; |
| |
| if (!_dbus_type_writer_recurse (writer, DBUS_TYPE_STRUCT, |
| NULL, 0, &sub)) |
| goto append_failed; |
| |
| field_byte = field; |
| if (!_dbus_type_writer_write_basic (&sub, DBUS_TYPE_BYTE, |
| &field_byte)) |
| goto append_failed; |
| |
| buf[0] = type; |
| buf[1] = '\0'; |
| _dbus_string_init_const_len (&contained_type, buf, 1); |
| |
| if (!_dbus_type_writer_recurse (&sub, DBUS_TYPE_VARIANT, |
| &contained_type, 0, &variant)) |
| goto append_failed; |
| |
| if (!_dbus_type_writer_write_basic (&variant, type, value)) |
| goto append_failed; |
| |
| if (!_dbus_type_writer_unrecurse (&sub, &variant)) |
| goto append_failed; |
| |
| if (!_dbus_type_writer_unrecurse (writer, &sub)) |
| goto append_failed; |
| |
| return TRUE; |
| |
| append_failed: |
| _dbus_string_delete (writer->value_str, |
| start, |
| _dbus_string_get_length (writer->value_str) - start - padding); |
| return FALSE; |
| } |
| |
| /** |
| * Sets a struct of { byte, variant } with the given basic type. |
| * |
| * @param reader the reader (should be iterating over the array pointing at the field to set) |
| * @param type the type of the value |
| * @param value the value as for _dbus_marshal_set_basic() |
| * @param realign_root where to realign from |
| * @returns #FALSE if no memory |
| */ |
| static dbus_bool_t |
| set_basic_field (DBusTypeReader *reader, |
| int field, |
| int type, |
| const void *value, |
| const DBusTypeReader *realign_root) |
| { |
| DBusTypeReader sub; |
| DBusTypeReader variant; |
| |
| _dbus_type_reader_recurse (reader, &sub); |
| |
| _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_BYTE); |
| #ifndef DBUS_DISABLE_ASSERT |
| { |
| unsigned char v_BYTE; |
| _dbus_type_reader_read_basic (&sub, &v_BYTE); |
| _dbus_assert (((int) v_BYTE) == field); |
| } |
| #endif |
| |
| if (!_dbus_type_reader_next (&sub)) |
| _dbus_assert_not_reached ("no variant field?"); |
| |
| _dbus_type_reader_recurse (&sub, &variant); |
| _dbus_assert (_dbus_type_reader_get_current_type (&variant) == type); |
| |
| if (!_dbus_type_reader_set_basic (&variant, value, realign_root)) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |
| /** |
| * Gets the type of the message. |
| * |
| * @param header the header |
| * @returns the type |
| */ |
| int |
| _dbus_header_get_message_type (DBusHeader *header) |
| { |
| int type; |
| |
| type = _dbus_string_get_byte (&header->data, TYPE_OFFSET); |
| _dbus_assert (type != DBUS_MESSAGE_TYPE_INVALID); |
| |
| return type; |
| } |
| |
| /** |
| * Sets the serial number of a header. This can only be done once on |
| * a header. |
| * |
| * @param header the header |
| * @param serial the serial |
| */ |
| void |
| _dbus_header_set_serial (DBusHeader *header, |
| dbus_uint32_t serial) |
| { |
| /* we use this function to set the serial on outgoing |
| * messages, and to reset the serial in dbus_message_copy; |
| * this assertion should catch a double-set on outgoing. |
| */ |
| _dbus_assert (_dbus_header_get_serial (header) == 0 || |
| serial == 0); |
| |
| _dbus_marshal_set_uint32 (&header->data, |
| SERIAL_OFFSET, |
| serial, |
| header->byte_order); |
| } |
| |
| /** |
| * See dbus_message_get_serial() |
| * |
| * @param header the header |
| * @returns the client serial |
| */ |
| dbus_uint32_t |
| _dbus_header_get_serial (DBusHeader *header) |
| { |
| return _dbus_marshal_read_uint32 (&header->data, |
| SERIAL_OFFSET, |
| header->byte_order, |
| NULL); |
| } |
| |
| /** |
| * Re-initializes a header that was previously initialized and never |
| * freed. After this, to make the header valid you have to call |
| * _dbus_header_create(). |
| * |
| * @param header header to re-initialize |
| * @param byte_order byte order of the header |
| */ |
| void |
| _dbus_header_reinit (DBusHeader *header, |
| int byte_order) |
| { |
| _dbus_string_set_length (&header->data, 0); |
| |
| header->byte_order = byte_order; |
| header->padding = 0; |
| |
| _dbus_header_cache_invalidate_all (header); |
| } |
| |
| /** |
| * Initializes a header, but doesn't prepare it for use; |
| * to make the header valid, you have to call _dbus_header_create(). |
| * |
| * @param header header to initialize |
| * @param byte_order byte order of the header |
| * @returns #FALSE if not enough memory |
| */ |
| dbus_bool_t |
| _dbus_header_init (DBusHeader *header, |
| int byte_order) |
| { |
| if (!_dbus_string_init_preallocated (&header->data, 32)) |
| return FALSE; |
| |
| _dbus_header_reinit (header, byte_order); |
| |
| return TRUE; |
| } |
| |
| /** |
| * Frees a header. |
| * |
| * @param header the header |
| */ |
| void |
| _dbus_header_free (DBusHeader *header) |
| { |
| _dbus_string_free (&header->data); |
| } |
| |
| /** |
| * Initializes dest with a copy of the given header. |
| * Resets the message serial to 0 on the copy. |
| * |
| * @param header header to copy |
| * @param dest destination for copy |
| * @returns #FALSE if not enough memory |
| */ |
| dbus_bool_t |
| _dbus_header_copy (const DBusHeader *header, |
| DBusHeader *dest) |
| { |
| *dest = *header; |
| |
| if (!_dbus_string_init_preallocated (&dest->data, |
| _dbus_string_get_length (&header->data))) |
| return FALSE; |
| |
| if (!_dbus_string_copy (&header->data, 0, &dest->data, 0)) |
| { |
| _dbus_string_free (&dest->data); |
| return FALSE; |
| } |
| |
| /* Reset the serial */ |
| _dbus_header_set_serial (dest, 0); |
| |
| return TRUE; |
| } |
| |
| /** |
| * Fills in the primary fields of the header, so the header is ready |
| * for use. #NULL may be specified for some or all of the fields to |
| * avoid adding those fields. Some combinations of fields don't make |
| * sense, and passing them in will trigger an assertion failure. |
| * |
| * @param header the header |
| * @param message_type the message type |
| * @param destination destination field or #NULL |
| * @param path path field or #NULL |
| * @param interface interface field or #NULL |
| * @param member member field or #NULL |
| * @param error_name error name or #NULL |
| * @returns #FALSE if not enough memory |
| */ |
| dbus_bool_t |
| _dbus_header_create (DBusHeader *header, |
| int message_type, |
| const char *destination, |
| const char *path, |
| const char *interface, |
| const char *member, |
| const char *error_name) |
| { |
| unsigned char v_BYTE; |
| dbus_uint32_t v_UINT32; |
| DBusTypeWriter writer; |
| DBusTypeWriter array; |
| |
| _dbus_assert (((interface || message_type != DBUS_MESSAGE_TYPE_SIGNAL) && member) || |
| (error_name) || |
| !(interface || member || error_name)); |
| _dbus_assert (_dbus_string_get_length (&header->data) == 0); |
| |
| if (!reserve_header_padding (header)) |
| return FALSE; |
| |
| _dbus_type_writer_init_values_only (&writer, header->byte_order, |
| &_dbus_header_signature_str, 0, |
| &header->data, |
| HEADER_END_BEFORE_PADDING (header)); |
| |
| v_BYTE = header->byte_order; |
| if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE, |
| &v_BYTE)) |
| goto oom; |
| |
| v_BYTE = message_type; |
| if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE, |
| &v_BYTE)) |
| goto oom; |
| |
| v_BYTE = 0; /* flags */ |
| if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE, |
| &v_BYTE)) |
| goto oom; |
| |
| v_BYTE = DBUS_MAJOR_PROTOCOL_VERSION; |
| if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE, |
| &v_BYTE)) |
| goto oom; |
| |
| v_UINT32 = 0; /* body length */ |
| if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_UINT32, |
| &v_UINT32)) |
| goto oom; |
| |
| v_UINT32 = 0; /* serial */ |
| if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_UINT32, |
| &v_UINT32)) |
| goto oom; |
| |
| if (!_dbus_type_writer_recurse (&writer, DBUS_TYPE_ARRAY, |
| &_dbus_header_signature_str, |
| FIELDS_ARRAY_SIGNATURE_OFFSET, |
| &array)) |
| goto oom; |
| |
| /* Marshal all the fields (Marshall Fields?) */ |
| |
| if (path != NULL) |
| { |
| if (!write_basic_field (&array, |
| DBUS_HEADER_FIELD_PATH, |
| DBUS_TYPE_OBJECT_PATH, |
| &path)) |
| goto oom; |
| } |
| |
| if (destination != NULL) |
| { |
| if (!write_basic_field (&array, |
| DBUS_HEADER_FIELD_DESTINATION, |
| DBUS_TYPE_STRING, |
| &destination)) |
| goto oom; |
| } |
| |
| if (interface != NULL) |
| { |
| if (!write_basic_field (&array, |
| DBUS_HEADER_FIELD_INTERFACE, |
| DBUS_TYPE_STRING, |
| &interface)) |
| goto oom; |
| } |
| |
| if (member != NULL) |
| { |
| if (!write_basic_field (&array, |
| DBUS_HEADER_FIELD_MEMBER, |
| DBUS_TYPE_STRING, |
| &member)) |
| goto oom; |
| } |
| |
| if (error_name != NULL) |
| { |
| if (!write_basic_field (&array, |
| DBUS_HEADER_FIELD_ERROR_NAME, |
| DBUS_TYPE_STRING, |
| &error_name)) |
| goto oom; |
| } |
| |
| if (!_dbus_type_writer_unrecurse (&writer, &array)) |
| goto oom; |
| |
| correct_header_padding (header); |
| |
| return TRUE; |
| |
| oom: |
| _dbus_string_delete (&header->data, 0, |
| _dbus_string_get_length (&header->data) - header->padding); |
| correct_header_padding (header); |
| |
| return FALSE; |
| } |
| |
| /** |
| * Given data long enough to contain the length of the message body |
| * and the fields array, check whether the data is long enough to |
| * contain the entire message (assuming the claimed lengths are |
| * accurate). Also checks that the lengths are in sanity parameters. |
| * |
| * @param max_message_length maximum length of a valid message |
| * @param validity return location for why the data is invalid if it is |
| * @param byte_order return location for byte order |
| * @param fields_array_len return location for claimed fields array length |
| * @param header_len return location for claimed header length |
| * @param body_len return location for claimed body length |
| * @param str the data |
| * @param start start of data, 8-aligned |
| * @param len length of data |
| * @returns #TRUE if the data is long enough for the claimed length, and the lengths were valid |
| */ |
| dbus_bool_t |
| _dbus_header_have_message_untrusted (int max_message_length, |
| DBusValidity *validity, |
| int *byte_order, |
| int *fields_array_len, |
| int *header_len, |
| int *body_len, |
| const DBusString *str, |
| int start, |
| int len) |
| |
| { |
| dbus_uint32_t header_len_unsigned; |
| dbus_uint32_t fields_array_len_unsigned; |
| dbus_uint32_t body_len_unsigned; |
| |
| _dbus_assert (start >= 0); |
| _dbus_assert (start < _DBUS_INT32_MAX / 2); |
| _dbus_assert (len >= 0); |
| |
| _dbus_assert (start == (int) _DBUS_ALIGN_VALUE (start, 8)); |
| |
| *byte_order = _dbus_string_get_byte (str, start + BYTE_ORDER_OFFSET); |
| |
| if (*byte_order != DBUS_LITTLE_ENDIAN && *byte_order != DBUS_BIG_ENDIAN) |
| { |
| *validity = DBUS_INVALID_BAD_BYTE_ORDER; |
| return FALSE; |
| } |
| |
| _dbus_assert (FIELDS_ARRAY_LENGTH_OFFSET + 4 <= len); |
| fields_array_len_unsigned = _dbus_marshal_read_uint32 (str, start + FIELDS_ARRAY_LENGTH_OFFSET, |
| *byte_order, NULL); |
| |
| if (fields_array_len_unsigned > (unsigned) max_message_length) |
| { |
| *validity = DBUS_INVALID_INSANE_FIELDS_ARRAY_LENGTH; |
| return FALSE; |
| } |
| |
| _dbus_assert (BODY_LENGTH_OFFSET + 4 < len); |
| body_len_unsigned = _dbus_marshal_read_uint32 (str, start + BODY_LENGTH_OFFSET, |
| *byte_order, NULL); |
| |
| if (body_len_unsigned > (unsigned) max_message_length) |
| { |
| *validity = DBUS_INVALID_INSANE_BODY_LENGTH; |
| return FALSE; |
| } |
| |
| header_len_unsigned = FIRST_FIELD_OFFSET + fields_array_len_unsigned; |
| header_len_unsigned = _DBUS_ALIGN_VALUE (header_len_unsigned, 8); |
| |
| /* overflow should be impossible since the lengths aren't allowed to |
| * be huge. |
| */ |
| _dbus_assert (max_message_length < _DBUS_INT32_MAX / 2); |
| if (body_len_unsigned + header_len_unsigned > (unsigned) max_message_length) |
| { |
| *validity = DBUS_INVALID_MESSAGE_TOO_LONG; |
| return FALSE; |
| } |
| |
| _dbus_assert (body_len_unsigned < (unsigned) _DBUS_INT32_MAX); |
| _dbus_assert (fields_array_len_unsigned < (unsigned) _DBUS_INT32_MAX); |
| _dbus_assert (header_len_unsigned < (unsigned) _DBUS_INT32_MAX); |
| |
| *body_len = body_len_unsigned; |
| *fields_array_len = fields_array_len_unsigned; |
| *header_len = header_len_unsigned; |
| |
| *validity = DBUS_VALID; |
| |
| _dbus_verbose ("have %d bytes, need body %u + header %u = %u\n", |
| len, body_len_unsigned, header_len_unsigned, |
| body_len_unsigned + header_len_unsigned); |
| |
| return (body_len_unsigned + header_len_unsigned) <= (unsigned) len; |
| } |
| |
| static DBusValidity |
| check_mandatory_fields (DBusHeader *header) |
| { |
| #define REQUIRE_FIELD(name) do { if (header->fields[DBUS_HEADER_FIELD_##name].value_pos < 0) return DBUS_INVALID_MISSING_##name; } while (0) |
| |
| switch (_dbus_header_get_message_type (header)) |
| { |
| case DBUS_MESSAGE_TYPE_SIGNAL: |
| REQUIRE_FIELD (INTERFACE); |
| /* FALL THRU - signals also require the path and member */ |
| case DBUS_MESSAGE_TYPE_METHOD_CALL: |
| REQUIRE_FIELD (PATH); |
| REQUIRE_FIELD (MEMBER); |
| break; |
| case DBUS_MESSAGE_TYPE_ERROR: |
| REQUIRE_FIELD (ERROR_NAME); |
| REQUIRE_FIELD (REPLY_SERIAL); |
| break; |
| case DBUS_MESSAGE_TYPE_METHOD_RETURN: |
| REQUIRE_FIELD (REPLY_SERIAL); |
| break; |
| default: |
| /* other message types allowed but ignored */ |
| break; |
| } |
| |
| return DBUS_VALID; |
| } |
| |
| static DBusValidity |
| load_and_validate_field (DBusHeader *header, |
| int field, |
| DBusTypeReader *variant_reader) |
| { |
| int type; |
| int expected_type; |
| const DBusString *value_str; |
| int value_pos; |
| int str_data_pos; |
| dbus_uint32_t v_UINT32; |
| int bad_string_code; |
| dbus_bool_t (* string_validation_func) (const DBusString *str, |
| int start, int len); |
| |
| /* Supposed to have been checked already */ |
| _dbus_assert (field <= DBUS_HEADER_FIELD_LAST); |
| _dbus_assert (field != DBUS_HEADER_FIELD_INVALID); |
| |
| /* Before we can cache a field, we need to know it has the right type */ |
| type = _dbus_type_reader_get_current_type (variant_reader); |
| |
| _dbus_assert (_dbus_header_field_types[field].code == field); |
| |
| expected_type = EXPECTED_TYPE_OF_FIELD (field); |
| if (type != expected_type) |
| { |
| _dbus_verbose ("Field %d should have type %d but has %d\n", |
| field, expected_type, type); |
| return DBUS_INVALID_HEADER_FIELD_HAS_WRONG_TYPE; |
| } |
| |
| /* If the field was provided twice, we aren't happy */ |
| if (header->fields[field].value_pos >= 0) |
| { |
| _dbus_verbose ("Header field %d seen a second time\n", field); |
| return DBUS_INVALID_HEADER_FIELD_APPEARS_TWICE; |
| } |
| |
| /* Now we can cache and look at the field content */ |
| _dbus_verbose ("initially caching field %d\n", field); |
| _dbus_header_cache_one (header, field, variant_reader); |
| |
| string_validation_func = NULL; |
| |
| /* make compiler happy that all this is initialized */ |
| v_UINT32 = 0; |
| value_str = NULL; |
| value_pos = -1; |
| str_data_pos = -1; |
| bad_string_code = DBUS_VALID; |
| |
| if (expected_type == DBUS_TYPE_UINT32) |
| { |
| _dbus_header_get_field_basic (header, field, expected_type, |
| &v_UINT32); |
| } |
| else if (expected_type == DBUS_TYPE_STRING || |
| expected_type == DBUS_TYPE_OBJECT_PATH || |
| expected_type == DBUS_TYPE_SIGNATURE) |
| { |
| _dbus_header_get_field_raw (header, field, |
| &value_str, &value_pos); |
| str_data_pos = _DBUS_ALIGN_VALUE (value_pos, 4) + 4; |
| } |
| else |
| { |
| _dbus_assert_not_reached ("none of the known fields should have this type"); |
| } |
| |
| switch (field) |
| { |
| case DBUS_HEADER_FIELD_DESTINATION: |
| string_validation_func = _dbus_validate_bus_name; |
| bad_string_code = DBUS_INVALID_BAD_DESTINATION; |
| break; |
| case DBUS_HEADER_FIELD_INTERFACE: |
| string_validation_func = _dbus_validate_interface; |
| bad_string_code = DBUS_INVALID_BAD_INTERFACE; |
| |
| if (_dbus_string_equal_substring (&_dbus_local_interface_str, |
| 0, |
| _dbus_string_get_length (&_dbus_local_interface_str), |
| value_str, str_data_pos)) |
| { |
| _dbus_verbose ("Message is on the local interface\n"); |
| return DBUS_INVALID_USES_LOCAL_INTERFACE; |
| } |
| break; |
| |
| case DBUS_HEADER_FIELD_MEMBER: |
| string_validation_func = _dbus_validate_member; |
| bad_string_code = DBUS_INVALID_BAD_MEMBER; |
| break; |
| |
| case DBUS_HEADER_FIELD_ERROR_NAME: |
| string_validation_func = _dbus_validate_error_name; |
| bad_string_code = DBUS_INVALID_BAD_ERROR_NAME; |
| break; |
| |
| case DBUS_HEADER_FIELD_SENDER: |
| string_validation_func = _dbus_validate_bus_name; |
| bad_string_code = DBUS_INVALID_BAD_SENDER; |
| break; |
| |
| case DBUS_HEADER_FIELD_PATH: |
| /* OBJECT_PATH was validated generically due to its type */ |
| string_validation_func = NULL; |
| |
| if (_dbus_string_equal_substring (&_dbus_local_path_str, |
| 0, |
| _dbus_string_get_length (&_dbus_local_path_str), |
| value_str, str_data_pos)) |
| { |
| _dbus_verbose ("Message is from the local path\n"); |
| return DBUS_INVALID_USES_LOCAL_PATH; |
| } |
| break; |
| |
| case DBUS_HEADER_FIELD_REPLY_SERIAL: |
| /* Can't be 0 */ |
| if (v_UINT32 == 0) |
| { |
| return DBUS_INVALID_BAD_SERIAL; |
| } |
| break; |
| |
| case DBUS_HEADER_FIELD_UNIX_FDS: |
| /* Every value makes sense */ |
| break; |
| |
| case DBUS_HEADER_FIELD_SIGNATURE: |
| /* SIGNATURE validated generically due to its type */ |
| string_validation_func = NULL; |
| break; |
| |
| default: |
| _dbus_assert_not_reached ("unknown field shouldn't be seen here"); |
| break; |
| } |
| |
| if (string_validation_func) |
| { |
| dbus_uint32_t len; |
| |
| _dbus_assert (bad_string_code != DBUS_VALID); |
| |
| len = _dbus_marshal_read_uint32 (value_str, value_pos, |
| header->byte_order, NULL); |
| |
| #if 0 |
| _dbus_verbose ("Validating string header field; code %d if fails\n", |
| bad_string_code); |
| #endif |
| if (!(*string_validation_func) (value_str, str_data_pos, len)) |
| return bad_string_code; |
| } |
| |
| return DBUS_VALID; |
| } |
| |
| /** |
| * Creates a message header from potentially-untrusted data. The |
| * return value is #TRUE if there was enough memory and the data was |
| * valid. If it returns #TRUE, the header will be created. If it |
| * returns #FALSE and *validity == #DBUS_VALIDITY_UNKNOWN_OOM_ERROR, |
| * then there wasn't enough memory. If it returns #FALSE |
| * and *validity != #DBUS_VALIDITY_UNKNOWN_OOM_ERROR then the data was |
| * invalid. |
| * |
| * The byte_order, fields_array_len, and body_len args should be from |
| * _dbus_header_have_message_untrusted(). Validation performed in |
| * _dbus_header_have_message_untrusted() is assumed to have been |
| * already done. |
| * |
| * @param header the header (must be initialized) |
| * @param mode whether to do validation |
| * @param validity return location for invalidity reason |
| * @param byte_order byte order from header |
| * @param fields_array_len claimed length of fields array |
| * @param body_len claimed length of body |
| * @param header_len claimed length of header |
| * @param str a string |
| * @param start start of header, 8-aligned |
| * @param len length of string to look at |
| * @returns #FALSE if no memory or data was invalid, #TRUE otherwise |
| */ |
| dbus_bool_t |
| _dbus_header_load (DBusHeader *header, |
| DBusValidationMode mode, |
| DBusValidity *validity, |
| int byte_order, |
| int fields_array_len, |
| int header_len, |
| int body_len, |
| const DBusString *str, |
| int start, |
| int len) |
| { |
| int leftover; |
| DBusValidity v; |
| DBusTypeReader reader; |
| DBusTypeReader array_reader; |
| unsigned char v_byte; |
| dbus_uint32_t v_uint32; |
| dbus_uint32_t serial; |
| int padding_start; |
| int padding_len; |
| int i; |
| |
| _dbus_assert (start == (int) _DBUS_ALIGN_VALUE (start, 8)); |
| _dbus_assert (header_len <= len); |
| _dbus_assert (_dbus_string_get_length (&header->data) == 0); |
| |
| if (!_dbus_string_copy_len (str, start, header_len, &header->data, 0)) |
| { |
| _dbus_verbose ("Failed to copy buffer into new header\n"); |
| *validity = DBUS_VALIDITY_UNKNOWN_OOM_ERROR; |
| return FALSE; |
| } |
| |
| if (mode == DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY) |
| { |
| leftover = len - header_len - body_len - start; |
| } |
| else |
| { |
| v = _dbus_validate_body_with_reason (&_dbus_header_signature_str, 0, |
| byte_order, |
| &leftover, |
| str, start, len); |
| |
| if (v != DBUS_VALID) |
| { |
| *validity = v; |
| goto invalid; |
| } |
| } |
| |
| _dbus_assert (leftover < len); |
| |
| padding_len = header_len - (FIRST_FIELD_OFFSET + fields_array_len); |
| padding_start = start + FIRST_FIELD_OFFSET + fields_array_len; |
| _dbus_assert (start + header_len == (int) _DBUS_ALIGN_VALUE (padding_start, 8)); |
| _dbus_assert (start + header_len == padding_start + padding_len); |
| |
| if (mode != DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY) |
| { |
| if (!_dbus_string_validate_nul (str, padding_start, padding_len)) |
| { |
| *validity = DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; |
| goto invalid; |
| } |
| } |
| |
| header->padding = padding_len; |
| |
| if (mode == DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY) |
| { |
| *validity = DBUS_VALID; |
| return TRUE; |
| } |
| |
| /* We now know the data is well-formed, but we have to check that |
| * it's valid. |
| */ |
| |
| _dbus_type_reader_init (&reader, |
| byte_order, |
| &_dbus_header_signature_str, 0, |
| str, start); |
| |
| /* BYTE ORDER */ |
| _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE); |
| _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == BYTE_ORDER_OFFSET); |
| _dbus_type_reader_read_basic (&reader, &v_byte); |
| _dbus_type_reader_next (&reader); |
| |
| _dbus_assert (v_byte == byte_order); |
| header->byte_order = byte_order; |
| |
| /* MESSAGE TYPE */ |
| _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE); |
| _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == TYPE_OFFSET); |
| _dbus_type_reader_read_basic (&reader, &v_byte); |
| _dbus_type_reader_next (&reader); |
| |
| /* unknown message types are supposed to be ignored, so only validation here is |
| * that it isn't invalid |
| */ |
| if (v_byte == DBUS_MESSAGE_TYPE_INVALID) |
| { |
| *validity = DBUS_INVALID_BAD_MESSAGE_TYPE; |
| goto invalid; |
| } |
| |
| /* FLAGS */ |
| _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE); |
| _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == FLAGS_OFFSET); |
| _dbus_type_reader_read_basic (&reader, &v_byte); |
| _dbus_type_reader_next (&reader); |
| |
| /* unknown flags should be ignored */ |
| |
| /* PROTOCOL VERSION */ |
| _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE); |
| _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == VERSION_OFFSET); |
| _dbus_type_reader_read_basic (&reader, &v_byte); |
| _dbus_type_reader_next (&reader); |
| |
| if (v_byte != DBUS_MAJOR_PROTOCOL_VERSION) |
| { |
| *validity = DBUS_INVALID_BAD_PROTOCOL_VERSION; |
| goto invalid; |
| } |
| |
| /* BODY LENGTH */ |
| _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_UINT32); |
| _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == BODY_LENGTH_OFFSET); |
| _dbus_type_reader_read_basic (&reader, &v_uint32); |
| _dbus_type_reader_next (&reader); |
| |
| _dbus_assert (body_len == (signed) v_uint32); |
| |
| /* SERIAL */ |
| _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_UINT32); |
| _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == SERIAL_OFFSET); |
| _dbus_type_reader_read_basic (&reader, &serial); |
| _dbus_type_reader_next (&reader); |
| |
| if (serial == 0) |
| { |
| *validity = DBUS_INVALID_BAD_SERIAL; |
| goto invalid; |
| } |
| |
| _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_ARRAY); |
| _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == FIELDS_ARRAY_LENGTH_OFFSET); |
| |
| _dbus_type_reader_recurse (&reader, &array_reader); |
| while (_dbus_type_reader_get_current_type (&array_reader) != DBUS_TYPE_INVALID) |
| { |
| DBusTypeReader struct_reader; |
| DBusTypeReader variant_reader; |
| unsigned char field_code; |
| |
| _dbus_assert (_dbus_type_reader_get_current_type (&array_reader) == DBUS_TYPE_STRUCT); |
| |
| _dbus_type_reader_recurse (&array_reader, &struct_reader); |
| |
| _dbus_assert (_dbus_type_reader_get_current_type (&struct_reader) == DBUS_TYPE_BYTE); |
| _dbus_type_reader_read_basic (&struct_reader, &field_code); |
| _dbus_type_reader_next (&struct_reader); |
| |
| if (field_code == DBUS_HEADER_FIELD_INVALID) |
| { |
| _dbus_verbose ("invalid header field code\n"); |
| *validity = DBUS_INVALID_HEADER_FIELD_CODE; |
| goto invalid; |
| } |
| |
| if (field_code > DBUS_HEADER_FIELD_LAST) |
| { |
| _dbus_verbose ("unknown header field code %d, skipping\n", |
| field_code); |
| goto next_field; |
| } |
| |
| _dbus_assert (_dbus_type_reader_get_current_type (&struct_reader) == DBUS_TYPE_VARIANT); |
| _dbus_type_reader_recurse (&struct_reader, &variant_reader); |
| |
| v = load_and_validate_field (header, field_code, &variant_reader); |
| if (v != DBUS_VALID) |
| { |
| _dbus_verbose ("Field %d was invalid\n", field_code); |
| *validity = v; |
| goto invalid; |
| } |
| |
| next_field: |
| _dbus_type_reader_next (&array_reader); |
| } |
| |
| /* Anything we didn't fill in is now known not to exist */ |
| i = 0; |
| while (i <= DBUS_HEADER_FIELD_LAST) |
| { |
| if (header->fields[i].value_pos == _DBUS_HEADER_FIELD_VALUE_UNKNOWN) |
| header->fields[i].value_pos = _DBUS_HEADER_FIELD_VALUE_NONEXISTENT; |
| ++i; |
| } |
| |
| v = check_mandatory_fields (header); |
| if (v != DBUS_VALID) |
| { |
| _dbus_verbose ("Mandatory fields were missing, code %d\n", v); |
| *validity = v; |
| goto invalid; |
| } |
| |
| *validity = DBUS_VALID; |
| return TRUE; |
| |
| invalid: |
| _dbus_string_set_length (&header->data, 0); |
| return FALSE; |
| } |
| |
| /** |
| * Fills in the correct body length. |
| * |
| * @param header the header |
| * @param body_len the length of the body |
| */ |
| void |
| _dbus_header_update_lengths (DBusHeader *header, |
| int body_len) |
| { |
| _dbus_marshal_set_uint32 (&header->data, |
| BODY_LENGTH_OFFSET, |
| body_len, |
| header->byte_order); |
| } |
| |
| static dbus_bool_t |
| find_field_for_modification (DBusHeader *header, |
| int field, |
| DBusTypeReader *reader, |
| DBusTypeReader *realign_root) |
| { |
| dbus_bool_t retval; |
| |
| retval = FALSE; |
| |
| _dbus_type_reader_init (realign_root, |
| header->byte_order, |
| &_dbus_header_signature_str, |
| FIELDS_ARRAY_SIGNATURE_OFFSET, |
| &header->data, |
| FIELDS_ARRAY_LENGTH_OFFSET); |
| |
| _dbus_type_reader_recurse (realign_root, reader); |
| |
| while (_dbus_type_reader_get_current_type (reader) != DBUS_TYPE_INVALID) |
| { |
| DBusTypeReader sub; |
| unsigned char field_code; |
| |
| _dbus_type_reader_recurse (reader, &sub); |
| |
| _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_BYTE); |
| _dbus_type_reader_read_basic (&sub, &field_code); |
| |
| if (field_code == (unsigned) field) |
| { |
| _dbus_assert (_dbus_type_reader_get_current_type (reader) == DBUS_TYPE_STRUCT); |
| retval = TRUE; |
| goto done; |
| } |
| |
| _dbus_type_reader_next (reader); |
| } |
| |
| done: |
| return retval; |
| } |
| |
| /** |
| * Sets the value of a field with basic type. If the value is a string |
| * value, it isn't allowed to be #NULL. If the field doesn't exist, |
| * it will be created. |
| * |
| * @param header the header |
| * @param field the field to set |
| * @param type the type of the value |
| * @param value the value as for _dbus_marshal_set_basic() |
| * @returns #FALSE if no memory |
| */ |
| dbus_bool_t |
| _dbus_header_set_field_basic (DBusHeader *header, |
| int field, |
| int type, |
| const void *value) |
| { |
| _dbus_assert (field <= DBUS_HEADER_FIELD_LAST); |
| |
| if (!reserve_header_padding (header)) |
| return FALSE; |
| |
| /* If the field exists we set, otherwise we append */ |
| if (_dbus_header_cache_check (header, field)) |
| { |
| DBusTypeReader reader; |
| DBusTypeReader realign_root; |
| |
| if (!find_field_for_modification (header, field, |
| &reader, &realign_root)) |
| _dbus_assert_not_reached ("field was marked present in cache but wasn't found"); |
| |
| if (!set_basic_field (&reader, field, type, value, &realign_root)) |
| return FALSE; |
| } |
| else |
| { |
| DBusTypeWriter writer; |
| DBusTypeWriter array; |
| |
| _dbus_type_writer_init_values_only (&writer, |
| header->byte_order, |
| &_dbus_header_signature_str, |
| FIELDS_ARRAY_SIGNATURE_OFFSET, |
| &header->data, |
| FIELDS_ARRAY_LENGTH_OFFSET); |
| |
| /* recurse into array without creating a new length, and jump to |
| * end of array. |
| */ |
| if (!_dbus_type_writer_append_array (&writer, |
| &_dbus_header_signature_str, |
| FIELDS_ARRAY_ELEMENT_SIGNATURE_OFFSET, |
| &array)) |
| _dbus_assert_not_reached ("recurse into ARRAY should not have used memory"); |
| |
| _dbus_assert (array.u.array.len_pos == FIELDS_ARRAY_LENGTH_OFFSET); |
| _dbus_assert (array.u.array.start_pos == FIRST_FIELD_OFFSET); |
| _dbus_assert (array.value_pos == HEADER_END_BEFORE_PADDING (header)); |
| |
| if (!write_basic_field (&array, |
| field, type, value)) |
| return FALSE; |
| |
| if (!_dbus_type_writer_unrecurse (&writer, &array)) |
| _dbus_assert_not_reached ("unrecurse from ARRAY should not have used memory"); |
| } |
| |
| correct_header_padding (header); |
| |
| /* We could be smarter about this (only invalidate fields after the |
| * one we modified, or even only if the one we modified changed |
| * length). But this hack is a start. |
| */ |
| _dbus_header_cache_invalidate_all (header); |
| |
| return TRUE; |
| } |
| |
| /** |
| * Gets the value of a field with basic type. If the field |
| * doesn't exist, returns #FALSE, otherwise returns #TRUE. |
| * |
| * @param header the header |
| * @param field the field to get |
| * @param type the type of the value |
| * @param value the value as for _dbus_marshal_read_basic() |
| * @returns #FALSE if the field doesn't exist |
| */ |
| dbus_bool_t |
| _dbus_header_get_field_basic (DBusHeader *header, |
| int field, |
| int type, |
| void *value) |
| { |
| _dbus_assert (field != DBUS_HEADER_FIELD_INVALID); |
| _dbus_assert (field <= DBUS_HEADER_FIELD_LAST); |
| _dbus_assert (_dbus_header_field_types[field].code == field); |
| /* in light of this you might ask why the type is passed in; |
| * the only rationale I can think of is so the caller has |
| * to specify its expectation and breaks if we change it |
| */ |
| _dbus_assert (type == EXPECTED_TYPE_OF_FIELD (field)); |
| |
| if (!_dbus_header_cache_check (header, field)) |
| return FALSE; |
| |
| _dbus_assert (header->fields[field].value_pos >= 0); |
| |
| _dbus_marshal_read_basic (&header->data, |
| header->fields[field].value_pos, |
| type, value, header->byte_order, |
| NULL); |
| |
| return TRUE; |
| } |
| |
| /** |
| * Gets the raw marshaled data for a field. If the field doesn't |
| * exist, returns #FALSE, otherwise returns #TRUE. Returns the start |
| * of the marshaled data, i.e. usually the byte where the length |
| * starts (for strings and arrays) or for basic types just the value |
| * itself. |
| * |
| * @param header the header |
| * @param field the field to get |
| * @param str return location for the data string |
| * @param pos return location for start of field value |
| * @returns #FALSE if the field doesn't exist |
| */ |
| dbus_bool_t |
| _dbus_header_get_field_raw (DBusHeader *header, |
| int field, |
| const DBusString **str, |
| int *pos) |
| { |
| if (!_dbus_header_cache_check (header, field)) |
| return FALSE; |
| |
| if (str) |
| *str = &header->data; |
| if (pos) |
| *pos = header->fields[field].value_pos; |
| |
| return TRUE; |
| } |
| |
| /** |
| * Deletes a field, if it exists. |
| * |
| * @param header the header |
| * @param field the field to delete |
| * @returns #FALSE if no memory |
| */ |
| dbus_bool_t |
| _dbus_header_delete_field (DBusHeader *header, |
| int field) |
| { |
| DBusTypeReader reader; |
| DBusTypeReader realign_root; |
| |
| if (_dbus_header_cache_known_nonexistent (header, field)) |
| return TRUE; /* nothing to do */ |
| |
| /* Scan to the field we want, delete and realign, reappend |
| * padding. Field may turn out not to exist. |
| */ |
| if (!find_field_for_modification (header, field, |
| &reader, &realign_root)) |
| return TRUE; /* nothing to do */ |
| |
| if (!reserve_header_padding (header)) |
| return FALSE; |
| |
| if (!_dbus_type_reader_delete (&reader, |
| &realign_root)) |
| return FALSE; |
| |
| correct_header_padding (header); |
| |
| _dbus_header_cache_invalidate_all (header); |
| |
| _dbus_assert (!_dbus_header_cache_check (header, field)); /* Expensive assertion ... */ |
| |
| return TRUE; |
| } |
| |
| /** |
| * Toggles a message flag bit, turning on the bit if value = TRUE and |
| * flipping it off if value = FALSE. |
| * |
| * @param header the header |
| * @param flag the message flag to toggle |
| * @param value toggle on or off |
| */ |
| void |
| _dbus_header_toggle_flag (DBusHeader *header, |
| dbus_uint32_t flag, |
| dbus_bool_t value) |
| { |
| unsigned char *flags_p; |
| |
| flags_p = _dbus_string_get_data_len (&header->data, FLAGS_OFFSET, 1); |
| |
| if (value) |
| *flags_p |= flag; |
| else |
| *flags_p &= ~flag; |
| } |
| |
| /** |
| * Gets a message flag bit, returning TRUE if the bit is set. |
| * |
| * @param header the header |
| * @param flag the message flag to get |
| * @returns #TRUE if the flag is set |
| */ |
| dbus_bool_t |
| _dbus_header_get_flag (DBusHeader *header, |
| dbus_uint32_t flag) |
| { |
| const unsigned char *flags_p; |
| |
| flags_p = _dbus_string_get_const_data_len (&header->data, FLAGS_OFFSET, 1); |
| |
| return (*flags_p & flag) != 0; |
| } |
| |
| /** |
| * Swaps the header into the given order if required. |
| * |
| * @param header the header |
| * @param new_order the new byte order |
| */ |
| void |
| _dbus_header_byteswap (DBusHeader *header, |
| int new_order) |
| { |
| if (header->byte_order == new_order) |
| return; |
| |
| _dbus_marshal_byteswap (&_dbus_header_signature_str, |
| 0, header->byte_order, |
| new_order, |
| &header->data, 0); |
| |
| header->byte_order = new_order; |
| } |
| |
| /** @} */ |
| |
| #ifdef DBUS_BUILD_TESTS |
| #include "dbus-test.h" |
| #include <stdio.h> |
| |
| dbus_bool_t |
| _dbus_marshal_header_test (void) |
| { |
| |
| return TRUE; |
| } |
| |
| #endif /* DBUS_BUILD_TESTS */ |