Exemple #1
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;
}
Exemple #2
0
/**
 * http_ftp_fetch_response:
 * @self: HttpProxy instance
 * @status: returned FTP status code
 * @msg: returned FTP message
 * @msglen: size of the @msg buffer
 *
 * This function is called to fetch a response from the FTP server. Line
 * continuations are supported however only the first line will be returned
 * in @msg.
 **/
static gboolean
http_ftp_fetch_response(HttpProxy *self, gint *status, gchar *msg, gsize msglen)
{
  gchar *line;
  gsize length;
  gboolean continuation = TRUE, first = TRUE;
  gint mul, value, i;
  
  msg[0] = 0;
  while (continuation)
    {
      if (z_stream_line_get(self->super.endpoints[EP_SERVER], &line, &length, NULL) != G_IO_STATUS_NORMAL)
        return FALSE;
      if (length < 4)
        {
          /*LOG
            This message indicates that the response given by the FTP server
            was too short, not even the mandatory status code was included.
           */
          z_proxy_log(self, HTTP_VIOLATION, 2, "Invalid FTP response, line too short; line='%.*s'", (gint)length, line);
          return FALSE;
        }
      value = 0;
      mul = 100;
      for (i = 0; i < 3; i++)
        {
          if (!isdigit(line[i]))
            {
              /*LOG
                This message indicates that the FTP server gave an invalid
                response, the status code returned by the server was not
                numeric.
               */
              z_proxy_log(self, HTTP_VIOLATION, 2, "Invalid FTP response, response code not numeric; line='%.*s'", (gint)length, line);
              return FALSE;
            }
          value = value + mul * (line[i] - '0');
          mul = mul / 10;
        }
      if (first)
        {
          gint copy = MIN(msglen-1, length - 4 + 1);
          
          *status = value;
          memcpy(msg, &line[4], copy);
          msg[copy] = 0;
        }
      else if (*status != value)
        {
          /*LOG
            This message indicates that the FTP server gave an invalid response as the
            status code changed from the one which was present on the first line.
           */
          z_proxy_log(self, HTTP_VIOLATION, 2, "Invalid FTP response, continuation line contains different status code; ftp_status='%d', line='%.*s'", *status, (gint)length, line);
          return FALSE;
        }
      continuation = line[3] == '-';
      
    }
  return TRUE;
}
Exemple #3
0
int main(void)
{
  ZStream *input;
  int pair[2], status = 0;
  pid_t pid;
  gchar *line;
  gsize length;
  
  if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) < 0)
    {
      perror("socketpair()");
      return 254;
    }
  
  pid = fork();
  if (pid < 0)
    {
      perror("fork()");
      return 254;
    }
  else if (pid != 0) 
    {
      close(pair[1]);
      g_return_val_if_fail(write(pair[0], "1\n2\n3\n", 6) == 6, 253);
      g_return_val_if_fail(write(pair[0], "0123", 4) == 4, 253);
      g_return_val_if_fail(write(pair[0], "4567\n", 5) == 5, 253);
      g_return_val_if_fail(write(pair[0], "0123456789", 10) == 10, 253);
      g_return_val_if_fail(write(pair[0], "abc\nAabc\nabcdef\n", 16) == 16, 253); /* Because of truncate A will be eliminated */
      g_return_val_if_fail(write(pair[0], "0123456789", 10) == 10, 253);
      g_return_val_if_fail(write(pair[0], "0123456789", 10) == 10, 253);
      g_return_val_if_fail(write(pair[0], "012345678\nAabcdef\n", 18) == 18, 253); /* Because of truncate A will be eliminated */
      g_return_val_if_fail(write(pair[0], "012345678\nAabcdef", 17) == 17, 253);
      close(pair[0]);
      waitpid(pid, &status, 0);
    }
  else
    {
      gint rc;
      guint i;
      
      printf("%d\n", getpid());
      sleep(1);  
      close(pair[0]);  
      input = z_stream_fd_new(pair[1], "sockpair input");
      input = z_stream_line_new(input, 10, ZRL_EOL_NL | ZRL_TRUNCATE);
      
      i = 0;

      if (!z_stream_unget(input, "A", 1, NULL))
        {
          printf("Error on unget\n");
          _exit(1);
        }
      rc = z_stream_line_get(input, &line, &length, NULL);
      while (rc == G_IO_STATUS_NORMAL)
        {
          if (i >= (sizeof(expected_outputs) / sizeof(gchar *)) ||
              line[0] != 'A' ||
              strlen(expected_outputs[i]) != length - 1 ||
              memcmp(expected_outputs[i], line + 1, length - 1) != 0)
            {
              printf("Error checking line: [%.*s] (length: %"G_GSIZE_FORMAT"), should be: %s\n", (int) length - 1, line + 1, length - 1, expected_outputs[i]);
              _exit(1);
            }
          else
            {
              printf("line ok: %.*s\n", (int) length, line);
            }
          if (!z_stream_unget(input, "A", 1, NULL))
            {
              printf("Error on unget\n");
              _exit(1);
            }
          rc = z_stream_line_get(input, &line, &length, NULL);
          i++;
        }
      if (i < (sizeof(expected_outputs) / sizeof(gchar *)))
        {
          printf("Missing output %u of %"G_GSIZE_FORMAT"\n", i, (sizeof(expected_outputs) / sizeof(gchar *)));
          _exit(1);
        }
      close(pair[1]);
      _exit(0);
    }
  return status >> 8;
}