summaryrefslogtreecommitdiff
path: root/src/message.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/message.c')
-rw-r--r--src/message.c148
1 files changed, 148 insertions, 0 deletions
diff --git a/src/message.c b/src/message.c
new file mode 100644
index 0000000..61e2589
--- /dev/null
+++ b/src/message.c
@@ -0,0 +1,148 @@
+#include "message.h"
+#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) { &mumble_proto__##b##__descriptor, #a },
+ 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
+cmumble_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);
+
+ 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(ctx->con.output, preamble, PREAMBLE_SIZE, NULL, NULL);
+ g_output_stream_write(ctx->con.output, buffer.data, buffer.len, NULL, NULL);
+ g_static_mutex_unlock(&write_mutex);
+
+ PROTOBUF_C_BUFFER_SIMPLE_CLEAR(&buffer);
+}
+
+int
+cmumble_recv_msg(struct context *ctx)
+{
+ uint8_t preamble[PREAMBLE_SIZE];
+ ProtobufCMessage *msg;
+ gchar *data;
+ int type, len;
+ gssize ret;
+ GError *error = NULL;
+
+ g_assert(ctx->callbacks);
+
+ ret = g_pollable_input_stream_read_nonblocking(ctx->con.input,
+ preamble, PREAMBLE_SIZE,
+ NULL, &error);
+
+ if (ret <= 0) {
+ if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+ return 0;
+ g_printerr("read failed: %ld\n", ret);
+ return 0;
+ }
+
+ get_preamble(preamble, &type, &len);
+
+ if (!(type >= 0 && type < G_N_ELEMENTS(messages))) {
+ printf("unknown message type: %d\n", type);
+ return 0;
+ }
+
+ if (len <= 0) {
+ g_printerr("length 0\n");
+ return 0;
+ }
+
+ data = g_malloc(len);
+ if (data == NULL) {
+ g_printerr("out of mem\n");
+ g_main_loop_quit (ctx->loop);
+ }
+ ret = g_input_stream_read(G_INPUT_STREAM(ctx->con.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 = (uint8_t *) data;
+
+ if (ctx->callbacks[UDPTunnel])
+ ctx->callbacks[UDPTunnel](&udptunnel.base, ctx);
+
+ g_free(data);
+ return 0;
+ }
+
+ msg = protobuf_c_message_unpack(messages[type].descriptor, NULL,
+ len, (uint8_t *) data);
+ if (msg == NULL) {
+ g_printerr("message unpack failure\n");
+ return 0;
+ }
+
+ g_print("debug: received message: %s type:%d, len:%d\n", messages[type].name, type, len);
+ if (ctx->callbacks[type])
+ ctx->callbacks[type](msg, ctx);
+
+ protobuf_c_message_free_unpacked(msg, NULL);
+ g_free(data);
+
+ return 1;
+}