Beispiel #1
0
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);
}
Beispiel #2
0
/**
 * 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);
}
Beispiel #3
0
/**
 * 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;
}
Beispiel #4
0
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);
}
Beispiel #5
0
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);
}
Beispiel #6
0
/**
 * 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);
}
Beispiel #7
0
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;
}