static gboolean relay_length (CockpitHttpStream *self, CockpitChannel *channel, GByteArray *buffer) { GBytes *message = NULL; gsize block; g_assert (self->response_length >= 0); if (self->response_length == 0) { /* All done, yay */ g_debug ("%s: received enough bytes", self->name); cockpit_channel_close (channel, NULL); g_assert (self->state == FINISHED); } else if (buffer->len == 0) { /* Not enough data? */ return FALSE; } else { block = MIN (buffer->len, self->response_length); self->response_length -= block; message = cockpit_pipe_consume (buffer, 0, block, 0); relay_data (channel, message); g_bytes_unref (message); } return TRUE; }
static void on_pipe_read (CockpitPipe *pipe, GByteArray *input, gboolean end_of_data, gpointer user_data) { CockpitPipeTransport *self = COCKPIT_PIPE_TRANSPORT (user_data); GBytes *message; GBytes *payload; gchar *channel; guint32 size; for (;;) { if (input->len < sizeof (size)) { if (!end_of_data) g_debug ("%s: want more data", self->name); break; } memcpy (&size, input->data, sizeof (size)); size = GUINT32_FROM_BE (size); if (input->len < size + sizeof (size)) { g_debug ("%s: want more data", self->name); break; } message = cockpit_pipe_consume (input, sizeof (size), size); payload = cockpit_transport_parse_frame (message, &channel); if (payload) { g_debug ("%s: received a %d byte payload", self->name, (int)size); cockpit_transport_emit_recv ((CockpitTransport *)self, channel, payload); g_bytes_unref (payload); g_free (channel); } g_bytes_unref (message); } if (end_of_data) { /* Received a partial message */ if (input->len > 0) { g_warning ("%s: received truncated %d byte frame", self->name, input->len); cockpit_pipe_close (pipe, "internal-error"); } } }
static void drain_buffer (CockpitSshTransport *self) { GBytes *message; GBytes *payload; gchar *channel; guint32 size; for (;;) { if (self->buffer->len < sizeof (size)) { if (!self->received_eof) g_debug ("%s: want len have %d", self->logname, (int)self->buffer->len); break; } memcpy (&size, self->buffer->data, sizeof (size)); size = GUINT32_FROM_BE (size); if (self->buffer->len < size + sizeof (size)) { g_debug ("%s: want %d have %d", self->logname, (int)(size + sizeof (size)), (int)self->buffer->len); break; } message = cockpit_pipe_consume (self->buffer, sizeof (size), size, 0); payload = cockpit_transport_parse_frame (message, &channel); if (payload) { g_debug ("%s: received a %d byte payload", self->logname, (int)g_bytes_get_size (payload)); cockpit_transport_emit_recv ((CockpitTransport *)self, channel, payload); g_bytes_unref (payload); g_free (channel); } g_bytes_unref (message); } if (self->received_eof) { /* Received a partial message */ if (self->buffer->len > 0) { g_warning ("%s: received truncated %d byte frame", self->logname, (int)self->buffer->len); close_immediately (self, "internal-error"); } } }
static gboolean relay_all (CockpitHttpStream *self, CockpitChannel *channel, GByteArray *buffer) { GBytes *message; if (buffer->len == 0) { /* Not enough data? */ return FALSE; } message = cockpit_pipe_consume (buffer, 0, buffer->len, 0); relay_data (channel, message); g_bytes_unref (message); return TRUE; }
static void on_pipe_read (CockpitPipe *pipe, GByteArray *input, gboolean end_of_data, gpointer user_data) { CockpitInteractTransport *self = COCKPIT_INTERACT_TRANSPORT (user_data); GBytes *message; GBytes *payload; gchar *channel; guint8 *pos; guint32 size; for (;;) { pos = NULL; if (input->len > 0) pos = (guint8 *)g_strstr_len ((gchar *)input->data, input->len, self->delimiter); if (!pos) { if (!end_of_data) g_debug ("%s: want more data", self->name); break; } size = pos - input->data; message = cockpit_pipe_consume (input, 0, size, self->delimiter_len); payload = cockpit_transport_parse_frame (message, &channel); if (payload) { g_debug ("%s: received a %d byte payload", self->name, (int)size); cockpit_transport_emit_recv ((CockpitTransport *)self, channel, payload); g_bytes_unref (payload); g_free (channel); } g_bytes_unref (message); } }
/** * cockpit_transport_read_from_pipe: * * Meant to be used in a "read" handler for a #CockpitPipe * Closed is pointer to a boolean value that may be updated * during the read and parse loop. */ void cockpit_transport_read_from_pipe (CockpitTransport *self, const gchar *logname, CockpitPipe *pipe, gboolean *closed, GByteArray *input, gboolean end_of_data) { GBytes *message; GBytes *payload; gchar *channel; guint32 i, size; gchar *data; /* This may be updated during the loop. */ g_assert (closed != NULL); g_object_ref (self); while (!*closed) { size = 0; data = (gchar *)input->data; for (i = 0; i < input->len; i++) { /* Check invalid characters, prevent integer overflow, limit max length */ if (i > 7 || data[i] < '0' || data[i] > '9') break; size *= 10; size += data[i] - '0'; } if (i == input->len) { if (!end_of_data) g_debug ("%s: want more data", logname); break; } if (data[i] != '\n') { g_warning ("%s: incorrect protocol: received invalid length prefix", logname); cockpit_pipe_close (pipe, "protocol-error"); break; } if (input->len < i + 1 + size) { g_debug ("%s: want more data 2", logname); break; } message = cockpit_pipe_consume (input, i + 1, size, 0); payload = cockpit_transport_parse_frame (message, &channel); if (payload) { g_debug ("%s: received a %d byte payload", logname, (int)size); cockpit_transport_emit_recv (self, channel, payload); g_bytes_unref (payload); g_free (channel); } g_bytes_unref (message); } if (end_of_data) { /* Received a partial message */ if (input->len > 0) { g_debug ("%s: received truncated %d byte frame", logname, input->len); cockpit_pipe_close (pipe, "disconnected"); } } g_object_unref (self); }
static void on_pipe_read (CockpitPipe *pipe, GByteArray *input, gboolean end_of_data, gpointer user_data) { CockpitPipeTransport *self = COCKPIT_PIPE_TRANSPORT (user_data); GBytes *message; GBytes *payload; gchar *channel; guint32 i, size; gchar *data; g_object_ref (self); for (;;) { size = 0; data = (gchar *)input->data; for (i = 0; i < input->len; i++) { /* Check invalid characters, prevent integer overflow, limit max length */ if (i > 7 || data[i] < '0' || data[i] > '9') break; size *= 10; size += data[i] - '0'; } if (i == input->len) { if (!end_of_data) g_debug ("%s: want more data", self->name); break; } if (data[i] != '\n') { g_warning ("%s: incorrect protocol: received invalid length prefix", self->name); cockpit_pipe_close (pipe, "protocol-error"); break; } if (input->len < i + 1 + size) { g_debug ("%s: want more data 2", self->name); break; } message = cockpit_pipe_consume (input, i + 1, size, 0); payload = cockpit_transport_parse_frame (message, &channel); if (payload) { g_debug ("%s: received a %d byte payload", self->name, (int)size); cockpit_transport_emit_recv ((CockpitTransport *)self, channel, payload); g_bytes_unref (payload); g_free (channel); } g_bytes_unref (message); } if (end_of_data) { /* Received a partial message */ if (input->len > 0) { g_debug ("%s: received truncated %d byte frame", self->name, input->len); cockpit_pipe_close (pipe, "disconnected"); } } g_object_unref (self); }
static gboolean relay_chunked (CockpitHttpStream *self, CockpitChannel *channel, GByteArray *buffer) { GBytes *message; const gchar *data; const gchar *pos; guint64 size; gsize length; gsize beg; gchar *end; data = (const gchar *)buffer->data; length = buffer->len; pos = memchr (data, '\r', length); if (pos == NULL) return FALSE; /* want more data */ beg = (pos + 2) - data; if (length < beg) { /* have to have a least the ending chars */ return FALSE; /* want more data */ } size = g_ascii_strtoull (data, &end, 16); if (pos[1] != '\n' || end != pos) { cockpit_channel_fail (channel, "protocol-error", "%s: received invalid HTTP chunk", self->name); } else if (size > G_MAXSSIZE) { cockpit_channel_fail (channel, "protocol-error", "%s: received extremely large HTTP chunk", self->name); } else if (length < beg + size + 2) { return FALSE; /* want more data */ } else if (data[beg + size] != '\r' || data[beg + size + 1] != '\n') { cockpit_channel_fail (channel, "protocol-error", "%s: received invalid HTTP chunk data", self->name); } else if (size == 0) { /* All done, yay */ g_debug ("%s: received last chunk", self->name); cockpit_pipe_skip (buffer, beg + 2); cockpit_channel_close (channel, NULL); g_assert (self->state == FINISHED); } else { message = cockpit_pipe_consume (buffer, beg, size, 2); relay_data (channel, message); g_bytes_unref (message); return TRUE; } return TRUE; }