diff options
Diffstat (limited to 'src/messages.c')
-rw-r--r-- | src/messages.c | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/src/messages.c b/src/messages.c new file mode 100644 index 0000000..a5f2f6f --- /dev/null +++ b/src/messages.c @@ -0,0 +1,139 @@ +#include "cmumble.h" + +#define PREAMBLE_SIZE 6 + +GStaticMutex write_mutex = G_STATIC_MUTEX_INIT; + +static const struct { + const ProtobufCMessageDescriptor *descriptor; + const char *name; +} messages[] = { +#define MUMBLE_MSG(a,b,c) { &mumble_proto_##b##__descriptor, c }, + MUMBLE_MSGS +#undef MUMBLE_MSG +}; + +static void +add_preamble(uint8_t *buffer, uint16_t type, uint32_t len) +{ + buffer[1] = (type) & 0xff; + buffer[0] = (type >> 8) & 0xff; + + buffer[5] = (len) & 0xff; + buffer[4] = (len >> 8) & 0xff; + buffer[3] = (len >> 16) & 0xff; + buffer[2] = (len >> 24) & 0xff; +} + +static void +get_preamble(uint8_t *buffer, int *type, int *len) +{ + uint16_t msgType; + uint32_t msgLen; + + msgType = buffer[1] | (buffer[0] << 8); + msgLen = buffer[5] | (buffer[4] << 8) | (buffer[3] << 16) | (buffer[2] << 24); + *type = (int)msgType; + *len = (int)msgLen; +} + +void +send_msg(struct context *ctx, ProtobufCMessage *msg) +{ + uint8_t pad[128]; + uint8_t preamble[PREAMBLE_SIZE]; + int type = -1; + int i; + ProtobufCBufferSimple buffer = PROTOBUF_C_BUFFER_SIMPLE_INIT(pad); + GOutputStream *output = g_io_stream_get_output_stream(ctx->iostream); + + for (i = 0; i < G_N_ELEMENTS(messages); ++i) + if (messages[i].descriptor == msg->descriptor) + type = i; + assert(type >= 0); + + if (type == UDPTunnel) { + MumbleProto__UDPTunnel *tunnel = (MumbleProto__UDPTunnel *) msg; + buffer.data = tunnel->packet.data; + buffer.len = tunnel->packet.len; + buffer.must_free_data = 0; + } else { + protobuf_c_message_pack_to_buffer(msg, &buffer.base); + } + + add_preamble(preamble, type, buffer.len); + + g_static_mutex_lock(&write_mutex); + g_output_stream_write(output, preamble, PREAMBLE_SIZE, NULL, NULL); + g_output_stream_write(output, buffer.data, buffer.len, NULL, NULL); + g_static_mutex_unlock(&write_mutex); + + PROTOBUF_C_BUFFER_SIMPLE_CLEAR(&buffer); +} + +void +recv_msg(struct context *ctx, const callback_t *callbacks, uint32_t callback_size) +{ + uint8_t preamble[PREAMBLE_SIZE]; + ProtobufCMessage *msg; + gchar *data; + int type, len; + gssize ret; + GInputStream *input = g_io_stream_get_input_stream(ctx->iostream); + + ret = g_input_stream_read(input, preamble, PREAMBLE_SIZE, NULL, NULL); + + if (ret <= 0) { + g_printerr("read failed: %ld\n", ret); + return; + } + + get_preamble(preamble, &type, &len); + + if (!(type >= 0 && type < G_N_ELEMENTS(messages))) { + printf("unknown message type: %d\n", type); + return; + } + + if (len <= 0) { + g_printerr("length 0\n"); + return; + } + + data = g_malloc(len); + if (data == NULL) { + g_printerr("out of mem\n"); + g_main_loop_quit (ctx->loop); + } + ret = g_input_stream_read(input, data, len, NULL, NULL); + + /* tunneled udp data - not a regular protobuf message + * create dummy ProtobufCMessage */ + if (type == UDPTunnel) { + MumbleProto__UDPTunnel udptunnel; + mumble_proto__udptunnel__init(&udptunnel); + + udptunnel.packet.len = len; + udptunnel.packet.data = data; + + if (callbacks[UDPTunnel]) + callbacks[UDPTunnel](&udptunnel.base, ctx); + + g_free(data); + return; + } + + msg = protobuf_c_message_unpack(messages[type].descriptor, NULL, + len, data); + if (msg == NULL) { + g_printerr("message unpack failure\n"); + return; + } + + g_print("debug: received message: %s type:%d, len:%d\n", messages[type].name, type, len); + if (callbacks[type]) + callbacks[type](msg, ctx); + + protobuf_c_message_free_unpacked(msg, NULL); + g_free(data); +} |