diff --git a/server.c b/server.c index 467f7b1..e466ee2 100644 --- a/server.c +++ b/server.c @@ -327,8 +327,15 @@ int bus_broadcast_message(Bus *s, BusClient *sender_client, WireMsg *msg, WireCt uint32_t previous_cursor = ctx->byte_cursor; if (match_rule_check(sender_client, c->matches[j], msg, ctx) >= 0) { - TRYST(wire_compose_unicast_reply(reply_ctx, ctx, msg, sender_client->unique_name->name, false)); - TRYST(send(c->fd, reply_ctx->data, reply_ctx->byte_cursor, 0)); + struct iovec bufs[2] = {0}; + uint8_t header_data[384]; + WireCtx header_ctx = { + .byte_cursor = 0, + .data_len = 384, + .data = header_data + }; + TRYST(wire_compose_unicast_reply(ctx, msg, sender_client->unique_name->name, bufs, &header_ctx)); + TRYST(writev(c->fd, bufs, sizeof(bufs) / sizeof(bufs[0]))); // TODO? memset(reply_ctx->data, 0, reply_ctx->data_len); } @@ -375,17 +382,17 @@ int bus_broadcast_signal(Bus *s, BusClient *client, WireCtx *ctx, WireMsg *msg) int bus_unicast_message(Bus *s, WireMsg *msg, WireCtx *ctx, char *target_name, char *sender_unique_name, WireCtx *reply_ctx) { BusClient *target = TRYPTR(bus_name_find_client(s, target_name)); - TRYST(wire_compose_unicast_reply(reply_ctx, ctx, msg, sender_unique_name, true)); - - struct iovec bufs[] = { - { reply_ctx->data, reply_ctx->byte_cursor }, /* header made by wire_compose_unicast_reply */ - { ctx->data + reply_ctx->byte_cursor, msg->body_length } /* body */ + struct iovec bufs[2] = {0}; + uint8_t header_data[384]; + WireCtx header_ctx = { + .byte_cursor = 0, + .data_len = 384, + .data = header_data }; - ctx->byte_cursor += msg->body_length; - reply_ctx->byte_cursor += msg->body_length; - + TRYST(wire_compose_unicast_reply(ctx, msg, sender_unique_name, bufs, &header_ctx)); TRYST(writev(target->fd, bufs, sizeof(bufs) / sizeof(bufs[0]))); + return 0; } diff --git a/wire.c b/wire.c index 9773015..63e2ed4 100644 --- a/wire.c +++ b/wire.c @@ -1,5 +1,6 @@ #include #include +#include #include "try.h" #include "util.h" #include "wire.h" @@ -131,7 +132,13 @@ int wire_parse_message(WireCtx *c, WireMsg *msg) msg->serial = *(uint32_t*)TRYPTR(wire_get_u32(c)); msg->header_fields_length = *(uint32_t*)TRYPTR(wire_get_u32(c)); + TRYPTR(wire_write_align(c, 8)); + + msg->header_length = c->byte_cursor; size_t header_fields_end = c->byte_cursor + msg->header_fields_length; + if (unlikely(header_fields_end >= c->data_len)) { + return -1; + } VERBOSE("start parse message: type=%d, no_reply_expected=%d\n", msg->type, msg->flags & DBUS_FLAG_NO_REPLY_EXPECTED); @@ -412,134 +419,35 @@ int wire_compose_error(WireCtx *c, WireMsg *msg, const char *error_name) return 0; } -int wire_compose_unicast_reply(WireCtx *c, WireCtx *msg_c, WireMsg *msg, char *sender_unique_name, bool partial) +int wire_compose_unicast_reply(WireCtx *msg_c, WireMsg *msg, char *sender_unique_name, struct iovec bufs[2], WireCtx *header_ctx) { - TRYPTR(wire_set_u8(c, msg->endianness)); /* endianness */ - TRYPTR(wire_set_u8(c, msg->type)); /* type */ - TRYPTR(wire_set_u8(c, msg->flags)); /* 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 */ + TRYPTR(wire_set_u8(header_ctx, msg->endianness)); /* endianness */ + TRYPTR(wire_set_u8(header_ctx, msg->type)); /* type */ + TRYPTR(wire_set_u8(header_ctx, msg->flags)); /* flags */ + TRYPTR(wire_set_u8(header_ctx, DBUS_PROTOCOL_VERSION)); /* protocol_version */ + TRYPTR(wire_set_u32(header_ctx, msg->body_length)); /* body length */ + TRYPTR(wire_set_u32(header_ctx, msg->serial)); /* serial */ + uint32_t *header_fields_length = TRYPTR(wire_set_u32(header_ctx, 0)); /* header_fields_length */ - uint32_t header_fields_start = c->byte_cursor; + TRYPTR(wire_write_align(header_ctx, 8)); - switch (msg->type) { - case DBUS_MESSAGE_SIGNAL: /* through */ - 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 - WireMsgField *path_field = &msg->fields[DBUS_HEADER_FIELD_PATH]; - WireMsgField *interface_field = &msg->fields[DBUS_HEADER_FIELD_INTERFACE]; - WireMsgField *member_field = &msg->fields[DBUS_HEADER_FIELD_MEMBER]; - WireMsgField *signature_field = &msg->fields[DBUS_HEADER_FIELD_SIGNATURE]; - if (!path_field->present || !interface_field->present || !interface_field->present) { - return -1; - } + uint32_t header_fields_start = header_ctx->byte_cursor; - TRYPTR(wire_set_u8(c, DBUS_HEADER_FIELD_PATH)); - TRYPTR(wire_set_signature_single_char(c, 'o')); - TRYPTR(wire_set_string(c, path_field->t.str)); - TRYPTR(wire_write_align(c, 8)); + TRYPTR(wire_set_u8(header_ctx, DBUS_HEADER_FIELD_SENDER)); + TRYPTR(wire_set_signature_single_char(header_ctx, 's')); + TRYPTR(wire_set_string(header_ctx, sender_unique_name)); + TRYPTR(wire_write_align(header_ctx, 8)); - TRYPTR(wire_set_u8(c, DBUS_HEADER_FIELD_INTERFACE)); - TRYPTR(wire_set_signature_single_char(c, 's')); - TRYPTR(wire_set_string(c, interface_field->t.str)); - TRYPTR(wire_write_align(c, 8)); + *header_fields_length = header_ctx->byte_cursor - header_fields_start + msg->header_fields_length; - TRYPTR(wire_set_u8(c, DBUS_HEADER_FIELD_MEMBER)); - TRYPTR(wire_set_signature_single_char(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_single_char(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_single_char(c, 's')); - TRYPTR(wire_set_string(c, sender_unique_name)); - // last element, don't align - } break; - - case DBUS_MESSAGE_METHOD_RETURN: { - WireMsgField *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_single_char(c, 'u')); - TRYPTR(wire_set_u32(c, reply_serial_field->t.u32)); - TRYPTR(wire_write_align(c, 8)); - - WireMsgField *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_single_char(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_single_char(c, 's')); - TRYPTR(wire_set_string(c, sender_unique_name)); - // last element, don't align - } break; - - case DBUS_MESSAGE_ERROR: { - WireMsgField *reply_serial_field = &msg->fields[DBUS_HEADER_FIELD_REPLY_SERIAL]; - WireMsgField *error_name_field = &msg->fields[DBUS_HEADER_FIELD_ERROR_NAME]; - if (!reply_serial_field->present || !error_name_field->present) { - return -1; - } - - TRYPTR(wire_set_u8(c, DBUS_HEADER_FIELD_REPLY_SERIAL)); - TRYPTR(wire_set_signature_single_char(c, 'u')); - TRYPTR(wire_set_u32(c, reply_serial_field->t.u32)); - TRYPTR(wire_write_align(c, 8)); - - WireMsgField *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_single_char(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_ERROR_NAME)); - TRYPTR(wire_set_signature_single_char(c, 's')); - TRYPTR(wire_set_string(c, error_name_field->t.str)); - TRYPTR(wire_write_align(c, 8)); - - TRYPTR(wire_set_u8(c, DBUS_HEADER_FIELD_SENDER)); - TRYPTR(wire_set_signature_single_char(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 || msg_c->byte_cursor + msg->body_length >= msg_c->data_len) { + if (msg_c->byte_cursor + msg->body_length >= msg_c->data_len) { return -1; } - if (!partial) { - memcpy(&c->data[c->byte_cursor], &msg_c->data[msg_c->byte_cursor], msg->body_length); - c->byte_cursor += msg->body_length; - msg_c->byte_cursor += msg->body_length; - } + bufs[0] = (struct iovec){ header_ctx->data, header_ctx->byte_cursor }; + bufs[1] = (struct iovec){ msg_c->data + msg->header_length, msg_c->byte_cursor + msg->body_length - msg->header_length }; + + msg_c->byte_cursor += msg->body_length; return 0; } diff --git a/wire.h b/wire.h index 4eb1ace..447d270 100644 --- a/wire.h +++ b/wire.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "util.h" #include "try.h" @@ -57,7 +58,7 @@ typedef struct { #define WIRE_MESSAGE_MAX_HEADER_FIELDS 9 typedef struct { uint8_t endianness, type, flags, protocol_version; - uint32_t body_length, serial, header_fields_length; + uint32_t body_length, serial, header_fields_length, header_length; uint8_t header_fields_count; WireMsgField fields[WIRE_MESSAGE_MAX_HEADER_FIELDS]; /* cannot have more than 9 header fields */ } WireMsg; @@ -128,7 +129,7 @@ char *wire_get_string_check(WireCtx *c, int min_length, int max_length); int wire_parse_message(WireCtx *c, WireMsg *msg); int wire_compose_reply(WireCtx *c, WireMsg *msg, const char *signature, uint32_t **out_body_length); int wire_compose_error(WireCtx *c, WireMsg *msg, const char *error_name); -int wire_compose_unicast_reply(WireCtx *c, WireCtx *msg_c, WireMsg *msg, char *sender_unique_name, bool partial); +int wire_compose_unicast_reply(WireCtx *msg_c, WireMsg *msg, char *sender_unique_name, struct iovec bufs[2], WireCtx *header_ctx); int wire_compose_signal(WireCtx *c, WireMsg *out_msg, const char *signature, const char *member, uint32_t **out_body_length); int wire_collect_strings(WireCtx *c, WireMsg *msg, WireMsgBodyString *strings, int strings_count);