basic unicast relay mechanism
This commit is contained in:
parent
36162d106e
commit
ec156a1b14
4 changed files with 234 additions and 89 deletions
2
Makefile
2
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 $@ $^
|
||||
|
|
180
server.c
180
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;
|
||||
|
|
137
wire.c
137
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;
|
||||
}
|
||||
|
|
4
wire.h
4
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
|
||||
|
|
Loading…
Reference in a new issue