#include #include #include "try.h" #include "wire.h" bool wire_align(wire_context_t *c, uint32_t alignment) { if ((c->byte_cursor % alignment) == 0) { return true; } uint32_t new_cursor = c->byte_cursor + alignment - (c->byte_cursor % alignment); if (new_cursor >= c->data_len || new_cursor < c->byte_cursor) { return false; } c->byte_cursor = new_cursor; return true; } bool wire_write_align(wire_context_t *c, uint32_t alignment) { if ((c->byte_cursor % alignment) == 0) { return true; } uint32_t new_cursor = c->byte_cursor + alignment - (c->byte_cursor % alignment); if (new_cursor >= c->data_len || new_cursor < c->byte_cursor) { return false; } while (c->byte_cursor < new_cursor) { c->data[c->byte_cursor] = '\0'; c->byte_cursor++; } return true; } // SPEC: https://dbus.freedesktop.org/doc/dbus-specification.html#id-1.4.6 _WIRE_DEF_BYTE_TYPE(i8, int8_t); _WIRE_DEF_BYTE_TYPE(u8, uint8_t); _WIRE_DEF_TYPE(i16, int16_t, 2); _WIRE_DEF_TYPE(u16, uint16_t, 2); _WIRE_DEF_TYPE(i32, int32_t, 4); _WIRE_DEF_TYPE(u32, uint32_t, 4); _WIRE_DEF_TYPE(boolean, uint32_t, 4); char *wire_get_string(wire_context_t *c, uint32_t *out_len) { uint32_t len = *(uint32_t*)TRYPTR_NIL(wire_get_u32(c)); if (out_len) { *out_len = len; } uint32_t new_cursor = c->byte_cursor + len + 1; /* + 1 because of the extra null termination that strings have */ if (new_cursor >= c->data_len) { return NULL; } char *str = (char *)&c->data[c->byte_cursor]; c->byte_cursor = new_cursor; return str; } char *wire_set_string(wire_context_t *c, const char *str) { uint32_t len = strlen(str); TRYPTR_NIL(wire_set_u32(c, len)); uint32_t new_cursor = c->byte_cursor + len + 1; /* + 1 because of the extra null termination that strings have */ if (new_cursor >= c->data_len) { return NULL; } // TODO? char *dest = (char*)&c->data[c->byte_cursor]; memcpy(dest, str, len + 1); c->byte_cursor = new_cursor; return dest; } char *wire_get_signature(wire_context_t *c, uint8_t *out_len) { uint8_t len = *(uint8_t*)TRYPTR_NIL(wire_get_u8(c)); if (out_len) { *out_len = len; } uint32_t new_cursor = c->byte_cursor + len + 1; /* + 1 because of the extra null termination that strings have */ if (new_cursor >= c->data_len) { return NULL; } char *str = (char *)&c->data[c->byte_cursor]; c->byte_cursor = new_cursor; return str; } char *wire_set_signature(wire_context_t *c, const char *str) { uint8_t len = strlen(str); TRYPTR_NIL(wire_set_u8(c, len)); uint32_t new_cursor = c->byte_cursor + len + 1; /* + 1 because of the extra null termination that strings have */ if (new_cursor >= c->data_len) { return NULL; } // TODO? char *dest = (char*)&c->data[c->byte_cursor]; memcpy(dest, str, len + 1); c->byte_cursor = new_cursor; return dest; } int wire_parse_message(wire_context_t *c, wire_message_t *msg) { // SPEC: https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-messages msg->endianness = *(uint8_t*)TRYPTR(wire_get_u8(c)); msg->type = *(uint8_t*)TRYPTR(wire_get_u8(c)); msg->flags = *(uint8_t*)TRYPTR(wire_get_u8(c)); msg->protocol_version = *(uint8_t*)TRYPTR(wire_get_u8(c)); msg->body_length = *(uint32_t*)TRYPTR(wire_get_u32(c)); msg->serial = *(uint32_t*)TRYPTR(wire_get_u32(c)); msg->header_fields_length = *(uint32_t*)TRYPTR(wire_get_u32(c)); size_t header_fields_end = c->byte_cursor + msg->header_fields_length; while (c->byte_cursor < header_fields_end) { uint8_t field_code = *(uint8_t*)TRYPTR(wire_get_u8(c)); TRYPTR(wire_get_signature(c, NULL)); switch (field_code) { case DBUS_HEADER_FIELD_PATH: /* through */ case DBUS_HEADER_FIELD_INTERFACE: /* through */ case DBUS_HEADER_FIELD_MEMBER: /* through */ case DBUS_HEADER_FIELD_DESTINATION: { char *str = TRYPTR(wire_get_string(c, NULL)); printf("field: %s\n", str); msg->fields[field_code].t.str = str; } break; case DBUS_HEADER_FIELD_SIGNATURE: { char *str = TRYPTR(wire_get_signature(c, NULL)); printf("field: %s\n", str); msg->fields[field_code].t.str = str; } break; case DBUS_HEADER_FIELD_REPLY_SERIAL: { uint32_t u = *(uint32_t*)TRYPTR(wire_get_u32(c)); printf("field: %d\n", u); msg->fields[field_code].t.u32 = u; } break; default: { printf("header: unknown field: %d\n", field_code); return -1; } break; } msg->fields[field_code].present = true; // Structs are always aligned to `8`. // SPEC: https://dbus.freedesktop.org/doc/dbus-specification.html#id-1.4.6 TRYPTR(wire_align(c, 8)); if (msg->header_fields_count++ > WIRE_MESSAGE_MAX_HEADER_FIELDS) { return -1; } } /* header ends at 8 byte boundry */ TRYPTR(wire_align(c, 8)); return 0; } int wire_compose_reply(wire_context_t *c, wire_message_t *msg, const char *signature, uint32_t **out_body_length) { TRYPTR(wire_set_u8(c, msg->endianness)); /* endianness */ TRYPTR(wire_set_u8(c, DBUS_MESSAGE_METHOD_RETURN)); /* type */ TRYPTR(wire_set_u8(c, 0)); /* flags */ TRYPTR(wire_set_u8(c, DBUS_PROTOCOL_VERSION)); /* protocol_version */ uint32_t *body_length = TRYPTR(wire_set_u32(c, 0)); /* body length */ TRYPTR(wire_set_u32(c, msg->serial+1)); /* serial */ uint32_t *header_fields_length = TRYPTR(wire_set_u32(c, 0)); /* header_fields_length */ uint32_t header_fields_start = c->byte_cursor; /* header fields */ { /* SIGNATURE */ { /* byte (field code) */ TRYPTR(wire_set_u8(c, DBUS_HEADER_FIELD_SIGNATURE)); /* variant */ /* signature */ TRYPTR(wire_set_signature(c, "g")); /* signature */ TRYPTR(wire_set_signature(c, signature)); // need to align to 8 byte boundry after each array element? TRYPTR(wire_write_align(c, 8)); } /* REPLY_SERIAL */ { /* byte (field code) */ TRYPTR(wire_set_u8(c, DBUS_HEADER_FIELD_REPLY_SERIAL)); /* variant */ /* signature */ TRYPTR(wire_set_signature(c, "u")); /* reply serial */ TRYPTR(wire_set_u32(c, msg->serial)); } } *header_fields_length = c->byte_cursor - header_fields_start; // header ends on an 8 byte alignment TRYPTR(wire_write_align(c, 8)); if (out_body_length) { *out_body_length = body_length; } return 0; } int wire_compose_error(wire_context_t *c, wire_message_t *msg, const char *error_name) { TRYPTR(wire_set_u8(c, msg->endianness)); /* endianness */ TRYPTR(wire_set_u8(c, DBUS_MESSAGE_ERROR)); /* type */ TRYPTR(wire_set_u8(c, 0)); /* flags */ TRYPTR(wire_set_u8(c, DBUS_PROTOCOL_VERSION)); /* protocol_version */ TRYPTR(wire_set_u32(c, 0)); /* body length */ TRYPTR(wire_set_u32(c, msg->serial+1)); /* serial */ uint32_t *header_fields_length = TRYPTR(wire_set_u32(c, 0)); /* header_fields_length */ uint32_t header_fields_start = c->byte_cursor; /* header fields */ { /* ERROR_NAME */ { /* byte (field code) */ TRYPTR(wire_set_u8(c, DBUS_HEADER_FIELD_ERROR_NAME)); /* variant */ /* signature */ TRYPTR(wire_set_signature(c, "s")); /* string */ TRYPTR(wire_set_string(c, error_name)); // need to align to 8 byte boundry after each array element? TRYPTR(wire_write_align(c, 8)); } /* REPLY_SERIAL */ { /* byte (field code) */ TRYPTR(wire_set_u8(c, DBUS_HEADER_FIELD_REPLY_SERIAL)); /* variant */ /* signature */ TRYPTR(wire_set_signature(c, "u")); /* reply serial */ TRYPTR(wire_set_u32(c, msg->serial)); } } *header_fields_length = c->byte_cursor - header_fields_start; // header ends on an 8 byte alignment TRYPTR(wire_write_align(c, 8)); return 0; } int wire_compose_unicast_reply(wire_context_t *c, wire_context_t *msg_c, wire_message_t *msg, char *sender_unique_name) { TRYPTR(wire_set_u8(c, msg->endianness)); /* endianness */ TRYPTR(wire_set_u8(c, msg->type)); /* type */ TRYPTR(wire_set_u8(c, 0)); /* flags */ TRYPTR(wire_set_u8(c, DBUS_PROTOCOL_VERSION)); /* protocol_version */ TRYPTR(wire_set_u32(c, msg->body_length)); /* body length */ TRYPTR(wire_set_u32(c, msg->serial)); /* serial */ uint32_t *header_fields_length = TRYPTR(wire_set_u32(c, 0)); /* header_fields_length */ uint32_t header_fields_start = c->byte_cursor; switch (msg->type) { case DBUS_MESSAGE_METHOD_CALL: { // these fields are required for DBUS_MESSAGE_METHOD_CALL, so we must (hackily) check for their presence // interface is not strictly required by the spec, however we require it here anyways wire_message_field_t *path_field = &msg->fields[DBUS_HEADER_FIELD_PATH]; wire_message_field_t *interface_field = &msg->fields[DBUS_HEADER_FIELD_INTERFACE]; wire_message_field_t *member_field = &msg->fields[DBUS_HEADER_FIELD_MEMBER]; wire_message_field_t *signature_field = &msg->fields[DBUS_HEADER_FIELD_SIGNATURE]; if (!path_field->present || !interface_field->present || !interface_field->present) { return -1; } TRYPTR(wire_set_u8(c, DBUS_HEADER_FIELD_PATH)); TRYPTR(wire_set_signature(c, "o")); TRYPTR(wire_set_string(c, path_field->t.str)); TRYPTR(wire_write_align(c, 8)); TRYPTR(wire_set_u8(c, DBUS_HEADER_FIELD_INTERFACE)); TRYPTR(wire_set_signature(c, "s")); TRYPTR(wire_set_string(c, interface_field->t.str)); TRYPTR(wire_write_align(c, 8)); TRYPTR(wire_set_u8(c, DBUS_HEADER_FIELD_MEMBER)); TRYPTR(wire_set_signature(c, "s")); TRYPTR(wire_set_string(c, member_field->t.str)); TRYPTR(wire_write_align(c, 8)); if (signature_field->present) { TRYPTR(wire_set_u8(c, DBUS_HEADER_FIELD_SIGNATURE)); TRYPTR(wire_set_signature(c, "g")); TRYPTR(wire_set_signature(c, signature_field->t.str)); TRYPTR(wire_write_align(c, 8)); } TRYPTR(wire_set_u8(c, DBUS_HEADER_FIELD_SENDER)); TRYPTR(wire_set_signature(c, "s")); TRYPTR(wire_set_string(c, sender_unique_name)); // last element, don't align } break; case DBUS_MESSAGE_METHOD_RETURN: { wire_message_field_t *reply_serial_field = &msg->fields[DBUS_HEADER_FIELD_REPLY_SERIAL]; if (!reply_serial_field->present) { return -1; } TRYPTR(wire_set_u8(c, DBUS_HEADER_FIELD_REPLY_SERIAL)); TRYPTR(wire_set_signature(c, "u")); TRYPTR(wire_set_u32(c, reply_serial_field->t.u32)); TRYPTR(wire_write_align(c, 8)); wire_message_field_t *signature_field = &msg->fields[DBUS_HEADER_FIELD_SIGNATURE]; if (signature_field->present) { TRYPTR(wire_set_u8(c, DBUS_HEADER_FIELD_SIGNATURE)); TRYPTR(wire_set_signature(c, "g")); TRYPTR(wire_set_string(c, signature_field->t.str)); TRYPTR(wire_write_align(c, 8)); } TRYPTR(wire_set_u8(c, DBUS_HEADER_FIELD_SENDER)); TRYPTR(wire_set_signature(c, "s")); TRYPTR(wire_set_string(c, sender_unique_name)); // last element, don't align } break; default: { return -1; } break; } *header_fields_length = c->byte_cursor - header_fields_start; // header ends on an 8 byte alignment TRYPTR(wire_write_align(c, 8)); if (c->byte_cursor + msg->body_length > c->data_len) { return -1; } memcpy(&c->data[c->byte_cursor], &msg_c->data[msg_c->byte_cursor], msg->body_length); c->byte_cursor += msg->body_length; return 0; }