diff --git a/ssb-gtk/sbot.c b/ssb-gtk/sbot.c index d551033..662129d 100644 --- a/ssb-gtk/sbot.c +++ b/ssb-gtk/sbot.c @@ -4,6 +4,9 @@ #include "sbot.h" +#define MAX_MESSAGE_SIZE 32768 +#define BOXS_MAXLEN 4096 + struct _SsbScuttler { GObject parent_instance; @@ -20,6 +23,7 @@ struct _SsbScuttler { guchar nonce1[24]; guchar nonce2[24]; guchar rx_nonce[24]; + guchar rx_buf[BOXS_MAXLEN]; gsize rx_buf_pos; gsize rx_buf_len; gboolean noauth; @@ -56,9 +60,6 @@ typedef enum { SSB_STREAM_STATE_ENDED_ERROR, } SsbStreamState; -#define MAX_MESSAGE_SIZE 32768 -#define BOXS_MAXLEN 4096 - G_DEFINE_QUARK(ssb_scttler_error_quark, ssb_scuttler_error); G_DEFINE_TYPE(SsbScuttler, ssb_scuttler, G_TYPE_INITIALLY_UNOWNED); @@ -367,7 +368,7 @@ read_inbound(GSocket *socket, GIOCondition condition, SsbScuttler *scuttler) data->read_buffer = g_malloc(MAX_MESSAGE_SIZE); g_input_stream_read_async(stream, - data->read_buffer, MAX_MESSAGE_SIZE, + data->read_buffer, 2, G_PRIORITY_DEFAULT, NULL, data_read, data); @@ -870,6 +871,206 @@ ssb_scuttler_send_packet(SsbScuttler *scuttler, return TRUE; } +static gboolean +bs_read_packet(SsbScuttler *scuttler, gpointer buf, gsize *lenp, GError **error) +{ + guchar boxed_header[34]; + gsize len; + GError *err = NULL; + + if (!ssb_scuttler_read(scuttler, boxed_header, 34, &err)) { + if (err->code == G_IO_ERROR_BROKEN_PIPE) { + g_propagate_error(error, err); + } else { + g_set_error(error, + SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_READ, + "Failed to read boxed packet header"); + } + + return FALSE; + } + + BoxsHeader header; + + if (crypto_secretbox_open_easy((guchar * + )&header, boxed_header, 34, scuttler->rx_nonce, scuttler->decrypt_key) < 0) { + g_set_error(error, + SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_DECRYPT, + "Failed to unbox packet header"); + + return FALSE; + } + + increment_nonce(scuttler->rx_nonce); + + if ((header.len == 0) && !memcmp(header.mac, zeros, 16)) { + g_set_error(error, + G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE, + "Broken pipe"); + + return FALSE; + } + + len = g_ntohs(header.len); + + if (len > BOXS_MAXLEN) { + g_set_error(error, + SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_RESPONSE_TOO_LARGE, + "Received boxed packet too large"); + + return FALSE; + } + + guchar boxed_data[len + 16]; + + if (!ssb_scuttler_read(scuttler, boxed_data + 16, len, &err)) { + g_propagate_error(error, err); + + return FALSE; + } + + memcpy(boxed_data, header.mac, 16); + + if (crypto_secretbox_open_easy(buf, boxed_data, len + 16, scuttler->rx_nonce, scuttler->decrypt_key) < 0) { + g_set_error(error, + SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_CRYPT, + "Failed to unbx packet data"); + + return FALSE; + } + + increment_nonce(scuttler->rx_nonce); + + if (lenp) { + *lenp = len; + } + + return TRUE; +} + +static gboolean +bs_read(SsbScuttler *scuttler, gpointer buf, gsize len) +{ + size_t remaining; + GError *err = NULL; + + while (len > 0) { + remaining = (scuttler->rx_buf_len > len) ? len : scuttler->rx_buf_len; + + if (buf) { + memcpy(buf, scuttler->rx_buf + scuttler->rx_buf_pos, remaining); + } + + scuttler->rx_buf_len -= remaining; + scuttler->rx_buf_pos += remaining; + len -= remaining; + buf += remaining; + + if (len == 0) { + return TRUE; + } + + if (bs_read_packet(scuttler, scuttler->rx_buf, &(scuttler->rx_buf_len), &err) < 0) { + return FALSE; + } + + scuttler->rx_buf_pos = 0; + } + + return TRUE; +} + +static gboolean +ps_read_header(SsbScuttler *scuttler, gsize *len, guint32 *req_id, SsbPacketFlags *flags) +{ + char buf[9]; + PacketHeader header; + + if (!bs_read(scuttler, buf, sizeof(buf))) { + return FALSE; + } + + memcpy(&header, buf + 1, 8); + + if (len) { + *len = g_ntohl(header.len); + } + + if (req_id) { + *req_id = g_ntohl(header.request_id); + } + + if (flags) { + *flags = buf[0]; + } + + return TRUE; +} + +static gboolean +ps_reject(SsbScuttler *scuttler, gsize len, guint32 req, SsbPacketFlags flags, GError **error) +{ + g_debug("Ignoring packet"); + + if (bs_read_out(scuttler, len) < 0) { + g_set_error(error, + SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_READ, + "bs_read_out"); + + return FALSE; + } +} + +static gboolean +ssb_scuttler_read_async(SsbScuttler *scuttler, guint32 req_id, GError **error) +{ + guint32 req; + gsize len; + SsbPacketFlags flags; + + while (TRUE) { + if (!ps_read_header(scuttler, &len, &req, &flags)) { + g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_READ, "ps_read_header"); + + return FALSE; + } + + if (req == -req_id) { + break; + } + + if ((req == 0) && (len == 0)) { + g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_STREAM_END, "Unexpected end of parent stream"); + + return FALSE; + } + + ps_reject(scuttler, len, req, flags); + } + + if (flags & SSB_PACKET_FLAGS_END) { + gint rc; + + if ((rc = ps_read_error(scuttler, flags, len)) < 0) { + g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_READ, "bs_read_error"); + + return FALSE; + } + + if (rc == 1) { + return 2; // TODO: What is this for? + } + + return 1; + } + + if (bs_read_out(bs, buffer, len) < 0) { + g_set_error(error, SSB_SCUTTLER_ERROR, SSB_SCUTTLER_ERROR_READ, "bs_read_out"); + + return FALSE; + } +} + // was: muxrpc_call /** * ssb_scuttler_muxrpc_call: diff --git a/ssb-gtk/sbot.h b/ssb-gtk/sbot.h index 9f4c604..0aa7532 100644 --- a/ssb-gtk/sbot.h +++ b/ssb-gtk/sbot.h @@ -15,6 +15,11 @@ typedef enum { SSB_SCUTTLER_ERROR_REQUEST_TOO_LARGE, SSB_SCUTTLER_ERROR_SEND, SSB_SCUTTLER_ERROR_NOTCONNECTED, + SSB_SCUTTLER_ERROR_READ, + SSB_SCUTTLER_ERROR_CRYPT, + SSB_SCUTTLER_ERROR_DECRYPT, + SSB_SCUTTLER_ERROR_RESPONSE_TOO_LARGE, + SSB_SCUTTLER_ERROR_STREAM_END, } SsbScuttlerError; G_BEGIN_DECLS