* Routines handling protocols with a request/response line, headers,
* a blank line, and an optional body.
*
- * $Id: req_resp_hdrs.c,v 1.2 2003/12/24 09:50:54 guy Exp $
+ * $Id: req_resp_hdrs.c,v 1.5 2004/05/04 06:53:47 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
#include <glib.h>
#include <epan/packet.h>
#include <epan/strutil.h>
+#include <string.h>
#include "req_resp_hdrs.h"
gint next_offset_sav;
gint length_remaining, reported_length_remaining;
int linelen;
+ gchar *header_val;
long int content_length;
gboolean content_length_found = FALSE;
+ gboolean chunked_encoding = FALSE;
/*
* Do header desegmentation if we've been told to.
}
/*
- * Is this a Content-Length header?
- * If not, it either means that we are in
+ * Is this a Content-Length or Transfer-Encoding
+ * header? If not, it either means that we are in
* a different header line, or that we are
* at the end of the headers, or that there
* isn't enough data; the two latter cases
/*
* Check if we've found Content-Length.
*/
- if (tvb_strneql(tvb, next_offset_sav,
+ if (tvb_strncaseeql(tvb, next_offset_sav,
"Content-Length:", 15) == 0) {
- if (sscanf(
- tvb_get_string(tvb,
- next_offset_sav + 15,
- linelen - 15),
+ header_val = tvb_get_string(tvb,
+ next_offset_sav + 15,
+ linelen - 15);
+ if (sscanf(header_val,
"%li", &content_length)
== 1)
content_length_found = TRUE;
+ g_free(header_val);
+ } else if (tvb_strncaseeql(tvb,
+ next_offset_sav,
+ "Transfer-Encoding:", 18) == 0) {
+ /*
+ * Find out if this Transfer-Encoding is
+ * chunked. It should be, since there
+ * really aren't any other types, but
+ * RFC 2616 allows for them.
+ */
+ gchar *p;
+ gint len;
+
+ header_val = tvb_get_string(tvb,
+ next_offset_sav + 18, linelen - 18);
+ p = header_val;
+ len = strlen(header_val);
+ /* Skip white space */
+ while (p < header_val + len &&
+ (*p == ' ' || *p == '\t'))
+ p++;
+ if (p <= header_val + len) {
+ if (strncasecmp(p, "chunked", 7)
+ == 0) {
+ /*
+ * Don't bother looking
+ * for extensions;
+ * since we don't
+ * understand them,
+ * they should be
+ * ignored.
+ */
+ chunked_encoding = TRUE;
+ }
+ }
+ g_free(header_val);
}
}
}
/*
* The above loop ends when we reached the end of the headers, so
- * there should be content_length byte after the 4 terminating bytes
+ * there should be content_length bytes after the 4 terminating bytes
* and next_offset points to after the end of the headers.
*/
- if (desegment_body && content_length_found) {
- /* next_offset has been set because content-length was found */
- if (!tvb_bytes_exist(tvb, next_offset, content_length)) {
- length_remaining = tvb_length_remaining(tvb,
- next_offset);
- reported_length_remaining =
- tvb_reported_length_remaining(tvb, next_offset);
- if (length_remaining < reported_length_remaining) {
+ if (desegment_body) {
+ if (content_length_found) {
+ /* next_offset has been set to the end of the headers */
+ if (!tvb_bytes_exist(tvb, next_offset, content_length)) {
+ length_remaining = tvb_length_remaining(tvb,
+ next_offset);
+ reported_length_remaining =
+ tvb_reported_length_remaining(tvb, next_offset);
+ if (length_remaining < reported_length_remaining) {
+ /*
+ * It's a waste of time asking for more
+ * data, because that data wasn't captured.
+ */
+ return TRUE;
+ }
+ if (length_remaining == -1)
+ length_remaining = 0;
+ pinfo->desegment_offset = offset;
+ pinfo->desegment_len =
+ content_length - length_remaining;
+ return FALSE;
+ }
+ } else if (chunked_encoding) {
+ /*
+ * This data is chunked, so we need to keep pulling
+ * data until we reach the end of the stream, or a
+ * zero sized chunk.
+ *
+ * XXX
+ * This doesn't bother with trailing headers; I don't
+ * think they are really used, and we'd have to use
+ * is_http_request_or_reply() to determine if it was
+ * a trailing header, or the start of a new response.
+ */
+ gboolean done_chunking = FALSE;
+
+ while (!done_chunking) {
+ gint chunk_size = 0;
+ gint chunk_offset = 0;
+ gchar *chunk_string = NULL;
+ gchar *c = NULL;
+
+ length_remaining = tvb_length_remaining(tvb,
+ next_offset);
+ reported_length_remaining =
+ tvb_reported_length_remaining(tvb,
+ next_offset);
+
+ if (reported_length_remaining < 1) {
+ pinfo->desegment_offset = offset;
+ pinfo->desegment_len = 1;
+ return FALSE;
+ }
+
+ linelen = tvb_find_line_end(tvb, next_offset,
+ -1, &chunk_offset, TRUE);
+
+ if (linelen == -1 &&
+ length_remaining >=
+ reported_length_remaining) {
+ pinfo->desegment_offset = offset;
+ pinfo->desegment_len = 2;
+ return FALSE;
+ }
+
+ /* We have a line with the chunk size in it.*/
+ chunk_string = tvb_get_string(tvb, next_offset,
+ linelen);
+ c = chunk_string;
+
/*
- * It's a waste of time asking for more
- * data, because that data wasn't captured.
+ * We don't care about the extensions.
*/
- return TRUE;
+ if ((c = strchr(c, ';'))) {
+ *c = '\0';
+ }
+
+ if ((sscanf(chunk_string, "%x",
+ &chunk_size) < 0) || chunk_size < 0) {
+ /* We couldn't get the chunk size,
+ * so stop trying.
+ */
+ return TRUE;
+ }
+
+ if (chunk_size == 0) {
+ /*
+ * This is the last chunk. Let's pull in the
+ * trailing CRLF.
+ */
+ linelen = tvb_find_line_end(tvb,
+ chunk_offset, -1, &chunk_offset, TRUE);
+
+ if (linelen == -1 &&
+ length_remaining >=
+ reported_length_remaining) {
+ pinfo->desegment_offset = offset;
+ pinfo->desegment_len = 1;
+ return FALSE;
+ }
+
+ pinfo->desegment_offset = chunk_offset;
+ pinfo->desegment_len = 0;
+ done_chunking = TRUE;
+ } else {
+ /*
+ * Skip to the next chunk if we
+ * already have it
+ */
+ if (reported_length_remaining >
+ chunk_size) {
+
+ next_offset = chunk_offset
+ + chunk_size + 2;
+ } else {
+ /*
+ * Fetch this chunk, plus the
+ * trailing CRLF.
+ */
+ pinfo->desegment_offset = offset;
+ pinfo->desegment_len =
+ chunk_size + 1 -
+ reported_length_remaining;
+ return FALSE;
+ }
+ }
+
}
- if (length_remaining == -1)
- length_remaining = 0;
- pinfo->desegment_offset = offset;
- pinfo->desegment_len =
- content_length - length_remaining;
- return FALSE;
}
+
}
/*