diff --git a/Makefile b/Makefile index 4eab122..b2bc867 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ CC?=gcc -CFLAGS+=-Werror -Wall -Wextra -std=c99 +CFLAGS+=-Werror -Wall -Wextra -std=c99 -ggdb jitterbug: wire.c server.c main.c $(CC) $(CFLAGS) -o $@ $^ diff --git a/server.c b/server.c index 0e4c614..8d50c05 100644 --- a/server.c +++ b/server.c @@ -60,6 +60,24 @@ int jb_server_name_find(struct jb_server *s, char *name) return -1; } +struct jb_client *jb_server_name_find_client(struct jb_server *s, char *name) +{ + int name_index = jb_server_name_find(s, name); + if (name_index < 0) { + return NULL; + } + + if (s->names[name_index].client_index < 0) { + return NULL; + } + + if (s->clients[s->names[name_index].client_index].state != JB_CLIENT_STATE_READY) { + return NULL; + } + + return &s->clients[s->names[name_index].client_index]; +} + int jb_server_name_add(struct jb_server *s, char *name, int client_index) { if (jb_server_name_find(s, name) >= 0) { @@ -185,90 +203,98 @@ int jb_server_client_process_message(struct jb_server *s, int i, uint8_t *data, TRY_NONNEGATIVE(int, wire_parse_message(&ctx, &msg), -1); - uint8_t reply_data[512]; - memset(reply_data, 0, 512); + uint8_t reply_data[4096]; + memset(reply_data, 0, 4096); wire_context_t reply_ctx = { .byte_cursor = 0, .data = reply_data, - .data_len = 511, + .data_len = 4096, }; + wire_message_field_t *destination_field = &msg.fields[DBUS_HEADER_FIELD_DESTINATION]; + wire_message_field_t *member_field = &msg.fields[DBUS_HEADER_FIELD_MEMBER]; + + bool should_unicast = false; + switch (msg.type) { case DBUS_MESSAGE_METHOD_CALL: { - bool for_dbus = false; - char *member; - - // TODO: maybe the fields in the msg.fields array should be indexed based on their type? - for (int i = 0; i < msg.header_fields_count; i++) { - if (msg.fields[i].type == DBUS_HEADER_FIELD_DESTINATION) { - if (strcmp(msg.fields[i].t.str, "org.freedesktop.DBus") == 0) { - for_dbus = true; - } - } else if (msg.fields[i].type == DBUS_HEADER_FIELD_MEMBER) { - member = msg.fields[i].t.str; - } + if (!destination_field->present || !member_field->present) { + return -1; } - if (for_dbus) { - if (strcmp(member, "Hello") == 0) { - int unique_name_index = jb_server_client_assign_unique_name(s, i); - if (unique_name_index < 0) { - return -1; - } + char *member = member_field->t.str; - uint32_t *body_length; - TRY_NONNEGATIVE(int, wire_compose_reply(&reply_ctx, &msg, "s", &body_length), -1); - uint32_t body_start = reply_ctx.byte_cursor; - TRY_NONNULL(char*, wire_set_string(&reply_ctx, s->names[unique_name_index].name), -1); - *body_length = reply_ctx.byte_cursor - body_start; - if (send(s->fds[i].fd, reply_data, reply_ctx.byte_cursor, 0) != reply_ctx.byte_cursor) { - return -1; - } - printf("assigned unique name '%s' to connection %d\n", s->names[unique_name_index].name, i); - } else if (strcmp(member, "RequestName") == 0) { - char *name = TRY_NONNULL(char*, wire_get_string(&ctx, NULL), -1); - int name_len = strlen(name); - if (name_len < 1 || name_len > 256) { - return -1; - } + if (strcmp(destination_field->t.str, "org.freedesktop.DBus") != 0) { + // not for dbus. + should_unicast = true; + break; + } - char *name_str = string_dup(name); - if (!name_str) { - return -1; - } + if (strcmp(member, "Hello") == 0) { + int unique_name_index = jb_server_client_assign_unique_name(s, i); + if (unique_name_index < 0) { + return -1; + } - int status_code = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER; + uint32_t *body_length; + TRY_NONNEGATIVE(int, wire_compose_reply(&reply_ctx, &msg, "s", &body_length), -1); + uint32_t body_start = reply_ctx.byte_cursor; + TRY_NONNULL(char*, wire_set_string(&reply_ctx, s->names[unique_name_index].name), -1); + *body_length = reply_ctx.byte_cursor - body_start; + if (send(s->fds[i].fd, reply_data, reply_ctx.byte_cursor, 0) != reply_ctx.byte_cursor) { + return -1; + } + printf("assigned unique name '%s' to connection %d\n", s->names[unique_name_index].name, i); + } else if (strcmp(member, "RequestName") == 0) { + char *name = TRY_NONNULL(char*, wire_get_string(&ctx, NULL), -1); + int name_len = strlen(name); + if (name_len < 1 || name_len > 256) { + return -1; + } - if (jb_server_client_assign_own_name(s, i, name_str) < 0) { - free(name_str); - // TODO: report the actual error - status_code = DBUS_REQUEST_NAME_REPLY_EXISTS; - } + char *name_str = string_dup(name); + if (!name_str) { + return -1; + } - uint32_t *body_length; - TRY_NONNEGATIVE(int, wire_compose_reply(&reply_ctx, &msg, "u", &body_length), -1); - uint32_t body_start = reply_ctx.byte_cursor; - TRY_NONNULL(uint32_t*, wire_set_u32(&reply_ctx, status_code), -1); - *body_length = reply_ctx.byte_cursor - body_start; - if (send(s->fds[i].fd, reply_data, reply_ctx.byte_cursor, 0) != reply_ctx.byte_cursor) { - return -1; - } + int status_code = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER; - if (status_code == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - printf("client '%s' (index=%d) now owns name '%s'\n", s->names[client->unique_name_index].name, i, name_str); - } - } else { - TRY_NONNEGATIVE(int, wire_compose_error(&reply_ctx, &msg, "org.freedesktop.DBus.Error.UnknownMethod"), -1); - if (send(s->fds[i].fd, reply_data, reply_ctx.byte_cursor, 0) != reply_ctx.byte_cursor) { - return -1; - } + if (jb_server_client_assign_own_name(s, i, name_str) < 0) { + free(name_str); + // TODO: report the actual error + status_code = DBUS_REQUEST_NAME_REPLY_EXISTS; + } + + uint32_t *body_length; + TRY_NONNEGATIVE(int, wire_compose_reply(&reply_ctx, &msg, "u", &body_length), -1); + uint32_t body_start = reply_ctx.byte_cursor; + TRY_NONNULL(uint32_t*, wire_set_u32(&reply_ctx, status_code), -1); + *body_length = reply_ctx.byte_cursor - body_start; + if (send(s->fds[i].fd, reply_data, reply_ctx.byte_cursor, 0) != reply_ctx.byte_cursor) { + return -1; + } + + if (status_code == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + printf("client '%s' (index=%d) now owns name '%s'\n", s->names[client->unique_name_index].name, i, name_str); } } else { - printf("jb_server_client_process_message: skipping message not for dbus for now\n"); + TRY_NONNEGATIVE(int, wire_compose_error(&reply_ctx, &msg, "org.freedesktop.DBus.Error.UnknownMethod"), -1); + if (send(s->fds[i].fd, reply_data, reply_ctx.byte_cursor, 0) != reply_ctx.byte_cursor) { + return -1; + } } } break; + case DBUS_MESSAGE_METHOD_RETURN: { + if (!destination_field->present) { + return -1; + } + + should_unicast = true; + } break; + default: { + printf("xyz.hippoz.jitterbug.NotImplemented\n"); TRY_NONNEGATIVE(int, wire_compose_error(&reply_ctx, &msg, "xyz.hippoz.jitterbug.NotImplemented"), -1); if (send(s->fds[i].fd, reply_data, reply_ctx.byte_cursor, 0) != reply_ctx.byte_cursor) { return -1; @@ -277,6 +303,30 @@ int jb_server_client_process_message(struct jb_server *s, int i, uint8_t *data, } break; } + if (should_unicast) { + if (client->unique_name_index < 0 || !destination_field->present) { + return -1; + } + printf("sending unicast message from %s\n", s->names[client->unique_name_index].name); + struct jb_client *target = jb_server_name_find_client(s, destination_field->t.str); + if (!target) { + uint8_t error_reply_data[512]; + memset(error_reply_data, 0, 512); + wire_context_t error_reply_ctx = { + .byte_cursor = 0, + .data = error_reply_data, + .data_len = 511, + }; + TRY_NONNEGATIVE(int, wire_compose_error(&error_reply_ctx, &msg, "xyz.hippoz.jitterbug.NotImplemented"), -1); + if (send(s->fds[i].fd, error_reply_data, error_reply_ctx.byte_cursor, 0) != error_reply_ctx.byte_cursor) { + return -1; + } + return 0; + } + TRY_NONNEGATIVE(int, wire_compose_unicast_reply(&reply_ctx, &ctx, &msg, s->names[client->unique_name_index].name), -1); + TRY_NONNEGATIVE(int, send(target->fd, reply_data, reply_ctx.byte_cursor, 0), -1); + } + return 0; } @@ -345,7 +395,7 @@ int jb_server_turn(struct jb_server *s) { static const char agree_unix_fd[] = "AGREE_UNIX_FD\r\n"; static const char auth_ok[] = "OK 1234deadbeef\r\n"; - static const int data_buffer_len = 256; + static const int data_buffer_len = 4096; TRY_NONNEGATIVE(int, poll(s->fds, s->fd_num, -1), -1); @@ -395,7 +445,7 @@ int jb_server_turn(struct jb_server *s) char *auth_string = data + 1; - if (strcmp(auth_string, "AUTH EXTERNAL 31303030\r\n") != 0) + if (strcmp(auth_string, "AUTH EXTERNAL 31303030\r\n") != 0 && strcmp(auth_string, "AUTH\r\n") != 0) _client_die("bad auth"); send(fd, auth_ok, sizeof(auth_ok) - 1, 0); c->state = JB_CLIENT_STATE_WAIT_BEGIN; diff --git a/wire.c b/wire.c index be01dbc..4fcedb2 100644 --- a/wire.c +++ b/wire.c @@ -130,44 +130,37 @@ int wire_parse_message(wire_context_t *c, wire_message_t *msg) size_t header_fields_end = c->byte_cursor + msg->header_fields_length; + printf(" > start parse!\n"); + while (c->byte_cursor < header_fields_end) { uint8_t field_code = *TRY_NONNULL(uint8_t*, wire_get_u8(c), -1); TRY_NONNULL(char*, wire_get_signature(c, NULL), -1); - msg->fields[msg->header_fields_count].type = field_code; - switch (field_code) { - case DBUS_HEADER_FIELD_PATH: { - char *str = TRY_NONNULL(char*, wire_get_string(c, NULL), -1); - msg->fields[msg->header_fields_count].t.str = str; - printf("DBUS_HEADER_FIELD_PATH: %s\n", str); - } break; - case DBUS_HEADER_FIELD_INTERFACE: { - char *str = TRY_NONNULL(char*, wire_get_string(c, NULL), -1); - msg->fields[msg->header_fields_count].t.str = str; - printf("DBUS_HEADER_FIELD_INTERFACE: %s\n", str); - } break; - case DBUS_HEADER_FIELD_MEMBER: { - char *str = TRY_NONNULL(char*, wire_get_string(c, NULL), -1); - msg->fields[msg->header_fields_count].t.str = str; - printf("DBUS_HEADER_FIELD_MEMBER: %s\n", str); - } break; + 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 = TRY_NONNULL(char*, wire_get_string(c, NULL), -1); - msg->fields[msg->header_fields_count].t.str = str; - printf("DBUS_HEADER_FIELD_DESTINATION: %s\n", str); + printf("field: %s\n", str); + msg->fields[field_code].t.str = str; } break; case DBUS_HEADER_FIELD_SIGNATURE: { char *str = TRY_NONNULL(char*, wire_get_signature(c, NULL), -1); - msg->fields[msg->header_fields_count].t.str = str; - printf("DBUS_HEADER_FIELD_SIGNATURE: %s\n", str); + printf("field: %s\n", str); + msg->fields[field_code].t.str = str; + } break; + case DBUS_HEADER_FIELD_REPLY_SERIAL: { + uint32_t u = *TRY_NONNULL(uint32_t*, wire_get_u32(c), -1); + 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 @@ -178,6 +171,8 @@ int wire_parse_message(wire_context_t *c, wire_message_t *msg) } } + printf(" > parsed!!\n"); + /* header ends at 8 byte boundry */ TRY_NONNULL(bool, wire_align(c, 8), -1); @@ -285,3 +280,101 @@ int wire_compose_error(wire_context_t *c, wire_message_t *msg, const char *error return 0; } + +int wire_compose_unicast_reply(wire_context_t *c, wire_context_t *msg_c, wire_message_t *msg, char *sender_unique_name) +{ + + TRY_NONNULL(uint8_t*, wire_set_u8(c, msg->endianness), -1); /* endianness */ + TRY_NONNULL(uint8_t*, wire_set_u8(c, msg->type), -1); /* type */ + TRY_NONNULL(uint8_t*, wire_set_u8(c, 0), -1); /* flags */ + TRY_NONNULL(uint8_t*, wire_set_u8(c, DBUS_PROTOCOL_VERSION), -1); /* protocol_version */ + TRY_NONNULL(uint32_t*, wire_set_u32(c, msg->body_length), -1); /* body length */ + TRY_NONNULL(uint32_t*, wire_set_u32(c, msg->serial), -1); /* serial */ + uint32_t *header_fields_length = TRY_NONNULL(uint32_t*, wire_set_u32(c, 0), -1); /* 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; + } + + TRY_NONNULL(uint8_t*, wire_set_u8(c, DBUS_HEADER_FIELD_PATH), -1); + TRY_NONNULL(char*, wire_set_signature(c, "o"), -1); + TRY_NONNULL(char*, wire_set_string(c, path_field->t.str), -1); + TRY_NONNULL(bool, wire_write_align(c, 8), -1); + + TRY_NONNULL(uint8_t*, wire_set_u8(c, DBUS_HEADER_FIELD_INTERFACE), -1); + TRY_NONNULL(char*, wire_set_signature(c, "s"), -1); + TRY_NONNULL(char*, wire_set_string(c, interface_field->t.str), -1); + TRY_NONNULL(bool, wire_write_align(c, 8), -1); + + TRY_NONNULL(uint8_t*, wire_set_u8(c, DBUS_HEADER_FIELD_MEMBER), -1); + TRY_NONNULL(char*, wire_set_signature(c, "s"), -1); + TRY_NONNULL(char*, wire_set_string(c, member_field->t.str), -1); + TRY_NONNULL(bool, wire_write_align(c, 8), -1); + + if (signature_field->present) { + TRY_NONNULL(uint8_t*, wire_set_u8(c, DBUS_HEADER_FIELD_SIGNATURE), -1); + TRY_NONNULL(char*, wire_set_signature(c, "g"), -1); + TRY_NONNULL(char*, wire_set_signature(c, signature_field->t.str), -1); + TRY_NONNULL(bool, wire_write_align(c, 8), -1); + } + + TRY_NONNULL(uint8_t*, wire_set_u8(c, DBUS_HEADER_FIELD_SENDER), -1); + TRY_NONNULL(char*, wire_set_signature(c, "s"), -1); + TRY_NONNULL(char*, wire_set_string(c, sender_unique_name), -1); + // 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; + } + + TRY_NONNULL(uint8_t*, wire_set_u8(c, DBUS_HEADER_FIELD_REPLY_SERIAL), -1); + TRY_NONNULL(char*, wire_set_signature(c, "u"), -1); + TRY_NONNULL(uint32_t*, wire_set_u32(c, reply_serial_field->t.u32), -1); + TRY_NONNULL(bool, wire_write_align(c, 8), -1); + + wire_message_field_t *signature_field = &msg->fields[DBUS_HEADER_FIELD_SIGNATURE]; + if (signature_field->present) { + TRY_NONNULL(uint8_t*, wire_set_u8(c, DBUS_HEADER_FIELD_SIGNATURE), -1); + TRY_NONNULL(char*, wire_set_signature(c, "g"), -1); + TRY_NONNULL(char*, wire_set_string(c, signature_field->t.str), -1); + TRY_NONNULL(bool, wire_write_align(c, 8), -1); + } + + TRY_NONNULL(uint8_t*, wire_set_u8(c, DBUS_HEADER_FIELD_SENDER), -1); + TRY_NONNULL(char*, wire_set_signature(c, "s"), -1); + TRY_NONNULL(char*, wire_set_string(c, sender_unique_name), -1); + // 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 + TRY_NONNULL(bool, wire_write_align(c, 8), -1); + + 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; +} diff --git a/wire.h b/wire.h index 6ae35dd..fb4b563 100644 --- a/wire.h +++ b/wire.h @@ -52,9 +52,10 @@ typedef struct wire_context { // SPEC: https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-messages typedef struct wire_message_field { - uint8_t type; + bool present; union { char *str; + uint32_t u32; } t; } wire_message_field_t; #define WIRE_MESSAGE_MAX_HEADER_FIELDS 9 @@ -128,5 +129,6 @@ char *wire_set_signature(wire_context_t *c, const char *str); int wire_parse_message(wire_context_t *c, wire_message_t *msg); int wire_compose_reply(wire_context_t *c, wire_message_t *msg, const char *signature, uint32_t **out_body_length); int wire_compose_error(wire_context_t *c, wire_message_t *msg, const char *error_name); +int wire_compose_unicast_reply(wire_context_t *c, wire_context_t *msg_c, wire_message_t *msg, char *sender_unique_name); #endif // _JITTERBUG__WIRE_H