basic unicast relay mechanism

This commit is contained in:
hippoz 2022-12-23 23:58:18 +02:00
parent 36162d106e
commit ec156a1b14
Signed by: hippoz
GPG key ID: 56C4E02A85F2FBED
4 changed files with 234 additions and 89 deletions

View file

@ -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 $@ $^

180
server.c
View file

@ -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;

137
wire.c
View file

@ -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;
}

4
wire.h
View file

@ -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