static guint z_plug_read_input(ZPlugSession *self, ZStream *input, ZPlugIOBuffer *buf) { GIOStatus rc; z_enter(); rc = z_stream_read(input, buf->buf, self->session_data->buffer_size, &buf->end, NULL); if (rc == G_IO_STATUS_NORMAL) { buf->packet_bytes += buf->end; buf->packet_count++; self->global_packet_count++; if (self->session_data->packet_stats_interval_packet && (self->global_packet_count % self->session_data->packet_stats_interval_packet) == 0) { if (!self->session_data->packet_stats(self, self->buffers[EP_CLIENT].packet_bytes, self->buffers[EP_CLIENT].packet_count, self->buffers[EP_SERVER].packet_bytes, self->buffers[EP_SERVER].packet_count, self->user_data)) { z_plug_update_eof_mask(self, EOF_ALL); rc = G_IO_STATUS_EOF; } } } z_return(rc); }
/** * telnet_stream_read: * @self: * @buf: * @ep: * * * * Returns: * */ static GIOStatus telnet_stream_read(TelnetProxy *self, ZIOBuffer *buf, guint ep) { GIOStatus res; gsize len; z_proxy_enter(self); len = 0; res = z_stream_read(self->super.endpoints[ep], buf->buf + buf->end, sizeof(buf->buf) - buf->end, &len, NULL); buf->end += len; switch (res) { case G_IO_STATUS_NORMAL: z_proxy_return(self, res); case G_IO_STATUS_EOF: z_proxy_return(self, res); case G_IO_STATUS_AGAIN: z_proxy_return(self, res); default: break; } z_proxy_return(self, G_IO_STATUS_ERROR); }
/** * z_transfer2_read_source: * @self: ZTransfer2 instance * @endpoint: endpoint to fetch data from * @buf: store fetched information into this buffer * @error: error details are stored here * * This function is called to fetch data from the specified endpoint. When * it is a proxy-connected stream then the proxy provided callbacks are * used to fetch information, otherwise z_stream_read is called directly. **/ static GIOStatus z_transfer2_read_source(ZTransfer2 *self, gint endpoint, ZTransfer2Buffer *buf, GError **error) { ZStream *from = z_transfer2_get_stream(self, endpoint); GIOStatus res = G_IO_STATUS_NORMAL; GError *local_error = NULL; gsize read_len; z_proxy_enter(self->owner); if (endpoint & ZT2E_STACKED) { res = z_stream_read(from, &buf->buf[buf->end], buf->size - buf->end, &read_len, &local_error); } else if (endpoint == ZT2E_SOURCE) { res = z_transfer2_src_read(self, self->endpoints[endpoint], &buf->buf[buf->end], buf->size - buf->end, &read_len, &local_error); } if (res == G_IO_STATUS_NORMAL) { buf->end += read_len; } if (local_error) g_propagate_error(error, local_error); z_proxy_leave(self->owner); return res; }
static GIOStatus ftp_transfer_src_read(ZTransfer2 *s, ZStream *stream, gchar *buf, gsize count, gsize *bytes_read, GError **err) { FtpProxy *owner = (FtpProxy *) s->owner; GIOStatus res; z_proxy_enter(owner); res = z_stream_read(stream, buf, count, bytes_read, err); z_proxy_return(owner, res); }
static gboolean telnet_read(TelnetProxy *self, ZStream *stream, ZEndpoint ep) { gboolean res = FALSE; z_proxy_enter(self); ZPktBuf *buf = z_pktbuf_new(); z_pktbuf_resize(buf, TELNET_BUFFER_SIZE); gsize bytes_read = 0; GIOStatus status = z_stream_read(stream, buf->data, buf->allocated, &bytes_read, NULL); if (status == G_IO_STATUS_ERROR || status == G_IO_STATUS_EOF) { /* error already logged */ z_poll_quit(self->poll); return FALSE; } else if (status == G_IO_STATUS_AGAIN) { return TRUE; } buf->length += bytes_read; telnet_protocol_process_data(&self->protocol[ep], buf); z_pktbuf_unref(buf); res = telnet_protocol_is_running(&self->protocol[ep]); if (!res) z_poll_quit(self->poll); z_proxy_return(self, res); }
/** * Write data read from a stream into a blob. * * @param[in] self this * @param[in] pos position to write to * @param[in] stream stream to read from * @param[in] count length of data to read * @param[in] timeout timeout * @param[out] error stream error if return value is G_IO_STATUS_ERROR * * Write some data into the given position of the blob, expanding it if * necessary. The function takes multiple passes and supports copying gint64 * chunks and ensures that all the requested data be copied unless an error * occurs, thus there is no bytes_read argument. * * @returns GLib I/O status **/ GIOStatus z_blob_read_from_stream(ZBlob *self, gint64 pos, ZStream *stream, gint64 count, gint timeout, GError **error) { off_t err; GIOStatus res = G_IO_STATUS_NORMAL; guchar *copybuf; GError *local_error = NULL; gsize left; z_enter(); g_assert(self); g_assert(pos >= 0); g_return_val_if_fail((error == NULL) || (*error == NULL), G_IO_STATUS_ERROR); if (z_blob_lock(self, timeout)) { if (self->is_in_file) { if (self->size < pos) z_blob_alloc(self, pos); err = lseek(self->fd, pos, SEEK_SET); if (err < 0) { z_log(NULL, CORE_ERROR, 0, "Blob error, lseek() failed; file='%s', error='%s'", self->filename, g_strerror(errno)); g_assert(0); } copybuf = g_new(guchar, Z_BLOB_COPY_BUFSIZE); left = count; while (left != 0) { gsize br; gssize bw; gsize bytes; gssize remain; bytes = MIN(left, Z_BLOB_COPY_BUFSIZE); if (self->alloc_size < (pos + (gssize) bytes)) z_blob_alloc(self, pos + bytes); res = z_stream_read(stream, copybuf, bytes, &br, &local_error); if (res != G_IO_STATUS_NORMAL) goto exit_stats; left -= br; pos += br; if (self->size < pos) self->size = pos; remain = br; while (remain > 0) { bw = write(self->fd, copybuf, remain); if (bw < 0) { if (errno == EINTR) { continue; } else { z_log(NULL, CORE_ERROR, 0, "Blob error, write() failed; file='%s', error='%s'", self->filename, g_strerror(errno)); g_assert(0); } } remain -= bw; } } g_free(copybuf); } else { left = count; while (left != 0) { gsize br; gsize bytes; bytes = MIN(left, Z_BLOB_COPY_BUFSIZE); if (self->alloc_size < (pos + (gssize) bytes)) z_blob_alloc(self, pos + count); res = z_stream_read(stream, self->data + pos, bytes, &br, &local_error); if (res != G_IO_STATUS_NORMAL) goto exit_stats; left -= br; pos += br; if (self->size < pos) self->size = pos; } } exit_stats: self->stat.req_wr++; self->stat.total_wr += count; self->stat.last_accessed = time(NULL); z_blob_unlock(self); } else { /* FIXME: how to map this error ? */ res = G_IO_STATUS_ERROR; g_set_error(error, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_FAILED, "Error acquiring blob lock"); } if (local_error) g_propagate_error(error, local_error); z_return(res); }
static GIOStatus http_transfer_src_read(ZTransfer2 *s, ZStream *stream, gchar *buf, gsize count, gsize *bytes_read, GError **err) { HttpTransfer *self = Z_CAST(s, HttpTransfer); HttpProxy *owner = Z_CAST(s->owner, HttpProxy); GError *local_error = NULL; gsize br; GIOStatus res = G_IO_STATUS_NORMAL; if (self->src_read_state == HTTP_SR_INITIAL) self->src_read_state = HTTP_SR_PUSH_HEADERS; if (self->src_read_state >= HTTP_SR_PUSH_HEADERS && self->src_read_state <= HTTP_SR_PUSH_HEADERS_MAX) { if (self->push_mime_headers && self->stacked_preamble->len > 0) { gint move; *bytes_read = 0; move = MIN(count, self->stacked_preamble->len - self->stacked_preamble_ofs); memmove(buf, self->stacked_preamble->str + self->stacked_preamble_ofs, move); self->stacked_preamble_ofs += move; *bytes_read = move; if (self->stacked_preamble_ofs == self->stacked_preamble->len) { z_transfer2_set_proxy_out(s, FALSE); self->src_read_state = HTTP_SR_READ_INITIAL; } return G_IO_STATUS_NORMAL; } else { self->src_read_state = HTTP_SR_READ_INITIAL; } } *bytes_read = 0; if (self->src_chunked) { /* read as a chunked stream */ switch (self->src_read_state) { case HTTP_SR_READ_INITIAL: self->src_whole_length = 0; self->src_read_state = HTTP_SR_READ_CHUNK_LENGTH; z_stream_line_set_poll_partial(stream, FALSE); /* fallthrough */ case HTTP_SR_READ_CHUNK_LENGTH: { gchar line_buf[32], *line; gsize line_length; guint32 chunk_length; gchar *end; res = z_stream_line_get(stream, &line, &line_length, NULL); if (res == G_IO_STATUS_NORMAL) { /* a complete line was read, check if it is a valid chunk length */ if (line_length >= sizeof(line_buf) - 1) { /*LOG This message indicates that the chunk length line is too long. It is likely caused by a buggy client or server. */ z_proxy_log(self->super.owner, HTTP_VIOLATION, 1, "Chunk length line too long; line='%.*s'", (gint) line_length, line); res = G_IO_STATUS_ERROR; break; } /* we already checked that line_buf is large enough */ memcpy(line_buf, line, line_length); line_buf[line_length] = 0; chunk_length = strtoul(line_buf, &end, 16); if (end == line_buf) { /* hmm... invalid chunk length */ /*LOG This message indicates that the chunk length is invalid. It is likely caused by a buggy client or server. */ z_proxy_log(self->super.owner, HTTP_VIOLATION, 1, "Invalid chunk length; line='%s'", line_buf); res = G_IO_STATUS_ERROR; break; } /* * NOTE: the string pointed by end is NUL terminated, thus * we will not overflow our buffer */ while (*end == ' ') end++; if (*end == ';') { /* ignore and strip chunk extensions */ *end = 0; } if (*end) { /* hmm... invalid chunk length */ /*LOG This message indicates that the chunk length is invalid. It is likely caused by a buggy client or server. */ z_proxy_log(self->super.owner, HTTP_VIOLATION, 1, "Invalid chunk length; line='%s'", line_buf); res = G_IO_STATUS_ERROR; break; } if ((owner->max_chunk_length && chunk_length > owner->max_chunk_length) || (chunk_length & 0x80000000)) { /*LOG This message indicates that the length of the chunk is larger than allowed or is a negative number. Check the 'max_chunk_length' attribute. */ z_proxy_log(self->super.owner, HTTP_POLICY, 2, "Chunk too large; length='%d', max_chunk_length='%d'", chunk_length, owner->max_chunk_length); res = G_IO_STATUS_ERROR; break; } if (owner->max_body_length && (guint) self->src_whole_length + chunk_length > owner->max_body_length) { /* this chunk would be over body_length limit */ chunk_length = owner->max_body_length - self->src_whole_length; self->src_chunk_left = chunk_length; self->force_nonpersistent_mode = TRUE; self->src_chunk_truncated = TRUE; } self->src_chunk_left = chunk_length; self->src_last_chunk = chunk_length == 0; self->src_read_state = HTTP_SR_READ_CHUNK; z_stream_line_set_poll_partial(stream, TRUE); /* fall through */ } else break; } case HTTP_SR_READ_CHUNK: if (!self->src_last_chunk) { res = z_stream_read(stream, buf, MIN(self->src_chunk_left, count), &br, &local_error); if (res == G_IO_STATUS_NORMAL) { self->src_whole_length += br; self->src_chunk_left -= br; *bytes_read = br; } else if (res == G_IO_STATUS_EOF) { /* unexpected eof */ /*LOG This message indicates that Zorp unexpectedly got EOF during chunk encoded data transfer. It is likely a caused by a buggy client or server. */ z_proxy_log(self->super.owner, HTTP_VIOLATION, 1, "Unexpected EOF while dechunking stream;"); res = G_IO_STATUS_ERROR; break; } if (self->src_chunk_left == 0) { self->src_read_state = HTTP_SR_READ_FOOTER; z_stream_line_set_poll_partial(stream, FALSE); } break; } else { self->src_read_state = HTTP_SR_READ_FOOTER; z_stream_line_set_poll_partial(stream, FALSE); /* fallthrough */ } case HTTP_SR_READ_FOOTER: { gchar *line; gsize line_length; if (!self->src_chunk_truncated) { res = z_stream_line_get(stream, &line, &line_length, NULL); } else { res = G_IO_STATUS_EOF; } if (res == G_IO_STATUS_NORMAL) { if (line_length != 0) { /*LOG This message indicates that the chunk footer contains data. It is likely caused by a buggy client or server. */ z_proxy_log(self->super.owner, HTTP_VIOLATION, 1, "Chunk footer is not an empty line;"); res = G_IO_STATUS_ERROR; break; } if (self->src_last_chunk) { res = G_IO_STATUS_EOF; } else { self->src_read_state = HTTP_SR_READ_CHUNK_LENGTH; z_stream_line_set_poll_partial(stream, TRUE); /* come back later */ res = G_IO_STATUS_AGAIN; } break; } break; } } } else { /* copy until EOF or self->content_length bytes */ if (self->content_length == HTTP_LENGTH_NONE) { res = G_IO_STATUS_EOF; } else { if (self->src_read_state == HTTP_SR_INITIAL) { self->src_whole_length = 0; self->src_read_state = HTTP_SR_READ_ENTITY; } if (self->content_length == HTTP_LENGTH_UNKNOWN) { if (owner->max_body_length && self->src_whole_length + count >= owner->max_body_length) { count = owner->max_body_length - self->src_whole_length; } if (count == 0) { self->force_nonpersistent_mode = TRUE; res = G_IO_STATUS_EOF; } else res = z_stream_read(stream, buf, count, &br, &local_error); } else { /* for specified content-length, max_body_length has already been processed, and content_length contains the number of bytes to be transferred, but maximum max_body_length */ if (self->content_length >= 0 && (guint64) self->content_length == self->src_whole_length) res = G_IO_STATUS_EOF; else res = z_stream_read(stream, buf, MIN(count, self->content_length - self->src_whole_length), &br, &local_error); } if (res == G_IO_STATUS_NORMAL) { self->src_whole_length += br; *bytes_read = br; } } } if (local_error) g_propagate_error(err, local_error); return res; }